From 1adf22303bdae7c73cf8ed37ecdcca28a88aa49d Mon Sep 17 00:00:00 2001 From: Alwin Antreich Date: Wed, 5 Sep 2018 10:02:44 +0200 Subject: [PATCH] update sources to 12.2.8 Signed-off-by: Alwin Antreich --- Makefile | 2 +- ceph/CMakeLists.txt | 9 +- ceph/README.alpine.md | 4 +- ceph/alpine/APKBUILD | 7 +- ceph/alpine/APKBUILD.in | 1 - ceph/ceph.spec | 11 +- ceph/ceph.spec.in | 5 +- ceph/cmake/modules/MergeStaticLibraries.cmake | 85 ---- ceph/debian/changelog | 6 + ceph/debian/control | 1 + ceph/debian/rules | 2 +- ceph/do_freebsd.sh | 1 - ceph/doc/ceph-volume/index.rst | 3 +- ceph/doc/ceph-volume/lvm/batch.rst | 139 ++++++ ceph/doc/man/8/ceph-bluestore-tool.rst | 14 +- ceph/doc/man/8/ceph-volume.rst | 31 +- .../configuration/filestore-config-ref.rst | 2 +- .../rados/configuration/osd-config-ref.rst | 208 ++++---- ceph/doc/radosgw/s3/bucketops.rst | 25 +- ceph/doc/radosgw/swift/containerops.rst | 7 + ceph/examples/librados/Makefile | 2 +- ceph/install-deps.sh | 2 +- ceph/qa/run-standalone.sh | 9 + ceph/qa/standalone/ceph-helpers.sh | 33 +- ceph/qa/standalone/mon/osd-pool-create.sh | 1 - ceph/qa/standalone/osd/osd-backfill-stats.sh | 2 +- ceph/qa/standalone/osd/osd-recovery-stats.sh | 2 +- ceph/qa/standalone/osd/repro_long_log.sh | 6 +- .../qa/standalone/scrub/osd-scrub-distrust.sh | 346 +++++++++++++ ceph/qa/standalone/scrub/osd-scrub-repair.sh | 369 +++++++++++++- ceph/qa/standalone/scrub/osd-scrub-snaps.sh | 6 +- .../standalone/scrub/osd-unexpected-clone.sh | 88 ++++ ceph/qa/suites/krbd/rbd-nomount/conf.yaml | 2 + ceph/qa/suites/krbd/rbd/conf.yaml | 2 + ceph/qa/suites/krbd/singleton/conf.yaml | 2 + ceph/qa/suites/krbd/thrash/conf.yaml | 2 + ceph/qa/suites/krbd/wac/sysfs/conf.yaml | 2 + ceph/qa/suites/krbd/wac/wac/conf.yaml | 2 + .../tasks/cfuse_workunit_suites_fsync.yaml | 6 + .../powercycle/osd/whitelist_health.yaml | 1 + .../jewel-x/ceph-deploy/slow_requests.yaml | 1 + .../jewel-x/parallel/slow_requests.yaml | 4 + .../slow_requests.yaml | 1 + .../jewel-x/stress-split/slow_requests.yaml | 1 + .../kraken-x/parallel/0-cluster/start.yaml | 2 + .../stress-split/0-cluster/start.yaml | 2 + .../luminous-p2p/point-to-point-upgrade.yaml | 19 +- ceph/qa/suites/upgrade/luminous-x/parallel/% | 0 .../upgrade/luminous-x/parallel/0-cluster/+ | 0 .../parallel/0-cluster/openstack.yaml | 4 - .../luminous-x/parallel/0-cluster/start.yaml | 40 -- .../parallel/1-ceph-install/luminous.yaml | 43 -- .../upgrade/luminous-x/parallel/2-workload/+ | 0 .../parallel/2-workload/blogbench.yaml | 14 - .../parallel/2-workload/ec-rados-default.yaml | 24 - .../parallel/2-workload/rados_api.yaml | 11 - .../parallel/2-workload/rados_loadgenbig.yaml | 11 - .../parallel/2-workload/test_rbd_api.yaml | 11 - .../parallel/2-workload/test_rbd_python.yaml | 11 - .../3-upgrade-sequence/upgrade-all.yaml | 16 - .../upgrade-mon-osd-mds.yaml | 35 -- .../luminous-x/parallel/5-final-workload/+ | 0 .../parallel/5-final-workload/blogbench.yaml | 13 - .../rados-snaps-few-objects.yaml | 17 - .../5-final-workload/rados_loadgenmix.yaml | 9 - .../5-final-workload/rados_mon_thrash.yaml | 18 - .../parallel/5-final-workload/rbd_cls.yaml | 9 - .../rbd_import_export_no_upgrated.yaml | 13 - .../rbd_import_export_upgrated.yaml | 12 - .../parallel/5-final-workload/rgw_swift.yaml | 13 - .../upgrade/luminous-x/parallel/distros | 1 - .../upgrade/luminous-x/parallel/objectstore | 1 - .../luminous-x/stress-split-erasure-code/% | 0 .../stress-split-erasure-code/0-cluster | 1 - .../stress-split-erasure-code/1-ceph-install | 1 - .../2-partial-upgrade | 1 - .../3-thrash/default.yaml | 25 - .../4-ec-workload.yaml | 22 - .../5-finish-upgrade.yaml | 1 - .../7-final-workload.yaml | 35 -- .../stress-split-erasure-code/distros | 1 - .../stress-split-erasure-code/objectstore | 1 - .../thrashosds-health.yaml | 1 - .../suites/upgrade/luminous-x/stress-split/% | 0 .../luminous-x/stress-split/0-cluster/+ | 0 .../stress-split/0-cluster/openstack.yaml | 6 - .../stress-split/0-cluster/start.yaml | 29 -- .../stress-split/1-ceph-install/luminous.yaml | 17 - .../2-partial-upgrade/firsthalf.yaml | 12 - .../stress-split/3-thrash/default.yaml | 25 - .../luminous-x/stress-split/4-workload/+ | 0 .../stress-split/4-workload/radosbench.yaml | 40 -- .../stress-split/4-workload/rbd-cls.yaml | 10 - .../4-workload/rbd-import-export.yaml | 12 - .../stress-split/4-workload/rbd_api.yaml | 10 - .../stress-split/4-workload/readwrite.yaml | 16 - .../4-workload/snaps-few-objects.yaml | 18 - .../stress-split/5-finish-upgrade.yaml | 9 - .../stress-split/7-final-workload/+ | 0 .../7-final-workload/rbd-python.yaml | 10 - .../7-final-workload/rgw-swift.yaml | 11 - .../7-final-workload/snaps-many-objects.yaml | 16 - .../upgrade/luminous-x/stress-split/distros | 1 - .../stress-split/objectstore/bluestore.yaml | 1 - .../objectstore/filestore-xfs.yaml | 1 - .../stress-split/thrashosds-health.yaml | 1 - ceph/qa/tasks/ceph_fuse.py | 10 + ceph/qa/tasks/cephfs/filesystem.py | 6 + ceph/qa/tasks/cephfs/test_failover.py | 53 ++ ceph/qa/tasks/cephfs/test_forward_scrub.py | 9 +- ceph/qa/tasks/cephfs/test_scrub.py | 21 +- ceph/qa/tasks/thrashosds-health.yaml | 1 + ceph/qa/tasks/vstart_runner.py | 8 + ceph/qa/workunits/cephtool/test.sh | 72 ++- ceph/qa/workunits/rbd/cli_generic.sh | 36 +- ceph/qa/workunits/suites/blogbench.sh | 2 +- ceph/qa/workunits/suites/ffsb.patch | 12 + ceph/qa/workunits/suites/ffsb.sh | 3 +- ceph/qa/workunits/suites/iogen.sh | 2 +- ceph/qa/workunits/suites/pjd.sh | 2 +- ceph/src/.git_version | 4 +- ceph/src/CMakeLists.txt | 4 - ceph/src/ceph-volume/ceph_volume/__init__.py | 6 + ceph/src/ceph-volume/ceph_volume/api/lvm.py | 325 +++++++++++- .../ceph_volume/devices/lvm/activate.py | 18 +- .../ceph_volume/devices/lvm/batch.py | 221 +++++++++ .../ceph_volume/devices/lvm/listing.py | 11 +- .../ceph_volume/devices/lvm/main.py | 2 + .../ceph_volume/devices/lvm/prepare.py | 8 +- .../devices/lvm/strategies/__init__.py | 1 + .../devices/lvm/strategies/bluestore.py | 248 ++++++++++ .../devices/lvm/strategies/filestore.py | 318 ++++++++++++ .../devices/lvm/strategies/validators.py | 48 ++ .../ceph_volume/devices/lvm/zap.py | 140 +++--- .../src/ceph-volume/ceph_volume/exceptions.py | 13 + ceph/src/ceph-volume/ceph_volume/main.py | 5 +- ceph/src/ceph-volume/ceph_volume/process.py | 9 +- .../ceph_volume/systemd/systemctl.py | 9 +- .../ceph_volume/tests/api/test_lvm.py | 283 ++++++++++- .../ceph-volume/ceph_volume/tests/conftest.py | 26 +- .../tests/devices/lvm/test_listing.py | 20 + .../ceph_volume/tests/devices/test_zap.py | 13 +- .../bluestore/single-type-dmcrypt/Vagrantfile | 1 + .../single-type-dmcrypt/group_vars/all | 23 + .../bluestore/single-type-dmcrypt/hosts | 8 + .../bluestore/single-type-dmcrypt/test.yml | 1 + .../single-type-dmcrypt/vagrant_variables.yml | 56 +++ .../centos7/bluestore/single-type/Vagrantfile | 1 + .../bluestore/single-type/group_vars/all | 22 + .../batch/centos7/bluestore/single-type/hosts | 8 + .../centos7/bluestore/single-type/test.yml | 1 + .../single-type/vagrant_variables.yml | 56 +++ .../filestore/single-type-dmcrypt/Vagrantfile | 1 + .../single-type-dmcrypt/group_vars/all | 25 + .../filestore/single-type-dmcrypt/hosts | 8 + .../filestore/single-type-dmcrypt/test.yml | 1 + .../single-type-dmcrypt/vagrant_variables.yml | 56 +++ .../centos7/filestore/single-type/Vagrantfile | 1 + .../filestore/single-type/group_vars/all | 22 + .../batch/centos7/filestore/single-type/hosts | 8 + .../centos7/filestore/single-type/test.yml | 1 + .../single-type/vagrant_variables.yml | 56 +++ .../batch/playbooks/test_bluestore.yml | 46 ++ .../playbooks/test_bluestore_dmcrypt.yml | 46 ++ .../batch/playbooks/test_filestore.yml | 46 ++ .../playbooks/test_filestore_dmcrypt.yml | 46 ++ .../tests/functional/batch/tox.ini | 63 +++ .../batch/xenial/bluestore/.DS_Store | Bin 0 -> 6148 bytes .../bluestore/single-type-dmcrypt/Vagrantfile | 1 + .../single-type-dmcrypt/group_vars/all | 23 + .../bluestore/single-type-dmcrypt/hosts | 8 + .../bluestore/single-type-dmcrypt/test.yml | 1 + .../single-type-dmcrypt/vagrant_variables.yml | 56 +++ .../xenial/bluestore/single-type/Vagrantfile | 1 + .../bluestore/single-type/group_vars/all | 22 + .../batch/xenial/bluestore/single-type/hosts | 8 + .../xenial/bluestore/single-type/test.yml | 1 + .../single-type/vagrant_variables.yml | 56 +++ .../filestore/single-type-dmcrypt/Vagrantfile | 1 + .../single-type-dmcrypt/group_vars/all | 25 + .../filestore/single-type-dmcrypt/hosts | 8 + .../filestore/single-type-dmcrypt/test.yml | 1 + .../single-type-dmcrypt/vagrant_variables.yml | 56 +++ .../xenial/filestore/single-type/Vagrantfile | 1 + .../filestore/single-type/group_vars/all | 22 + .../batch/xenial/filestore/single-type/hosts | 8 + .../xenial/filestore/single-type/test.yml | 1 + .../single-type/vagrant_variables.yml | 56 +++ .../lvm/centos7/bluestore/create/hosts | 3 + .../lvm/centos7/bluestore/dmcrypt/hosts | 3 + .../lvm/centos7/bluestore/dmcrypt/test.yml | 5 + .../lvm/centos7/filestore/create/hosts | 3 + .../lvm/centos7/filestore/dmcrypt/hosts | 3 + .../lvm/centos7/filestore/dmcrypt/test.yml | 5 + .../lvm/playbooks/test_bluestore.yml | 5 + .../lvm/playbooks/test_filestore.yml | 5 + .../ceph_volume/tests/functional/lvm/tox.ini | 12 +- .../lvm/xenial/bluestore/create/hosts | 3 + .../lvm/xenial/bluestore/dmcrypt/hosts | 3 + .../lvm/xenial/bluestore/dmcrypt/test.yml | 5 + .../lvm/xenial/filestore/create/hosts | 3 + .../lvm/xenial/filestore/dmcrypt/hosts | 3 + .../lvm/xenial/filestore/dmcrypt/test.yml | 5 + .../tests/functional/playbooks/deploy.yml | 123 +++++ .../tests/functional/scripts/vagrant_up.sh | 12 + .../tests/functional/simple/tox.ini | 11 +- .../ceph_volume/tests/test_process.py | 18 + .../tests/util/test_arg_validators.py | 14 + .../ceph_volume/tests/util/test_device.py | 66 +++ .../ceph_volume/tests/util/test_disk.py | 463 ++++++++++++++++++ .../ceph_volume/tests/util/test_encryption.py | 16 + .../ceph_volume/tests/util/test_prepare.py | 119 ++++- .../ceph_volume/tests/util/test_system.py | 24 + .../ceph_volume/tests/util/test_util.py | 96 ++++ .../ceph-volume/ceph_volume/util/__init__.py | 75 +++ .../ceph_volume/util/arg_validators.py | 13 + .../ceph-volume/ceph_volume/util/device.py | 98 ++++ ceph/src/ceph-volume/ceph_volume/util/disk.py | 435 ++++++++++++++++ .../ceph_volume/util/encryption.py | 4 + .../ceph-volume/ceph_volume/util/prepare.py | 68 ++- .../ceph-volume/ceph_volume/util/system.py | 22 + .../ceph-volume/ceph_volume/util/templates.py | 25 + ceph/src/ceph_mds.cc | 4 - ceph/src/ceph_mon.cc | 11 +- ceph/src/ceph_osd.cc | 15 - ceph/src/client/Client.cc | 415 ++++++++++------ ceph/src/client/Client.h | 14 +- ceph/src/client/Inode.h | 1 - ceph/src/cls/CMakeLists.txt | 26 - ceph/src/cls/rgw/cls_rgw.cc | 47 +- ceph/src/cls/rgw/cls_rgw_ops.h | 3 +- ceph/src/cls/rgw/cls_rgw_types.cc | 16 +- ceph/src/common/DecayCounter.cc | 1 + ceph/src/common/TrackedOp.cc | 4 + ceph/src/common/TrackedOp.h | 4 + ceph/src/common/config.cc | 11 + ceph/src/common/config.h | 3 + ceph/src/common/hobject.h | 12 +- ceph/src/common/legacy_config_opts.h | 3 + ceph/src/common/options.cc | 30 +- ceph/src/common/perf_counters.cc | 25 +- ceph/src/common/perf_counters.h | 24 +- ceph/src/common/scrub_types.h | 6 + ceph/src/common/strtol.cc | 269 +++++++--- ceph/src/common/strtol.h | 5 + ceph/src/common/util.cc | 67 --- ceph/src/compressor/CMakeLists.txt | 15 - ceph/src/compressor/lz4/CMakeLists.txt | 5 - .../compressor/lz4/CompressionPluginLZ4.cc | 4 - ceph/src/compressor/snappy/CMakeLists.txt | 5 - .../snappy/CompressionPluginSnappy.cc | 4 - ceph/src/compressor/zlib/CMakeLists.txt | 6 - .../compressor/zlib/CompressionPluginZlib.cc | 3 - ceph/src/compressor/zstd/CMakeLists.txt | 5 - .../compressor/zstd/CompressionPluginZstd.cc | 4 - ceph/src/erasure-code/CMakeLists.txt | 8 - ceph/src/erasure-code/isa/CMakeLists.txt | 5 - .../erasure-code/isa/ErasureCodePluginIsa.cc | 4 - ceph/src/erasure-code/jerasure/CMakeLists.txt | 8 - .../jerasure/ErasureCodePluginJerasure.cc | 4 - ceph/src/erasure-code/lrc/CMakeLists.txt | 5 - .../erasure-code/lrc/ErasureCodePluginLrc.cc | 4 - ceph/src/erasure-code/shec/CMakeLists.txt | 6 - .../shec/ErasureCodePluginShec.cc | 4 - ceph/src/global/global_init.cc | 47 +- ceph/src/include/ceph_fs.h | 2 + ceph/src/include/cephd/libcephd.h | 108 ---- ceph/src/include/config-h.in.cmake | 3 - ceph/src/include/rados/objclass.h | 7 - ceph/src/include/types.h | 143 +++--- ceph/src/include/util.h | 2 - ceph/src/key_value_store/CMakeLists.txt | 5 - ceph/src/kv/RocksDBStore.cc | 8 +- ceph/src/libcephd/CMakeLists.txt | 51 -- ceph/src/libcephd/libcephd.cc | 262 ---------- ceph/src/libcephfs.cc | 36 +- ceph/src/librados/CMakeLists.txt | 5 - ceph/src/librados/IoCtxImpl.cc | 8 +- ceph/src/librbd/CMakeLists.txt | 5 - ceph/src/librbd/ImageCtx.cc | 12 +- ceph/src/librbd/image/RemoveRequest.cc | 8 + ceph/src/librbd/object_map/RefreshRequest.cc | 4 +- ceph/src/mds/Beacon.cc | 2 +- ceph/src/mds/CDir.cc | 12 +- ceph/src/mds/CDir.h | 9 + ceph/src/mds/CInode.cc | 20 +- ceph/src/mds/CInode.h | 7 + ceph/src/mds/FSMap.cc | 2 +- ceph/src/mds/FSMap.h | 33 +- ceph/src/mds/Locker.cc | 11 +- ceph/src/mds/MDCache.cc | 23 +- ceph/src/mds/MDSDaemon.cc | 24 +- ceph/src/mds/MDSDaemon.h | 2 +- ceph/src/mds/MDSMap.cc | 18 +- ceph/src/mds/MDSMap.h | 71 +-- ceph/src/mds/MDSRank.cc | 140 +++--- ceph/src/mds/Migrator.cc | 100 +++- ceph/src/mds/Migrator.h | 3 + ceph/src/mds/PurgeQueue.cc | 8 + ceph/src/mds/Server.cc | 2 + ceph/src/messages/MClientReply.h | 2 +- ceph/src/messages/MExportCapsAck.h | 6 +- ceph/src/messages/MMgrReport.h | 9 +- ceph/src/messages/MMonSubscribeAck.h | 1 - ceph/src/mgr/ActivePyModules.cc | 9 +- ceph/src/mgr/DaemonServer.cc | 3 +- ceph/src/mgr/MgrClient.cc | 4 +- ceph/src/mon/DataHealthService.cc | 10 +- ceph/src/mon/FSCommands.cc | 2 +- ceph/src/mon/HealthMonitor.cc | 19 +- ceph/src/mon/LogMonitor.cc | 1 - ceph/src/mon/MDSMonitor.cc | 411 ++++++++-------- ceph/src/mon/MDSMonitor.h | 47 +- ceph/src/mon/MonMap.h | 1 - ceph/src/mon/Monitor.cc | 6 +- ceph/src/mon/OSDMonitor.cc | 27 +- ceph/src/mon/PGMap.cc | 126 ++--- ceph/src/mon/Paxos.cc | 10 +- ceph/src/mon/PaxosFSMap.h | 9 - ceph/src/mon/Session.h | 7 +- ceph/src/msg/async/Stack.h | 4 +- ceph/src/msg/async/dpdk/DPDK.cc | 8 +- ceph/src/msg/async/rdma/RDMAStack.cc | 4 +- ceph/src/os/bluestore/BlueFS.cc | 28 +- ceph/src/os/bluestore/BlueStore.cc | 144 +++--- ceph/src/os/bluestore/BlueStore.h | 1 + ceph/src/os/bluestore/KernelDevice.cc | 4 +- ceph/src/os/bluestore/NVMEDevice.cc | 8 +- ceph/src/os/bluestore/PMEMDevice.cc | 4 +- ceph/src/os/filestore/CollectionIndex.h | 2 +- ceph/src/os/filestore/FileStore.cc | 7 +- ceph/src/os/filestore/FileStore.h | 2 +- ceph/src/os/filestore/HashIndex.cc | 32 +- ceph/src/os/filestore/HashIndex.h | 7 +- ceph/src/os/filestore/WBThrottle.cc | 4 +- ceph/src/osd/OSD.cc | 50 +- ceph/src/osd/OSDMap.cc | 18 +- ceph/src/osd/OSDMap.h | 2 - ceph/src/osd/PG.cc | 59 ++- ceph/src/osd/PG.h | 11 +- ceph/src/osd/PGBackend.cc | 86 +++- ceph/src/osd/PGBackend.h | 3 +- ceph/src/osd/PrimaryLogPG.cc | 56 ++- ceph/src/osd/Session.h | 2 +- ceph/src/osd/osd_types.h | 7 +- ceph/src/osdc/ObjectCacher.cc | 8 +- ceph/src/osdc/Objecter.cc | 2 +- ceph/src/pybind/mgr/balancer/module.py | 63 +-- ceph/src/pybind/mgr/dashboard/base.html | 5 +- .../pybind/mgr/dashboard/config_options.html | 120 +++++ ceph/src/pybind/mgr/dashboard/module.py | 59 +++ ceph/src/pybind/mgr/dashboard/osd_perf.html | 1 + ceph/src/pybind/mgr/influx/module.py | 3 +- ceph/src/pybind/mgr/mgr_module.py | 13 +- ceph/src/pybind/mgr/restful/common.py | 5 +- ceph/src/pybind/mgr/restful/module.py | 48 +- ceph/src/pybind/mgr/status/module.py | 9 +- ceph/src/pybind/rados/rados.pyx | 20 +- ceph/src/rgw/CMakeLists.txt | 13 - ceph/src/rgw/rgw_admin.cc | 32 +- ceph/src/rgw/rgw_auth_s3.h | 49 +- ceph/src/rgw/rgw_bucket.cc | 9 +- ceph/src/rgw/rgw_cache.cc | 21 +- ceph/src/rgw/rgw_common.cc | 53 +- ceph/src/rgw/rgw_common.h | 56 ++- ceph/src/rgw/rgw_data_sync.cc | 6 + ceph/src/rgw/rgw_gc.cc | 2 +- ceph/src/rgw/rgw_http_client.cc | 7 + ceph/src/rgw/rgw_lc.cc | 11 +- ceph/src/rgw/rgw_main.cc | 4 - ceph/src/rgw/rgw_op.cc | 12 +- ceph/src/rgw/rgw_op.h | 6 +- ceph/src/rgw/rgw_rados.cc | 435 +++++++++++++--- ceph/src/rgw/rgw_rados.h | 79 ++- ceph/src/rgw/rgw_reshard.cc | 3 +- ceph/src/rgw/rgw_rest_metadata.cc | 31 +- ceph/src/rgw/rgw_rest_s3.cc | 9 +- ceph/src/rgw/rgw_rest_swift.cc | 17 +- ceph/src/rgw/rgw_sync.cc | 2 +- ceph/src/sample.ceph.conf | 2 +- ceph/src/test/CMakeLists.txt | 3 - .../cli-integration/rbd/formatted-output.t | 98 ++-- ceph/src/test/cls_rgw/test_cls_rgw.cc | 173 ++++++- ceph/src/test/common/test_util.cc | 14 - ceph/src/test/libcephd/CMakeLists.txt | 23 - ceph/src/test/libcephd/misc.cc | 7 - ceph/src/test/libcephd/test.cc | 2 - .../librbd/image/test_mock_RemoveRequest.cc | 69 ++- ceph/src/test/librbd/test_ObjectMap.cc | 29 +- ceph/src/test/mon/PGMap.cc | 34 +- .../test/objectstore/workload_generator.cc | 2 +- ceph/src/test/objectstore_bench.cc | 2 +- ceph/src/test/perf_counters.cc | 2 +- ceph/src/test/pybind/test_ceph_argparse.py | 5 +- ceph/src/test/strtol.cc | 187 ++++++- ceph/src/tools/ceph_kvstore_tool.cc | 6 +- ceph/src/tools/ceph_monstore_tool.cc | 4 +- ceph/src/tools/ceph_objectstore_tool.cc | 60 ++- ceph/src/tools/cephfs/Dumper.cc | 25 +- ceph/src/tools/rados/rados.cc | 14 +- ceph/src/tools/rbd/ArgumentTypes.cc | 8 +- ceph/src/tools/rbd/action/Bench.cc | 6 +- ceph/src/tools/rbd/action/DiskUsage.cc | 8 +- ceph/src/tools/rbd/action/Info.cc | 8 +- ceph/src/tools/rbd/action/Journal.cc | 2 +- ceph/src/tools/rbd/action/List.cc | 4 +- ceph/src/tools/rbd/action/Snap.cc | 2 +- ceph/src/tools/rbd_nbd/rbd-nbd.cc | 4 +- ceph/systemd/rbdmap.service | 5 +- 409 files changed, 8949 insertions(+), 3487 deletions(-) delete mode 100644 ceph/cmake/modules/MergeStaticLibraries.cmake create mode 100644 ceph/doc/ceph-volume/lvm/batch.rst create mode 100755 ceph/qa/standalone/scrub/osd-scrub-distrust.sh create mode 100755 ceph/qa/standalone/scrub/osd-unexpected-clone.sh create mode 120000 ceph/qa/suites/upgrade/jewel-x/ceph-deploy/slow_requests.yaml create mode 100644 ceph/qa/suites/upgrade/jewel-x/parallel/slow_requests.yaml create mode 120000 ceph/qa/suites/upgrade/jewel-x/stress-split-erasure-code/slow_requests.yaml create mode 120000 ceph/qa/suites/upgrade/jewel-x/stress-split/slow_requests.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/% delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/+ delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/openstack.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/start.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/1-ceph-install/luminous.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/+ delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/blogbench.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/ec-rados-default.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/rados_api.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/rados_loadgenbig.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/test_rbd_api.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/test_rbd_python.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/3-upgrade-sequence/upgrade-all.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/3-upgrade-sequence/upgrade-mon-osd-mds.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/+ delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/blogbench.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados-snaps-few-objects.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados_loadgenmix.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados_mon_thrash.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_cls.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_import_export_no_upgrated.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_import_export_upgrated.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rgw_swift.yaml delete mode 120000 ceph/qa/suites/upgrade/luminous-x/parallel/distros delete mode 120000 ceph/qa/suites/upgrade/luminous-x/parallel/objectstore delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/% delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/0-cluster delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/1-ceph-install delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/2-partial-upgrade delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/3-thrash/default.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/4-ec-workload.yaml delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/5-finish-upgrade.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/7-final-workload.yaml delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/distros delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/objectstore delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/thrashosds-health.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/% delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/+ delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/openstack.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/start.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/1-ceph-install/luminous.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/2-partial-upgrade/firsthalf.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/3-thrash/default.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/+ delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/radosbench.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd-cls.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd-import-export.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd_api.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/readwrite.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/snaps-few-objects.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/5-finish-upgrade.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/+ delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/rbd-python.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/rgw-swift.yaml delete mode 100644 ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/snaps-many-objects.yaml delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split/distros delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split/objectstore/bluestore.yaml delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split/objectstore/filestore-xfs.yaml delete mode 120000 ceph/qa/suites/upgrade/luminous-x/stress-split/thrashosds-health.yaml create mode 100644 ceph/qa/workunits/suites/ffsb.patch create mode 100644 ceph/src/ceph-volume/ceph_volume/devices/lvm/batch.py create mode 100644 ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/__init__.py create mode 100644 ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/bluestore.py create mode 100644 ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/filestore.py create mode 100644 ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/validators.py create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/Vagrantfile create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/group_vars/all create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/hosts create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/test.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/vagrant_variables.yml create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/Vagrantfile create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/group_vars/all create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/hosts create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/test.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/vagrant_variables.yml create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/Vagrantfile create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/group_vars/all create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/hosts create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/test.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/vagrant_variables.yml create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/Vagrantfile create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/group_vars/all create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/hosts create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/test.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/vagrant_variables.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_bluestore.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_bluestore_dmcrypt.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_filestore.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_filestore_dmcrypt.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/tox.ini create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/.DS_Store create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type-dmcrypt/Vagrantfile create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type-dmcrypt/group_vars/all create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type-dmcrypt/hosts create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type-dmcrypt/test.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type-dmcrypt/vagrant_variables.yml create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type/Vagrantfile create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type/group_vars/all create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type/hosts create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type/test.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/single-type/vagrant_variables.yml create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type-dmcrypt/Vagrantfile create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type-dmcrypt/group_vars/all create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type-dmcrypt/hosts create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type-dmcrypt/test.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type-dmcrypt/vagrant_variables.yml create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type/Vagrantfile create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type/group_vars/all create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type/hosts create mode 120000 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type/test.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/filestore/single-type/vagrant_variables.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/playbooks/deploy.yml create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/functional/scripts/vagrant_up.sh create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/util/test_device.py create mode 100644 ceph/src/ceph-volume/ceph_volume/tests/util/test_util.py create mode 100644 ceph/src/ceph-volume/ceph_volume/util/device.py create mode 100644 ceph/src/ceph-volume/ceph_volume/util/templates.py delete mode 100644 ceph/src/include/cephd/libcephd.h delete mode 100644 ceph/src/libcephd/CMakeLists.txt delete mode 100644 ceph/src/libcephd/libcephd.cc create mode 100644 ceph/src/pybind/mgr/dashboard/config_options.html delete mode 100644 ceph/src/test/libcephd/CMakeLists.txt delete mode 100644 ceph/src/test/libcephd/misc.cc delete mode 100644 ceph/src/test/libcephd/test.cc diff --git a/Makefile b/Makefile index 9bc9fbe46..94633d15c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ RELEASE=5.2 PACKAGE=ceph -VER=12.2.7 +VER=12.2.8 DEBREL=pve1 SRCDIR=ceph diff --git a/ceph/CMakeLists.txt b/ceph/CMakeLists.txt index 2e2df1f6e..c507dfaa2 100644 --- a/ceph/CMakeLists.txt +++ b/ceph/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8.11) project(ceph) -set(VERSION 12.2.7) +set(VERSION 12.2.8) if(POLICY CMP0046) # Tweak policies (this one disables "missing" dependency warning) @@ -254,13 +254,6 @@ if(WITH_KRBD AND WITHOUT_RBD) message(FATAL_ERROR "Cannot have WITH_KRBD with WITH_RBD.") endif() -# embedded ceph daemon static library -# NOTE: Ceph is mostly LGPL (see COPYING), which means that -# static linking brings with it restrictions. Please be sure -# to look at the LGPL license carefully before linking this library to -# your code. See http://www.gnu.org/licenses/gpl-faq.html#LGPLStaticVsDynamic. -option(WITH_EMBEDDED "build the embedded ceph daemon library" ON) - option(WITH_LEVELDB "LevelDB is here" ON) if(WITH_LEVELDB) if(LEVELDB_PREFIX) diff --git a/ceph/README.alpine.md b/ceph/README.alpine.md index fb0424a88..0fe91088c 100644 --- a/ceph/README.alpine.md +++ b/ceph/README.alpine.md @@ -10,7 +10,7 @@ git clone https://github.com/ceph/ceph ### Build ``` -./run-make-check.sh -DWITH_EMBEDDED=OFF -DWITH_SYSTEM_BOOST=ON -DWITH_LTTNG=OFF -DWITH_REENTRANT_STRSIGNAL=ON -DWITH_THREAD_SAFE_RES_QUERY=ON +./run-make-check.sh -DWITH_SYSTEM_BOOST=ON -DWITH_LTTNG=OFF -DWITH_REENTRANT_STRSIGNAL=ON -DWITH_THREAD_SAFE_RES_QUERY=ON ``` ### Packaging @@ -28,7 +28,7 @@ cd ceph/src or -./test/docker-test.sh --os-type alpine --os-version edge -- ./run-make-check.sh -DWITH_EMBEDDED=OFF -DWITH_SYSTEM_BOOST=ON -DWITH_LTTNG=OFF -DWITH_REENTRANT_STRSIGNAL=ON -DWITH_THREAD_SAFE_RES_QUERY=ON +./test/docker-test.sh --os-type alpine --os-version edge -- ./run-make-check.sh -DWITH_SYSTEM_BOOST=ON -DWITH_LTTNG=OFF -DWITH_REENTRANT_STRSIGNAL=ON -DWITH_THREAD_SAFE_RES_QUERY=ON ``` diff --git a/ceph/alpine/APKBUILD b/ceph/alpine/APKBUILD index 90553c49e..bfa3a349b 100644 --- a/ceph/alpine/APKBUILD +++ b/ceph/alpine/APKBUILD @@ -1,7 +1,7 @@ # Contributor: John Coyle # Maintainer: John Coyle pkgname=ceph -pkgver=12.2.7 +pkgver=12.2.8 pkgrel=0 pkgdesc="Ceph is a distributed object store and file system" pkgusers="ceph" @@ -63,7 +63,7 @@ makedepends=" xmlstarlet yasm " -source="ceph-12.2.7.tar.bz2" +source="ceph-12.2.8.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-12.2.7 +builddir=$srcdir/ceph-12.2.8 build() { export CEPH_BUILD_VIRTUALENV=$builddir @@ -142,7 +142,6 @@ build() { -DWITH_PYTHON3=OFF \ -DWITH_LTTNG=OFF \ -DWITH_SYSTEM_BOOST=ON \ - -DWITH_EMBEDDED=OFF \ -DWITH_TESTS=${_with_tests:-OFF} \ || return 1 make -j${JOBS:-2} || return 1 diff --git a/ceph/alpine/APKBUILD.in b/ceph/alpine/APKBUILD.in index e82dd20a5..e987a1e00 100644 --- a/ceph/alpine/APKBUILD.in +++ b/ceph/alpine/APKBUILD.in @@ -142,7 +142,6 @@ build() { -DWITH_PYTHON3=OFF \ -DWITH_LTTNG=OFF \ -DWITH_SYSTEM_BOOST=ON \ - -DWITH_EMBEDDED=OFF \ -DWITH_TESTS=${_with_tests:-OFF} \ || return 1 make -j${JOBS:-2} || return 1 diff --git a/ceph/ceph.spec b/ceph/ceph.spec index 08ccdb6c2..9309442d3 100644 --- a/ceph/ceph.spec +++ b/ceph/ceph.spec @@ -61,7 +61,7 @@ # main package definition ################################################################################# Name: ceph -Version: 12.2.7 +Version: 12.2.8 Release: 0%{?dist} %if 0%{?fedora} || 0%{?rhel} Epoch: 2 @@ -77,7 +77,7 @@ License: LGPL-2.1 and CC-BY-SA-3.0 and GPL-2.0 and BSL-1.0 and BSD-3-Clause and Group: System/Filesystems %endif URL: http://ceph.com/ -Source0: http://ceph.com/download/ceph-12.2.7.tar.bz2 +Source0: http://ceph.com/download/ceph-12.2.8.tar.bz2 %if 0%{?suse_version} %if 0%{?is_opensuse} ExclusiveArch: x86_64 aarch64 ppc64 ppc64le @@ -142,6 +142,7 @@ BuildRequires: python BuildRequires: python-devel BuildRequires: python-nose BuildRequires: python-requests +BuildRequires: python-six BuildRequires: python-virtualenv BuildRequires: snappy-devel BuildRequires: udev @@ -322,6 +323,7 @@ Summary: Ceph Manager Daemon Group: System/Filesystems %endif Requires: ceph-base = %{_epoch_prefix}%{version}-%{release} +Requires: python-six %if 0%{?fedora} || 0%{?rhel} Requires: python-cherrypy Requires: python-jinja2 @@ -779,7 +781,7 @@ python-rbd, python-rgw or python-cephfs instead. # common ################################################################################# %prep -%autosetup -p1 -n ceph-12.2.7 +%autosetup -p1 -n ceph-12.2.8 %build %if 0%{with cephfs_java} @@ -822,7 +824,6 @@ cmake .. \ -DCMAKE_INSTALL_MANDIR=%{_mandir} \ -DCMAKE_INSTALL_DOCDIR=%{_docdir}/ceph \ -DCMAKE_INSTALL_INCLUDEDIR=%{_includedir} \ - -DWITH_EMBEDDED=OFF \ -DWITH_MANPAGE=ON \ -DWITH_PYTHON3=ON \ -DWITH_SYSTEMD=ON \ @@ -1448,7 +1449,7 @@ fi /usr/lib/systemd/systemd-sysctl %{_sysctldir}/90-ceph-osd.conf > /dev/null 2>&1 || : %endif # work around https://tracker.ceph.com/issues/24903 -chown -h ceph:ceph /var/lib/ceph/osd/*/block* 2>&1 > /dev/null || : +chown -f -h ceph:ceph /var/lib/ceph/osd/*/block* 2>&1 > /dev/null || : %preun osd %if 0%{?suse_version} diff --git a/ceph/ceph.spec.in b/ceph/ceph.spec.in index 05e3e330e..bb394f06b 100644 --- a/ceph/ceph.spec.in +++ b/ceph/ceph.spec.in @@ -142,6 +142,7 @@ BuildRequires: python BuildRequires: python-devel BuildRequires: python-nose BuildRequires: python-requests +BuildRequires: python-six BuildRequires: python-virtualenv BuildRequires: snappy-devel BuildRequires: udev @@ -322,6 +323,7 @@ Summary: Ceph Manager Daemon Group: System/Filesystems %endif Requires: ceph-base = %{_epoch_prefix}%{version}-%{release} +Requires: python-six %if 0%{?fedora} || 0%{?rhel} Requires: python-cherrypy Requires: python-jinja2 @@ -822,7 +824,6 @@ cmake .. \ -DCMAKE_INSTALL_MANDIR=%{_mandir} \ -DCMAKE_INSTALL_DOCDIR=%{_docdir}/ceph \ -DCMAKE_INSTALL_INCLUDEDIR=%{_includedir} \ - -DWITH_EMBEDDED=OFF \ -DWITH_MANPAGE=ON \ -DWITH_PYTHON3=ON \ -DWITH_SYSTEMD=ON \ @@ -1448,7 +1449,7 @@ fi /usr/lib/systemd/systemd-sysctl %{_sysctldir}/90-ceph-osd.conf > /dev/null 2>&1 || : %endif # work around https://tracker.ceph.com/issues/24903 -chown -h ceph:ceph /var/lib/ceph/osd/*/block* 2>&1 > /dev/null || : +chown -f -h ceph:ceph /var/lib/ceph/osd/*/block* 2>&1 > /dev/null || : %preun osd %if 0%{?suse_version} diff --git a/ceph/cmake/modules/MergeStaticLibraries.cmake b/ceph/cmake/modules/MergeStaticLibraries.cmake deleted file mode 100644 index 92d41567b..000000000 --- a/ceph/cmake/modules/MergeStaticLibraries.cmake +++ /dev/null @@ -1,85 +0,0 @@ -# This function is a helper that will merge static libraries. -# For example, -# -# merge_static_libraries(mylib staticlibX staticlibY) -# -# mylib.a will generate a new static library mylib that is -# a combination of staticlibX and staticlibY -# -function(merge_static_libraries target) - - set(dummy_source ${CMAKE_CURRENT_BINARY_DIR}/${target}_dummy.c) - add_library(${target} STATIC ${dummy_source}) - - # remove duplicates - set(libs ${ARGN}) - list(REMOVE_DUPLICATES libs) - - # validate that all libs are static - foreach(lib ${libs}) - if (NOT TARGET ${lib}) - message(FATAL_ERROR "${lib} not a valid target") - endif() - - get_target_property(libtype ${lib} TYPE) - if(NOT libtype STREQUAL "STATIC_LIBRARY") - message(FATAL_ERROR "${lib} not a static library") - endif() - - # add a dependency on the lib - add_dependencies(${target} ${lib}) - endforeach() - - # Force the merged Make the generated dummy source file depended on all static input - # libs. If input lib changes,the source file is touched - # which causes the desired effect (relink). - add_custom_command( - OUTPUT ${dummy_source} - COMMAND ${CMAKE_COMMAND} -E touch ${dummy_source} - DEPENDS ${libs}) - - # only LINUX is currently supported. OSX's libtool and windows lib.exe - # have native support for merging static libraries, and support for them - # can be easily added if required. - if(LINUX) - # generate a script to merge the static libraries in to the target - # library. see https://sourceware.org/binutils/docs/binutils/ar-scripts.html - set(mri_script "open $=") - foreach(lib ${libs}) - # we use the generator expression TARGET_FILE to get the location - # of the library. this will not be expanded until the script file - # is written below - set(mri_script "${mri_script} addlib $=") - endforeach() - set(mri_script "${mri_script} save=end") - - add_custom_command( - TARGET ${target} POST_BUILD - COMMAND echo ${mri_script} | tr = \\\\n | ${CMAKE_AR} -M) - endif(LINUX) - - message("-- MergeStaticLibraries: ${target}: merged ${libs}") - - # we want to set the target_link_libraries correctly for the new merged - # static library. First we get the list of link libraries for each - # of the libs we are merging - set(link_libs) - foreach(lib ${libs}) - get_property(trans TARGET ${lib} PROPERTY LINK_LIBRARIES) - list(APPEND link_libs ${trans}) - endforeach() - - if (link_libs) - # now remove the duplicates and any of the libraries we already merged - list(REMOVE_DUPLICATES link_libs) - foreach(lib ${libs}) - list(REMOVE_ITEM link_libs ${lib}) - endforeach() - - # set the target link libraries - target_link_libraries(${target} ${link_libs}) - - message("-- MergeStaticLibraries: ${target}: remaining ${link_libs}") - endif() - -endfunction() diff --git a/ceph/debian/changelog b/ceph/debian/changelog index a95f9dc26..642fc450f 100644 --- a/ceph/debian/changelog +++ b/ceph/debian/changelog @@ -1,3 +1,9 @@ +ceph (12.2.8-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 30 Aug 2018 17:24:37 +0000 + ceph (12.2.7-1) stable; urgency=medium * New upstream release diff --git a/ceph/debian/control b/ceph/debian/control index bdc74471d..d5bc16e9e 100644 --- a/ceph/debian/control +++ b/ceph/debian/control @@ -55,6 +55,7 @@ Build-Depends: bc, python-pecan, python-prettytable, python-setuptools, + python-six, python-sphinx, python-werkzeug, python3-all-dev, diff --git a/ceph/debian/rules b/ceph/debian/rules index 9c63c99c3..99a3e8e98 100755 --- a/ceph/debian/rules +++ b/ceph/debian/rules @@ -5,7 +5,7 @@ export DESTDIR=$(CURDIR)/debian/tmp export DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) -extraopts += -DUSE_CRYPTOPP=OFF -DWITH_OCF=ON -DWITH_LTTNG=ON -DWITH_PYTHON3=ON -DWITH_EMBEDDED=OFF +extraopts += -DUSE_CRYPTOPP=OFF -DWITH_OCF=ON -DWITH_LTTNG=ON -DWITH_PYTHON3=ON extraopts += -DWITH_CEPHFS_JAVA=ON # assumes that ceph is exmpt from multiarch support, so we override the libdir. extraopts += -DCMAKE_INSTALL_LIBDIR=/usr/lib diff --git a/ceph/do_freebsd.sh b/ceph/do_freebsd.sh index bf9007488..946f83e57 100755 --- a/ceph/do_freebsd.sh +++ b/ceph/do_freebsd.sh @@ -38,7 +38,6 @@ rm -rf build && ./do_cmake.sh "$*" \ -D CEPH_MAN_DIR=man \ -D WITH_LIBCEPHFS=OFF \ -D WITH_CEPHFS=OFF \ - -D WITH_EMBEDDED=OFF \ -D WITH_MGR=YES \ 2>&1 | tee cmake.log diff --git a/ceph/doc/ceph-volume/index.rst b/ceph/doc/ceph-volume/index.rst index 387596d61..34094b733 100644 --- a/ceph/doc/ceph-volume/index.rst +++ b/ceph/doc/ceph-volume/index.rst @@ -20,7 +20,7 @@ that may have been deployed with ``ceph-disk``. Migrating --------- -Starting on Ceph version 12.2.2, ``ceph-disk`` is deprecated. Deprecation +Starting on Ceph version 13.0.0, ``ceph-disk`` is deprecated. Deprecation warnings will show up that will link to this page. It is strongly suggested that users start consuming ``ceph-volume``. There are two paths for migrating: @@ -53,6 +53,7 @@ and ``ceph-disk`` is fully disabled. Encryption is fully supported. systemd lvm/index lvm/activate + lvm/batch lvm/encryption lvm/prepare lvm/scan diff --git a/ceph/doc/ceph-volume/lvm/batch.rst b/ceph/doc/ceph-volume/lvm/batch.rst new file mode 100644 index 000000000..bf484b017 --- /dev/null +++ b/ceph/doc/ceph-volume/lvm/batch.rst @@ -0,0 +1,139 @@ +.. _ceph-volume-lvm-batch: + +``batch`` +=========== +This subcommand allows for multiple OSDs to be created at the same time given +an input of devices. Depending on the device type (spinning drive, or solid +state), the internal engine will decide the best approach to create the OSDs. + +This decision abstracts away the many nuances when creating an OSD: how large +should a ``block.db`` be? How can one mix a solid state device with spinning +devices in an efficient way? + +The process is similar to :ref:`ceph-volume-lvm-create`, and will do the +preparation and activation at once, following the same workflow for each OSD. + +All the features that ``ceph-volume lvm create`` supports, like ``dmcrypt``, +avoiding ``systemd`` units from starting, defining bluestore or filestore, +are supported. Any fine-grained option that may affect a single OSD is not +supported, for example: specifying where journals should be placed. + + +.. _ceph-volume-lvm-batch_bluestore: + +``bluestore`` +------------- +The :term:`bluestore` objectstore (the default) is used when creating multiple OSDs +with the ``batch`` sub-command. It allows a few different scenarios depending +on the input of devices: + +#. Devices are all spinning HDDs: 1 OSD is created per device +#. Devices are all spinning SSDs: 2 OSDs are created per device +#. Devices are a mix of HDDS and SSDs: data is placed on the spinning device, + the ``block.db`` is created on the SSD, as large as possible. + + +.. note:: Although operations in ``ceph-volume lvm create`` allow usage of + ``block.wal`` it isn't supported with the ``batch`` sub-command + + +.. _ceph-volume-lvm-batch_filestore: + +``filestore`` +------------- +The :term:`filestore` objectstore can be used when creating multiple OSDs +with the ``batch`` sub-command. It allows two different scenarios depending +on the input of devices: + +#. Devices are all the same type (for example all spinning HDD or all SSDs): + 1 OSD is created per device, collocating the journal in the same HDD. +#. Devices are a mix of HDDS and SSDs: data is placed on the spinning device, + while the journal is created on the SSD using the sizing options from + ceph.conf and falling back to the default journal size of 5GB. + + +When a mix of solid and spinning devices are used, ``ceph-volume`` will try to +detect existing volume groups on the solid devices. If a VG is found, it will +try to create the logical volume from there, otherwise raising an error if +space is insufficient. + +If a raw solid device is used along with a device that has a volume group in +addition to some spinning devices, ``ceph-volume`` will try to extend the +existing volume group and then create a logical volume. + +.. _ceph-volume-lvm-batch_report: + +Reporting +========= +When a call is received to create OSDs, the tool will prompt the user to +continue if the pre-computed output is acceptable. This output is useful to +understand the outcome of the received devices. Once confirmation is accepted, +the process continues. + +Although prompts are good to understand outcomes, it is incredibly useful to +try different inputs to find the best product possible. With the ``--report`` +flag, one can prevent any actual operations and just verify outcomes from +inputs. + +**pretty reporting** +For two spinning devices, this is how the ``pretty`` report (the default) would +look:: + + $ ceph-volume lvm batch --report /dev/sdb /dev/sdc + + Total OSDs: 2 + + Type Path LV Size % of device + -------------------------------------------------------------------------------- + [data] /dev/sdb 10.74 GB 100% + -------------------------------------------------------------------------------- + [data] /dev/sdc 10.74 GB 100% + + + +**JSON reporting** +Reporting can produce a richer output with ``JSON``, which gives a few more +hints on sizing. This feature might be better for other tooling to consume +information that will need to be transformed. + +For two spinning devices, this is how the ``JSON`` report would look:: + + $ ceph-volume lvm batch --report --format=json /dev/sdb /dev/sdc + { + "osds": [ + { + "block.db": {}, + "data": { + "human_readable_size": "10.74 GB", + "parts": 1, + "path": "/dev/sdb", + "percentage": 100, + "size": 11534336000.0 + } + }, + { + "block.db": {}, + "data": { + "human_readable_size": "10.74 GB", + "parts": 1, + "path": "/dev/sdc", + "percentage": 100, + "size": 11534336000.0 + } + } + ], + "vgs": [ + { + "devices": [ + "/dev/sdb" + ], + "parts": 1 + }, + { + "devices": [ + "/dev/sdc" + ], + "parts": 1 + } + ] + } diff --git a/ceph/doc/man/8/ceph-bluestore-tool.rst b/ceph/doc/man/8/ceph-bluestore-tool.rst index 7a7b0ea6a..f5e5fe2b8 100644 --- a/ceph/doc/man/8/ceph-bluestore-tool.rst +++ b/ceph/doc/man/8/ceph-bluestore-tool.rst @@ -31,31 +31,31 @@ operations on a BlueStore instance. Commands ======== -.. option:: help +:command:`help` show help -.. option:: fsck +:command:`fsck` [ --deep ] run consistency check on BlueStore metadata. If *--deep* is specified, also read all object data and verify checksums. -.. option:: repair +:command:`repair` Run a consistency check *and* repair any errors we can. -.. option:: bluefs-export +:command:`bluefs-export` Export the contents of BlueFS (i.e., rocksdb files) to an output directory. -.. option:: bluefs-bdev-sizes --path *osd path* +:command:`bluefs-bdev-sizes` --path *osd path* Print the device sizes, as understood by BlueFS, to stdout. -.. option:: bluefs-bdev-expand --path *osd path* +:command:`bluefs-bdev-expand` --path *osd path* Instruct BlueFS to check the size of its block devices and, if they have expanded, make use of the additional space. -.. option:: show-label --dev *device* [...] +:command:`show-label` --dev *device* [...] Show device label(s). diff --git a/ceph/doc/man/8/ceph-volume.rst b/ceph/doc/man/8/ceph-volume.rst index 50a2e5280..197bea1ae 100644 --- a/ceph/doc/man/8/ceph-volume.rst +++ b/ceph/doc/man/8/ceph-volume.rst @@ -13,7 +13,7 @@ Synopsis | [--log-path LOG_PATH] | **ceph-volume** **lvm** [ *trigger* | *create* | *activate* | *prepare* -| *zap* | *list*] +| *zap* | *list* | *batch*] | **ceph-volume** **simple** [ *trigger* | *scan* | *activate* ] @@ -43,6 +43,35 @@ activated. Subcommands: +**batch** +Creates OSDs from a list of devices using a ``filestore`` +or ``bluestore`` (default) setup. It will create all necessary volume groups +and logical volumes required to have a working OSD. + +Example usage with three devices:: + + ceph-volume lvm batch --bluestore /dev/sda /dev/sdb /dev/sdc + +Optional arguments: + +* [-h, --help] show the help message and exit +* [--bluestore] Use the bluestore objectstore (default) +* [--filestore] Use the filestore objectstore +* [--yes] Skip the report and prompt to continue provisioning +* [--dmcrypt] Enable encryption for the underlying OSD devices +* [--crush-device-class] Define a CRUSH device class to assign the OSD to +* [--no-systemd] Do not enable or create any systemd units +* [--report] Report what the potential outcome would be for the + current input (requires devices to be passed in) +* [--format] Output format when reporting (used along with + --report), can be one of 'pretty' (default) or 'json' + +Required positional arguments: + +* Full path to a raw device, like ``/dev/sda``. Multiple + ```` paths can be passed in. + + **activate** Enables a systemd unit that persists the OSD ID and its UUID (also called ``fsid`` in Ceph CLI tools), so that at boot time it can understand what OSD is diff --git a/ceph/doc/rados/configuration/filestore-config-ref.rst b/ceph/doc/rados/configuration/filestore-config-ref.rst index 4dff60c1e..7f62cfeb3 100644 --- a/ceph/doc/rados/configuration/filestore-config-ref.rst +++ b/ceph/doc/rados/configuration/filestore-config-ref.rst @@ -297,7 +297,7 @@ Misc NOTE: A negative value means to disable subdir merging :Type: Integer :Required: No -:Default: ``10`` +:Default: ``-10`` ``filestore split multiple`` diff --git a/ceph/doc/rados/configuration/osd-config-ref.rst b/ceph/doc/rados/configuration/osd-config-ref.rst index fae707893..f839122cf 100644 --- a/ceph/doc/rados/configuration/osd-config-ref.rst +++ b/ceph/doc/rados/configuration/osd-config-ref.rst @@ -23,13 +23,13 @@ configuration file. To add settings directly to a specific Ceph OSD Daemon file. For example: .. code-block:: ini - + [osd] - osd journal size = 1024 - + osd journal size = 5120 + [osd.0] host = osd-host-a - + [osd.1] host = osd-host-b @@ -41,8 +41,10 @@ General Settings The following settings provide an Ceph OSD Daemon's ID, and determine paths to data and journals. Ceph deployment scripts typically generate the UUID -automatically. We **DO NOT** recommend changing the default paths for data or -journals, as it makes it more problematic to troubleshoot Ceph later. +automatically. + +.. warning:: **DO NOT** change the default paths for data or journals, as it + makes it more problematic to troubleshoot Ceph later. The journal size should be at least twice the product of the expected drive speed multiplied by ``filestore max sync interval``. However, the most common @@ -55,21 +57,21 @@ that Ceph uses the entire partition for the journal. :Description: The universally unique identifier (UUID) for the Ceph OSD Daemon. :Type: UUID :Default: The UUID. -:Note: The ``osd uuid`` applies to a single Ceph OSD Daemon. The ``fsid`` +:Note: The ``osd uuid`` applies to a single Ceph OSD Daemon. The ``fsid`` applies to the entire cluster. -``osd data`` +``osd data`` -:Description: The path to the OSDs data. You must create the directory when - deploying Ceph. You should mount a drive for OSD data at this - mount point. We do not recommend changing the default. +:Description: The path to the OSDs data. You must create the directory when + deploying Ceph. You should mount a drive for OSD data at this + mount point. We do not recommend changing the default. :Type: String :Default: ``/var/lib/ceph/osd/$cluster-$id`` -``osd max write size`` +``osd max write size`` :Description: The maximum size of a write in megabytes. :Type: 32-bit Integer @@ -80,10 +82,10 @@ that Ceph uses the entire partition for the journal. :Description: The largest client data message allowed in memory. :Type: 64-bit Unsigned Integer -:Default: 500MB default. ``500*1024L*1024L`` +:Default: 500MB default. ``500*1024L*1024L`` -``osd class dir`` +``osd class dir`` :Description: The class path for RADOS class plug-ins. :Type: String @@ -96,7 +98,7 @@ File System Settings ==================== Ceph builds and mounts file systems which are used for Ceph OSDs. -``osd mkfs options {fs-type}`` +``osd mkfs options {fs-type}`` :Description: Options used when creating a new Ceph OSD of type {fs-type}. @@ -107,7 +109,7 @@ Ceph builds and mounts file systems which are used for Ceph OSDs. For example:: ``osd mkfs options xfs = -f -d agcount=24`` -``osd mount options {fs-type}`` +``osd mount options {fs-type}`` :Description: Options used when mounting a Ceph OSD of type {fs-type}. @@ -129,32 +131,25 @@ the following path:: /var/lib/ceph/osd/$cluster-$id/journal -Without performance optimization, Ceph stores the journal on the same disk as -the Ceph OSD Daemons data. An Ceph OSD Daemon optimized for performance may use -a separate disk to store journal data (e.g., a solid state drive delivers high -performance journaling). +When using a single device type (for example, spinning drives), the journals +should be *colocated*: the logical volume (or partition) should be in the same +device as the ``data`` logical volume. -Ceph's default ``osd journal size`` is 0, so you will need to set this in your -``ceph.conf`` file. A journal size should find the product of the ``filestore -max sync interval`` and the expected throughput, and multiply the product by -two (2):: - - osd journal size = {2 * (expected throughput * filestore max sync interval)} +When using a mix of fast (SSDs, NVMe) devices with slower ones (like spinning +drives) it makes sense to place the journal on the faster device, while +``data`` occupies the slower device fully. -The expected throughput number should include the expected disk throughput -(i.e., sustained data transfer rate), and network throughput. For example, -a 7200 RPM disk will likely have approximately 100 MB/s. Taking the ``min()`` -of the disk and network throughput should provide a reasonable expected -throughput. Some users just start off with a 10GB journal size. For -example:: +The default ``osd journal size`` value is 5120 (5 gigabytes), but it can be +larger, in which case it will need to be set in the ``ceph.conf`` file:: - osd journal size = 10000 + osd journal size = 10240 -``osd journal`` + +``osd journal`` :Description: The path to the OSD's journal. This may be a path to a file or a - block device (such as a partition of an SSD). If it is a file, + block device (such as a partition of an SSD). If it is a file, you must create the directory to contain it. We recommend using a drive separate from the ``osd data`` drive. @@ -162,17 +157,12 @@ example:: :Default: ``/var/lib/ceph/osd/$cluster-$id/journal`` -``osd journal size`` +``osd journal size`` -:Description: The size of the journal in megabytes. If this is 0, and the - journal is a block device, the entire block device is used. - Since v0.54, this is ignored if the journal is a block device, - and the entire block device is used. +:Description: The size of the journal in megabytes. :Type: 32-bit Integer :Default: ``5120`` -:Recommended: Begin with 1GB. Should be at least twice the product of the - expected speed multiplied by ``filestore max sync interval``. See `Journal Config Reference`_ for additional details. @@ -211,13 +201,13 @@ performance. You can adjust the following settings to increase or decrease scrubbing operations. -``osd max scrubs`` +``osd max scrubs`` -:Description: The maximum number of simultaneous scrub operations for +:Description: The maximum number of simultaneous scrub operations for a Ceph OSD Daemon. :Type: 32-bit Int -:Default: ``1`` +:Default: ``1`` ``osd scrub begin hour`` @@ -248,33 +238,33 @@ scrubbing operations. :Default: ``true`` -``osd scrub thread timeout`` +``osd scrub thread timeout`` :Description: The maximum time in seconds before timing out a scrub thread. :Type: 32-bit Integer -:Default: ``60`` +:Default: ``60`` -``osd scrub finalize thread timeout`` +``osd scrub finalize thread timeout`` -:Description: The maximum time in seconds before timing out a scrub finalize +:Description: The maximum time in seconds before timing out a scrub finalize thread. :Type: 32-bit Integer :Default: ``60*10`` -``osd scrub load threshold`` +``osd scrub load threshold`` :Description: The maximum load. Ceph will not scrub when the system load (as defined by ``getloadavg()``) is higher than this number. Default is ``0.5``. :Type: Float -:Default: ``0.5`` +:Default: ``0.5`` -``osd scrub min interval`` +``osd scrub min interval`` :Description: The minimal interval in seconds for scrubbing the Ceph OSD Daemon when the Ceph Storage Cluster load is low. @@ -283,9 +273,9 @@ scrubbing operations. :Default: Once per day. ``60*60*24`` -``osd scrub max interval`` +``osd scrub max interval`` -:Description: The maximum interval in seconds for scrubbing the Ceph OSD Daemon +:Description: The maximum interval in seconds for scrubbing the Ceph OSD Daemon irrespective of cluster load. :Type: Float @@ -320,7 +310,7 @@ scrubbing operations. ``osd deep scrub interval`` -:Description: The interval for "deep" scrubbing (fully reading all data). The +:Description: The interval for "deep" scrubbing (fully reading all data). The ``osd scrub load threshold`` does not affect this setting. :Type: Float @@ -411,21 +401,21 @@ recovery operations to ensure optimal performance during recovery. ``osd client op priority`` -:Description: The priority set for client operations. It is relative to +:Description: The priority set for client operations. It is relative to ``osd recovery op priority``. :Type: 32-bit Integer -:Default: ``63`` +:Default: ``63`` :Valid Range: 1-63 ``osd recovery op priority`` -:Description: The priority set for recovery operations. It is relative to +:Description: The priority set for recovery operations. It is relative to ``osd client op priority``. :Type: 32-bit Integer -:Default: ``3`` +:Default: ``3`` :Valid Range: 1-63 @@ -449,30 +439,30 @@ recovery operations to ensure optimal performance during recovery. :Valid Range: 1-63 -``osd op thread timeout`` +``osd op thread timeout`` :Description: The Ceph OSD Daemon operation thread timeout in seconds. :Type: 32-bit Integer -:Default: ``15`` +:Default: ``15`` -``osd op complaint time`` +``osd op complaint time`` :Description: An operation becomes complaint worthy after the specified number of seconds have elapsed. :Type: Float -:Default: ``30`` +:Default: ``30`` -``osd disk threads`` +``osd disk threads`` -:Description: The number of disk threads, which are used to perform background - disk intensive OSD operations such as scrubbing and snap +:Description: The number of disk threads, which are used to perform background + disk intensive OSD operations such as scrubbing and snap trimming. :Type: 32-bit Integer -:Default: ``1`` +:Default: ``1`` ``osd disk thread ioprio class`` @@ -487,7 +477,7 @@ recovery operations to ensure optimal performance during recovery. operations. ``be`` is the default and is the same priority as all other threads in the OSD. ``rt`` means the disk thread will have precendence over all other - threads in the OSD. Note: Only works with the Linux Kernel + threads in the OSD. Note: Only works with the Linux Kernel CFQ scheduler. Since Jewel scrubbing is no longer carried out by the disk iothread, see osd priority options instead. :Type: String @@ -807,7 +797,7 @@ Daemons to restore the balance. The process of migrating placement groups and the objects they contain can reduce the cluster's operational performance considerably. To maintain operational performance, Ceph performs this migration with 'backfilling', which allows Ceph to set backfill operations to a lower -priority than requests to read or write data. +priority than requests to read or write data. ``osd max backfills`` @@ -817,20 +807,20 @@ priority than requests to read or write data. :Default: ``1`` -``osd backfill scan min`` +``osd backfill scan min`` :Description: The minimum number of objects per backfill scan. :Type: 32-bit Integer -:Default: ``64`` +:Default: ``64`` -``osd backfill scan max`` +``osd backfill scan max`` :Description: The maximum number of objects per backfill scan. :Type: 32-bit Integer -:Default: ``512`` +:Default: ``512`` ``osd backfill retry interval`` @@ -844,19 +834,19 @@ priority than requests to read or write data. OSD Map ======= -OSD maps reflect the OSD daemons operating in the cluster. Over time, the +OSD maps reflect the OSD daemons operating in the cluster. Over time, the number of map epochs increases. Ceph provides some settings to ensure that Ceph performs well as the OSD map grows larger. ``osd map dedup`` -:Description: Enable removing duplicates in the OSD map. +:Description: Enable removing duplicates in the OSD map. :Type: Boolean :Default: ``true`` -``osd map cache size`` +``osd map cache size`` :Description: The number of OSD maps to keep cached. :Type: 32-bit Integer @@ -865,21 +855,21 @@ Ceph performs well as the OSD map grows larger. ``osd map cache bl size`` -:Description: The size of the in-memory OSD map cache in OSD daemons. +:Description: The size of the in-memory OSD map cache in OSD daemons. :Type: 32-bit Integer :Default: ``50`` ``osd map cache bl inc size`` -:Description: The size of the in-memory OSD map cache incrementals in +:Description: The size of the in-memory OSD map cache incrementals in OSD daemons. :Type: 32-bit Integer :Default: ``100`` -``osd map message max`` +``osd map message max`` :Description: The maximum map entries allowed per MOSDMap message. :Type: 32-bit Integer @@ -908,33 +898,33 @@ intensive. To maintain operational performance, Ceph performs recovery with limitations on the number recovery requests, threads and object chunk sizes which allows Ceph -perform well in a degraded state. +perform well in a degraded state. -``osd recovery delay start`` +``osd recovery delay start`` -:Description: After peering completes, Ceph will delay for the specified number +:Description: After peering completes, Ceph will delay for the specified number of seconds before starting to recover objects. :Type: Float -:Default: ``0`` +:Default: ``0`` -``osd recovery max active`` +``osd recovery max active`` -:Description: The number of active recovery requests per OSD at one time. More - requests will accelerate recovery, but the requests places an +:Description: The number of active recovery requests per OSD at one time. More + requests will accelerate recovery, but the requests places an increased load on the cluster. :Type: 32-bit Integer :Default: ``3`` -``osd recovery max chunk`` +``osd recovery max chunk`` -:Description: The maximum size of a recovered chunk of data to push. +:Description: The maximum size of a recovered chunk of data to push. :Type: 64-bit Unsigned Integer -:Default: ``8 << 20`` +:Default: ``8 << 20`` ``osd recovery max single start`` @@ -945,7 +935,7 @@ perform well in a degraded state. :Default: ``1`` -``osd recovery thread timeout`` +``osd recovery thread timeout`` :Description: The maximum time in seconds before timing out a recovery thread. :Type: 32-bit Integer @@ -954,7 +944,7 @@ perform well in a degraded state. ``osd recover clone overlap`` -:Description: Preserves clone overlap during recovery. Should always be set +:Description: Preserves clone overlap during recovery. Should always be set to ``true``. :Type: Boolean @@ -1022,67 +1012,67 @@ Miscellaneous ============= -``osd snap trim thread timeout`` +``osd snap trim thread timeout`` :Description: The maximum time in seconds before timing out a snap trim thread. :Type: 32-bit Integer -:Default: ``60*60*1`` +:Default: ``60*60*1`` -``osd backlog thread timeout`` +``osd backlog thread timeout`` :Description: The maximum time in seconds before timing out a backlog thread. :Type: 32-bit Integer -:Default: ``60*60*1`` +:Default: ``60*60*1`` -``osd default notify timeout`` +``osd default notify timeout`` :Description: The OSD default notification timeout (in seconds). :Type: 32-bit Unsigned Integer -:Default: ``30`` +:Default: ``30`` -``osd check for log corruption`` +``osd check for log corruption`` :Description: Check log files for corruption. Can be computationally expensive. :Type: Boolean -:Default: ``false`` +:Default: ``false`` -``osd remove thread timeout`` +``osd remove thread timeout`` :Description: The maximum time in seconds before timing out a remove OSD thread. :Type: 32-bit Integer :Default: ``60*60`` -``osd command thread timeout`` +``osd command thread timeout`` :Description: The maximum time in seconds before timing out a command thread. :Type: 32-bit Integer -:Default: ``10*60`` +:Default: ``10*60`` -``osd command max records`` +``osd command max records`` -:Description: Limits the number of lost objects to return. +:Description: Limits the number of lost objects to return. :Type: 32-bit Integer -:Default: ``256`` +:Default: ``256`` -``osd auto upgrade tmap`` +``osd auto upgrade tmap`` :Description: Uses ``tmap`` for ``omap`` on old objects. :Type: Boolean :Default: ``true`` - -``osd tmapput sets users tmap`` + +``osd tmapput sets users tmap`` :Description: Uses ``tmap`` for debugging only. :Type: Boolean -:Default: ``false`` +:Default: ``false`` ``osd fast fail on connection refused`` diff --git a/ceph/doc/radosgw/s3/bucketops.rst b/ceph/doc/radosgw/s3/bucketops.rst index c7cd5b4fd..ed1f2a4f6 100644 --- a/ceph/doc/radosgw/s3/bucketops.rst +++ b/ceph/doc/radosgw/s3/bucketops.rst @@ -91,18 +91,19 @@ Syntax Parameters ~~~~~~~~~~ -+-----------------+-----------+-----------------------------------------------------------------------+ -| Name | Type | Description | -+=================+===========+=======================================================================+ -| ``prefix`` | String | Only returns objects that contain the specified prefix. | -+-----------------+-----------+-----------------------------------------------------------------------+ -| ``delimiter`` | String | The delimiter between the prefix and the rest of the object name. | -+-----------------+-----------+-----------------------------------------------------------------------+ -| ``marker`` | String | A beginning index for the list of objects returned. | -+-----------------+-----------+-----------------------------------------------------------------------+ -| ``max-keys`` | Integer | The maximum number of keys to return. Default is 1000. | -+-----------------+-----------+-----------------------------------------------------------------------+ - ++---------------------+-----------+-------------------------------------------------------------------------------------------------+ +| Name | Type | Description | ++=====================+===========+=================================================================================================+ +| ``prefix`` | String | Only returns objects that contain the specified prefix. | ++---------------------+-----------+-------------------------------------------------------------------------------------------------+ +| ``delimiter`` | String | The delimiter between the prefix and the rest of the object name. | ++---------------------+-----------+-------------------------------------------------------------------------------------------------+ +| ``marker`` | String | A beginning index for the list of objects returned. | ++---------------------+-----------+-------------------------------------------------------------------------------------------------+ +| ``max-keys`` | Integer | The maximum number of keys to return. Default is 1000. | ++---------------------+-----------+-------------------------------------------------------------------------------------------------+ +| ``allow-unordered`` | Boolean | Non-standard extension. Allows results to be returned unordered. Cannot be used with delimiter. | ++---------------------+-----------+-------------------------------------------------------------------------------------------------+ HTTP Response ~~~~~~~~~~~~~ diff --git a/ceph/doc/radosgw/swift/containerops.rst b/ceph/doc/radosgw/swift/containerops.rst index 463d91c6c..f97429579 100644 --- a/ceph/doc/radosgw/swift/containerops.rst +++ b/ceph/doc/radosgw/swift/containerops.rst @@ -147,6 +147,13 @@ Parameters :Type: String :Required: No +``allow_unordered`` + +:Description: Allows the results to be returned unordered to reduce computation overhead. Cannot be used with ``delimiter``. +:Type: Boolean +:Required: No +:Non-Standard Extension: Yes + Response Entities ~~~~~~~~~~~~~~~~~ diff --git a/ceph/examples/librados/Makefile b/ceph/examples/librados/Makefile index 533a4c647..2b6109c4c 100644 --- a/ceph/examples/librados/Makefile +++ b/ceph/examples/librados/Makefile @@ -1,7 +1,7 @@ CXX?=g++ CXX_FLAGS?=-std=c++11 -Wall -Wextra -Werror -g -CXX_LIBS?=-lboost_system -lrados -lradosstriper +CXX_LIBS?=-lrados -lradosstriper CXX_INC?=$(LOCAL_LIBRADOS_INC) CXX_CC=$(CXX) $(CXX_FLAGS) $(CXX_INC) $(LOCAL_LIBRADOS) $(CXX_LIBS) diff --git a/ceph/install-deps.sh b/ceph/install-deps.sh index 7e408ae14..9ead1056d 100755 --- a/ceph/install-deps.sh +++ b/ceph/install-deps.sh @@ -187,7 +187,7 @@ else $SUDO $builddepcmd $DIR/ceph.spec 2>&1 | tee $DIR/yum-builddep.out ! grep -q -i error: $DIR/yum-builddep.out || exit 1 ;; - opensuse|suse|sles) + opensuse*|suse|sles) echo "Using zypper to install dependencies" $SUDO zypper --gpg-auto-import-keys --non-interactive install lsb-release systemd-rpm-macros munge_ceph_spec_in $DIR/ceph.spec diff --git a/ceph/qa/run-standalone.sh b/ceph/qa/run-standalone.sh index 3be6121f6..9321cba65 100755 --- a/ceph/qa/run-standalone.sh +++ b/ceph/qa/run-standalone.sh @@ -61,6 +61,15 @@ if [ "$precore" = "$COREPATTERN" ]; then else sudo sysctl -w ${KERNCORE}=${COREPATTERN} fi +# Clean out any cores in core target directory (currently .) +if ls $(dirname $(sysctl -n $KERNCORE)) | grep -q '^core\|core$' ; then + mkdir found.cores.$$ 2> /dev/null || true + for i in $(ls $(dirname $(sysctl -n $KERNCORE)) | grep '^core\|core$'); do + mv $i found.cores.$$ + done + echo "Stray cores put in $(pwd)/found.cores.$$" +fi + ulimit -c unlimited for f in $(cd $location ; find . -perm $exec_mode -type f) do diff --git a/ceph/qa/standalone/ceph-helpers.sh b/ceph/qa/standalone/ceph-helpers.sh index b9dd86bf2..f12f0698a 100755 --- a/ceph/qa/standalone/ceph-helpers.sh +++ b/ceph/qa/standalone/ceph-helpers.sh @@ -150,6 +150,7 @@ function test_setup() { # subvolumes that relate to it. # # @param dir path name of the environment +# @param dumplogs pass "1" to dump logs otherwise it will only if cores found # @return 0 on success, 1 on error # function teardown() { @@ -169,7 +170,7 @@ function teardown() { pattern="" fi # Local we start with core and teuthology ends with core - if ls $(dirname $pattern) | grep -q '^core\|core$' ; then + if ls $(dirname "$pattern") | grep -q '^core\|core$' ; then cores="yes" if [ -n "$LOCALRUN" ]; then mkdir /tmp/cores.$$ 2> /dev/null || true @@ -179,7 +180,13 @@ function teardown() { fi fi if [ "$cores" = "yes" -o "$dumplogs" = "1" ]; then - display_logs $dir + if [ -n "$LOCALRUN" ]; then + display_logs $dir + else + # Move logs to where Teuthology will archive it + mkdir -p $TESTDIR/archive/log + mv $dir/*.log $TESTDIR/archive/log + fi fi rm -fr $dir rm -rf $(get_asok_dir) @@ -1447,10 +1454,11 @@ function test_wait_for_clean() { ####################################################################### ## -# Wait until the cluster becomes HEALTH_OK again or if it does not make progress -# for $TIMEOUT seconds. +# Wait until the cluster has health condition passed as arg +# again for $TIMEOUT seconds. # -# @return 0 if the cluster is HEALTHY, 1 otherwise +# @param string to grep for in health detail +# @return 0 if the cluster health matches request, 1 otherwise # function wait_for_health() { local grepstr=$1 @@ -1467,6 +1475,12 @@ function wait_for_health() { done } +## +# Wait until the cluster becomes HEALTH_OK again or if it does not make progress +# for $TIMEOUT seconds. +# +# @return 0 if the cluster is HEALTHY, 1 otherwise +# function wait_for_health_ok() { wait_for_health "HEALTH_OK" || return 1 } @@ -2002,6 +2016,15 @@ function inject_eio() { done } +function multidiff() { + if ! diff $@ ; then + if [ "$DIFFCOLOPTS" = "" ]; then + return 1 + fi + diff $DIFFCOLOPTS $@ + fi +} + # Local Variables: # compile-command: "cd ../../src ; make -j4 && ../qa/standalone/ceph-helpers.sh TESTS # test_get_config" # End: diff --git a/ceph/qa/standalone/mon/osd-pool-create.sh b/ceph/qa/standalone/mon/osd-pool-create.sh index 39eb1c4c3..5b19c0095 100755 --- a/ceph/qa/standalone/mon/osd-pool-create.sh +++ b/ceph/qa/standalone/mon/osd-pool-create.sh @@ -213,7 +213,6 @@ function TEST_pool_create_rep_expected_num_objects() { setup $dir || return 1 # disable pg dir merge - CEPH_ARGS+="--filestore-merge-threshold=-10 " export CEPH_ARGS run_mon $dir a || return 1 run_osd $dir 0 || return 1 diff --git a/ceph/qa/standalone/osd/osd-backfill-stats.sh b/ceph/qa/standalone/osd/osd-backfill-stats.sh index 8fbef6117..f1fed4bc9 100755 --- a/ceph/qa/standalone/osd/osd-backfill-stats.sh +++ b/ceph/qa/standalone/osd/osd-backfill-stats.sh @@ -706,7 +706,7 @@ function TEST_backfill_ec_down_out() { } -main recout "$@" +main osd-backfill-stats "$@" # Local Variables: # compile-command: "make -j4 && ../qa/run-standalone.sh osd-backfill-stats.sh" diff --git a/ceph/qa/standalone/osd/osd-recovery-stats.sh b/ceph/qa/standalone/osd/osd-recovery-stats.sh index 8b50274e3..46f51adb8 100755 --- a/ceph/qa/standalone/osd/osd-recovery-stats.sh +++ b/ceph/qa/standalone/osd/osd-recovery-stats.sh @@ -423,7 +423,7 @@ function TEST_recovery_erasure_remapped() { kill_daemons $dir || return 1 } -main recout "$@" +main osd-recovery-stats "$@" # Local Variables: # compile-command: "make -j4 && ../qa/run-standalone.sh osd-recovery-stats.sh" diff --git a/ceph/qa/standalone/osd/repro_long_log.sh b/ceph/qa/standalone/osd/repro_long_log.sh index 93201a2a5..7284fedb2 100755 --- a/ceph/qa/standalone/osd/repro_long_log.sh +++ b/ceph/qa/standalone/osd/repro_long_log.sh @@ -67,10 +67,10 @@ function setup_log_test() { ceph tell osd.\* injectargs -- --osd-pg-log-trim-min 10 || return 1 ceph tell osd.\* injectargs -- --osd-pg-log-dups-tracked 10 || return 1 - touch foo + touch $dir/foo for i in $(seq 1 20) do - rados -p test put foo foo || return 1 + rados -p test put foo $dir/foo || return 1 done test_log_size $PGID 20 || return 1 @@ -93,7 +93,7 @@ function TEST_repro_long_log1() setup_log_test $dir || return 1 # regular write should trim the log - rados -p test put foo foo || return 1 + rados -p test put foo $dir/foo || return 1 test_log_size $PGID 22 || return 1 } diff --git a/ceph/qa/standalone/scrub/osd-scrub-distrust.sh b/ceph/qa/standalone/scrub/osd-scrub-distrust.sh new file mode 100755 index 000000000..80ad9633f --- /dev/null +++ b/ceph/qa/standalone/scrub/osd-scrub-distrust.sh @@ -0,0 +1,346 @@ +#!/bin/bash -x +# +# Copyright (C) 2014 Red Hat +# +# Author: Loic Dachary +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Public License for more details. +# +source $CEPH_ROOT/qa/standalone/ceph-helpers.sh + +# Test development and debugging +# Set to "yes" in order to ignore diff errors and save results to update test +getjson="yes" + +# Filter out mtime and local_mtime dates, version, prior_version and last_reqid (client) from any object_info. +jqfilter='def walk(f): + . as $in + | if type == "object" then + reduce keys[] as $key + ( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f + elif type == "array" then map( walk(f) ) | f + else f + end; +walk(if type == "object" then del(.mtime) else . end) +| walk(if type == "object" then del(.local_mtime) else . end) +| walk(if type == "object" then del(.last_reqid) else . end) +| walk(if type == "object" then del(.version) else . end) +| walk(if type == "object" then del(.prior_version) else . end) +| walk(if type == "object" then del(.redirect_target) else . end) +| walk(if type == "object" then del(.legacy_snaps) else . end)' + +sortkeys='import json; import sys ; JSON=sys.stdin.read() ; ud = json.loads(JSON) ; print json.dumps(ud, sort_keys=True, indent=2)' + +function run() { + local dir=$1 + shift + + export CEPH_MON="127.0.0.1:7107" # git grep '\<7107\>' : there must be only one + export CEPH_ARGS + CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none " + CEPH_ARGS+="--mon-host=$CEPH_MON " + CEPH_ARGS+="--osd-distrust-data-digest=true " + + local funcs=${@:-$(set | sed -n -e 's/^\(TEST_[0-9a-z_]*\) .*/\1/p')} + for func in $funcs ; do + $func $dir || return 1 + done +} + +function add_something() { + local dir=$1 + local poolname=$2 + local obj=${3:-SOMETHING} + local scrub=${4:-noscrub} + + if [ "$scrub" = "noscrub" ]; + then + ceph osd set noscrub || return 1 + ceph osd set nodeep-scrub || return 1 + else + ceph osd unset noscrub || return 1 + ceph osd unset nodeep-scrub || return 1 + fi + + local payload=ABCDEF + echo $payload > $dir/ORIGINAL + rados --pool $poolname put $obj $dir/ORIGINAL || return 1 +} + +# +# Test automatic repair with distrust set +# +function TEST_distrust_scrub_replicated() { + local dir=$1 + local poolname=dsr_pool + local total_objs=2 + + setup $dir || return 1 + run_mon $dir a --osd_pool_default_size=2 || return 1 + run_mgr $dir x || return 1 + run_osd $dir 0 || return 1 + run_osd $dir 1 || return 1 + create_rbd_pool || return 1 + wait_for_clean || return 1 + + create_pool foo 1 || return 1 + create_pool $poolname 1 1 || return 1 + wait_for_clean || return 1 + + for i in $(seq 1 $total_objs) ; do + objname=ROBJ${i} + add_something $dir $poolname $objname || return 1 + done + + local pg=$(get_pg $poolname ROBJ0) + + for i in $(seq 1 $total_objs) ; do + objname=ROBJ${i} + + case $i in + 1) + # Deep-scrub only (all replicas are diffent than the object info + local payload=XROBJ1 + echo $payload > $dir/new.ROBJ1 + objectstore_tool $dir 0 $objname set-bytes $dir/new.ROBJ1 || return 1 + objectstore_tool $dir 1 $objname set-bytes $dir/new.ROBJ1 || return 1 + ;; + + 2) + # Deep-scrub only (all replicas are diffent than the object info + local payload=XROBJ2 + echo $payload > $dir/new.ROBJ2 + objectstore_tool $dir 0 $objname set-bytes $dir/new.ROBJ2 || return 1 + objectstore_tool $dir 1 $objname set-bytes $dir/new.ROBJ2 || return 1 + # Make one replica have a different object info, so a full repair must happen too + objectstore_tool $dir 0 $objname corrupt-info || return 1 + ;; + esac + done + + # This should fix the data_digest because osd-distrust-data-digest is true + pg_deep_scrub $pg + + # This hangs if the scrub didn't repair the data_digest + timeout 30 rados -p $poolname get ROBJ1 $dir/robj1.out || return 1 + diff -q $dir/new.ROBJ1 $dir/robj1.out || return 1 + rm -f $dir/new.ROBJ1 $dir/robj1.out || return 1 + + rados list-inconsistent-pg $poolname > $dir/json || return 1 + # Check pg count + test $(jq '. | length' $dir/json) = "1" || return 1 + # Check pgid + test $(jq -r '.[0]' $dir/json) = $pg || return 1 + + rados list-inconsistent-obj $pg > $dir/json || return 1 + # Get epoch for repair-get requests + epoch=$(jq .epoch $dir/json) + + jq "$jqfilter" << EOF | jq '.inconsistents' | python -c "$sortkeys" > $dir/checkcsjson +{ + "inconsistents": [ + { + "shards": [ + { + "object_info": { + "watchers": {}, + "manifest": { + "redirect_target": { + "namespace": "", + "pool": -9223372036854776000, + "max": 0, + "hash": 0, + "snapid": 0, + "key": "", + "oid": "" + }, + "type": 0 + }, + "alloc_hint_flags": 255, + "expected_write_size": 0, + "local_mtime": "2018-07-24 15:05:56.027234", + "mtime": "2018-07-24 15:05:56.021775", + "size": 7, + "user_version": 2, + "last_reqid": "client.4137.0:1", + "prior_version": "0'0", + "version": "23'2", + "oid": { + "namespace": "", + "pool": 3, + "max": 0, + "hash": 2026323607, + "snapid": -2, + "key": "", + "oid": "ROBJ2" + }, + "lost": 0, + "flags": [ + "dirty", + "data_digest" + ], + "legacy_snaps": [], + "truncate_seq": 0, + "truncate_size": 0, + "data_digest": "0x2ddbf8f5", + "omap_digest": "0xffffffff", + "expected_object_size": 0 + }, + "data_digest": "0x0bb7ab52", + "omap_digest": "0xffffffff", + "size": 7, + "errors": [], + "primary": false, + "osd": 0 + }, + { + "object_info": { + "watchers": {}, + "manifest": { + "redirect_target": { + "namespace": "", + "pool": -9223372036854776000, + "max": 0, + "hash": 0, + "snapid": 0, + "key": "", + "oid": "" + }, + "type": 0 + }, + "alloc_hint_flags": 0, + "expected_write_size": 0, + "local_mtime": "2018-07-24 15:05:56.027234", + "mtime": "2018-07-24 15:05:56.021775", + "size": 7, + "user_version": 2, + "last_reqid": "client.4137.0:1", + "prior_version": "0'0", + "version": "23'2", + "oid": { + "namespace": "", + "pool": 3, + "max": 0, + "hash": 2026323607, + "snapid": -2, + "key": "", + "oid": "ROBJ2" + }, + "lost": 0, + "flags": [ + "dirty", + "data_digest" + ], + "legacy_snaps": [], + "truncate_seq": 0, + "truncate_size": 0, + "data_digest": "0x2ddbf8f5", + "omap_digest": "0xffffffff", + "expected_object_size": 0 + }, + "data_digest": "0x0bb7ab52", + "omap_digest": "0xffffffff", + "size": 7, + "errors": [], + "primary": true, + "osd": 1 + } + ], + "selected_object_info": { + "watchers": {}, + "manifest": { + "redirect_target": { + "namespace": "", + "pool": -9223372036854776000, + "max": 0, + "hash": 0, + "snapid": 0, + "key": "", + "oid": "" + }, + "type": 0 + }, + "alloc_hint_flags": 0, + "expected_write_size": 0, + "local_mtime": "2018-07-24 15:05:56.027234", + "mtime": "2018-07-24 15:05:56.021775", + "size": 7, + "user_version": 2, + "last_reqid": "client.4137.0:1", + "prior_version": "0'0", + "version": "23'2", + "oid": { + "namespace": "", + "pool": 3, + "max": 0, + "hash": 2026323607, + "snapid": -2, + "key": "", + "oid": "ROBJ2" + }, + "lost": 0, + "flags": [ + "dirty", + "data_digest" + ], + "legacy_snaps": [], + "truncate_seq": 0, + "truncate_size": 0, + "data_digest": "0x2ddbf8f5", + "omap_digest": "0xffffffff", + "expected_object_size": 0 + }, + "union_shard_errors": [], + "errors": [ + "object_info_inconsistency" + ], + "object": { + "version": 2, + "snap": "head", + "locator": "", + "nspace": "", + "name": "ROBJ2" + } + } + ], + "epoch": 42 +} +EOF + + jq "$jqfilter" $dir/json | jq '.inconsistents' | python -c "$sortkeys" > $dir/csjson + diff ${DIFFCOLOPTS} $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 + if test $getjson = "yes" + then + jq '.' $dir/json > save1.json + fi + + if test "$LOCALRUN" = "yes" && which jsonschema > /dev/null; + then + jsonschema -i $dir/json $CEPH_ROOT/doc/rados/command/list-inconsistent-obj.json || return 1 + fi + + repair $pg + wait_for_clean + + timeout 30 rados -p $poolname get ROBJ2 $dir/robj2.out || return 1 + diff -q $dir/new.ROBJ2 $dir/robj2.out || return 1 + rm -f $dir/new.ROBJ2 $dir/robj2.out || return 1 + + rados rmpool $poolname $poolname --yes-i-really-really-mean-it + teardown $dir || return 1 +} + +main osd-scrub-distrust "$@" + +# Local Variables: +# compile-command: "cd build ; make -j4 && \ +# ../qa/run-standalone.sh osd-scrub-distrust" +# End: diff --git a/ceph/qa/standalone/scrub/osd-scrub-repair.sh b/ceph/qa/standalone/scrub/osd-scrub-repair.sh index a3732ba32..257b9dbaf 100755 --- a/ceph/qa/standalone/scrub/osd-scrub-repair.sh +++ b/ceph/qa/standalone/scrub/osd-scrub-repair.sh @@ -490,7 +490,7 @@ function TEST_list_missing_erasure_coded_overwrites() { function TEST_corrupt_scrub_replicated() { local dir=$1 local poolname=csr_pool - local total_objs=16 + local total_objs=18 setup $dir || return 1 run_mon $dir a --osd_pool_default_size=2 || return 1 @@ -612,6 +612,24 @@ function TEST_corrupt_scrub_replicated() { objectstore_tool $dir 0 $objname rm-attr snapset || return 1 echo -n bad-val > $dir/bad-val objectstore_tool $dir 1 $objname set-attr snapset $dir/bad-val || return 1 + ;; + + 17) + # Deep-scrub only (all replicas are diffent than the object info + local payload=ROBJ17 + echo $payload > $dir/new.ROBJ17 + objectstore_tool $dir 0 $objname set-bytes $dir/new.ROBJ17 || return 1 + objectstore_tool $dir 1 $objname set-bytes $dir/new.ROBJ17 || return 1 + ;; + + 18) + # Deep-scrub only (all replicas are diffent than the object info + local payload=ROBJ18 + echo $payload > $dir/new.ROBJ18 + objectstore_tool $dir 0 $objname set-bytes $dir/new.ROBJ18 || return 1 + objectstore_tool $dir 1 $objname set-bytes $dir/new.ROBJ18 || return 1 + # Make one replica have a different object info, so a full repair must happen too + objectstore_tool $dir $osd $objname corrupt-info || return 1 esac done @@ -1039,6 +1057,125 @@ function TEST_corrupt_scrub_replicated() { ] }, { + "errors": [ + "object_info_inconsistency" + ], + "object": { + "locator": "", + "name": "ROBJ18", + "nspace": "", + "snap": "head" + }, + "selected_object_info": { + "alloc_hint_flags": 255, + "data_digest": "0x2ddbf8f5", + "expected_object_size": 0, + "expected_write_size": 0, + "flags": [ + "dirty", + "omap", + "data_digest", + "omap_digest" + ], + "lost": 0, + "manifest": { + "type": 0 + }, + "oid": { + "hash": 1629828556, + "key": "", + "max": 0, + "namespace": "", + "oid": "ROBJ18", + "pool": 3, + "snapid": -2 + }, + "omap_digest": "0xddc3680f", + "size": 7, + "truncate_seq": 0, + "truncate_size": 0, + "user_version": 54, + "watchers": {} + }, + "shards": [ + { + "errors": [], + "object_info": { + "alloc_hint_flags": 0, + "data_digest": "0x2ddbf8f5", + "expected_object_size": 0, + "expected_write_size": 0, + "flags": [ + "dirty", + "omap", + "data_digest", + "omap_digest" + ], + "lost": 0, + "manifest": { + "type": 0 + }, + "oid": { + "hash": 1629828556, + "key": "", + "max": 0, + "namespace": "", + "oid": "ROBJ18", + "pool": 3, + "snapid": -2 + }, + "omap_digest": "0xddc3680f", + "size": 7, + "truncate_seq": 0, + "truncate_size": 0, + "user_version": 54, + "watchers": {} + }, + "osd": 0, + "primary": false, + "size": 7 + }, + { + "errors": [], + "object_info": { + "alloc_hint_flags": 255, + "data_digest": "0x2ddbf8f5", + "expected_object_size": 0, + "expected_write_size": 0, + "flags": [ + "dirty", + "omap", + "data_digest", + "omap_digest" + ], + "lost": 0, + "manifest": { + "type": 0 + }, + "oid": { + "hash": 1629828556, + "key": "", + "max": 0, + "namespace": "", + "oid": "ROBJ18", + "pool": 3, + "snapid": -2 + }, + "omap_digest": "0xddc3680f", + "size": 7, + "truncate_seq": 0, + "truncate_size": 0, + "user_version": 54, + "watchers": {} + }, + "osd": 1, + "primary": true, + "size": 7 + } + ], + "union_shard_errors": [] + }, + { "shards": [ { "size": 7, @@ -1154,7 +1291,7 @@ function TEST_corrupt_scrub_replicated() { "version": "79'66", "prior_version": "79'65", "last_reqid": "client.4554.0:1", - "user_version": 66, + "user_version": 74, "size": 7, "mtime": "", "local_mtime": "", @@ -1206,7 +1343,7 @@ function TEST_corrupt_scrub_replicated() { "version": "95'67", "prior_version": "51'64", "last_reqid": "client.4649.0:1", - "user_version": 67, + "user_version": 75, "size": 1, "mtime": "", "local_mtime": "", @@ -1292,7 +1429,7 @@ function TEST_corrupt_scrub_replicated() { "version": "95'67", "prior_version": "51'64", "last_reqid": "client.4649.0:1", - "user_version": 67, + "user_version": 75, "size": 1, "mtime": "", "local_mtime": "", @@ -1335,7 +1472,7 @@ function TEST_corrupt_scrub_replicated() { EOF jq "$jqfilter" $dir/json | jq '.inconsistents' | python -c "$sortkeys" > $dir/csjson - diff ${DIFFCOLOPTS} $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 + multidiff $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 if test $getjson = "yes" then jq '.' $dir/json > save1.json @@ -1907,7 +2044,201 @@ EOF ] }, { - "shards": [ + "errors": [], + "object": { + "locator": "", + "name": "ROBJ17", + "nspace": "", + "snap": "head" + }, + "selected_object_info": { + "alloc_hint_flags": 0, + "data_digest": "0x2ddbf8f5", + "expected_object_size": 0, + "expected_write_size": 0, + "flags": [ + "dirty", + "omap", + "data_digest", + "omap_digest" + ], + "lost": 0, + "manifest": { + "type": 0 + }, + "oid": { + "hash": 1884071249, + "key": "", + "max": 0, + "namespace": "", + "oid": "ROBJ17", + "pool": 3, + "snapid": -2 + }, + "omap_digest": "0xe9572720", + "size": 7, + "truncate_seq": 0, + "truncate_size": 0, + "user_version": 51, + "watchers": {} + }, + "shards": [ + { + "data_digest": "0x5af0c3ef", + "errors": [ + "data_digest_mismatch_info" + ], + "omap_digest": "0xe9572720", + "osd": 0, + "primary": false, + "size": 7 + }, + { + "data_digest": "0x5af0c3ef", + "errors": [ + "data_digest_mismatch_info" + ], + "omap_digest": "0xe9572720", + "osd": 1, + "primary": true, + "size": 7 + } + ], + "union_shard_errors": [ + "data_digest_mismatch_info" + ] + }, + { + "errors": [ + "object_info_inconsistency" + ], + "object": { + "locator": "", + "name": "ROBJ18", + "nspace": "", + "snap": "head" + }, + "selected_object_info": { + "alloc_hint_flags": 255, + "data_digest": "0x2ddbf8f5", + "expected_object_size": 0, + "expected_write_size": 0, + "flags": [ + "dirty", + "omap", + "data_digest", + "omap_digest" + ], + "lost": 0, + "manifest": { + "type": 0 + }, + "oid": { + "hash": 1629828556, + "key": "", + "max": 0, + "namespace": "", + "oid": "ROBJ18", + "pool": 3, + "snapid": -2 + }, + "omap_digest": "0xddc3680f", + "size": 7, + "truncate_seq": 0, + "truncate_size": 0, + "user_version": 54, + "watchers": {} + }, + "shards": [ + { + "data_digest": "0xbd89c912", + "errors": [ + "data_digest_mismatch_info" + ], + "object_info": { + "alloc_hint_flags": 0, + "data_digest": "0x2ddbf8f5", + "expected_object_size": 0, + "expected_write_size": 0, + "flags": [ + "dirty", + "omap", + "data_digest", + "omap_digest" + ], + "lost": 0, + "manifest": { + "type": 0 + }, + "oid": { + "hash": 1629828556, + "key": "", + "max": 0, + "namespace": "", + "oid": "ROBJ18", + "pool": 3, + "snapid": -2 + }, + "omap_digest": "0xddc3680f", + "size": 7, + "truncate_seq": 0, + "truncate_size": 0, + "user_version": 54, + "watchers": {} + }, + "omap_digest": "0xddc3680f", + "osd": 0, + "primary": false, + "size": 7 + }, + { + "data_digest": "0xbd89c912", + "errors": [ + "data_digest_mismatch_info" + ], + "object_info": { + "alloc_hint_flags": 255, + "data_digest": "0x2ddbf8f5", + "expected_object_size": 0, + "expected_write_size": 0, + "flags": [ + "dirty", + "omap", + "data_digest", + "omap_digest" + ], + "lost": 0, + "manifest": { + "type": 0 + }, + "oid": { + "hash": 1629828556, + "key": "", + "max": 0, + "namespace": "", + "oid": "ROBJ18", + "pool": 3, + "snapid": -2 + }, + "omap_digest": "0xddc3680f", + "size": 7, + "truncate_seq": 0, + "truncate_size": 0, + "user_version": 54, + "watchers": {} + }, + "omap_digest": "0xddc3680f", + "osd": 1, + "primary": true, + "size": 7 + } + ], + "union_shard_errors": [ + "data_digest_mismatch_info" + ] + }, + { + "shards": [ { "data_digest": "0x578a4830", "omap_digest": "0xf8e11918", @@ -2383,7 +2714,7 @@ EOF "version": "79'66", "prior_version": "79'65", "last_reqid": "client.4554.0:1", - "user_version": 66, + "user_version": 74, "size": 7, "mtime": "2018-04-05 14:34:05.598688", "local_mtime": "2018-04-05 14:34:05.599698", @@ -2481,7 +2812,7 @@ EOF "version": "119'68", "prior_version": "51'64", "last_reqid": "client.4834.0:1", - "user_version": 68, + "user_version": 76, "size": 3, "mtime": "2018-04-05 14:35:01.500659", "local_mtime": "2018-04-05 14:35:01.502117", @@ -2525,7 +2856,7 @@ EOF "version": "119'68", "prior_version": "51'64", "last_reqid": "client.4834.0:1", - "user_version": 68, + "user_version": 76, "size": 3, "mtime": "2018-04-05 14:35:01.500659", "local_mtime": "2018-04-05 14:35:01.502117", @@ -2568,7 +2899,7 @@ EOF EOF jq "$jqfilter" $dir/json | jq '.inconsistents' | python -c "$sortkeys" > $dir/csjson - diff ${DIFFCOLOPTS} $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 + multidiff $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 if test $getjson = "yes" then jq '.' $dir/json > save2.json @@ -2579,6 +2910,18 @@ EOF jsonschema -i $dir/json $CEPH_ROOT/doc/rados/command/list-inconsistent-obj.json || return 1 fi + repair $pg + wait_for_clean + + # This hangs if the repair doesn't work + timeout 30 rados -p $poolname get ROBJ17 $dir/robj17.out || return 1 + timeout 30 rados -p $poolname get ROBJ18 $dir/robj18.out || return 1 + # Even though we couldn't repair all of the introduced errors, we can fix ROBJ17 + diff -q $dir/new.ROBJ17 $dir/robj17.out || return 1 + rm -f $dir/new.ROBJ17 $dir/robj17.out || return 1 + diff -q $dir/new.ROBJ18 $dir/robj18.out || return 1 + rm -f $dir/new.ROBJ18 $dir/robj18.out || return 1 + rados rmpool $poolname $poolname --yes-i-really-really-mean-it teardown $dir || return 1 } @@ -3308,7 +3651,7 @@ function corrupt_scrub_erasure() { EOF jq "$jqfilter" $dir/json | jq '.inconsistents' | python -c "$sortkeys" > $dir/csjson - diff ${DIFFCOLOPTS} $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 + multidiff $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 if test $getjson = "yes" then jq '.' $dir/json > save3.json @@ -4722,7 +5065,7 @@ EOF fi jq "$jqfilter" $dir/json | jq '.inconsistents' | python -c "$sortkeys" > $dir/csjson - diff ${DIFFCOLOPTS} $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 + multidiff $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 if test $getjson = "yes" then if [ "$allow_overwrites" = "true" ] @@ -5092,7 +5435,7 @@ function TEST_corrupt_snapset_scrub_rep() { EOF jq "$jqfilter" $dir/json | jq '.inconsistents' | python -c "$sortkeys" > $dir/csjson - diff ${DIFFCOLOPTS} $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 + multidiff $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 if test $getjson = "yes" then jq '.' $dir/json > save6.json diff --git a/ceph/qa/standalone/scrub/osd-scrub-snaps.sh b/ceph/qa/standalone/scrub/osd-scrub-snaps.sh index a83cfe75c..3370332de 100755 --- a/ceph/qa/standalone/scrub/osd-scrub-snaps.sh +++ b/ceph/qa/standalone/scrub/osd-scrub-snaps.sh @@ -207,7 +207,7 @@ function TEST_scrub_snaps() { EOF jq "$jqfilter" $dir/json | python -c "$sortkeys" > $dir/csjson - diff ${DIFFCOLOPTS} $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 + multidiff $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 rados list-inconsistent-snapset $pgid > $dir/json || return 1 @@ -691,7 +691,7 @@ EOF EOF jq "$jqfilter" $dir/json | python -c "$sortkeys" > $dir/csjson - diff ${DIFFCOLOPTS} $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 + multidiff $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 if test $getjson = "yes" then jq '.' $dir/json > save1.json @@ -1178,7 +1178,7 @@ EOF fi jq "$jqfilter" $dir/json | python -c "$sortkeys" > $dir/csjson - diff ${DIFFCOLOPTS} $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 + multidiff $dir/checkcsjson $dir/csjson || test $getjson = "yes" || return 1 if test $getjson = "yes" then jq '.' $dir/json > save1.json diff --git a/ceph/qa/standalone/scrub/osd-unexpected-clone.sh b/ceph/qa/standalone/scrub/osd-unexpected-clone.sh new file mode 100755 index 000000000..552644048 --- /dev/null +++ b/ceph/qa/standalone/scrub/osd-unexpected-clone.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# +# Copyright (C) 2015 Intel +# Copyright (C) 2014, 2015 Red Hat +# +# Author: Xiaoxi Chen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Public License for more details. +# + +source $CEPH_ROOT/qa/standalone/ceph-helpers.sh + +function run() { + local dir=$1 + shift + + export CEPH_MON="127.0.0.1:7144" # git grep '\<7144\>' : there must be only one + export CEPH_ARGS + CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none " + CEPH_ARGS+="--mon-host=$CEPH_MON " + + local funcs=${@:-$(set | sed -n -e 's/^\(TEST_[0-9a-z_]*\) .*/\1/p')} + for func in $funcs ; do + setup $dir || return 1 + $func $dir || return 1 + teardown $dir || return 1 + done +} + +function TEST_recover_unexpected() { + local dir=$1 + + run_mon $dir a || return 1 + run_mgr $dir x || return 1 + run_osd $dir 0 || return 1 + run_osd $dir 1 || return 1 + run_osd $dir 2 || return 1 + + ceph osd pool create foo 1 + rados -p foo put foo /etc/passwd + rados -p foo mksnap snap + rados -p foo put foo /etc/group + + wait_for_clean || return 1 + + local osd=$(get_primary foo foo) + + JSON=`objectstore_tool $dir $osd --op list foo | grep snapid.:1` + echo "JSON is $JSON" + rm -f $dir/_ $dir/data + objectstore_tool $dir $osd "$JSON" get-attr _ > $dir/_ || return 1 + objectstore_tool $dir $osd "$JSON" get-bytes $dir/data || return 1 + + rados -p foo rmsnap snap + + sleep 5 + + objectstore_tool $dir $osd "$JSON" set-bytes $dir/data || return 1 + objectstore_tool $dir $osd "$JSON" set-attr _ $dir/_ || return 1 + + sleep 5 + + ceph pg repair 1.0 || return 1 + + sleep 10 + + ceph log last + + # make sure osds are still up + timeout 60 ceph tell osd.0 version || return 1 + timeout 60 ceph tell osd.1 version || return 1 + timeout 60 ceph tell osd.2 version || return 1 +} + + +main osd-unexpected-clone "$@" + +# Local Variables: +# compile-command: "cd ../.. ; make -j4 && test/osd/osd-bench.sh" +# End: diff --git a/ceph/qa/suites/krbd/rbd-nomount/conf.yaml b/ceph/qa/suites/krbd/rbd-nomount/conf.yaml index 8279674df..f1ebeb500 100644 --- a/ceph/qa/suites/krbd/rbd-nomount/conf.yaml +++ b/ceph/qa/suites/krbd/rbd-nomount/conf.yaml @@ -5,3 +5,5 @@ overrides: ms die on skipped message: false client: rbd default features: 5 + log-whitelist: + - slow request diff --git a/ceph/qa/suites/krbd/rbd/conf.yaml b/ceph/qa/suites/krbd/rbd/conf.yaml index 8279674df..f1ebeb500 100644 --- a/ceph/qa/suites/krbd/rbd/conf.yaml +++ b/ceph/qa/suites/krbd/rbd/conf.yaml @@ -5,3 +5,5 @@ overrides: ms die on skipped message: false client: rbd default features: 5 + log-whitelist: + - slow request diff --git a/ceph/qa/suites/krbd/singleton/conf.yaml b/ceph/qa/suites/krbd/singleton/conf.yaml index 8279674df..f1ebeb500 100644 --- a/ceph/qa/suites/krbd/singleton/conf.yaml +++ b/ceph/qa/suites/krbd/singleton/conf.yaml @@ -5,3 +5,5 @@ overrides: ms die on skipped message: false client: rbd default features: 5 + log-whitelist: + - slow request diff --git a/ceph/qa/suites/krbd/thrash/conf.yaml b/ceph/qa/suites/krbd/thrash/conf.yaml index 8279674df..f1ebeb500 100644 --- a/ceph/qa/suites/krbd/thrash/conf.yaml +++ b/ceph/qa/suites/krbd/thrash/conf.yaml @@ -5,3 +5,5 @@ overrides: ms die on skipped message: false client: rbd default features: 5 + log-whitelist: + - slow request diff --git a/ceph/qa/suites/krbd/wac/sysfs/conf.yaml b/ceph/qa/suites/krbd/wac/sysfs/conf.yaml index 8279674df..f1ebeb500 100644 --- a/ceph/qa/suites/krbd/wac/sysfs/conf.yaml +++ b/ceph/qa/suites/krbd/wac/sysfs/conf.yaml @@ -5,3 +5,5 @@ overrides: ms die on skipped message: false client: rbd default features: 5 + log-whitelist: + - slow request diff --git a/ceph/qa/suites/krbd/wac/wac/conf.yaml b/ceph/qa/suites/krbd/wac/wac/conf.yaml index 8279674df..f1ebeb500 100644 --- a/ceph/qa/suites/krbd/wac/wac/conf.yaml +++ b/ceph/qa/suites/krbd/wac/wac/conf.yaml @@ -5,3 +5,5 @@ overrides: ms die on skipped message: false client: rbd default features: 5 + log-whitelist: + - slow request diff --git a/ceph/qa/suites/powercycle/osd/tasks/cfuse_workunit_suites_fsync.yaml b/ceph/qa/suites/powercycle/osd/tasks/cfuse_workunit_suites_fsync.yaml index c6043e209..2cbb03c77 100644 --- a/ceph/qa/suites/powercycle/osd/tasks/cfuse_workunit_suites_fsync.yaml +++ b/ceph/qa/suites/powercycle/osd/tasks/cfuse_workunit_suites_fsync.yaml @@ -1,3 +1,9 @@ +overrides: + ceph: + conf: + global: + osd_pg_log_dups_tracked: 10000 + tasks: - ceph-fuse: - workunit: diff --git a/ceph/qa/suites/powercycle/osd/whitelist_health.yaml b/ceph/qa/suites/powercycle/osd/whitelist_health.yaml index 0235037b5..f9ab0a62b 100644 --- a/ceph/qa/suites/powercycle/osd/whitelist_health.yaml +++ b/ceph/qa/suites/powercycle/osd/whitelist_health.yaml @@ -2,4 +2,5 @@ overrides: ceph: log-whitelist: - \(MDS_TRIM\) + - \(MDS_SLOW_REQUEST\) - Behind on trimming diff --git a/ceph/qa/suites/upgrade/jewel-x/ceph-deploy/slow_requests.yaml b/ceph/qa/suites/upgrade/jewel-x/ceph-deploy/slow_requests.yaml new file mode 120000 index 000000000..bf4f8b45e --- /dev/null +++ b/ceph/qa/suites/upgrade/jewel-x/ceph-deploy/slow_requests.yaml @@ -0,0 +1 @@ +../parallel/slow_requests.yaml \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/jewel-x/parallel/slow_requests.yaml b/ceph/qa/suites/upgrade/jewel-x/parallel/slow_requests.yaml new file mode 100644 index 000000000..81503137a --- /dev/null +++ b/ceph/qa/suites/upgrade/jewel-x/parallel/slow_requests.yaml @@ -0,0 +1,4 @@ +overrides: + ceph: + log-whitelist: + - slow request diff --git a/ceph/qa/suites/upgrade/jewel-x/stress-split-erasure-code/slow_requests.yaml b/ceph/qa/suites/upgrade/jewel-x/stress-split-erasure-code/slow_requests.yaml new file mode 120000 index 000000000..bf4f8b45e --- /dev/null +++ b/ceph/qa/suites/upgrade/jewel-x/stress-split-erasure-code/slow_requests.yaml @@ -0,0 +1 @@ +../parallel/slow_requests.yaml \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/jewel-x/stress-split/slow_requests.yaml b/ceph/qa/suites/upgrade/jewel-x/stress-split/slow_requests.yaml new file mode 120000 index 000000000..bf4f8b45e --- /dev/null +++ b/ceph/qa/suites/upgrade/jewel-x/stress-split/slow_requests.yaml @@ -0,0 +1 @@ +../parallel/slow_requests.yaml \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/kraken-x/parallel/0-cluster/start.yaml b/ceph/qa/suites/upgrade/kraken-x/parallel/0-cluster/start.yaml index 4d10158b7..2b8219c96 100644 --- a/ceph/qa/suites/upgrade/kraken-x/parallel/0-cluster/start.yaml +++ b/ceph/qa/suites/upgrade/kraken-x/parallel/0-cluster/start.yaml @@ -27,6 +27,8 @@ overrides: - wrongly marked - \(POOL_APP_NOT_ENABLED\) - overall HEALTH_ + - \(REQUEST_SLOW\) + - slow request conf: global: enable experimental unrecoverable data corrupting features: "*" diff --git a/ceph/qa/suites/upgrade/kraken-x/stress-split/0-cluster/start.yaml b/ceph/qa/suites/upgrade/kraken-x/stress-split/0-cluster/start.yaml index b8a28f986..c12e10b98 100644 --- a/ceph/qa/suites/upgrade/kraken-x/stress-split/0-cluster/start.yaml +++ b/ceph/qa/suites/upgrade/kraken-x/stress-split/0-cluster/start.yaml @@ -10,6 +10,8 @@ overrides: - overall HEALTH_ - \(MON_DOWN\) - \(MGR_DOWN\) + - \(REQUEST_SLOW\) + - slow request conf: global: enable experimental unrecoverable data corrupting features: "*" diff --git a/ceph/qa/suites/upgrade/luminous-p2p/point-to-point-upgrade.yaml b/ceph/qa/suites/upgrade/luminous-p2p/point-to-point-upgrade.yaml index f660e2a2f..ffa21378c 100644 --- a/ceph/qa/suites/upgrade/luminous-p2p/point-to-point-upgrade.yaml +++ b/ceph/qa/suites/upgrade/luminous-p2p/point-to-point-upgrade.yaml @@ -7,6 +7,8 @@ meta: run workload and upgrade-sequence in parallel install ceph/luminous v12.2.5 point version run workload and upgrade-sequence in parallel + install ceph/luminous v12.2.7 point version + run workload and upgrade-sequence in parallel install ceph/luminous latest version run workload and upgrade-sequence in parallel overrides: @@ -28,6 +30,7 @@ overrides: - PG_AVAILABILITY - PG_DEGRADED - application not enabled + - overall HEALTH_ fs: xfs conf: mon: @@ -87,13 +90,25 @@ tasks: - workload_luminous - upgrade-sequence_luminous - print: "**** done parallel luminous v12.2.5" + +#### upgrade to v12.2.7 +- install.upgrade: + #exclude_packages: ['ceph-mgr','libcephfs2','libcephfs-devel','libcephfs-dev'] + mon.a: + tag: v12.2.7 + mon.b: + tag: v12.2.7 + # Note that client.a IS NOT upgraded at this point +- parallel: + - workload_luminous + - upgrade-sequence_luminous +- print: "**** done parallel luminous v12.2.7" + #### upgrade to latest luminous - install.upgrade: #exclude_packages: ['ceph-mgr','libcephfs2','libcephfs-devel','libcephfs-dev'] mon.a: - branch: luminous mon.b: - branch: luminous # Note that client.a IS NOT upgraded at this point - parallel: - workload_luminous diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/% b/ceph/qa/suites/upgrade/luminous-x/parallel/% deleted file mode 100644 index e69de29bb..000000000 diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/+ b/ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/+ deleted file mode 100644 index e69de29bb..000000000 diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/openstack.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/openstack.yaml deleted file mode 100644 index f4d1349b4..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/openstack.yaml +++ /dev/null @@ -1,4 +0,0 @@ -openstack: - - volumes: # attached to each instance - count: 3 - size: 30 # GB diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/start.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/start.yaml deleted file mode 100644 index f8732212d..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/0-cluster/start.yaml +++ /dev/null @@ -1,40 +0,0 @@ -meta: -- desc: | - Run ceph on two nodes, - with a separate client 0,1,2 third node. - Use xfs beneath the osds. - CephFS tests running on client 2,3 -roles: -- - mon.a - - mgr.x - - mds.a - - osd.0 - - osd.1 -- - mon.b - - mon.c - - osd.2 - - osd.3 -- - client.0 - - client.1 - - client.2 - - client.3 -- - client.4 -overrides: - ceph: - log-whitelist: - - scrub mismatch - - ScrubResult - - wrongly marked - - \(POOL_APP_NOT_ENABLED\) - - overall HEALTH_ - conf: - global: - enable experimental unrecoverable data corrupting features: "*" - mon: - mon warn on osd down out interval zero: false - osd: - osd_class_load_list: "cephfs hello journal lock log numops rbd refcount - replica_log rgw sdk statelog timeindex user version" - osd_class_default_list: "cephfs hello journal lock log numops rbd refcount - replica_log rgw sdk statelog timeindex user version" - fs: xfs diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/1-ceph-install/luminous.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/1-ceph-install/luminous.yaml deleted file mode 100644 index 3d57f792b..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/1-ceph-install/luminous.yaml +++ /dev/null @@ -1,43 +0,0 @@ -meta: -- desc: | - install ceph/luminous latest - run workload and upgrade-sequence in parallel - upgrade the client node -tasks: -- install: - branch: luminous -- print: "**** done installing luminous" -- ceph: - log-whitelist: - - overall HEALTH_ - - \(FS_ - - \(MDS_ - - \(OSD_ - - \(MON_DOWN\) - - \(CACHE_POOL_ - - \(POOL_ - - \(MGR_DOWN\) - - \(PG_ - - \(SMALLER_PGP_NUM\) - - Monitor daemon marked osd - - Behind on trimming - - Manager daemon - conf: - global: - mon warn on pool no app: false -- exec: - osd.0: - - ceph osd require-osd-release luminous - - ceph osd set-require-min-compat-client luminous -- print: "**** done ceph" -- install.upgrade: - mon.a: - mon.b: -- print: "**** done install.upgrade both hosts" -- parallel: - - workload - - upgrade-sequence -- print: "**** done parallel" -- install.upgrade: - client.0: -- print: "**** done install.upgrade on client.0" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/+ b/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/+ deleted file mode 100644 index e69de29bb..000000000 diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/blogbench.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/blogbench.yaml deleted file mode 100644 index 021fcc681..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/blogbench.yaml +++ /dev/null @@ -1,14 +0,0 @@ -meta: -- desc: | - run a cephfs stress test - mount ceph-fuse on client.2 before running workunit -workload: - full_sequential: - - sequential: - - ceph-fuse: - - print: "**** done ceph-fuse 2-workload" - - workunit: - clients: - client.2: - - suites/blogbench.sh - - print: "**** done suites/blogbench.sh 2-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/ec-rados-default.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/ec-rados-default.yaml deleted file mode 100644 index 5c5a95880..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/ec-rados-default.yaml +++ /dev/null @@ -1,24 +0,0 @@ -meta: -- desc: | - run run randomized correctness test for rados operations - on an erasure-coded pool -workload: - full_sequential: - - rados: - clients: [client.0] - ops: 4000 - objects: 50 - ec_pool: true - write_append_excl: false - op_weights: - read: 100 - write: 0 - append: 100 - delete: 50 - snap_create: 50 - snap_remove: 50 - rollback: 50 - copy_from: 50 - setattr: 25 - rmattr: 25 - - print: "**** done rados ec task" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/rados_api.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/rados_api.yaml deleted file mode 100644 index e4cc9f961..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/rados_api.yaml +++ /dev/null @@ -1,11 +0,0 @@ -meta: -- desc: | - object class functional tests -workload: - full_sequential: - - workunit: - branch: luminous - clients: - client.0: - - cls - - print: "**** done cls 2-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/rados_loadgenbig.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/rados_loadgenbig.yaml deleted file mode 100644 index 874a8c5e0..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/rados_loadgenbig.yaml +++ /dev/null @@ -1,11 +0,0 @@ -meta: -- desc: | - generate read/write load with rados objects ranging from 1MB to 25MB -workload: - full_sequential: - - workunit: - branch: luminous - clients: - client.0: - - rados/load-gen-big.sh - - print: "**** done rados/load-gen-big.sh 2-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/test_rbd_api.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/test_rbd_api.yaml deleted file mode 100644 index 81563c906..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/test_rbd_api.yaml +++ /dev/null @@ -1,11 +0,0 @@ -meta: -- desc: | - librbd C and C++ api tests -workload: - full_sequential: - - workunit: - branch: luminous - clients: - client.0: - - rbd/test_librbd.sh - - print: "**** done rbd/test_librbd.sh 2-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/test_rbd_python.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/test_rbd_python.yaml deleted file mode 100644 index e17207d20..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/2-workload/test_rbd_python.yaml +++ /dev/null @@ -1,11 +0,0 @@ -meta: -- desc: | - librbd python api tests -workload: - full_sequential: - - workunit: - branch: luminous - clients: - client.0: - - rbd/test_librbd_python.sh - - print: "**** done rbd/test_librbd_python.sh 2-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/3-upgrade-sequence/upgrade-all.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/3-upgrade-sequence/upgrade-all.yaml deleted file mode 100644 index cff3a6836..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/3-upgrade-sequence/upgrade-all.yaml +++ /dev/null @@ -1,16 +0,0 @@ -meta: -- desc: | - upgrade the ceph cluster -upgrade-sequence: - sequential: - - ceph.restart: - daemons: [mon.a, mon.b, mon.c, mgr.x] - - ceph.restart: - daemons: [osd.0, osd.1, osd.2, osd.3] - wait-for-healthy: false - wait-for-osds-up: true - - ceph.restart: - daemons: [mds.a] - wait-for-healthy: false - wait-for-osds-up: true - - print: "**** done ceph.restart all" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/3-upgrade-sequence/upgrade-mon-osd-mds.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/3-upgrade-sequence/upgrade-mon-osd-mds.yaml deleted file mode 100644 index f197de679..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/3-upgrade-sequence/upgrade-mon-osd-mds.yaml +++ /dev/null @@ -1,35 +0,0 @@ -meta: -- desc: | - upgrade the ceph cluster, - upgrate in two steps - step one ordering: mon.a, osd.0, osd.1, mds.a - step two ordering: mon.b, mon.c, osd.2, osd.3 - ceph expected to be healthy state after each step -upgrade-sequence: - sequential: - - ceph.restart: - daemons: [mon.a] - wait-for-healthy: true - - sleep: - duration: 60 - - ceph.restart: - daemons: [mon.b, mon.c, mgr.x] - wait-for-healthy: true - - sleep: - duration: 60 - - ceph.restart: - daemons: [osd.0, osd.1] - wait-for-healthy: true - - sleep: - duration: 60 - - ceph.restart: [mds.a] - - sleep: - duration: 60 - - sleep: - duration: 60 - - ceph.restart: - daemons: [osd.2, osd.3] - wait-for-healthy: false - wait-for-osds-up: true - - sleep: - duration: 60 diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/+ b/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/+ deleted file mode 100644 index e69de29bb..000000000 diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/blogbench.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/blogbench.yaml deleted file mode 100644 index d2629c03f..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/blogbench.yaml +++ /dev/null @@ -1,13 +0,0 @@ -meta: -- desc: | - run a cephfs stress test - mount ceph-fuse on client.3 before running workunit -tasks: -- sequential: - - ceph-fuse: - - print: "**** done ceph-fuse 5-final-workload" - - workunit: - clients: - client.3: - - suites/blogbench.sh - - print: "**** done suites/blogbench.sh 5-final-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados-snaps-few-objects.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados-snaps-few-objects.yaml deleted file mode 100644 index d8b3dcb38..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados-snaps-few-objects.yaml +++ /dev/null @@ -1,17 +0,0 @@ -meta: -- desc: | - randomized correctness test for rados operations on a replicated pool with snapshots -tasks: - - rados: - clients: [client.1] - ops: 4000 - objects: 50 - write_append_excl: false - op_weights: - read: 100 - write: 100 - delete: 50 - snap_create: 50 - snap_remove: 50 - rollback: 50 - - print: "**** done rados 4-final-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados_loadgenmix.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados_loadgenmix.yaml deleted file mode 100644 index 922a9da4f..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados_loadgenmix.yaml +++ /dev/null @@ -1,9 +0,0 @@ -meta: -- desc: | - generate read/write load with rados objects ranging from 1 byte to 1MB -tasks: - - workunit: - clients: - client.1: - - rados/load-gen-mix.sh - - print: "**** done rados/load-gen-mix.sh 4-final-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados_mon_thrash.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados_mon_thrash.yaml deleted file mode 100644 index a42b7d2d7..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rados_mon_thrash.yaml +++ /dev/null @@ -1,18 +0,0 @@ -meta: -- desc: | - librados C and C++ api tests -overrides: - ceph: - log-whitelist: - - reached quota -tasks: - - mon_thrash: - revive_delay: 20 - thrash_delay: 1 - - print: "**** done mon_thrash 4-final-workload" - - workunit: - branch: luminous - clients: - client.1: - - rados/test.sh - - print: "**** done rados/test.sh 4-final-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_cls.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_cls.yaml deleted file mode 100644 index aaf0a3779..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_cls.yaml +++ /dev/null @@ -1,9 +0,0 @@ -meta: -- desc: | - rbd object class functional tests -tasks: - - workunit: - clients: - client.1: - - cls/test_cls_rbd.sh - - print: "**** done cls/test_cls_rbd.sh 4-final-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_import_export_no_upgrated.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_import_export_no_upgrated.yaml deleted file mode 100644 index 5de8a2361..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_import_export_no_upgrated.yaml +++ /dev/null @@ -1,13 +0,0 @@ -meta: -- desc: | - run basic import/export cli tests for rbd - on NO upgrated client -tasks: - - workunit: - branch: luminous - clients: - client.4: - - rbd/import_export.sh - env: - RBD_CREATE_ARGS: --new-format - - print: "**** done rbd/import_export.sh 4-final-workload on NO upgrated client" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_import_export_upgrated.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_import_export_upgrated.yaml deleted file mode 100644 index 2c7c484e1..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rbd_import_export_upgrated.yaml +++ /dev/null @@ -1,12 +0,0 @@ -meta: -- desc: | - run basic import/export cli tests for rbd - on upgrated client -tasks: - - workunit: - clients: - client.1: - - rbd/import_export.sh - env: - RBD_CREATE_ARGS: --new-format - - print: "**** done rbd/import_export.sh 4-final-workload on upgrated client" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rgw_swift.yaml b/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rgw_swift.yaml deleted file mode 100644 index 7a7659ff4..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/5-final-workload/rgw_swift.yaml +++ /dev/null @@ -1,13 +0,0 @@ -meta: -- desc: | - swift api tests for rgw -overrides: - rgw: - frontend: civetweb -tasks: - - rgw: [client.1] - - print: "**** done rgw 4-final-workload" - - swift: - client.1: - rgw_server: client.1 - - print: "**** done swift 4-final-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/distros b/ceph/qa/suites/upgrade/luminous-x/parallel/distros deleted file mode 120000 index ca99fee94..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/distros +++ /dev/null @@ -1 +0,0 @@ -../../../../distros/supported/ \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/parallel/objectstore b/ceph/qa/suites/upgrade/luminous-x/parallel/objectstore deleted file mode 120000 index 016cbf967..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/parallel/objectstore +++ /dev/null @@ -1 +0,0 @@ -../stress-split/objectstore/ \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/% b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/% deleted file mode 100644 index e69de29bb..000000000 diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/0-cluster b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/0-cluster deleted file mode 120000 index 358093728..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/0-cluster +++ /dev/null @@ -1 +0,0 @@ -../stress-split/0-cluster/ \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/1-ceph-install b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/1-ceph-install deleted file mode 120000 index 0479ac542..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/1-ceph-install +++ /dev/null @@ -1 +0,0 @@ -../stress-split/1-ceph-install/ \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/2-partial-upgrade b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/2-partial-upgrade deleted file mode 120000 index ab35fc1a5..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/2-partial-upgrade +++ /dev/null @@ -1 +0,0 @@ -../stress-split/2-partial-upgrade/ \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/3-thrash/default.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/3-thrash/default.yaml deleted file mode 100644 index edae7b3bd..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/3-thrash/default.yaml +++ /dev/null @@ -1,25 +0,0 @@ -meta: -- desc: | - randomly kill and revive osd - small chance to increase the number of pgs -overrides: - ceph: - log-whitelist: - - but it is still running - - wrongly marked me down - - objects unfound and apparently lost - - log bound mismatch -tasks: -- parallel: - - stress-tasks -stress-tasks: -- thrashosds: - timeout: 1200 - chance_pgnum_grow: 1 - chance_pgpnum_fix: 1 - min_in: 4 - chance_thrash_cluster_full: 0 - chance_thrash_pg_upmap: 0 - chance_thrash_pg_upmap_items: 0 - chance_force_recovery: 0 -- print: "**** done thrashosds 3-thrash" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/4-ec-workload.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/4-ec-workload.yaml deleted file mode 100644 index c89551e6b..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/4-ec-workload.yaml +++ /dev/null @@ -1,22 +0,0 @@ -meta: -- desc: | - randomized correctness test for rados operations on an erasure coded pool -stress-tasks: - - rados: - clients: [client.0] - ops: 4000 - objects: 50 - ec_pool: true - write_append_excl: false - op_weights: - read: 100 - write: 0 - append: 100 - delete: 50 - snap_create: 50 - snap_remove: 50 - rollback: 50 - copy_from: 50 - setattr: 25 - rmattr: 25 - - print: "**** done rados ec task" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/5-finish-upgrade.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/5-finish-upgrade.yaml deleted file mode 120000 index a66a7dc18..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/5-finish-upgrade.yaml +++ /dev/null @@ -1 +0,0 @@ -../stress-split/5-finish-upgrade.yaml \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/7-final-workload.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/7-final-workload.yaml deleted file mode 100644 index 50a146507..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/7-final-workload.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# k=3 implies a stripe_width of 1376*3 = 4128 which is different from -# the default value of 4096 It is also not a multiple of 1024*1024 and -# creates situations where rounding rules during recovery becomes -# necessary. -# -meta: -- desc: | - randomized correctness test for rados operations on an erasure coded pool - using the jerasure plugin with k=3 and m=1 -tasks: -- rados: - clients: [client.0] - ops: 4000 - objects: 50 - ec_pool: true - write_append_excl: false - erasure_code_profile: - name: jerasure31profile - plugin: jerasure - k: 3 - m: 1 - technique: reed_sol_van - crush-failure-domain: osd - op_weights: - read: 100 - write: 0 - append: 100 - delete: 50 - snap_create: 50 - snap_remove: 50 - rollback: 50 - copy_from: 50 - setattr: 25 - rmattr: 25 diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/distros b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/distros deleted file mode 120000 index ca99fee94..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/distros +++ /dev/null @@ -1 +0,0 @@ -../../../../distros/supported/ \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/objectstore b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/objectstore deleted file mode 120000 index 016cbf967..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/objectstore +++ /dev/null @@ -1 +0,0 @@ -../stress-split/objectstore/ \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/thrashosds-health.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/thrashosds-health.yaml deleted file mode 120000 index e0426dbe4..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split-erasure-code/thrashosds-health.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../tasks/thrashosds-health.yaml \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/% b/ceph/qa/suites/upgrade/luminous-x/stress-split/% deleted file mode 100644 index e69de29bb..000000000 diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/+ b/ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/+ deleted file mode 100644 index e69de29bb..000000000 diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/openstack.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/openstack.yaml deleted file mode 100644 index a0d5c2019..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/openstack.yaml +++ /dev/null @@ -1,6 +0,0 @@ -openstack: - - machine: - disk: 100 # GB - - volumes: # attached to each instance - count: 3 - size: 30 # GB diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/start.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/start.yaml deleted file mode 100644 index e3ad918e8..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/0-cluster/start.yaml +++ /dev/null @@ -1,29 +0,0 @@ -meta: -- desc: | - Run ceph on two nodes, - with a separate client-only node. - Use xfs beneath the osds. -overrides: - ceph: - fs: xfs - log-whitelist: - - overall HEALTH_ - - \(MON_DOWN\) - - \(MGR_DOWN\) - conf: - global: - enable experimental unrecoverable data corrupting features: "*" - mon: - mon warn on osd down out interval zero: false -roles: -- - mon.a - - mon.b - - mon.c - - mgr.x - - osd.0 - - osd.1 - - osd.2 -- - osd.3 - - osd.4 - - osd.5 -- - client.0 diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/1-ceph-install/luminous.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/1-ceph-install/luminous.yaml deleted file mode 100644 index 22305258e..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/1-ceph-install/luminous.yaml +++ /dev/null @@ -1,17 +0,0 @@ -meta: -- desc: install ceph/luminous latest -tasks: -- install: - branch: luminous -- print: "**** done install luminous" -- ceph: -- exec: - osd.0: - - ceph osd require-osd-release luminous - - ceph osd set-require-min-compat-client luminous -- print: "**** done ceph " -overrides: - ceph: - conf: - mon: - mon warn on osd down out interval zero: false diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/2-partial-upgrade/firsthalf.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/2-partial-upgrade/firsthalf.yaml deleted file mode 100644 index 87fa1d5fa..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/2-partial-upgrade/firsthalf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -meta: -- desc: | - install upgrade ceph/-x on one node only - 1st half - restart : osd.0,1,2 -tasks: -- install.upgrade: - osd.0: -- print: "**** done install.upgrade osd.0" -- ceph.restart: - daemons: [mon.a,mon.b,mon.c,mgr.x,osd.0,osd.1,osd.2] -- print: "**** done ceph.restart 1st half" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/3-thrash/default.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/3-thrash/default.yaml deleted file mode 100644 index b3fddefc7..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/3-thrash/default.yaml +++ /dev/null @@ -1,25 +0,0 @@ -meta: -- desc: | - randomly kill and revive osd - small chance to increase the number of pgs -overrides: - ceph: - log-whitelist: - - but it is still running - - wrongly marked me down - - objects unfound and apparently lost - - log bound mismatch -tasks: -- parallel: - - stress-tasks -stress-tasks: -- thrashosds: - timeout: 1200 - chance_pgnum_grow: 1 - chance_pgpnum_fix: 1 - chance_thrash_cluster_full: 0 - chance_thrash_pg_upmap: 0 - chance_thrash_pg_upmap_items: 0 - disable_objectstore_tool_tests: true - chance_force_recovery: 0 -- print: "**** done thrashosds 3-thrash" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/+ b/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/+ deleted file mode 100644 index e69de29bb..000000000 diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/radosbench.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/radosbench.yaml deleted file mode 100644 index 626ae8ea6..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/radosbench.yaml +++ /dev/null @@ -1,40 +0,0 @@ -meta: -- desc: | - run randomized correctness test for rados operations - generate write load with rados bench -stress-tasks: -- full_sequential: - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 - - radosbench: - clients: [client.0] - time: 150 -- print: "**** done radosbench 7-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd-cls.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd-cls.yaml deleted file mode 100644 index f8cc4d8ac..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd-cls.yaml +++ /dev/null @@ -1,10 +0,0 @@ -meta: -- desc: | - run basic cls tests for rbd -stress-tasks: -- workunit: - branch: luminous - clients: - client.0: - - cls/test_cls_rbd.sh -- print: "**** done cls/test_cls_rbd.sh 5-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd-import-export.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd-import-export.yaml deleted file mode 100644 index 30a677af6..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd-import-export.yaml +++ /dev/null @@ -1,12 +0,0 @@ -meta: -- desc: | - run basic import/export cli tests for rbd -stress-tasks: -- workunit: - branch: luminous - clients: - client.0: - - rbd/import_export.sh - env: - RBD_CREATE_ARGS: --new-format -- print: "**** done rbd/import_export.sh 5-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd_api.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd_api.yaml deleted file mode 100644 index 9079aa33b..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/rbd_api.yaml +++ /dev/null @@ -1,10 +0,0 @@ -meta: -- desc: | - librbd C and C++ api tests -stress-tasks: -- workunit: - branch: luminous - clients: - client.0: - - rbd/test_librbd.sh -- print: "**** done rbd/test_librbd.sh 7-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/readwrite.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/readwrite.yaml deleted file mode 100644 index 41e34d6d7..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/readwrite.yaml +++ /dev/null @@ -1,16 +0,0 @@ -meta: -- desc: | - randomized correctness test for rados operations on a replicated pool, - using only reads, writes, and deletes -stress-tasks: -- full_sequential: - - rados: - clients: [client.0] - ops: 4000 - objects: 500 - write_append_excl: false - op_weights: - read: 45 - write: 45 - delete: 10 -- print: "**** done rados/readwrite 5-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/snaps-few-objects.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/snaps-few-objects.yaml deleted file mode 100644 index f56d0de0f..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/4-workload/snaps-few-objects.yaml +++ /dev/null @@ -1,18 +0,0 @@ -meta: -- desc: | - randomized correctness test for rados operations on a replicated pool with snapshot operations -stress-tasks: -- full_sequential: - - rados: - clients: [client.0] - ops: 4000 - objects: 50 - write_append_excl: false - op_weights: - read: 100 - write: 100 - delete: 50 - snap_create: 50 - snap_remove: 50 - rollback: 50 -- print: "**** done rados/snaps-few-objects 5-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/5-finish-upgrade.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/5-finish-upgrade.yaml deleted file mode 100644 index 1d528cd5d..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/5-finish-upgrade.yaml +++ /dev/null @@ -1,9 +0,0 @@ -tasks: -- install.upgrade: - osd.3: - client.0: -- ceph.restart: - daemons: [osd.3, osd.4, osd.5] - wait-for-healthy: false - wait-for-osds-up: true - diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/+ b/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/+ deleted file mode 100644 index e69de29bb..000000000 diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/rbd-python.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/rbd-python.yaml deleted file mode 100644 index 92fe658b4..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/rbd-python.yaml +++ /dev/null @@ -1,10 +0,0 @@ -meta: -- desc: | - librbd python api tests -tasks: -- workunit: - branch: luminous - clients: - client.0: - - rbd/test_librbd_python.sh -- print: "**** done rbd/test_librbd_python.sh 9-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/rgw-swift.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/rgw-swift.yaml deleted file mode 100644 index 76e5d6fc2..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/rgw-swift.yaml +++ /dev/null @@ -1,11 +0,0 @@ -meta: -- desc: | - swift api tests for rgw -tasks: -- rgw: - client.0: -- print: "**** done rgw 9-workload" -- swift: - client.0: - rgw_server: client.0 -- print: "**** done swift 9-workload" diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/snaps-many-objects.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/snaps-many-objects.yaml deleted file mode 100644 index 805bf97c3..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/7-final-workload/snaps-many-objects.yaml +++ /dev/null @@ -1,16 +0,0 @@ -meta: -- desc: | - randomized correctness test for rados operations on a replicated pool with snapshot operations -tasks: -- rados: - clients: [client.0] - ops: 4000 - objects: 500 - write_append_excl: false - op_weights: - read: 100 - write: 100 - delete: 50 - snap_create: 50 - snap_remove: 50 - rollback: 50 diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/distros b/ceph/qa/suites/upgrade/luminous-x/stress-split/distros deleted file mode 120000 index ca99fee94..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/distros +++ /dev/null @@ -1 +0,0 @@ -../../../../distros/supported/ \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/objectstore/bluestore.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/objectstore/bluestore.yaml deleted file mode 120000 index d6445987d..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/objectstore/bluestore.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../../objectstore/bluestore.yaml \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/objectstore/filestore-xfs.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/objectstore/filestore-xfs.yaml deleted file mode 120000 index 03750e5ad..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/objectstore/filestore-xfs.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../../objectstore/filestore-xfs.yaml \ No newline at end of file diff --git a/ceph/qa/suites/upgrade/luminous-x/stress-split/thrashosds-health.yaml b/ceph/qa/suites/upgrade/luminous-x/stress-split/thrashosds-health.yaml deleted file mode 120000 index e0426dbe4..000000000 --- a/ceph/qa/suites/upgrade/luminous-x/stress-split/thrashosds-health.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../tasks/thrashosds-health.yaml \ No newline at end of file diff --git a/ceph/qa/tasks/ceph_fuse.py b/ceph/qa/tasks/ceph_fuse.py index c9d835496..14c698972 100644 --- a/ceph/qa/tasks/ceph_fuse.py +++ b/ceph/qa/tasks/ceph_fuse.py @@ -7,6 +7,8 @@ import logging from teuthology import misc as teuthology from cephfs.fuse_mount import FuseMount +from tasks.cephfs.filesystem import MDSCluster +from tasks.cephfs.filesystem import Filesystem log = logging.getLogger(__name__) @@ -103,6 +105,14 @@ def task(ctx, config): all_mounts = getattr(ctx, 'mounts', {}) mounted_by_me = {} + log.info('Wait for MDS to reach steady state...') + mds_cluster = MDSCluster(ctx) + status = mds_cluster.status() + for filesystem in status.get_filesystems(): + fs = Filesystem(ctx, fscid=filesystem['id']) + fs.wait_for_daemons() + log.info('Ready to start ceph-fuse...') + # Construct any new FuseMount instances for id_, remote in clients: client_config = config.get("client.%s" % id_) diff --git a/ceph/qa/tasks/cephfs/filesystem.py b/ceph/qa/tasks/cephfs/filesystem.py index b39504419..393d69e53 100644 --- a/ceph/qa/tasks/cephfs/filesystem.py +++ b/ceph/qa/tasks/cephfs/filesystem.py @@ -268,6 +268,12 @@ class MDSCluster(CephCluster): self._one_or_all(mds_id, _fail_restart) + def mds_signal(self, mds_id, sig, silent=False): + """ + signal a MDS daemon + """ + self.mds_daemons[mds_id].signal(sig, silent); + def newfs(self, name='cephfs', create=True): return Filesystem(self._ctx, name=name, create=create) diff --git a/ceph/qa/tasks/cephfs/test_failover.py b/ceph/qa/tasks/cephfs/test_failover.py index 9d3392c69..dd8416136 100644 --- a/ceph/qa/tasks/cephfs/test_failover.py +++ b/ceph/qa/tasks/cephfs/test_failover.py @@ -1,3 +1,5 @@ +import time +import signal import json import logging from unittest import case, SkipTest @@ -133,8 +135,59 @@ class TestFailover(CephFSTestCase): self.fs.mon_manager.raw_cluster_cmd('fs', 'set', self.fs.name, 'standby_count_wanted', '0') self.wait_for_health_clear(timeout=30) + def test_discontinuous_mdsmap(self): + """ + That discontinuous mdsmap does not affect failover. + See http://tracker.ceph.com/issues/24856. + """ + mds_ids = sorted(self.mds_cluster.mds_ids) + mds_a, mds_b = mds_ids[0:2] + # Assign mds to fixed ranks. To prevent standby mds from replacing frozen mds + rank = 0; + for mds_id in mds_ids: + self.set_conf("mds.{0}".format(mds_id), "mds_standby_for_rank", str(rank)) + rank += 1 + self.mds_cluster.mds_restart() + self.fs.wait_for_daemons() + + self.fs.set_max_mds(2) + self.fs.wait_for_state('up:active', rank=1) + + # Drop 'export prep' message, make import stay in 'discovered' state + self.fs.mds_asok(['config', 'set', 'mds_inject_migrator_message_loss', '82'], mds_id=mds_b) + + self.mount_a.run_shell(["mkdir", "a"]) + self.mount_a.setfattr("a", "ceph.dir.pin", "1") + self.mount_a.umount_wait() + + # Should be long enough for start the export + time.sleep(30) + + grace = float(self.fs.get_config("mds_beacon_grace", service_type="mon")) + monc_timeout = float(self.fs.get_config("mon_client_ping_timeout", service_type="mds")) + # Freeze mds_b + self.mds_cluster.mds_signal(mds_b, signal.SIGSTOP) + self.wait_until_true( + lambda: "laggy_since" in self.fs.mon_manager.get_mds_status(mds_b), + timeout=grace * 2 + ) + + self.mds_cluster.mds_restart(mds_a) + self.fs.wait_for_state('up:resolve', rank=0, timeout=30) + + # Make sure of mds_b's monitor connection gets reset + time.sleep(monc_timeout * 2) + + # Unfreeze mds_b, it will get discontinuous mdsmap + self.mds_cluster.mds_signal(mds_b, signal.SIGCONT) + self.wait_until_true( + lambda: "laggy_since" not in self.fs.mon_manager.get_mds_status(mds_b), + timeout=grace * 2 + ) + # Check if mds_b sends 'resolve' message to mds_a. If not, mds_a can't become active + self.fs.wait_for_state('up:active', rank=0, timeout=30) class TestStandbyReplay(CephFSTestCase): MDSS_REQUIRED = 4 diff --git a/ceph/qa/tasks/cephfs/test_forward_scrub.py b/ceph/qa/tasks/cephfs/test_forward_scrub.py index ac912dd0b..1f80366af 100644 --- a/ceph/qa/tasks/cephfs/test_forward_scrub.py +++ b/ceph/qa/tasks/cephfs/test_forward_scrub.py @@ -232,7 +232,8 @@ class TestForwardScrub(CephFSTestCase): self.mount_a.umount_wait() with self.assert_cluster_log("inode table repaired", invert_match=True): - self.fs.mds_asok(["scrub_path", "/", "repair", "recursive"]) + out_json = self.fs.mds_asok(["scrub_path", "/", "repair", "recursive"]) + self.assertNotEqual(out_json, None) self.mds_cluster.mds_stop() self.mds_cluster.mds_fail() @@ -254,7 +255,8 @@ class TestForwardScrub(CephFSTestCase): self.fs.wait_for_daemons() with self.assert_cluster_log("inode table repaired"): - self.fs.mds_asok(["scrub_path", "/", "repair", "recursive"]) + out_json = self.fs.mds_asok(["scrub_path", "/", "repair", "recursive"]) + self.assertNotEqual(out_json, None) self.mds_cluster.mds_stop() table_text = self.fs.table_tool(["0", "show", "inode"]) @@ -284,7 +286,8 @@ class TestForwardScrub(CephFSTestCase): "oh i'm sorry did i overwrite your xattr?") with self.assert_cluster_log("bad backtrace on inode"): - self.fs.mds_asok(["scrub_path", "/", "repair", "recursive"]) + out_json = self.fs.mds_asok(["scrub_path", "/", "repair", "recursive"]) + self.assertNotEqual(out_json, None) self.fs.mds_asok(["flush", "journal"]) backtrace = self.fs.read_backtrace(file_ino) self.assertEqual(['alpha', 'parent_a'], diff --git a/ceph/qa/tasks/cephfs/test_scrub.py b/ceph/qa/tasks/cephfs/test_scrub.py index 32371dd67..9469dfce6 100644 --- a/ceph/qa/tasks/cephfs/test_scrub.py +++ b/ceph/qa/tasks/cephfs/test_scrub.py @@ -14,7 +14,7 @@ log = logging.getLogger(__name__) ValidationError = namedtuple("ValidationError", ["exception", "backtrace"]) -class Workload(object): +class Workload(CephFSTestCase): def __init__(self, filesystem, mount): self._mount = mount self._filesystem = filesystem @@ -26,15 +26,6 @@ class Workload(object): # a string self._errors = [] - def assert_equal(self, a, b): - try: - if a != b: - raise AssertionError("{0} != {1}".format(a, b)) - except AssertionError as e: - self._errors.append( - ValidationError(e, traceback.format_exc(3)) - ) - def write(self): """ Write the workload files to the mount @@ -78,7 +69,7 @@ class BacktraceWorkload(Workload): self._filesystem.mds_asok(["flush", "journal"]) bt = self._filesystem.read_backtrace(st['st_ino']) parent = bt['ancestors'][0]['dname'] - self.assert_equal(parent, "sixmegs") + self.assertEqual(parent, 'sixmegs') return self._errors def damage(self): @@ -112,8 +103,9 @@ class DupInodeWorkload(Workload): self._filesystem.wait_for_daemons() def validate(self): - self._filesystem.mds_asok(["scrub_path", "/", "recursive", "repair"]) - self.assert_equal(self._filesystem.are_daemons_healthy(), True) + out_json = self._filesystem.mds_asok(["scrub_path", "/", "recursive", "repair"]) + self.assertNotEqual(out_json, None) + self.assertTrue(self._filesystem.are_daemons_healthy()) return self._errors @@ -137,7 +129,8 @@ class TestScrub(CephFSTestCase): # Apply any data damage the workload wants workload.damage() - self.fs.mds_asok(["scrub_path", "/", "recursive", "repair"]) + out_json = self.fs.mds_asok(["scrub_path", "/", "recursive", "repair"]) + self.assertNotEqual(out_json, None) # See that the files are present and correct errors = workload.validate() diff --git a/ceph/qa/tasks/thrashosds-health.yaml b/ceph/qa/tasks/thrashosds-health.yaml index 9defe69ef..111e2d8c4 100644 --- a/ceph/qa/tasks/thrashosds-health.yaml +++ b/ceph/qa/tasks/thrashosds-health.yaml @@ -12,3 +12,4 @@ overrides: - \(REQUEST_SLOW\) - \(TOO_FEW_PGS\) - \(MON_DOWN\) + - slow requests diff --git a/ceph/qa/tasks/vstart_runner.py b/ceph/qa/tasks/vstart_runner.py index c3988214a..e7f7f68f3 100644 --- a/ceph/qa/tasks/vstart_runner.py +++ b/ceph/qa/tasks/vstart_runner.py @@ -373,6 +373,14 @@ class LocalDaemon(object): self.proc = self.controller.run([os.path.join(BIN_PREFIX, "./ceph-{0}".format(self.daemon_type)), "-i", self.daemon_id]) + def signal(self, sig, silent=False): + if not self.running(): + raise RuntimeError("Can't send signal to non-running daemon") + + os.kill(self._get_pid(), sig) + if not silent: + log.info("Sent signal {0} to {1}.{2}".format(sig, self.daemon_type, self.daemon_id)) + def safe_kill(pid): """ diff --git a/ceph/qa/workunits/cephtool/test.sh b/ceph/qa/workunits/cephtool/test.sh index 00606a256..36f9dc43e 100755 --- a/ceph/qa/workunits/cephtool/test.sh +++ b/ceph/qa/workunits/cephtool/test.sh @@ -239,29 +239,60 @@ function test_mon_injectargs_SI() # We only aim at testing the units are parsed accordingly # and don't intend to test whether the options being set # actually expect SI units to be passed. - # Keep in mind that all integer based options (i.e., INT, - # LONG, U32, U64) will accept SI unit modifiers. + # Keep in mind that all integer based options that are not based on bytes + # (i.e., INT, LONG, U32, U64) will accept SI unit modifiers and be parsed to + # base 10. initial_value=$(get_config_value_or_die "mon.a" "mon_pg_warn_min_objects") $SUDO ceph daemon mon.a config set mon_pg_warn_min_objects 10 expect_config_value "mon.a" "mon_pg_warn_min_objects" 10 $SUDO ceph daemon mon.a config set mon_pg_warn_min_objects 10K - expect_config_value "mon.a" "mon_pg_warn_min_objects" 10240 + expect_config_value "mon.a" "mon_pg_warn_min_objects" 10000 $SUDO ceph daemon mon.a config set mon_pg_warn_min_objects 1G - expect_config_value "mon.a" "mon_pg_warn_min_objects" 1073741824 + expect_config_value "mon.a" "mon_pg_warn_min_objects" 1000000000 $SUDO ceph daemon mon.a config set mon_pg_warn_min_objects 10F > $TMPFILE || true check_response "'10F': (22) Invalid argument" # now test with injectargs ceph tell mon.a injectargs '--mon_pg_warn_min_objects 10' expect_config_value "mon.a" "mon_pg_warn_min_objects" 10 ceph tell mon.a injectargs '--mon_pg_warn_min_objects 10K' - expect_config_value "mon.a" "mon_pg_warn_min_objects" 10240 + expect_config_value "mon.a" "mon_pg_warn_min_objects" 10000 ceph tell mon.a injectargs '--mon_pg_warn_min_objects 1G' - expect_config_value "mon.a" "mon_pg_warn_min_objects" 1073741824 + expect_config_value "mon.a" "mon_pg_warn_min_objects" 1000000000 expect_false ceph tell mon.a injectargs '--mon_pg_warn_min_objects 10F' expect_false ceph tell mon.a injectargs '--mon_globalid_prealloc -1' $SUDO ceph daemon mon.a config set mon_pg_warn_min_objects $initial_value } +function test_mon_injectargs_IEC() +{ + # Test IEC units during injectargs and 'config set' + # We only aim at testing the units are parsed accordingly + # and don't intend to test whether the options being set + # actually expect IEC units to be passed. + # Keep in mind that all integer based options that are based on bytes + # (i.e., INT, LONG, U32, U64) will accept IEC unit modifiers, as well as SI + # unit modifiers (for backwards compatibility and convinience) and be parsed + # to base 2. + initial_value=$(get_config_value_or_die "mon.a" "mon_data_size_warn") + $SUDO ceph daemon mon.a config set mon_data_size_warn 15000000000 + expect_config_value "mon.a" "mon_data_size_warn" 15000000000 + $SUDO ceph daemon mon.a config set mon_data_size_warn 15G + expect_config_value "mon.a" "mon_data_size_warn" 16106127360 + $SUDO ceph daemon mon.a config set mon_data_size_warn 16Gi + expect_config_value "mon.a" "mon_data_size_warn" 17179869184 + $SUDO ceph daemon mon.a config set mon_data_size_warn 10F > $TMPFILE || true + check_response "'10F': (22) Invalid argument" + # now test with injectargs + ceph tell mon.a injectargs '--mon_data_size_warn 15000000000' + expect_config_value "mon.a" "mon_data_size_warn" 15000000000 + ceph tell mon.a injectargs '--mon_data_size_warn 15G' + expect_config_value "mon.a" "mon_data_size_warn" 16106127360 + ceph tell mon.a injectargs '--mon_data_size_warn 16Gi' + expect_config_value "mon.a" "mon_data_size_warn" 17179869184 + expect_false ceph tell mon.a injectargs '--mon_data_size_warn 10F' + $SUDO ceph daemon mon.a config set mon_data_size_warn $initial_value +} + function test_tiering_agent() { local slow=slow_eviction @@ -1731,18 +1762,35 @@ function test_mon_osd_pool_quota() ceph osd pool set-quota tmp-quota-pool max_bytes 10 ceph osd pool set-quota tmp-quota-pool max_objects 10M # - # get quotas - # - ceph osd pool get-quota tmp-quota-pool | grep 'max bytes.*10B' - ceph osd pool get-quota tmp-quota-pool | grep 'max objects.*10240k objects' - # # get quotas in json-pretty format # ceph osd pool get-quota tmp-quota-pool --format=json-pretty | \ - grep '"quota_max_objects":.*10485760' + grep '"quota_max_objects":.*10000000' ceph osd pool get-quota tmp-quota-pool --format=json-pretty | \ grep '"quota_max_bytes":.*10' # + # get quotas + # + ceph osd pool get-quota tmp-quota-pool | grep 'max bytes.*10B' + ceph osd pool get-quota tmp-quota-pool | grep 'max objects.*10M objects' + # + # set valid quotas with unit prefix + # + ceph osd pool set-quota tmp-quota-pool max_bytes 10K + # + # get quotas + # + ceph osd pool get-quota tmp-quota-pool | grep 'max bytes.*10Ki' + # + # set valid quotas with unit prefix + # + ceph osd pool set-quota tmp-quota-pool max_bytes 10Ki + # + # get quotas + # + ceph osd pool get-quota tmp-quota-pool | grep 'max bytes.*10Ki' + # + # # reset pool quotas # ceph osd pool set-quota tmp-quota-pool max_bytes 0 diff --git a/ceph/qa/workunits/rbd/cli_generic.sh b/ceph/qa/workunits/rbd/cli_generic.sh index f95852084..759464b83 100755 --- a/ceph/qa/workunits/rbd/cli_generic.sh +++ b/ceph/qa/workunits/rbd/cli_generic.sh @@ -42,8 +42,8 @@ test_others() { rbd export testimg1 /tmp/img3 # info - rbd info testimg1 | grep 'size 128 MB' - rbd info --snap=snap1 testimg1 | grep 'size 256 MB' + rbd info testimg1 | grep 'size 128MiB' + rbd info --snap=snap1 testimg1 | grep 'size 256MiB' # export-diff rm -rf /tmp/diff-testimg1-1 /tmp/diff-testimg1-2 @@ -56,10 +56,10 @@ test_others() { rbd import-diff --sparse-size 8K /tmp/diff-testimg1-2 testimg-diff1 # info - rbd info testimg1 | grep 'size 128 MB' - rbd info --snap=snap1 testimg1 | grep 'size 256 MB' - rbd info testimg-diff1 | grep 'size 128 MB' - rbd info --snap=snap1 testimg-diff1 | grep 'size 256 MB' + rbd info testimg1 | grep 'size 128MiB' + rbd info --snap=snap1 testimg1 | grep 'size 256MiB' + rbd info testimg-diff1 | grep 'size 128MiB' + rbd info --snap=snap1 testimg-diff1 | grep 'size 256MiB' # make copies rbd copy testimg1 --snap=snap1 testimg2 @@ -68,10 +68,10 @@ test_others() { rbd copy testimg-diff1 --sparse-size 768K testimg-diff3 # verify the result - rbd info testimg2 | grep 'size 256 MB' - rbd info testimg3 | grep 'size 128 MB' - rbd info testimg-diff2 | grep 'size 256 MB' - rbd info testimg-diff3 | grep 'size 128 MB' + rbd info testimg2 | grep 'size 256MiB' + rbd info testimg3 | grep 'size 128MiB' + rbd info testimg-diff2 | grep 'size 256MiB' + rbd info testimg-diff3 | grep 'size 128MiB' rbd export testimg1 /tmp/img1.new rbd export testimg2 /tmp/img2.new @@ -88,8 +88,8 @@ test_others() { # rollback rbd snap rollback --snap=snap1 testimg1 rbd snap rollback --snap=snap1 testimg-diff1 - rbd info testimg1 | grep 'size 256 MB' - rbd info testimg-diff1 | grep 'size 256 MB' + rbd info testimg1 | grep 'size 256MiB' + rbd info testimg-diff1 | grep 'size 256MiB' rbd export testimg1 /tmp/img1.snap1 rbd export testimg-diff1 /tmp/img-diff1.snap1 cmp /tmp/img2 /tmp/img1.snap1 @@ -147,8 +147,8 @@ test_ls() { rbd ls | grep test2 rbd ls | wc -l | grep 2 # look for fields in output of ls -l without worrying about space - rbd ls -l | grep 'test1.*1024k.*1' - rbd ls -l | grep 'test2.*1024k.*1' + rbd ls -l | grep 'test1.*1MiB.*1' + rbd ls -l | grep 'test2.*1MiB.*1' rbd rm test1 rbd rm test2 @@ -158,8 +158,8 @@ test_ls() { rbd ls | grep test1 rbd ls | grep test2 rbd ls | wc -l | grep 2 - rbd ls -l | grep 'test1.*1024k.*2' - rbd ls -l | grep 'test2.*1024k.*2' + rbd ls -l | grep 'test1.*1MiB.*2' + rbd ls -l | grep 'test2.*1MiB.*2' rbd rm test1 rbd rm test2 @@ -169,8 +169,8 @@ test_ls() { rbd ls | grep test1 rbd ls | grep test2 rbd ls | wc -l | grep 2 - rbd ls -l | grep 'test1.*1024k.*2' - rbd ls -l | grep 'test2.*1024k.*1' + rbd ls -l | grep 'test1.*1MiB.*2' + rbd ls -l | grep 'test2.*1MiB.*1' remove_images # test that many images can be shown by ls diff --git a/ceph/qa/workunits/suites/blogbench.sh b/ceph/qa/workunits/suites/blogbench.sh index 17c91c8c3..802a04c93 100755 --- a/ceph/qa/workunits/suites/blogbench.sh +++ b/ceph/qa/workunits/suites/blogbench.sh @@ -5,7 +5,7 @@ echo "getting blogbench" wget http://download.ceph.com/qa/blogbench-1.0.tar.bz2 #cp /home/gregf/src/blogbench-1.0.tar.bz2 . tar -xvf blogbench-1.0.tar.bz2 -cd blogbench* +cd blogbench-1.0/ echo "making blogbench" ./configure make diff --git a/ceph/qa/workunits/suites/ffsb.patch b/ceph/qa/workunits/suites/ffsb.patch new file mode 100644 index 000000000..a51217595 --- /dev/null +++ b/ceph/qa/workunits/suites/ffsb.patch @@ -0,0 +1,12 @@ +diff -urp 1/parser.c 2/parser.c +--- 1/parser.c 2008-10-28 04:17:05.000000000 +0800 ++++ 2/parser.c 2018-06-26 20:25:59.000000000 +0800 +@@ -203,7 +203,7 @@ static char *get_optstr(char *buf, char + len = strnlen(string, BUFSIZE); + sprintf(search_str, "%s=%%%ds\\n", string, BUFSIZE - len-1); + if (1 == sscanf(line, search_str, &temp)) { +- len = strnlen(temp, 4096); ++ len = strnlen(temp, 4095) + 1; + ret_buf = malloc(len); + strncpy(ret_buf, temp, len); + return ret_buf; diff --git a/ceph/qa/workunits/suites/ffsb.sh b/ceph/qa/workunits/suites/ffsb.sh index 9ed66ab00..c6a35e2e1 100755 --- a/ceph/qa/workunits/suites/ffsb.sh +++ b/ceph/qa/workunits/suites/ffsb.sh @@ -6,7 +6,8 @@ mydir=`dirname $0` wget http://download.ceph.com/qa/ffsb.tar.bz2 tar jxvf ffsb.tar.bz2 -cd ffsb-* +cd ffsb-6.0-rc2 +patch -p1 < $mydir/ffsb.patch ./configure make cd .. diff --git a/ceph/qa/workunits/suites/iogen.sh b/ceph/qa/workunits/suites/iogen.sh index d159bde97..03e08c78d 100755 --- a/ceph/qa/workunits/suites/iogen.sh +++ b/ceph/qa/workunits/suites/iogen.sh @@ -4,7 +4,7 @@ set -e echo "getting iogen" wget http://download.ceph.com/qa/iogen_3.1p0.tar tar -xvzf iogen_3.1p0.tar -cd iogen* +cd iogen_3.1p0 echo "making iogen" make echo "running iogen" diff --git a/ceph/qa/workunits/suites/pjd.sh b/ceph/qa/workunits/suites/pjd.sh index e6df309ad..447413177 100755 --- a/ceph/qa/workunits/suites/pjd.sh +++ b/ceph/qa/workunits/suites/pjd.sh @@ -4,7 +4,7 @@ set -e wget http://download.ceph.com/qa/pjd-fstest-20090130-RC-aclfixes.tgz tar zxvf pjd*.tgz -cd pjd* +cd pjd-fstest-20090130-RC make clean make cd .. diff --git a/ceph/src/.git_version b/ceph/src/.git_version index fa15181c2..9704f6797 100644 --- a/ceph/src/.git_version +++ b/ceph/src/.git_version @@ -1,2 +1,2 @@ -3ec878d1e53e1aeb47a9f619c49d9e7c0aa384d5 -v12.2.7 +ae699615bac534ea496ee965ac6192cb7e0e07c0 +v12.2.8 diff --git a/ceph/src/CMakeLists.txt b/ceph/src/CMakeLists.txt index 3d4baae39..80d4b351f 100644 --- a/ceph/src/CMakeLists.txt +++ b/ceph/src/CMakeLists.txt @@ -1145,7 +1145,3 @@ if (IS_DIRECTORY "${PROJECT_SOURCE_DIR}/.git") endif() add_subdirectory(script) - -if(WITH_EMBEDDED) - add_subdirectory(libcephd) -endif() diff --git a/ceph/src/ceph-volume/ceph_volume/__init__.py b/ceph/src/ceph-volume/ceph_volume/__init__.py index 6550db415..7652f181c 100644 --- a/ceph/src/ceph-volume/ceph_volume/__init__.py +++ b/ceph/src/ceph-volume/ceph_volume/__init__.py @@ -1,6 +1,10 @@ from collections import namedtuple +sys_info = namedtuple('sys_info', ['devices']) +sys_info.devices = dict() + + class UnloadedConfig(object): """ This class is used as the default value for conf.ceph so that if @@ -14,3 +18,5 @@ conf = namedtuple('config', ['ceph', 'cluster', 'verbosity', 'path', 'log_path'] conf.ceph = UnloadedConfig() __version__ = "1.0.0" + +__release__ = "luminous" diff --git a/ceph/src/ceph-volume/ceph_volume/api/lvm.py b/ceph/src/ceph-volume/ceph_volume/api/lvm.py index 2f2bd1738..e766671b3 100644 --- a/ceph/src/ceph-volume/ceph_volume/api/lvm.py +++ b/ceph/src/ceph-volume/ceph_volume/api/lvm.py @@ -5,8 +5,13 @@ set of utilities for interacting with LVM. """ import logging import os -from ceph_volume import process -from ceph_volume.exceptions import MultipleLVsError, MultipleVGsError, MultiplePVsError +import uuid +from math import floor +from ceph_volume import process, util +from ceph_volume.exceptions import ( + MultipleLVsError, MultipleVGsError, + MultiplePVsError, SizeAllocationError +) logger = logging.getLogger(__name__) @@ -46,6 +51,80 @@ def _output_parser(output, fields): return report +def _splitname_parser(line): + """ + Parses the output from ``dmsetup splitname``, that should contain prefixes + (--nameprefixes) and set the separator to ";" + + Output for /dev/mapper/vg-lv will usually look like:: + + DM_VG_NAME='/dev/mapper/vg';DM_LV_NAME='lv';DM_LV_LAYER='' + + + The ``VG_NAME`` will usually not be what other callers need (e.g. just 'vg' + in the example), so this utility will split ``/dev/mapper/`` out, so that + the actual volume group name is kept + + :returns: dictionary with stripped prefixes + """ + parts = line[0].split(';') + parsed = {} + for part in parts: + part = part.replace("'", '') + key, value = part.split('=') + if 'DM_VG_NAME' in key: + value = value.split('/dev/mapper/')[-1] + key = key.split('DM_')[-1] + parsed[key] = value + + return parsed + + +def sizing(device_size, parts=None, size=None): + """ + Calculate proper sizing to fully utilize the volume group in the most + efficient way possible. To prevent situations where LVM might accept + a percentage that is beyond the vg's capabilities, it will refuse with + an error when requesting a larger-than-possible parameter, in addition + to rounding down calculations. + + A dictionary with different sizing parameters is returned, to make it + easier for others to choose what they need in order to create logical + volumes:: + + >>> sizing(100, parts=2) + >>> {'parts': 2, 'percentages': 50, 'sizes': 50} + + """ + if parts is not None and size is not None: + raise ValueError( + "Cannot process sizing with both parts (%s) and size (%s)" % (parts, size) + ) + + if size and size > device_size: + raise SizeAllocationError(size, device_size) + + def get_percentage(parts): + return int(floor(100 / float(parts))) + + if parts is not None: + # Prevent parts being 0, falling back to 1 (100% usage) + parts = parts or 1 + percentages = get_percentage(parts) + + if size: + parts = int(device_size / size) or 1 + percentages = get_percentage(parts) + + sizes = device_size / parts if parts else int(floor(device_size)) + + return { + 'parts': parts, + 'percentages': percentages, + 'sizes': int(sizes), + } + + def parse_tags(lv_tags): """ Return a dictionary mapping of all the tags associated with @@ -167,6 +246,36 @@ def is_vdo(path): return '0' +def dmsetup_splitname(dev): + """ + Run ``dmsetup splitname`` and parse the results. + + .. warning:: This call does not ensure that the device is correct or that + it exists. ``dmsetup`` will happily take a non existing path and still + return a 0 exit status. + """ + command = [ + 'dmsetup', 'splitname', '--noheadings', + "--separator=';'", '--nameprefixes', dev + ] + out, err, rc = process.call(command) + return _splitname_parser(out) + + +def is_lv(dev, lvs=None): + """ + Boolean to detect if a device is an LV or not. + """ + splitname = dmsetup_splitname(dev) + # Allowing to optionally pass `lvs` can help reduce repetitive checks for + # multiple devices at once. + lvs = lvs if lvs is not None else Volumes() + if splitname.get('LV_NAME'): + lvs.filter(lv_name=splitname['LV_NAME'], vg_name=splitname['VG_NAME']) + return len(lvs) > 0 + return False + + def get_api_vgs(): """ Return the list of group volumes available in the system using flags to @@ -174,15 +283,17 @@ def get_api_vgs(): Command and sample delimited output should look like:: - $ vgs --noheadings --readonly --separator=';' \ + $ vgs --noheadings --units=g --readonly --separator=';' \ -o vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free ubuntubox-vg;1;2;0;wz--n-;299.52g;12.00m osd_vg;3;1;0;wz--n-;29.21g;9.21g + To normalize sizing, the units are forced in 'g' which is equivalent to + gigabytes, which uses multiples of 1024 (as opposed to 1000) """ - fields = 'vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free' + fields = 'vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free,vg_free_count' stdout, stderr, returncode = process.call( - ['vgs', '--noheadings', '--readonly', '--separator=";"', '-o', fields] + ['vgs', '--noheadings', '--readonly', '--units=g', '--separator=";"', '-o', fields] ) return _output_parser(stdout, fields) @@ -199,7 +310,7 @@ def get_api_lvs(): ;/dev/ubuntubox-vg/swap_1;swap_1;ubuntubox-vg """ - fields = 'lv_tags,lv_path,lv_name,vg_name,lv_uuid' + fields = 'lv_tags,lv_path,lv_name,vg_name,lv_uuid,lv_size' stdout, stderr, returncode = process.call( ['lvs', '--noheadings', '--readonly', '--separator=";"', '-o', fields] ) @@ -290,25 +401,64 @@ def create_pv(device): ]) -def create_vg(name, *devices): +def create_vg(devices, name=None, name_prefix=None): """ Create a Volume Group. Command looks like:: vgcreate --force --yes group_name device Once created the volume group is returned as a ``VolumeGroup`` object + + :param devices: A list of devices to create a VG. Optionally, a single + device (as a string) can be used. + :param name: Optionally set the name of the VG, defaults to 'ceph-{uuid}' + :param name_prefix: Optionally prefix the name of the VG, which will get combined + with a UUID string """ + if isinstance(devices, set): + devices = list(devices) + if not isinstance(devices, list): + devices = [devices] + if name_prefix: + name = "%s-%s" % (name_prefix, str(uuid.uuid4())) + elif name is None: + name = "ceph-%s" % str(uuid.uuid4()) process.run([ 'vgcreate', '--force', '--yes', - name] + list(devices) + name] + devices ) vg = get_vg(vg_name=name) return vg +def extend_vg(vg, devices): + """ + Extend a Volume Group. Command looks like:: + + vgextend --force --yes group_name [device, ...] + + Once created the volume group is extended and returned as a ``VolumeGroup`` object + + :param vg: A VolumeGroup object + :param devices: A list of devices to extend the VG. Optionally, a single + device (as a string) can be used. + """ + if not isinstance(devices, list): + devices = [devices] + process.run([ + 'vgextend', + '--force', + '--yes', + vg.name] + devices + ) + + vg = get_vg(vg_name=vg.name) + return vg + + def remove_vg(vg_name): """ Removes a volume group. @@ -363,7 +513,7 @@ def remove_lv(path): return True -def create_lv(name, group, size=None, tags=None): +def create_lv(name, group, extents=None, size=None, tags=None, uuid_name=False): """ Create a Logical Volume in a Volume Group. Command looks like:: @@ -374,7 +524,19 @@ def create_lv(name, group, size=None, tags=None): conform to the convention of prefixing them with "ceph." like:: {"ceph.block_device": "/dev/ceph/osd-1"} + + :param uuid_name: Optionally combine the ``name`` with UUID to ensure uniqueness """ + if uuid_name: + name = '%s-%s' % (name, uuid.uuid4()) + if tags is None: + tags = { + "ceph.osd_id": "null", + "ceph.type": "null", + "ceph.cluster_fsid": "null", + "ceph.osd_fsid": "null", + } + # XXX add CEPH_VOLUME_LVM_DEBUG to enable -vvvv on lv operations type_path_tag = { 'journal': 'ceph.journal_device', @@ -392,6 +554,14 @@ def create_lv(name, group, size=None, tags=None): '%s' % size, '-n', name, group ]) + elif extents: + process.run([ + 'lvcreate', + '--yes', + '-l', + '%s' % extents, + '-n', name, group + ]) # create the lv with all the space available, this is needed because the # system call is different for LVM else: @@ -416,6 +586,50 @@ def create_lv(name, group, size=None, tags=None): return lv +def create_lvs(volume_group, parts=None, size=None, name_prefix='ceph-lv'): + """ + Create multiple Logical Volumes from a Volume Group by calculating the + proper extents from ``parts`` or ``size``. A custom prefix can be used + (defaults to ``ceph-lv``), these names are always suffixed with a uuid. + + LV creation in ceph-volume will require tags, this is expected to be + pre-computed by callers who know Ceph metadata like OSD IDs and FSIDs. It + will probably not be the case when mass-creating LVs, so common/default + tags will be set to ``"null"``. + + .. note:: LVs that are not in use can be detected by querying LVM for tags that are + set to ``"null"``. + + :param volume_group: The volume group (vg) to use for LV creation + :type group: ``VolumeGroup()`` object + :param parts: Number of LVs to create *instead of* ``size``. + :type parts: int + :param size: Size (in gigabytes) of LVs to create, e.g. "as many 10gb LVs as possible" + :type size: int + :param extents: The number of LVM extents to use to create the LV. Useful if looking to have + accurate LV sizes (LVM rounds sizes otherwise) + """ + if parts is None and size is None: + # fallback to just one part (using 100% of the vg) + parts = 1 + lvs = [] + tags = { + "ceph.osd_id": "null", + "ceph.type": "null", + "ceph.cluster_fsid": "null", + "ceph.osd_fsid": "null", + } + sizing = volume_group.sizing(parts=parts, size=size) + for part in range(0, sizing['parts']): + size = sizing['sizes'] + extents = sizing['extents'] + lv_name = '%s-%s' % (name_prefix, uuid.uuid4()) + lvs.append( + create_lv(lv_name, volume_group.name, extents=extents, tags=tags) + ) + return lvs + + def get_vg(vg_name=None, vg_tags=None): """ Return a matching vg for the current system, requires ``vg_name`` or @@ -726,7 +940,7 @@ class PVolumes(list): ) if not pvs: return None - if len(pvs) > 1: + if len(pvs) > 1 and pv_tags: raise MultiplePVsError(pv_name) return pvs[0] @@ -748,6 +962,97 @@ class VolumeGroup(object): def __repr__(self): return self.__str__() + def _parse_size(self, size): + error_msg = "Unable to convert vg size to integer: '%s'" % str(size) + try: + integer, _ = size.split('g') + except ValueError: + logger.exception(error_msg) + raise RuntimeError(error_msg) + + return util.str_to_int(integer) + + @property + def free(self): + """ + Parse the available size in gigabytes from the ``vg_free`` attribute, that + will be a string with a character ('g') to indicate gigabytes in size. + Returns a rounded down integer to ease internal operations:: + + >>> data_vg.vg_free + '0.01g' + >>> data_vg.size + 0 + """ + return self._parse_size(self.vg_free) + + @property + def size(self): + """ + Parse the size in gigabytes from the ``vg_size`` attribute, that + will be a string with a character ('g') to indicate gigabytes in size. + Returns a rounded down integer to ease internal operations:: + + >>> data_vg.vg_size + '1024.9g' + >>> data_vg.size + 1024 + """ + return self._parse_size(self.vg_size) + + def sizing(self, parts=None, size=None): + """ + Calculate proper sizing to fully utilize the volume group in the most + efficient way possible. To prevent situations where LVM might accept + a percentage that is beyond the vg's capabilities, it will refuse with + an error when requesting a larger-than-possible parameter, in addition + to rounding down calculations. + + A dictionary with different sizing parameters is returned, to make it + easier for others to choose what they need in order to create logical + volumes:: + + >>> data_vg.free + 1024 + >>> data_vg.sizing(parts=4) + {'parts': 4, 'sizes': 256, 'percentages': 25} + >>> data_vg.sizing(size=512) + {'parts': 2, 'sizes': 512, 'percentages': 50} + + + :param parts: Number of parts to create LVs from + :param size: Size in gigabytes to divide the VG into + + :raises SizeAllocationError: When requested size cannot be allocated with + :raises ValueError: If both ``parts`` and ``size`` are given + """ + if parts is not None and size is not None: + raise ValueError( + "Cannot process sizing with both parts (%s) and size (%s)" % (parts, size) + ) + + # if size is given we need to map that to extents so that we avoid + # issues when trying to get this right with a size in gigabytes find + # the percentage first, cheating, because these values are thrown out + vg_free_count = util.str_to_int(self.vg_free_count) + + if size: + extents = int(size * vg_free_count / self.free) + disk_sizing = sizing(self.free, size=size, parts=parts) + else: + if parts is not None: + # Prevent parts being 0, falling back to 1 (100% usage) + parts = parts or 1 + size = int(self.free / parts) + extents = size * vg_free_count / self.free + disk_sizing = sizing(self.free, parts=parts) + + extent_sizing = sizing(vg_free_count, size=extents) + + disk_sizing['extents'] = int(extents) + disk_sizing['percentages'] = extent_sizing['percentages'] + return disk_sizing + class Volume(object): """ diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/activate.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/activate.py index 5e9376e5d..782a6d169 100644 --- a/ceph/src/ceph-volume/ceph_volume/devices/lvm/activate.py +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/activate.py @@ -3,7 +3,7 @@ import argparse import logging import os from textwrap import dedent -from ceph_volume import process, conf, decorators, terminal +from ceph_volume import process, conf, decorators, terminal, __release__ from ceph_volume.util import system, disk from ceph_volume.util import prepare as prepare_utils from ceph_volume.util import encryption as encryption_utils @@ -74,6 +74,9 @@ def activate_filestore(lvs, no_systemd=False): # enable the ceph-volume unit for this OSD systemctl.enable_volume(osd_id, osd_fsid, 'lvm') + # enable the OSD + systemctl.enable_osd(osd_id) + # start the OSD systemctl.start_osd(osd_id) terminal.success("ceph-volume lvm activate successful for osd ID: %s" % osd_id) @@ -148,10 +151,16 @@ def activate_bluestore(lvs, no_systemd=False): wal_device_path = get_osd_device_path(osd_lv, lvs, 'wal', dmcrypt_secret=dmcrypt_secret) # Once symlinks are removed, the osd dir can be 'primed again. - process.run([ + prime_command = [ 'ceph-bluestore-tool', '--cluster=%s' % conf.cluster, 'prime-osd-dir', '--dev', osd_lv_path, - '--path', osd_path]) + '--path', osd_path] + + if __release__ != "luminous": + # mon-config changes are not available in Luminous + prime_command.append('--no-mon-config') + + process.run(prime_command) # always re-do the symlink regardless if it exists, so that the block, # block.wal, and block.db devices that may have changed can be mapped # correctly every time @@ -173,6 +182,9 @@ def activate_bluestore(lvs, no_systemd=False): # enable the ceph-volume unit for this OSD systemctl.enable_volume(osd_id, osd_fsid, 'lvm') + # enable the OSD + systemctl.enable_osd(osd_id) + # start the OSD systemctl.start_osd(osd_id) terminal.success("ceph-volume lvm activate successful for osd ID: %s" % osd_id) diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/batch.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/batch.py new file mode 100644 index 000000000..4086064f5 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/batch.py @@ -0,0 +1,221 @@ +import argparse +from textwrap import dedent +from ceph_volume import terminal, decorators +from ceph_volume.util import disk, prompt_bool +from ceph_volume.util import arg_validators +from . import strategies + + +device_list_template = """ + * {path: <25} {size: <10} {state}""" + + +def device_formatter(devices): + lines = [] + for path, details in devices: + lines.append(device_list_template.format( + path=path, size=details['human_readable_size'], + state='solid' if details['rotational'] == '0' else 'rotational') + ) + + return ''.join(lines) + + +# Scenario filtering/detection +def bluestore_single_type(device_facts): + """ + Detect devices that are just HDDs or solid state so that a 1:1 + device-to-osd provisioning can be done + """ + types = [device.sys_api['rotational'] for device in device_facts] + if len(set(types)) == 1: + return strategies.bluestore.SingleType + + +def bluestore_mixed_type(device_facts): + """ + Detect if devices are HDDs as well as solid state so that block.db can be + placed in solid devices while data is kept in the spinning drives. + """ + types = [device.sys_api['rotational'] for device in device_facts] + if len(set(types)) > 1: + return strategies.bluestore.MixedType + + +def filestore_single_type(device_facts): + """ + Detect devices that are just HDDs or solid state so that a 1:1 + device-to-osd provisioning can be done, keeping the journal on the OSD + """ + types = [device.sys_api['rotational'] for device in device_facts] + if len(set(types)) == 1: + return strategies.filestore.SingleType + + +def filestore_mixed_type(device_facts): + """ + Detect if devices are HDDs as well as solid state so that the journal can be + placed in solid devices while data is kept in the spinning drives. + """ + types = [device.sys_api['rotational'] for device in device_facts] + if len(set(types)) > 1: + return strategies.filestore.MixedType + + +def get_strategy(args): + """ + Given a set of devices as input, go through the different detection + mechanisms to narrow down on a strategy to use. The strategies are 4 in + total: + + * Single device type on Bluestore + * Mixed device types on Bluestore + * Single device type on Filestore + * Mixed device types on Filestore + + When the function matches to a scenario it returns the strategy class. This + allows for dynamic loading of the conditions needed for each scenario, with + normalized classes + """ + bluestore_strategies = [bluestore_mixed_type, bluestore_single_type] + filestore_strategies = [filestore_mixed_type, filestore_single_type] + if args.bluestore: + strategies = bluestore_strategies + else: + strategies = filestore_strategies + + for strategy in strategies: + backend = strategy(args.devices) + if backend: + return backend(args.devices, args) + + +class Batch(object): + + help = 'Automatically size devices for multi-OSD provisioning with minimal interaction' + + _help = dedent(""" + Automatically size devices ready for OSD provisioning based on default strategies. + + Detected devices: + {detected_devices} + + Usage: + + ceph-volume lvm batch [DEVICE...] + + Optional reporting on possible outcomes is enabled with --report + + ceph-volume lvm batch --report [DEVICE...] + """) + + def __init__(self, argv): + self.argv = argv + + def get_devices(self): + all_devices = disk.get_devices() + # remove devices with partitions + # XXX Should be optional when getting device info + for device, detail in all_devices.items(): + if detail.get('partitions') != {}: + del all_devices[device] + devices = sorted(all_devices.items(), key=lambda x: (x[0], x[1]['size'])) + return device_formatter(devices) + + def print_help(self): + return self._help.format( + detected_devices=self.get_devices(), + ) + + def report(self, args): + strategy = get_strategy(args) + if args.format == 'pretty': + strategy.report_pretty() + elif args.format == 'json': + strategy.report_json() + else: + raise RuntimeError('report format must be "pretty" or "json"') + + def execute(self, args): + strategy = get_strategy(args) + if not args.yes: + strategy.report_pretty() + terminal.info('The above OSDs would be created if the operation continues') + if not prompt_bool('do you want to proceed? (yes/no)'): + terminal.error('aborting OSD provisioning for %s' % ','.join(args.devices)) + raise SystemExit(0) + + strategy.execute() + + @decorators.needs_root + def main(self): + parser = argparse.ArgumentParser( + prog='ceph-volume lvm batch', + formatter_class=argparse.RawDescriptionHelpFormatter, + description=self.print_help(), + ) + + parser.add_argument( + 'devices', + metavar='DEVICES', + nargs='*', + type=arg_validators.ValidDevice(), + default=[], + help='Devices to provision OSDs', + ) + parser.add_argument( + '--bluestore', + action='store_true', + help='bluestore objectstore (default)', + ) + parser.add_argument( + '--filestore', + action='store_true', + help='filestore objectstore', + ) + parser.add_argument( + '--report', + action='store_true', + help='Autodetect the objectstore by inspecting the OSD', + ) + parser.add_argument( + '--yes', + action='store_true', + help='Avoid prompting for confirmation when provisioning', + ) + parser.add_argument( + '--format', + help='output format, defaults to "pretty"', + default='pretty', + choices=['json', 'pretty'], + ) + parser.add_argument( + '--dmcrypt', + action='store_true', + help='Enable device encryption via dm-crypt', + ) + parser.add_argument( + '--crush-device-class', + dest='crush_device_class', + help='Crush device class to assign this OSD to', + ) + parser.add_argument( + '--no-systemd', + dest='no_systemd', + action='store_true', + help='Skip creating and enabling systemd units and starting OSD services', + ) + args = parser.parse_args(self.argv) + + if not args.devices: + return parser.print_help() + + # Default to bluestore here since defaulting it in add_argument may + # cause both to be True + if not args.bluestore and not args.filestore: + args.bluestore = True + + if args.report: + self.report(args) + else: + self.execute(args) diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/listing.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/listing.py index a84a39c18..a3975280d 100644 --- a/ceph/src/ceph-volume/ceph_volume/devices/lvm/listing.py +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/listing.py @@ -49,8 +49,15 @@ def pretty_report(report): value=value ) ) - output.append( - device_metadata_item_template.format(tag_name='devices', value=','.join(device['devices']))) + if not device.get('devices'): + continue + else: + output.append( + device_metadata_item_template.format( + tag_name='devices', + value=','.join(device['devices']) + ) + ) print(''.join(output)) diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/main.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/main.py index 8b698a03f..abe77b57e 100644 --- a/ceph/src/ceph-volume/ceph_volume/devices/lvm/main.py +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/main.py @@ -7,6 +7,7 @@ from . import create from . import trigger from . import listing from . import zap +from . import batch class LVM(object): @@ -21,6 +22,7 @@ class LVM(object): mapper = { 'activate': activate.Activate, + 'batch': batch.Batch, 'prepare': prepare.Prepare, 'create': create.Create, 'trigger': trigger.Trigger, diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/prepare.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/prepare.py index 2369cb6f3..aedb71ed5 100644 --- a/ceph/src/ceph-volume/ceph_volume/devices/lvm/prepare.py +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/prepare.py @@ -1,7 +1,6 @@ from __future__ import print_function import json import logging -import uuid from textwrap import dedent from ceph_volume.util import prepare as prepare_utils from ceph_volume.util import encryption as encryption_utils @@ -69,7 +68,7 @@ def prepare_filestore(device, journal, secrets, tags, osd_id, fsid): # get the latest monmap prepare_utils.get_monmap(osd_id) # prepare the osd filesystem - prepare_utils.osd_mkfs_filestore(osd_id, fsid) + prepare_utils.osd_mkfs_filestore(osd_id, fsid, cephx_secret) # write the OSD keyring if it doesn't exist already prepare_utils.write_keyring(osd_id, cephx_secret) if secrets.get('dmcrypt_key'): @@ -192,12 +191,11 @@ class Prepare(object): """ if disk.is_partition(arg) or disk.is_device(arg): # we must create a vg, and then a single lv - vg_name = "ceph-%s" % str(uuid.uuid4()) - api.create_vg(vg_name, arg) + vg = api.create_vg(arg) lv_name = "osd-%s-%s" % (device_type, osd_fsid) return api.create_lv( lv_name, - vg_name, # the volume group + vg.name, # the volume group tags={'ceph.type': device_type}) else: error = [ diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/__init__.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/__init__.py new file mode 100644 index 000000000..cb16d1162 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/__init__.py @@ -0,0 +1 @@ +from . import bluestore, filestore # noqa diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/bluestore.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/bluestore.py new file mode 100644 index 000000000..7b6052607 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/bluestore.py @@ -0,0 +1,248 @@ +from __future__ import print_function +import json +from uuid import uuid4 +from ceph_volume.util import disk +from ceph_volume.api import lvm +from . import validators +from ceph_volume.devices.lvm.create import Create +from ceph_volume.util import templates + + +class SingleType(object): + """ + Support for all SSDs, or all HDDS + """ + + def __init__(self, devices, args): + self.args = args + self.devices = devices + self.hdds = [device for device in devices if device.sys_api['rotational'] == '1'] + self.ssds = [device for device in devices if device.sys_api['rotational'] == '0'] + self.computed = {'osds': [], 'vgs': []} + self.validate() + self.compute() + + def report_json(self): + print(json.dumps(self.computed, indent=4, sort_keys=True)) + + def report_pretty(self): + string = "" + string += templates.total_osds.format( + total_osds=len(self.hdds) or len(self.ssds) * 2 + ) + string += templates.osd_component_titles + + for osd in self.computed['osds']: + string += templates.osd_header + string += templates.osd_component.format( + _type='[data]', + path=osd['data']['path'], + size=osd['data']['human_readable_size'], + percent=osd['data']['percentage'], + ) + + print(string) + + def validate(self): + """ + Ensure that the minimum requirements for this type of scenario is + met, raise an error if the provided devices would not work + """ + # validate minimum size for all devices + validators.minimum_device_size(self.devices) + + def compute(self): + """ + Go through the rules needed to properly size the lvs, return + a dictionary with the result + """ + osds = self.computed['osds'] + vgs = self.computed['vgs'] + for device in self.hdds: + vgs.append({'devices': [device.abspath], 'parts': 1}) + osd = {'data': {}, 'block.db': {}} + osd['data']['path'] = device.abspath + osd['data']['size'] = device.sys_api['size'] + osd['data']['parts'] = 1 + osd['data']['percentage'] = 100 + osd['data']['human_readable_size'] = str(disk.Size(b=device.sys_api['size'])) + osds.append(osd) + + for device in self.ssds: + # TODO: creates 2 OSDs per device, make this configurable (env var?) + extents = lvm.sizing(device.sys_api['size'], parts=2) + vgs.append({'devices': [device.abspath], 'parts': 2}) + for ssd in range(2): + osd = {'data': {}, 'block.db': {}} + osd['data']['path'] = device.abspath + osd['data']['size'] = extents['sizes'] + osd['data']['parts'] = extents['parts'] + osd['data']['percentage'] = 50 + osd['data']['human_readable_size'] = str(disk.Size(b=extents['sizes'])) + osds.append(osd) + + def execute(self): + """ + Create vgs/lvs from the incoming set of devices, assign their roles + (block, block.db, block.wal, etc..) and offload the OSD creation to + ``lvm create`` + """ + osd_vgs = dict([(osd['data']['path'], None) for osd in self.computed['osds']]) + + # create the vgs first, mapping them to the device path + for osd in self.computed['osds']: + vg = osd_vgs.get(osd['data']['path']) + if not vg: + vg = lvm.create_vg(osd['data']['path']) + osd_vgs[osd['data']['path']] = {'vg': vg, 'parts': osd['data']['parts']} + + # create the lvs from the vgs captured in the beginning + for create in osd_vgs.values(): + lvs = lvm.create_lvs(create['vg'], parts=create['parts'], name_prefix='osd-data') + vg_name = create['vg'].name + for lv in lvs: + command = ['--bluestore', '--data'] + command.append('%s/%s' % (vg_name, lv.name)) + if self.args.dmcrypt: + command.append('--dmcrypt') + if self.args.no_systemd: + command.append('--no-systemd') + if self.args.crush_device_class: + command.extend(['--crush-device-class', self.args.crush_device_class]) + + Create(command).main() + + +class MixedType(object): + + def __init__(self, devices, args): + self.args = args + self.devices = devices + self.hdds = [device for device in devices if device.sys_api['rotational'] == '1'] + self.ssds = [device for device in devices if device.sys_api['rotational'] == '0'] + self.computed = {'osds': [], 'vgs': []} + self.block_db_size = None + # For every HDD we get 1 block.db + self.db_lvs = len(self.hdds) + self.validate() + self.compute() + + def report_json(self): + print(json.dumps(self.computed, indent=4, sort_keys=True)) + + def report_pretty(self): + vg_extents = lvm.sizing(self.total_ssd_size.b, parts=self.db_lvs) + db_size = str(disk.Size(b=(vg_extents['sizes']))) + + string = "" + string += templates.total_osds.format( + total_osds=len(self.hdds) + ) + + string += templates.ssd_volume_group.format( + target='block.db', + total_lv_size=str(self.total_ssd_size), + total_lvs=vg_extents['parts'], + block_lv_size=db_size, + block_db_devices=', '.join([ssd.abspath for ssd in self.ssds]), + lv_size=str(disk.Size(b=(vg_extents['sizes']))), + total_osds=len(self.hdds) + ) + + string += templates.osd_component_titles + for osd in self.computed['osds']: + string += templates.osd_header + string += templates.osd_component.format( + _type='[data]', + path=osd['data']['path'], + size=osd['data']['human_readable_size'], + percent=osd['data']['percentage']) + + string += templates.osd_component.format( + _type='[block.db]', + path='(volume-group/lv)', + size=osd['block.db']['human_readable_size'], + percent=osd['block.db']['percentage']) + + print(string) + + def compute(self): + osds = self.computed['osds'] + for device in self.hdds: + osd = {'data': {}, 'block.db': {}} + osd['data']['path'] = device.abspath + osd['data']['size'] = device.sys_api['size'] + osd['data']['percentage'] = 100 + osd['data']['human_readable_size'] = str(disk.Size(b=(device.sys_api['size']))) + osd['block.db']['path'] = None + osd['block.db']['size'] = int(self.block_db_size.b) + osd['block.db']['human_readable_size'] = str(self.block_db_size) + osd['block.db']['percentage'] = self.vg_extents['percentages'] + osds.append(osd) + + self.computed['vgs'] = [{ + 'devices': [d.abspath for d in self.ssds], + 'parts': self.db_lvs, + 'percentages': self.vg_extents['percentages'], + 'sizes': self.vg_extents['sizes'], + 'size': int(self.total_ssd_size.b), + 'human_readable_sizes': str(disk.Size(b=self.vg_extents['sizes'])), + 'human_readable_size': str(self.total_ssd_size), + }] + + def execute(self): + """ + Create vgs/lvs from the incoming set of devices, assign their roles + (block, block.db, block.wal, etc..) and offload the OSD creation to + ``lvm create`` + """ + # create the single vg for all block.db lv's first + vg_info = self.computed['vgs'][0] + vg = lvm.create_vg(vg_info['devices']) + + # now produce all the block.db lvs needed from that single vg + db_lvs = lvm.create_lvs(vg, parts=vg_info['parts'], name_prefix='osd-block-db') + + # create the data lvs, and create the OSD with the matching block.db lvs from before + for osd in self.computed['osds']: + vg = lvm.create_vg(osd['data']['path']) + data_lv = lvm.create_lv('osd-data-%s' % str(uuid4()), vg.name) + db_lv = db_lvs.pop() + command = [ + '--bluestore', + '--data', "%s/%s" % (data_lv.vg_name, data_lv.name), + '--block.db', '%s/%s' % (db_lv.vg_name, db_lv.name) + ] + if self.args.dmcrypt: + command.append('--dmcrypt') + if self.args.no_systemd: + command.append('--no-systemd') + if self.args.crush_device_class: + command.extend(['--crush-device-class', self.args.crush_device_class]) + + Create(command).main() + + def validate(self): + """ + HDDs represent data devices, and solid state devices are for block.db, + make sure that the number of data devices would have enough LVs and + those LVs would be large enough to accommodate a block.db + """ + # validate minimum size for all devices + validators.minimum_device_size(self.devices) + + # add all the size available in solid drives and divide it by the + # expected number of osds, the expected output should be larger than + # the minimum alllowed for block.db + self.total_ssd_size = disk.Size(b=0) + for ssd in self.ssds: + self.total_ssd_size += disk.Size(b=ssd.sys_api['size']) + + self.block_db_size = self.total_ssd_size / self.db_lvs + self.vg_extents = lvm.sizing(self.total_ssd_size.b, parts=self.db_lvs) + + # min 2GB of block.db is allowed + msg = 'Total solid size (%s) is not enough for block.db LVs larger than 2 GB' + if self.block_db_size < disk.Size(gb=2): + # use ad-hoc exception here + raise RuntimeError(msg % self.total_ssd_size) diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/filestore.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/filestore.py new file mode 100644 index 000000000..9e80c5cbb --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/filestore.py @@ -0,0 +1,318 @@ +from __future__ import print_function +import json +from ceph_volume.util import disk, prepare +from ceph_volume.api import lvm +from . import validators +from ceph_volume.devices.lvm.create import Create +from ceph_volume.util import templates + + +class SingleType(object): + """ + Support for all SSDs, or all HDDs, data and journal LVs will be colocated + in the same device + """ + + def __init__(self, devices, args): + self.args = args + self.devices = devices + self.hdds = [device for device in devices if device.sys_api['rotational'] == '1'] + self.ssds = [device for device in devices if device.sys_api['rotational'] == '0'] + self.computed = {'osds': [], 'vgs': []} + self.validate() + self.compute() + + def report_json(self): + print(json.dumps(self.computed, indent=4, sort_keys=True)) + + def report_pretty(self): + string = "" + string += templates.total_osds.format( + total_osds=len(self.hdds) or len(self.ssds) * 2 + ) + string += templates.osd_component_titles + + for osd in self.computed['osds']: + string += templates.osd_header + string += templates.osd_component.format( + _type='[data]', + path=osd['data']['path'], + size=osd['data']['human_readable_size'], + percent=osd['data']['percentage'], + ) + string += templates.osd_component.format( + _type='[journal]', + path=osd['journal']['path'], + size=osd['journal']['human_readable_size'], + percent=osd['journal']['percentage'], + ) + + print(string) + + def validate(self): + """ + Ensure that the minimum requirements for this type of scenario is + met, raise an error if the provided devices would not work + """ + # validate minimum size for all devices + validators.minimum_device_size(self.devices) + + def compute(self): + """ + Go through the rules needed to properly size the lvs, return + a dictionary with the result + """ + # chose whichever is the one group we have to compute against + devices = self.hdds or self.ssds + osds = self.computed['osds'] + vgs = self.computed['vgs'] + for device in devices: + device_size = disk.Size(b=device.sys_api['size']) + journal_size = prepare.get_journal_size(lv_format=False) + data_size = device_size - journal_size + data_percentage = data_size * 100 / device_size + vgs.append({'devices': [device.abspath], 'parts': 2}) + osd = {'data': {}, 'journal': {}} + osd['data']['path'] = device.abspath + osd['data']['size'] = data_size.b + osd['data']['percentage'] = int(data_percentage) + osd['data']['human_readable_size'] = str(data_size) + osd['journal']['path'] = device.abspath + osd['journal']['size'] = journal_size.b + osd['journal']['percentage'] = int(100 - data_percentage) + osd['journal']['human_readable_size'] = str(journal_size) + osds.append(osd) + + def execute(self): + """ + Create vgs/lvs from the incoming set of devices, assign their roles + (data, journal) and offload the OSD creation to ``lvm create`` + """ + osd_vgs = [] + + # create the vgs first, one per device (since this is colocating, it + # picks the 'data' path) + for osd in self.computed['osds']: + vg = lvm.create_vg(osd['data']['path']) + osd_vgs.append(vg) + + journal_size = prepare.get_journal_size() + + # create the lvs from the vgs captured in the beginning + for vg in osd_vgs: + # this is called again, getting us the LVM formatted string + journal_lv = lvm.create_lv( + 'osd-journal', vg.name, size=journal_size, uuid_name=True + ) + # no extents or size means it will use 100%FREE + data_lv = lvm.create_lv('osd-data', vg.name) + + command = ['--filestore', '--data'] + command.append('%s/%s' % (vg.name, data_lv.name)) + command.extend(['--journal', '%s/%s' % (vg.name, journal_lv.name)]) + if self.args.dmcrypt: + command.append('--dmcrypt') + if self.args.no_systemd: + command.append('--no-systemd') + if self.args.crush_device_class: + command.extend(['--crush-device-class', self.args.crush_device_class]) + + Create(command).main() + + +class MixedType(object): + """ + Supports HDDs with SSDs, journals will be placed on SSDs, while HDDs will + be used fully for data. + + If an existing common VG is detected on SSDs, it will be extended if blank + SSDs are used, otherwise it will be used directly. + """ + + def __init__(self, devices, args): + self.args = args + self.devices = devices + self.hdds = [device for device in devices if device.sys_api['rotational'] == '1'] + self.ssds = [device for device in devices if device.sys_api['rotational'] == '0'] + self.computed = {'osds': [], 'vg': None} + self.blank_ssds = [] + self.journals_needed = len(self.hdds) + self.journal_size = prepare.get_journal_size(lv_format=False) + self.system_vgs = lvm.VolumeGroups() + self.validate() + self.compute() + + def report_json(self): + print(json.dumps(self.computed, indent=4, sort_keys=True)) + + def report_pretty(self): + string = "" + string += templates.total_osds.format( + total_osds=len(self.hdds) or len(self.ssds) * 2 + ) + + string += templates.ssd_volume_group.format( + target='journal', + total_lv_size=str(self.total_available_journal_space), + total_lvs=self.journals_needed, + block_db_devices=', '.join([d.path for d in self.ssds]), + lv_size=str(self.journal_size), + total_osds=self.journals_needed + ) + + string += templates.osd_component_titles + + for osd in self.computed['osds']: + string += templates.osd_header + string += templates.osd_component.format( + _type='[data]', + path=osd['data']['path'], + size=osd['data']['human_readable_size'], + percent=osd['data']['percentage'], + ) + string += templates.osd_component.format( + _type='[journal]', + path=osd['journal']['path'], + size=osd['journal']['human_readable_size'], + percent=osd['journal']['percentage'], + ) + + print(string) + + def get_common_vg(self): + # find all the vgs associated with the current device + for ssd in self.ssds: + for pv in ssd.pvs_api: + vg = self.system_vgs.get(vg_name=pv.vg_name) + if not vg: + continue + # this should give us just one VG, it would've been caught by + # the validator otherwise + return vg + + def validate(self): + """ + Ensure that the minimum requirements for this type of scenario is + met, raise an error if the provided devices would not work + """ + # validate minimum size for all devices + validators.minimum_device_size(self.devices) + + # make sure that data devices do not have any LVs + validators.no_lvm_membership(self.hdds) + + # do not allow non-common VG to continue + validators.has_common_vg(self.ssds) + + # find the common VG to calculate how much is available + self.common_vg = self.get_common_vg() + + # find how many journals are possible from the common VG + if self.common_vg: + common_vg_size = disk.Size(gb=self.common_vg.free) + else: + common_vg_size = disk.Size(gb=0) + + # non-VG SSDs + self.vg_ssds = set([d for d in self.ssds if d.is_lvm_member]) + self.blank_ssds = set(self.ssds).difference(self.vg_ssds) + self.total_blank_ssd_size = disk.Size(b=0) + for blank_ssd in self.blank_ssds: + self.total_blank_ssd_size += disk.Size(b=blank_ssd.sys_api['size']) + + self.total_available_journal_space = self.total_blank_ssd_size + common_vg_size + + try: + self.vg_extents = lvm.sizing( + self.total_available_journal_space.b, size=self.journal_size.b + ) + # FIXME with real exception catching from sizing that happens when the + # journal space is not enough + except Exception: + self.vg_extents = {'parts': 0, 'percentages': 0, 'sizes': 0} + + # validate that number of journals possible are enough for number of + # OSDs proposed + total_journals_possible = self.total_available_journal_space / self.journal_size + if len(self.hdds) > total_journals_possible: + msg = "Not enough %s journals (%s) can be created for %s OSDs" % ( + self.journal_size, total_journals_possible, len(self.hdds) + ) + raise RuntimeError(msg) + + def compute(self): + """ + Go through the rules needed to properly size the lvs, return + a dictionary with the result + """ + osds = self.computed['osds'] + + vg_free = int(self.total_available_journal_space.gb) + if not self.common_vg: + # there isn't a common vg, so a new one must be created with all + # the blank SSDs + self.computed['vg'] = { + 'devices': self.blank_ssds, + 'parts': self.journals_needed, + 'percentages': self.vg_extents['percentages'], + 'sizes': self.journal_size.b, + 'size': int(self.total_blank_ssd_size.b), + 'human_readable_sizes': str(self.journal_size), + 'human_readable_size': str(self.total_available_journal_space), + } + vg_name = 'lv/vg' + else: + vg_name = self.common_vg.name + + for device in self.hdds: + device_size = disk.Size(b=device.sys_api['size']) + data_size = device_size - self.journal_size + osd = {'data': {}, 'journal': {}} + osd['data']['path'] = device.path + osd['data']['size'] = data_size.b + osd['data']['percentage'] = 100 + osd['data']['human_readable_size'] = str(device_size) + osd['journal']['path'] = 'vg: %s' % vg_name + osd['journal']['size'] = self.journal_size.b + osd['journal']['percentage'] = int(self.journal_size.gb * 100 / vg_free) + osd['journal']['human_readable_size'] = str(self.journal_size) + osds.append(osd) + + def execute(self): + """ + Create vgs/lvs from the incoming set of devices, assign their roles + (data, journal) and offload the OSD creation to ``lvm create`` + """ + ssd_paths = [d.abspath for d in self.blank_ssds] + + # no common vg is found, create one with all the blank SSDs + if not self.common_vg: + journal_vg = lvm.create_vg(ssd_paths, name_prefix='ceph-journals') + # a vg exists that can be extended + elif self.common_vg and ssd_paths: + journal_vg = lvm.extend_vg(self.common_vg, ssd_paths) + # one common vg with nothing else to extend can be used directly + else: + journal_vg = self.common_vg + + journal_size = prepare.get_journal_size(lv_format=True) + + for osd in self.computed['osds']: + data_vg = lvm.create_vg(osd['data']['path'], name_prefix='ceph-data') + # no extents or size means it will use 100%FREE + data_lv = lvm.create_lv('osd-data', data_vg.name) + journal_lv = lvm.create_lv( + 'osd-journal', journal_vg.name, size=journal_size, uuid_name=True + ) + + command = ['--filestore', '--data'] + command.append('%s/%s' % (data_vg.name, data_lv.name)) + command.extend(['--journal', '%s/%s' % (journal_vg.name, journal_lv.name)]) + if self.args.dmcrypt: + command.append('--dmcrypt') + if self.args.no_systemd: + command.append('--no-systemd') + if self.args.crush_device_class: + command.extend(['--crush-device-class', self.args.crush_device_class]) + + Create(command).main() diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/validators.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/validators.py new file mode 100644 index 000000000..06c5ebeaf --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/strategies/validators.py @@ -0,0 +1,48 @@ +from ceph_volume.util import disk +from ceph_volume.api import lvm + + +def minimum_device_size(devices): + """ + Ensure that the minimum requirements for this type of scenario is + met, raise an error if the provided devices would not work + """ + msg = 'Unable to use device smaller than 5GB: %s (%s)' + for device in devices: + device_size = disk.Size(b=device.sys_api['size']) + if device_size < disk.Size(gb=5): + raise RuntimeError(msg % (device, device_size)) + + +def no_lvm_membership(devices): + """ + Do not allow devices that are part of LVM + """ + msg = 'Unable to use device, already a member of LVM: %s' + for device in devices: + if device.is_lvm_member: + raise RuntimeError(msg % device.abspath) + + +def has_common_vg(ssd_devices): + """ + Ensure that devices have a common VG between them + """ + msg = 'Could not find a common VG between devices: %s' + system_vgs = lvm.VolumeGroups() + ssd_vgs = {} + + for ssd_device in ssd_devices: + for pv in ssd_device.pvs_api: + vg = system_vgs.get(vg_name=pv.vg_name) + if not vg: + continue + try: + ssd_vgs[vg.name].append(ssd_device.abspath) + except KeyError: + ssd_vgs[vg.name] = [ssd_device.abspath] + # len of 1 means they all have a common vg, and len of 0 means that these + # are blank + if len(ssd_vgs) <= 1: + return + raise RuntimeError(msg % ', '.join(ssd_vgs.keys())) diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/zap.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/zap.py index ee5cdf97b..90c5447c0 100644 --- a/ceph/src/ceph-volume/ceph_volume/devices/lvm/zap.py +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/zap.py @@ -46,71 +46,80 @@ class Zap(object): def __init__(self, argv): self.argv = argv - @decorators.needs_root - def zap(self, args): - device = args.device - lv = api.get_lv_from_argument(device) - if lv: - # we are zapping a logical volume - path = lv.lv_path - else: - # we are zapping a partition - #TODO: ensure device is a partition - path = device - - mlogger.info("Zapping: %s", path) - - # check if there was a pv created with the - # name of device - pv = api.get_pv(pv_name=device) - if pv: - vg_name = pv.vg_name - lv = api.get_lv(vg_name=vg_name) - - dmcrypt = False - dmcrypt_uuid = None - if lv: - osd_path = "/var/lib/ceph/osd/{}-{}".format(lv.tags['ceph.cluster_name'], lv.tags['ceph.osd_id']) - dmcrypt_uuid = lv.lv_uuid - dmcrypt = lv.encrypted - if system.path_is_mounted(osd_path): - mlogger.info("Unmounting %s", osd_path) - system.unmount(osd_path) + def unmount_lv(self, lv): + if lv.tags.get('ceph.cluster_name') and lv.tags.get('ceph.osd_id'): + lv_path = "/var/lib/ceph/osd/{}-{}".format(lv.tags['ceph.cluster_name'], lv.tags['ceph.osd_id']) else: - # we're most likely dealing with a partition here, check to - # see if it was encrypted - partuuid = disk.get_partuuid(device) - if encryption.status("/dev/mapper/{}".format(partuuid)): - dmcrypt_uuid = partuuid - dmcrypt = True - + lv_path = lv.lv_path + dmcrypt_uuid = lv.lv_uuid + dmcrypt = lv.encrypted + if system.path_is_mounted(lv_path): + mlogger.info("Unmounting %s", lv_path) + system.unmount(lv_path) if dmcrypt and dmcrypt_uuid: - dmcrypt_path = "/dev/mapper/{}".format(dmcrypt_uuid) - mlogger.info("Closing encrypted path %s", dmcrypt_path) - encryption.dmcrypt_close(dmcrypt_path) - - if args.destroy and pv: - logger.info("Found a physical volume created from %s, will destroy all it's vgs and lvs", device) - vg_name = pv.vg_name - mlogger.info("Destroying volume group %s because --destroy was given", vg_name) - api.remove_vg(vg_name) - mlogger.info("Destroying physical volume %s because --destroy was given", device) - api.remove_pv(device) - elif args.destroy and not pv: - mlogger.info("Skipping --destroy because no associated physical volumes are found for %s", device) - - wipefs(path) - zap_data(path) + self.dmcrypt_close(dmcrypt_uuid) - if lv and not pv: - # remove all lvm metadata - lv.clear_tags() - - terminal.success("Zapping successful for: %s" % path) + @decorators.needs_root + def zap(self, args): + for device in args.devices: + if disk.is_mapper_device(device): + terminal.error("Refusing to zap the mapper device: {}".format(device)) + raise SystemExit(1) + lv = api.get_lv_from_argument(device) + if lv: + # we are zapping a logical volume + path = lv.lv_path + self.unmount_lv(lv) + else: + # we are zapping a partition + #TODO: ensure device is a partition + path = device + # check to if it is encrypted to close + partuuid = disk.get_partuuid(device) + if encryption.status("/dev/mapper/{}".format(partuuid)): + dmcrypt_uuid = partuuid + self.dmcrypt_close(dmcrypt_uuid) + + mlogger.info("Zapping: %s", path) + + # check if there was a pv created with the + # name of device + pvs = api.PVolumes() + pvs.filter(pv_name=device) + vgs = set([pv.vg_name for pv in pvs]) + for pv in pvs: + vg_name = pv.vg_name + lv = None + if pv.lv_uuid: + lv = api.get_lv(vg_name=vg_name, lv_uuid=pv.lv_uuid) + + if lv: + self.unmount_lv(lv) + + if args.destroy: + for vg_name in vgs: + mlogger.info("Destroying volume group %s because --destroy was given", vg_name) + api.remove_vg(vg_name) + mlogger.info("Destroying physical volume %s because --destroy was given", device) + api.remove_pv(device) + + wipefs(path) + zap_data(path) + + if lv and not pvs: + # remove all lvm metadata + lv.clear_tags() + + terminal.success("Zapping successful for: %s" % ", ".join(args.devices)) + + def dmcrypt_close(self, dmcrypt_uuid): + dmcrypt_path = "/dev/mapper/{}".format(dmcrypt_uuid) + mlogger.info("Closing encrypted path %s", dmcrypt_path) + encryption.dmcrypt_close(dmcrypt_path) def main(self): sub_command_help = dedent(""" - Zaps the given logical volume, raw device or partition for reuse by ceph-volume. + Zaps the given logical volume(s), raw device(s) or partition(s) for reuse by ceph-volume. If given a path to a logical volume it must be in the format of vg/lv. Any filesystems present on the given device, vg/lv, or partition will be removed and all data will be purged. @@ -130,6 +139,10 @@ class Zap(object): ceph-volume lvm zap /dev/sdc1 + Zapping many raw devices: + + ceph-volume lvm zap /dev/sda /dev/sdb /db/sdc + If the --destroy flag is given and you are zapping a raw device or partition then all vgs and lvs that exist on that raw device or partition will be destroyed. @@ -151,10 +164,11 @@ class Zap(object): ) parser.add_argument( - 'device', - metavar='DEVICE', - nargs='?', - help='Path to an lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)' + 'devices', + metavar='DEVICES', + nargs='*', + default=[], + help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)' ) parser.add_argument( '--destroy', diff --git a/ceph/src/ceph-volume/ceph_volume/exceptions.py b/ceph/src/ceph-volume/ceph_volume/exceptions.py index 211d9d09b..f40b7b11d 100644 --- a/ceph/src/ceph-volume/ceph_volume/exceptions.py +++ b/ceph/src/ceph-volume/ceph_volume/exceptions.py @@ -79,3 +79,16 @@ class MultipleVGsError(Exception): def __str__(self): msg = "Got more than 1 result looking for volume group: %s" % self.vg_name return msg + + +class SizeAllocationError(Exception): + + def __init__(self, requested, available): + self.requested = requested + self.available = available + + def __str__(self): + msg = 'Unable to allocate size (%s), not enough free space (%s)' % ( + self.requested, self.available + ) + return msg diff --git a/ceph/src/ceph-volume/ceph_volume/main.py b/ceph/src/ceph-volume/ceph_volume/main.py index b3543b1a2..94730cbe4 100644 --- a/ceph/src/ceph-volume/ceph_volume/main.py +++ b/ceph/src/ceph-volume/ceph_volume/main.py @@ -27,7 +27,10 @@ Ceph Conf: {ceph_path} """ def __init__(self, argv=None, parse=True): - self.mapper = {'lvm': devices.lvm.LVM, 'simple': devices.simple.Simple} + self.mapper = { + 'lvm': devices.lvm.LVM, + 'simple': devices.simple.Simple, + } self.plugin_help = "No plugins found/loaded" if argv is None: self.argv = sys.argv diff --git a/ceph/src/ceph-volume/ceph_volume/process.py b/ceph/src/ceph-volume/ceph_volume/process.py index 9eece1241..66872ea54 100644 --- a/ceph/src/ceph-volume/ceph_volume/process.py +++ b/ceph/src/ceph-volume/ceph_volume/process.py @@ -3,6 +3,7 @@ from os import O_NONBLOCK, read import subprocess from select import select from ceph_volume import terminal +from ceph_volume.util import as_bytes import logging @@ -52,7 +53,10 @@ def log_descriptors(reads, process, terminal_logging): for descriptor in reads: descriptor_name = descriptor_names[descriptor] try: - log_output(descriptor_name, read(descriptor, 1024), terminal_logging, True) + message = read(descriptor, 1024) + if not isinstance(message, str): + message = message.decode('utf-8') + log_output(descriptor_name, message, terminal_logging, True) except (IOError, OSError): # nothing else to log pass @@ -196,8 +200,9 @@ def call(command, **kw): close_fds=True, **kw ) + if stdin: - stdout_stream, stderr_stream = process.communicate(stdin) + stdout_stream, stderr_stream = process.communicate(as_bytes(stdin)) else: stdout_stream = process.stdout.read() stderr_stream = process.stderr.read() diff --git a/ceph/src/ceph-volume/ceph_volume/systemd/systemctl.py b/ceph/src/ceph-volume/ceph_volume/systemd/systemctl.py index 688433774..41dbbc19e 100644 --- a/ceph/src/ceph-volume/ceph_volume/systemd/systemctl.py +++ b/ceph/src/ceph-volume/ceph_volume/systemd/systemctl.py @@ -12,8 +12,11 @@ def stop(unit): process.run(['systemctl', 'stop', unit]) -def enable(unit): - process.run(['systemctl', 'enable', unit]) +def enable(unit, runtime=False): + if runtime: + process.run(['systemctl', 'enable', '--runtime', unit]) + else: + process.run(['systemctl', 'enable', unit]) def disable(unit): @@ -41,7 +44,7 @@ def stop_osd(id_): def enable_osd(id_): - return enable(osd_unit % id_) + return enable(osd_unit % id_, runtime=True) def disable_osd(id_): diff --git a/ceph/src/ceph-volume/ceph_volume/tests/api/test_lvm.py b/ceph/src/ceph-volume/ceph_volume/tests/api/test_lvm.py index adfa30c3b..d4c0b7231 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/api/test_lvm.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/api/test_lvm.py @@ -88,14 +88,6 @@ def volumes(monkeypatch): return volumes -@pytest.fixture -def pvolumes(monkeypatch): - monkeypatch.setattr(process, 'call', lambda x: ('', '', 0)) - pvolumes = api.PVolumes() - pvolumes._purge() - return pvolumes - - @pytest.fixture def volume_groups(monkeypatch): monkeypatch.setattr(process, 'call', lambda x: ('', '', 0)) @@ -143,6 +135,31 @@ class TestGetPV(object): monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) assert api.get_pv(pv_uuid='0000') == FooPVolume + def test_multiple_pvs_is_matched_by_uuid(self, pvolumes, monkeypatch): + FooPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}, lv_uuid="0000000") + BarPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}) + pvolumes.append(FooPVolume) + pvolumes.append(BarPVolume) + monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) + assert api.get_pv(pv_uuid='0000') == FooPVolume + + def test_multiple_pvs_is_matched_by_name(self, pvolumes, monkeypatch): + FooPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}, lv_uuid="0000000") + BarPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}) + pvolumes.append(FooPVolume) + pvolumes.append(BarPVolume) + monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) + assert api.get_pv(pv_name='/dev/sda') == FooPVolume + + def test_multiple_pvs_is_matched_by_tags(self, pvolumes, monkeypatch): + FooPVolume = api.PVolume(vg_name="vg1", pv_name='/dev/sdc', pv_uuid="1000", pv_tags="ceph.foo=bar", lv_uuid="0000000") + BarPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags="ceph.foo=bar") + pvolumes.append(FooPVolume) + pvolumes.append(BarPVolume) + monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) + with pytest.raises(exceptions.MultiplePVsError): + api.get_pv(pv_tags={"ceph.foo": "bar"}) + def test_single_pv_is_matched_by_uuid(self, pvolumes, monkeypatch): FooPVolume = api.PVolume( pv_name='/dev/vg/foo', @@ -346,6 +363,151 @@ class TestVolumeGroups(object): volume_groups.filter() +class TestVolumeGroupFree(object): + + def test_no_g_in_output(self): + vg = api.VolumeGroup(vg_name='nosize', vg_free='') + with pytest.raises(RuntimeError): + vg.free + + def test_g_without_size(self): + vg = api.VolumeGroup(vg_name='nosize', vg_free='g') + with pytest.raises(RuntimeError): + vg.free + + def test_size_without_g(self): + vg = api.VolumeGroup(vg_name='nosize', vg_free='1') + with pytest.raises(RuntimeError): + vg.free + + def test_error_message(self): + vg = api.VolumeGroup(vg_name='nosize', vg_free='F') + with pytest.raises(RuntimeError) as error: + vg.free + assert "Unable to convert vg size to integer: 'F'" in str(error) + + def test_invalid_float(self): + vg = api.VolumeGroup(vg_name='nosize', vg_free=' g') + with pytest.raises(RuntimeError) as error: + vg.free + assert "Unable to convert to integer: ' '" in str(error.value) + + def test_integer_gets_produced(self): + vg = api.VolumeGroup(vg_name='nosize', vg_free='100g') + assert vg.free == 100 + + def test_integer_gets_produced_whitespace(self): + vg = api.VolumeGroup(vg_name='nosize', vg_free=' 100g ') + assert vg.free == 100 + + def test_integer_gets_rounded_down(self): + vg = api.VolumeGroup(vg_name='nosize', vg_free='100.99g') + assert vg.free == 100 + + +class TestCreateLVs(object): + + def test_creates_correct_lv_number_from_parts(self, monkeypatch): + monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) + vg = api.VolumeGroup( + vg_name='ceph', vg_free='1024g', + vg_size='99999999g', vg_free_count='999' + ) + lvs = api.create_lvs(vg, parts=4) + assert len(lvs) == 4 + + def test_suffixes_the_size_arg(self, monkeypatch): + monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) + vg = api.VolumeGroup( + vg_name='ceph', vg_free='1024g', + vg_size='99999999g', vg_free_count='999' + ) + lvs = api.create_lvs(vg, parts=4) + assert lvs[0][1]['extents'] == 249 + + def test_only_uses_free_size(self, monkeypatch): + monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) + vg = api.VolumeGroup( + vg_name='ceph', vg_free='1024g', + vg_size='99999999g', vg_free_count='1000' + ) + lvs = api.create_lvs(vg, parts=4) + assert lvs[0][1]['extents'] == 250 + + def test_null_tags_are_set_by_default(self, monkeypatch): + monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) + vg = api.VolumeGroup( + vg_name='ceph', vg_free='1024g', + vg_size='99999999g', vg_free_count='999' + ) + kwargs = api.create_lvs(vg, parts=4)[0][1] + assert list(kwargs['tags'].values()) == ['null', 'null', 'null', 'null'] + + def test_fallback_to_one_part(self, monkeypatch): + monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) + vg = api.VolumeGroup( + vg_name='ceph', vg_free='1024g', + vg_size='99999999g', vg_free_count='999' + ) + lvs = api.create_lvs(vg) + assert len(lvs) == 1 + + +class TestVolumeGroupSizing(object): + + def setup(self): + self.vg = api.VolumeGroup( + vg_name='ceph', vg_free='1024g', + vg_free_count='261129' + ) + + def test_parts_and_size_errors(self): + with pytest.raises(ValueError) as error: + self.vg.sizing(parts=4, size=10) + assert "Cannot process sizing" in str(error) + + def test_zero_parts_produces_100_percent(self): + result = self.vg.sizing(parts=0) + assert result['percentages'] == 100 + + def test_two_parts_produces_50_percent(self): + result = self.vg.sizing(parts=2) + assert result['percentages'] == 50 + + def test_two_parts_produces_half_size(self): + result = self.vg.sizing(parts=2) + assert result['sizes'] == 512 + + def test_half_size_produces_round_sizes(self): + result = self.vg.sizing(size=512) + assert result['sizes'] == 512 + assert result['percentages'] == 50 + assert result['parts'] == 2 + + def test_bit_more_than_half_size_allocates_full_size(self): + # 513 can't allocate more than 1, so it just fallsback to using the + # whole device + result = self.vg.sizing(size=513) + assert result['sizes'] == 1024 + assert result['percentages'] == 100 + assert result['parts'] == 1 + + def test_extents_are_halfed_rounded_down(self): + result = self.vg.sizing(size=512) + # the real extents would've given 130564.5 + assert result['extents'] == 130564 + + def test_bit_less_size_rounds_down(self): + result = self.vg.sizing(size=129) + assert result['sizes'] == 146 + assert result['percentages'] == 14 + assert result['parts'] == 7 + + def test_unable_to_allocate_past_free_size(self): + with pytest.raises(exceptions.SizeAllocationError): + self.vg.sizing(size=2048) + + class TestGetLVFromArgument(object): def setup(self): @@ -416,6 +578,72 @@ class TestCreateLV(object): data_tag = ['lvchange', '--addtag', 'ceph.data_device=/path', '/path'] assert capture.calls[2]['args'][0] == data_tag + def test_uses_uuid(self, monkeypatch, capture): + monkeypatch.setattr(process, 'run', capture) + monkeypatch.setattr(process, 'call', capture) + monkeypatch.setattr(api, 'get_lv', lambda *a, **kw: self.foo_volume) + api.create_lv('foo', 'foo_group', size='5G', tags={'ceph.type': 'data'}, uuid_name=True) + result = capture.calls[0]['args'][0][5] + assert result.startswith('foo-') + assert len(result) == 40 + + +class TestExtendVG(object): + + def setup(self): + self.foo_volume = api.VolumeGroup(vg_name='foo', lv_tags='') + + def test_uses_single_device_in_list(self, monkeypatch, fake_run): + monkeypatch.setattr(api, 'get_vg', lambda **kw: True) + api.extend_vg(self.foo_volume, ['/dev/sda']) + expected = ['vgextend', '--force', '--yes', 'foo', '/dev/sda'] + assert fake_run.calls[0]['args'][0] == expected + + def test_uses_single_device(self, monkeypatch, fake_run): + monkeypatch.setattr(api, 'get_vg', lambda **kw: True) + api.extend_vg(self.foo_volume, '/dev/sda') + expected = ['vgextend', '--force', '--yes', 'foo', '/dev/sda'] + assert fake_run.calls[0]['args'][0] == expected + + def test_uses_multiple_devices(self, monkeypatch, fake_run): + monkeypatch.setattr(api, 'get_vg', lambda **kw: True) + api.extend_vg(self.foo_volume, ['/dev/sda', '/dev/sdb']) + expected = ['vgextend', '--force', '--yes', 'foo', '/dev/sda', '/dev/sdb'] + assert fake_run.calls[0]['args'][0] == expected + + +class TestCreateVG(object): + + def setup(self): + self.foo_volume = api.VolumeGroup(vg_name='foo', lv_tags='') + + def test_no_name(self, monkeypatch, fake_run): + monkeypatch.setattr(api, 'get_vg', lambda **kw: True) + api.create_vg('/dev/sda') + result = fake_run.calls[0]['args'][0] + assert '/dev/sda' in result + assert result[-2].startswith('ceph-') + + def test_devices_list(self, monkeypatch, fake_run): + monkeypatch.setattr(api, 'get_vg', lambda **kw: True) + api.create_vg(['/dev/sda', '/dev/sdb'], name='ceph') + result = fake_run.calls[0]['args'][0] + expected = ['vgcreate', '--force', '--yes', 'ceph', '/dev/sda', '/dev/sdb'] + assert result == expected + + def test_name_prefix(self, monkeypatch, fake_run): + monkeypatch.setattr(api, 'get_vg', lambda **kw: True) + api.create_vg('/dev/sda', name_prefix='master') + result = fake_run.calls[0]['args'][0] + assert '/dev/sda' in result + assert result[-2].startswith('master-') + + def test_specific_name(self, monkeypatch, fake_run): + monkeypatch.setattr(api, 'get_vg', lambda **kw: True) + api.create_vg('/dev/sda', name='master') + result = fake_run.calls[0]['args'][0] + assert '/dev/sda' in result + assert result[-2] == 'master' # # The following tests are pretty gnarly. VDO detection is very convoluted and @@ -561,3 +789,42 @@ class TestVDOParents(object): '/sys/block': block_path}) result = api._vdo_parents(['dm-3']) assert result == [] + + +class TestSplitNameParser(object): + + def test_keys_are_parsed_without_prefix(self): + line = ["DM_VG_NAME='/dev/mapper/vg';DM_LV_NAME='lv';DM_LV_LAYER=''"] + result = api._splitname_parser(line) + assert result['VG_NAME'] == 'vg' + assert result['LV_NAME'] == 'lv' + assert result['LV_LAYER'] == '' + + def test_vg_name_sans_mapper(self): + line = ["DM_VG_NAME='/dev/mapper/vg';DM_LV_NAME='lv';DM_LV_LAYER=''"] + result = api._splitname_parser(line) + assert '/dev/mapper' not in result['VG_NAME'] + + +class TestIsLV(object): + + def test_is_not_an_lv(self, monkeypatch): + monkeypatch.setattr(api, 'dmsetup_splitname', lambda x: {}) + assert api.is_lv('/dev/sda1', lvs=[]) is False + + def test_lvs_not_found(self, monkeypatch, volumes): + CephVolume = api.Volume(lv_name='foo', lv_path='/dev/vg/foo', lv_tags="ceph.type=data") + volumes.append(CephVolume) + splitname = {'LV_NAME': 'data', 'VG_NAME': 'ceph'} + monkeypatch.setattr(api, 'dmsetup_splitname', lambda x: splitname) + assert api.is_lv('/dev/sda1', lvs=volumes) is False + + def test_is_lv(self, monkeypatch, volumes): + CephVolume = api.Volume( + vg_name='ceph', lv_name='data', + lv_path='/dev/vg/foo', lv_tags="ceph.type=data" + ) + volumes.append(CephVolume) + splitname = {'LV_NAME': 'data', 'VG_NAME': 'ceph'} + monkeypatch.setattr(api, 'dmsetup_splitname', lambda x: splitname) + assert api.is_lv('/dev/sda1', lvs=volumes) is True diff --git a/ceph/src/ceph-volume/ceph_volume/tests/conftest.py b/ceph/src/ceph-volume/ceph_volume/tests/conftest.py index b1f858138..65279dc9b 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/conftest.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/conftest.py @@ -117,6 +117,14 @@ def volume_groups(monkeypatch): return vgs +@pytest.fixture +def pvolumes(monkeypatch): + monkeypatch.setattr('ceph_volume.process.call', lambda x: ('', '', 0)) + pvolumes = lvm_api.PVolumes() + pvolumes._purge() + return pvolumes + + @pytest.fixture def is_root(monkeypatch): """ @@ -132,9 +140,23 @@ def tmpfile(tmpdir): Create a temporary file, optionally filling it with contents, returns an absolute path to the file when called """ - def generate_file(name='file', contents=''): - path = os.path.join(str(tmpdir), name) + def generate_file(name='file', contents='', directory=None): + directory = directory or str(tmpdir) + path = os.path.join(directory, name) with open(path, 'w') as fp: fp.write(contents) return path return generate_file + + +@pytest.fixture +def device_info(monkeypatch): + def apply(devices=None, lsblk=None, lv=None): + devices = devices if devices else {} + lsblk = lsblk if lsblk else {} + lv = Factory(**lv) if lv else None + monkeypatch.setattr("ceph_volume.sys_info.devices", {}) + monkeypatch.setattr("ceph_volume.util.device.disk.get_devices", lambda: devices) + monkeypatch.setattr("ceph_volume.util.device.lvm.get_lv_from_argument", lambda path: lv) + monkeypatch.setattr("ceph_volume.util.device.disk.lsblk", lambda path: lsblk) + return apply diff --git a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_listing.py b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_listing.py index a49a3e9e6..173da9392 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_listing.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_listing.py @@ -210,6 +210,26 @@ class TestSingleReport(object): assert result['0'][0]['path'] == '/dev/VolGroup/lv' assert result['0'][0]['devices'] == ['/dev/sda1', '/dev/sdb1'] + def test_report_a_ceph_lv_with_multiple_pvs_of_same_name(self, pvolumes, monkeypatch): + tags = 'ceph.osd_id=0,ceph.journal_uuid=x,ceph.type=data' + lv = api.Volume( + lv_name='lv', vg_name='VolGroup', + lv_uuid='aaaa', lv_path='/dev/VolGroup/lv', lv_tags=tags + ) + monkeypatch.setattr(api, 'get_lv_from_argument', lambda device: None) + monkeypatch.setattr(api, 'get_lv', lambda vg_name: lv) + FooPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}, lv_uuid="aaaa") + BarPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}) + pvolumes.append(FooPVolume) + pvolumes.append(BarPVolume) + monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) + listing = lvm.listing.List([]) + result = listing.single_report('/dev/sda') + assert result['0'][0]['name'] == 'lv' + assert result['0'][0]['lv_tags'] == tags + assert result['0'][0]['path'] == '/dev/VolGroup/lv' + assert len(result) == 1 + def test_report_a_ceph_lv_with_no_matching_devices(self, volumes, monkeypatch): tags = 'ceph.osd_id=0,ceph.journal_uuid=x,ceph.type=data' lv = api.Volume( diff --git a/ceph/src/ceph-volume/ceph_volume/tests/devices/test_zap.py b/ceph/src/ceph-volume/ceph_volume/tests/devices/test_zap.py index 5e267fca7..493c74c50 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/devices/test_zap.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/devices/test_zap.py @@ -7,11 +7,20 @@ class TestZap(object): def test_main_spits_help_with_no_arguments(self, capsys): lvm.zap.Zap([]).main() stdout, stderr = capsys.readouterr() - assert 'Zaps the given logical volume, raw device or partition' in stdout + assert 'Zaps the given logical volume(s), raw device(s) or partition(s)' in stdout def test_main_shows_full_help(self, capsys): with pytest.raises(SystemExit): lvm.zap.Zap(argv=['--help']).main() stdout, stderr = capsys.readouterr() assert 'optional arguments' in stdout - assert 'positional arguments' in stdout + + @pytest.mark.parametrize('device_name', [ + '/dev/mapper/foo', + '/dev/dm-0', + ]) + def test_can_not_zap_mapper_device(self, capsys, is_root, device_name): + with pytest.raises(SystemExit): + lvm.zap.Zap(argv=[device_name]).main() + stdout, stderr = capsys.readouterr() + assert 'Refusing to zap' in stdout diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/Vagrantfile b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/Vagrantfile new file mode 120000 index 000000000..16076e424 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/Vagrantfile @@ -0,0 +1 @@ +../../../../Vagrantfile \ No newline at end of file diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/group_vars/all b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/group_vars/all new file mode 100644 index 000000000..5cb47c8d2 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/group_vars/all @@ -0,0 +1,23 @@ +--- + +ceph_dev: True +cluster: ceph +public_network: "192.168.3.0/24" +cluster_network: "192.168.4.0/24" +monitor_interface: eth1 +osd_objectstore: "bluestore" +osd_scenario: lvm +dmcrypt: true +ceph_origin: 'repository' +ceph_repository: 'dev' +copy_admin_key: false +devices: + - /dev/sdb + - /dev/sdc +os_tuning_params: + - { name: kernel.pid_max, value: 4194303 } + - { name: fs.file-max, value: 26234859 } +ceph_conf_overrides: + global: + osd_pool_default_pg_num: 8 + osd_pool_default_size: 1 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/hosts b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/hosts new file mode 100644 index 000000000..e1c1de6f8 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/hosts @@ -0,0 +1,8 @@ +[mons] +mon0 + +[osds] +osd0 + +[mgrs] +mon0 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/test.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/test.yml new file mode 120000 index 000000000..ac5ac6b0d --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/test.yml @@ -0,0 +1 @@ +../../../playbooks/test_bluestore_dmcrypt.yml \ No newline at end of file diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/vagrant_variables.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/vagrant_variables.yml new file mode 100644 index 000000000..7d1a4449a --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type-dmcrypt/vagrant_variables.yml @@ -0,0 +1,56 @@ +--- + +# DEFINE THE NUMBER OF VMS TO RUN +mon_vms: 1 +osd_vms: 1 +mds_vms: 0 +rgw_vms: 0 +nfs_vms: 0 +rbd_mirror_vms: 0 +client_vms: 0 +iscsi_gw_vms: 0 +mgr_vms: 0 + +# SUBNETS TO USE FOR THE VMS +public_subnet: 192.168.3 +cluster_subnet: 192.168.4 + +# MEMORY +# set 1024 for CentOS +memory: 512 + +# Ethernet interface name +# use eth1 for libvirt and ubuntu precise, enp0s8 for CentOS and ubuntu xenial +eth: 'eth1' + + +# VAGRANT BOX +# Ceph boxes are *strongly* suggested. They are under better control and will +# not get updated frequently unless required for build systems. These are (for +# now): +# +# * ceph/ubuntu-xenial +# +# Ubuntu: ceph/ubuntu-xenial bento/ubuntu-16.04 or ubuntu/trusty64 or ubuntu/wily64 +# CentOS: bento/centos-7.1 or puppetlabs/centos-7.0-64-puppet +# libvirt CentOS: centos/7 +# parallels Ubuntu: parallels/ubuntu-14.04 +# Debian: deb/jessie-amd64 - be careful the storage controller is named 'SATA Controller' +# For more boxes have a look at: +# - https://atlas.hashicorp.com/boxes/search?utf8=✓&sort=&provider=virtualbox&q= +# - https://download.gluster.org/pub/gluster/purpleidea/vagrant/ +vagrant_box: centos/7 +#ssh_private_key_path: "~/.ssh/id_rsa" +# The sync directory changes based on vagrant box +# Set to /home/vagrant/sync for Centos/7, /home/{ user }/vagrant for openstack and defaults to /vagrant +#vagrant_sync_dir: /home/vagrant/sync +#vagrant_sync_dir: / +# Disables synced folder creation. Not needed for testing, will skip mounting +# the vagrant directory on the remote box regardless of the provider. +vagrant_disable_synced_folder: true +# VAGRANT URL +# This is a URL to download an image from an alternate location. vagrant_box +# above should be set to the filename of the image. +# Fedora virtualbox: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-virtualbox.box +# Fedora libvirt: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-libvirt.box +# vagrant_box_url: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-virtualbox.box diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/Vagrantfile b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/Vagrantfile new file mode 120000 index 000000000..16076e424 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/Vagrantfile @@ -0,0 +1 @@ +../../../../Vagrantfile \ No newline at end of file diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/group_vars/all b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/group_vars/all new file mode 100644 index 000000000..87b031fd4 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/group_vars/all @@ -0,0 +1,22 @@ +--- + +ceph_dev: True +cluster: ceph +public_network: "192.168.3.0/24" +cluster_network: "192.168.4.0/24" +monitor_interface: eth1 +osd_objectstore: "bluestore" +osd_scenario: lvm +ceph_origin: 'repository' +ceph_repository: 'dev' +copy_admin_key: false +devices: + - /dev/sdb + - /dev/sdc +os_tuning_params: + - { name: kernel.pid_max, value: 4194303 } + - { name: fs.file-max, value: 26234859 } +ceph_conf_overrides: + global: + osd_pool_default_pg_num: 8 + osd_pool_default_size: 1 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/hosts b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/hosts new file mode 100644 index 000000000..e1c1de6f8 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/hosts @@ -0,0 +1,8 @@ +[mons] +mon0 + +[osds] +osd0 + +[mgrs] +mon0 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/test.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/test.yml new file mode 120000 index 000000000..165d9da29 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/test.yml @@ -0,0 +1 @@ +../../../playbooks/test_bluestore.yml \ No newline at end of file diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/vagrant_variables.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/vagrant_variables.yml new file mode 100644 index 000000000..7d1a4449a --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/bluestore/single-type/vagrant_variables.yml @@ -0,0 +1,56 @@ +--- + +# DEFINE THE NUMBER OF VMS TO RUN +mon_vms: 1 +osd_vms: 1 +mds_vms: 0 +rgw_vms: 0 +nfs_vms: 0 +rbd_mirror_vms: 0 +client_vms: 0 +iscsi_gw_vms: 0 +mgr_vms: 0 + +# SUBNETS TO USE FOR THE VMS +public_subnet: 192.168.3 +cluster_subnet: 192.168.4 + +# MEMORY +# set 1024 for CentOS +memory: 512 + +# Ethernet interface name +# use eth1 for libvirt and ubuntu precise, enp0s8 for CentOS and ubuntu xenial +eth: 'eth1' + + +# VAGRANT BOX +# Ceph boxes are *strongly* suggested. They are under better control and will +# not get updated frequently unless required for build systems. These are (for +# now): +# +# * ceph/ubuntu-xenial +# +# Ubuntu: ceph/ubuntu-xenial bento/ubuntu-16.04 or ubuntu/trusty64 or ubuntu/wily64 +# CentOS: bento/centos-7.1 or puppetlabs/centos-7.0-64-puppet +# libvirt CentOS: centos/7 +# parallels Ubuntu: parallels/ubuntu-14.04 +# Debian: deb/jessie-amd64 - be careful the storage controller is named 'SATA Controller' +# For more boxes have a look at: +# - https://atlas.hashicorp.com/boxes/search?utf8=✓&sort=&provider=virtualbox&q= +# - https://download.gluster.org/pub/gluster/purpleidea/vagrant/ +vagrant_box: centos/7 +#ssh_private_key_path: "~/.ssh/id_rsa" +# The sync directory changes based on vagrant box +# Set to /home/vagrant/sync for Centos/7, /home/{ user }/vagrant for openstack and defaults to /vagrant +#vagrant_sync_dir: /home/vagrant/sync +#vagrant_sync_dir: / +# Disables synced folder creation. Not needed for testing, will skip mounting +# the vagrant directory on the remote box regardless of the provider. +vagrant_disable_synced_folder: true +# VAGRANT URL +# This is a URL to download an image from an alternate location. vagrant_box +# above should be set to the filename of the image. +# Fedora virtualbox: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-virtualbox.box +# Fedora libvirt: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-libvirt.box +# vagrant_box_url: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-virtualbox.box diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/Vagrantfile b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/Vagrantfile new file mode 120000 index 000000000..16076e424 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/Vagrantfile @@ -0,0 +1 @@ +../../../../Vagrantfile \ No newline at end of file diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/group_vars/all b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/group_vars/all new file mode 100644 index 000000000..f6f67a992 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/group_vars/all @@ -0,0 +1,25 @@ +--- + +ceph_dev: True +cluster: ceph +public_network: "192.168.3.0/24" +cluster_network: "192.168.4.0/24" +monitor_interface: eth1 +osd_objectstore: "filestore" +osd_scenario: lvm +dmcrypt: true +ceph_origin: 'repository' +ceph_repository: 'dev' +copy_admin_key: false +devices: + - /dev/sdb + - /dev/sdc +os_tuning_params: + - { name: kernel.pid_max, value: 4194303 } + - { name: fs.file-max, value: 26234859 } +ceph_conf_overrides: + global: + osd_pool_default_pg_num: 8 + osd_pool_default_size: 1 + osd: + osd_journal_size: 2048 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/hosts b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/hosts new file mode 100644 index 000000000..e1c1de6f8 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/hosts @@ -0,0 +1,8 @@ +[mons] +mon0 + +[osds] +osd0 + +[mgrs] +mon0 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/test.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/test.yml new file mode 120000 index 000000000..8ed725f8a --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/test.yml @@ -0,0 +1 @@ +../../../playbooks/test_filestore_dmcrypt.yml \ No newline at end of file diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/vagrant_variables.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/vagrant_variables.yml new file mode 100644 index 000000000..7d1a4449a --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type-dmcrypt/vagrant_variables.yml @@ -0,0 +1,56 @@ +--- + +# DEFINE THE NUMBER OF VMS TO RUN +mon_vms: 1 +osd_vms: 1 +mds_vms: 0 +rgw_vms: 0 +nfs_vms: 0 +rbd_mirror_vms: 0 +client_vms: 0 +iscsi_gw_vms: 0 +mgr_vms: 0 + +# SUBNETS TO USE FOR THE VMS +public_subnet: 192.168.3 +cluster_subnet: 192.168.4 + +# MEMORY +# set 1024 for CentOS +memory: 512 + +# Ethernet interface name +# use eth1 for libvirt and ubuntu precise, enp0s8 for CentOS and ubuntu xenial +eth: 'eth1' + + +# VAGRANT BOX +# Ceph boxes are *strongly* suggested. They are under better control and will +# not get updated frequently unless required for build systems. These are (for +# now): +# +# * ceph/ubuntu-xenial +# +# Ubuntu: ceph/ubuntu-xenial bento/ubuntu-16.04 or ubuntu/trusty64 or ubuntu/wily64 +# CentOS: bento/centos-7.1 or puppetlabs/centos-7.0-64-puppet +# libvirt CentOS: centos/7 +# parallels Ubuntu: parallels/ubuntu-14.04 +# Debian: deb/jessie-amd64 - be careful the storage controller is named 'SATA Controller' +# For more boxes have a look at: +# - https://atlas.hashicorp.com/boxes/search?utf8=✓&sort=&provider=virtualbox&q= +# - https://download.gluster.org/pub/gluster/purpleidea/vagrant/ +vagrant_box: centos/7 +#ssh_private_key_path: "~/.ssh/id_rsa" +# The sync directory changes based on vagrant box +# Set to /home/vagrant/sync for Centos/7, /home/{ user }/vagrant for openstack and defaults to /vagrant +#vagrant_sync_dir: /home/vagrant/sync +#vagrant_sync_dir: / +# Disables synced folder creation. Not needed for testing, will skip mounting +# the vagrant directory on the remote box regardless of the provider. +vagrant_disable_synced_folder: true +# VAGRANT URL +# This is a URL to download an image from an alternate location. vagrant_box +# above should be set to the filename of the image. +# Fedora virtualbox: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-virtualbox.box +# Fedora libvirt: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-libvirt.box +# vagrant_box_url: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-virtualbox.box diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/Vagrantfile b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/Vagrantfile new file mode 120000 index 000000000..16076e424 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/Vagrantfile @@ -0,0 +1 @@ +../../../../Vagrantfile \ No newline at end of file diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/group_vars/all b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/group_vars/all new file mode 100644 index 000000000..d4b26c383 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/group_vars/all @@ -0,0 +1,22 @@ +--- + +ceph_dev: True +cluster: ceph +public_network: "192.168.3.0/24" +cluster_network: "192.168.4.0/24" +monitor_interface: eth1 +osd_objectstore: "filestore" +osd_scenario: lvm +ceph_origin: 'repository' +ceph_repository: 'dev' +copy_admin_key: false +devices: + - /dev/sdb + - /dev/sdc +os_tuning_params: + - { name: kernel.pid_max, value: 4194303 } + - { name: fs.file-max, value: 26234859 } +ceph_conf_overrides: + global: + osd_pool_default_pg_num: 8 + osd_pool_default_size: 1 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/hosts b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/hosts new file mode 100644 index 000000000..e1c1de6f8 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/hosts @@ -0,0 +1,8 @@ +[mons] +mon0 + +[osds] +osd0 + +[mgrs] +mon0 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/test.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/test.yml new file mode 120000 index 000000000..1a8c37c13 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/test.yml @@ -0,0 +1 @@ +../../../playbooks/test_filestore.yml \ No newline at end of file diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/vagrant_variables.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/vagrant_variables.yml new file mode 100644 index 000000000..7d1a4449a --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/centos7/filestore/single-type/vagrant_variables.yml @@ -0,0 +1,56 @@ +--- + +# DEFINE THE NUMBER OF VMS TO RUN +mon_vms: 1 +osd_vms: 1 +mds_vms: 0 +rgw_vms: 0 +nfs_vms: 0 +rbd_mirror_vms: 0 +client_vms: 0 +iscsi_gw_vms: 0 +mgr_vms: 0 + +# SUBNETS TO USE FOR THE VMS +public_subnet: 192.168.3 +cluster_subnet: 192.168.4 + +# MEMORY +# set 1024 for CentOS +memory: 512 + +# Ethernet interface name +# use eth1 for libvirt and ubuntu precise, enp0s8 for CentOS and ubuntu xenial +eth: 'eth1' + + +# VAGRANT BOX +# Ceph boxes are *strongly* suggested. They are under better control and will +# not get updated frequently unless required for build systems. These are (for +# now): +# +# * ceph/ubuntu-xenial +# +# Ubuntu: ceph/ubuntu-xenial bento/ubuntu-16.04 or ubuntu/trusty64 or ubuntu/wily64 +# CentOS: bento/centos-7.1 or puppetlabs/centos-7.0-64-puppet +# libvirt CentOS: centos/7 +# parallels Ubuntu: parallels/ubuntu-14.04 +# Debian: deb/jessie-amd64 - be careful the storage controller is named 'SATA Controller' +# For more boxes have a look at: +# - https://atlas.hashicorp.com/boxes/search?utf8=✓&sort=&provider=virtualbox&q= +# - https://download.gluster.org/pub/gluster/purpleidea/vagrant/ +vagrant_box: centos/7 +#ssh_private_key_path: "~/.ssh/id_rsa" +# The sync directory changes based on vagrant box +# Set to /home/vagrant/sync for Centos/7, /home/{ user }/vagrant for openstack and defaults to /vagrant +#vagrant_sync_dir: /home/vagrant/sync +#vagrant_sync_dir: / +# Disables synced folder creation. Not needed for testing, will skip mounting +# the vagrant directory on the remote box regardless of the provider. +vagrant_disable_synced_folder: true +# VAGRANT URL +# This is a URL to download an image from an alternate location. vagrant_box +# above should be set to the filename of the image. +# Fedora virtualbox: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-virtualbox.box +# Fedora libvirt: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-libvirt.box +# vagrant_box_url: https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-Vagrant-22-20150521.x86_64.vagrant-virtualbox.box diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_bluestore.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_bluestore.yml new file mode 100644 index 000000000..85c702e38 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_bluestore.yml @@ -0,0 +1,46 @@ + +- hosts: osds + become: yes + tasks: + + - name: stop ceph-osd@1 daemon + service: + name: ceph-osd@1 + state: stopped + + - name: stop ceph-osd@0 daemon + service: + name: ceph-osd@0 + state: stopped + + +- hosts: mons + become: yes + tasks: + + - name: destroy osd.1 + command: "ceph osd purge osd.1 --yes-i-really-mean-it" + + - name: destroy osd.0 + command: "ceph osd purge osd.0 --yes-i-really-mean-it" + + +- hosts: osds + become: yes + tasks: + + - name: zap /dev/sdd + command: "ceph-volume lvm zap /dev/sdb --destroy" + environment: + CEPH_VOLUME_DEBUG: 1 + + + - name: zap /dev/sdc + command: "ceph-volume lvm zap /dev/sdc --destroy" + environment: + CEPH_VOLUME_DEBUG: 1 + + - name: batch create /dev/sdb and /dev/sdc again + command: "ceph-volume lvm batch --yes --bluestore /dev/sdb /dev/sdc" + environment: + CEPH_VOLUME_DEBUG: 1 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_bluestore_dmcrypt.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_bluestore_dmcrypt.yml new file mode 100644 index 000000000..9e1a73f65 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_bluestore_dmcrypt.yml @@ -0,0 +1,46 @@ + +- hosts: osds + become: yes + tasks: + + - name: stop ceph-osd@1 daemon + service: + name: ceph-osd@1 + state: stopped + + - name: stop ceph-osd@0 daemon + service: + name: ceph-osd@0 + state: stopped + + +- hosts: mons + become: yes + tasks: + + - name: destroy osd.1 + command: "ceph osd purge osd.1 --yes-i-really-mean-it" + + - name: destroy osd.0 + command: "ceph osd purge osd.0 --yes-i-really-mean-it" + + +- hosts: osds + become: yes + tasks: + + - name: zap /dev/sdd + command: "ceph-volume lvm zap /dev/sdb --destroy" + environment: + CEPH_VOLUME_DEBUG: 1 + + + - name: zap /dev/sdc + command: "ceph-volume lvm zap /dev/sdc --destroy" + environment: + CEPH_VOLUME_DEBUG: 1 + + - name: batch create /dev/sdb and /dev/sdc again + command: "ceph-volume lvm batch --yes --bluestore --dmcrypt /dev/sdb /dev/sdc" + environment: + CEPH_VOLUME_DEBUG: 1 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_filestore.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_filestore.yml new file mode 100644 index 000000000..95909f97b --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_filestore.yml @@ -0,0 +1,46 @@ + +- hosts: osds + become: yes + tasks: + + - name: stop ceph-osd@1 daemon + service: + name: ceph-osd@1 + state: stopped + + - name: stop ceph-osd@0 daemon + service: + name: ceph-osd@0 + state: stopped + + +- hosts: mons + become: yes + tasks: + + - name: destroy osd.1 + command: "ceph osd purge osd.1 --yes-i-really-mean-it" + + - name: destroy osd.0 + command: "ceph osd purge osd.0 --yes-i-really-mean-it" + + +- hosts: osds + become: yes + tasks: + + - name: zap /dev/sdd + command: "ceph-volume lvm zap /dev/sdb --destroy" + environment: + CEPH_VOLUME_DEBUG: 1 + + + - name: zap /dev/sdc + command: "ceph-volume lvm zap /dev/sdc --destroy" + environment: + CEPH_VOLUME_DEBUG: 1 + + - name: batch create /dev/sdb and /dev/sdc again + command: "ceph-volume lvm batch --yes --filestore /dev/sdb /dev/sdc" + environment: + CEPH_VOLUME_DEBUG: 1 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_filestore_dmcrypt.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_filestore_dmcrypt.yml new file mode 100644 index 000000000..81f84e919 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/test_filestore_dmcrypt.yml @@ -0,0 +1,46 @@ + +- hosts: osds + become: yes + tasks: + + - name: stop ceph-osd@1 daemon + service: + name: ceph-osd@1 + state: stopped + + - name: stop ceph-osd@0 daemon + service: + name: ceph-osd@0 + state: stopped + + +- hosts: mons + become: yes + tasks: + + - name: destroy osd.1 + command: "ceph osd purge osd.1 --yes-i-really-mean-it" + + - name: destroy osd.0 + command: "ceph osd purge osd.0 --yes-i-really-mean-it" + + +- hosts: osds + become: yes + tasks: + + - name: zap /dev/sdd + command: "ceph-volume lvm zap /dev/sdb --destroy" + environment: + CEPH_VOLUME_DEBUG: 1 + + + - name: zap /dev/sdc + command: "ceph-volume lvm zap /dev/sdc --destroy" + environment: + CEPH_VOLUME_DEBUG: 1 + + - name: batch create /dev/sdb and /dev/sdc again + command: "ceph-volume lvm batch --yes --filestore --dmcrypt /dev/sdb /dev/sdc" + environment: + CEPH_VOLUME_DEBUG: 1 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/tox.ini b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/tox.ini new file mode 100644 index 000000000..6a43a110e --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/tox.ini @@ -0,0 +1,63 @@ +[tox] +envlist = {centos7,xenial}-{bluestore,filestore}-{single_type,single_type_dmcrypt} +skipsdist = True + +[testenv] +whitelist_externals = + vagrant + bash + git + cp +passenv=* +setenv= + ANSIBLE_SSH_ARGS = -F {changedir}/vagrant_ssh_config + ANSIBLE_ACTION_PLUGINS = {envdir}/tmp/ceph-ansible/plugins/actions + ANSIBLE_STDOUT_CALLBACK = debug + ANSIBLE_RETRY_FILES_ENABLED = False + ANSIBLE_SSH_RETRIES = 5 + VAGRANT_CWD = {changedir} + CEPH_VOLUME_DEBUG = 1 +deps= + ansible~=2.6,<2.7 + testinfra + pytest-xdist + notario>=0.0.13 +changedir= + centos7-filestore-single_type: {toxinidir}/centos7/filestore/single-type + centos7-filestore-single_type_dmcrypt: {toxinidir}/centos7/filestore/single-type-dmcrypt + centos7-bluestore-single_type: {toxinidir}/centos7/bluestore/single-type + centos7-bluestore-single_type_dmcrypt: {toxinidir}/centos7/bluestore/single-type-dmcrypt + xenial-filestore-single_type: {toxinidir}/xenial/filestore/single-type + xenial-filestore-single_type_dmcrypt: {toxinidir}/xenial/filestore/single-type-dmcrypt + xenial-bluestore-single_type: {toxinidir}/xenial/bluestore/single-type + xenial-bluestore-single_type_dmcrypt: {toxinidir}/xenial/bluestore/single-type-dmcrypt +commands= + git clone -b {env:CEPH_ANSIBLE_BRANCH:master} --single-branch https://github.com/ceph/ceph-ansible.git {envdir}/tmp/ceph-ansible + + bash {toxinidir}/../scripts/vagrant_up.sh {env:VAGRANT_UP_FLAGS:"--no-provision"} {posargs:--provider=virtualbox} + bash {toxinidir}/../scripts/generate_ssh_config.sh {changedir} + + cp {toxinidir}/../playbooks/deploy.yml {envdir}/tmp/ceph-ansible + + # use ceph-ansible to deploy a ceph cluster on the vms + ansible-playbook -vv -i {changedir}/hosts {envdir}/tmp/ceph-ansible/deploy.yml --extra-vars "fetch_directory={changedir}/fetch ceph_dev_branch={env:CEPH_DEV_BRANCH:master} ceph_dev_sha1={env:CEPH_DEV_SHA1:latest} toxinidir={toxinidir}" + + # prepare nodes for testing with testinfra + ansible-playbook -vv -i {changedir}/hosts {envdir}/tmp/ceph-ansible/tests/functional/setup.yml + + # test cluster state using ceph-ansible tests + testinfra -n 4 --sudo -v --connection=ansible --ansible-inventory={changedir}/hosts {envdir}/tmp/ceph-ansible/tests/functional/tests + + # reboot all vms - attempt + bash {toxinidir}/../scripts/vagrant_reload.sh {env:VAGRANT_UP_FLAGS:"--no-provision"} {posargs:--provider=virtualbox} + + # retest to ensure cluster came back up correctly after rebooting + testinfra -n 4 --sudo -v --connection=ansible --ansible-inventory={changedir}/hosts {envdir}/tmp/ceph-ansible/tests/functional/tests + + # destroy an OSD, zap it's device and recreate it using it's ID + ansible-playbook -vv -i {changedir}/hosts {changedir}/test.yml + + # retest to ensure cluster came back up correctly + testinfra -n 4 --sudo -v --connection=ansible --ansible-inventory={changedir}/hosts {envdir}/tmp/ceph-ansible/tests/functional/tests + + vagrant destroy {env:VAGRANT_DESTROY_FLAGS:"--force"} diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/.DS_Store b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/xenial/bluestore/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0=0.0.13 changedir= @@ -44,7 +46,7 @@ commands= # but the master branch doesn't pin dependencies so we can't guarantee to work correctly #pip install -r {envdir}/tmp/ceph-ansible/requirements.txt - vagrant up --no-provision {posargs:--provider=virtualbox} + bash {toxinidir}/../scripts/vagrant_up.sh {env:VAGRANT_UP_FLAGS:"--no-provision"} {posargs:--provider=virtualbox} bash {toxinidir}/../scripts/generate_ssh_config.sh {changedir} # create logical volumes to test with on the vms @@ -53,8 +55,10 @@ commands= # ad-hoc/local test setup for lvm ansible-playbook -vv -i {changedir}/hosts {changedir}/setup.yml + cp {toxinidir}/../playbooks/deploy.yml {envdir}/tmp/ceph-ansible + # use ceph-ansible to deploy a ceph cluster on the vms - ansible-playbook -vv -i {changedir}/hosts {envdir}/tmp/ceph-ansible/site.yml.sample --extra-vars "fetch_directory={changedir}/fetch ceph_dev_branch={env:CEPH_DEV_BRANCH:master} ceph_dev_sha1={env:CEPH_DEV_SHA1:latest}" + ansible-playbook -vv -i {changedir}/hosts {envdir}/tmp/ceph-ansible/deploy.yml --extra-vars "fetch_directory={changedir}/fetch ceph_dev_branch={env:CEPH_DEV_BRANCH:master} ceph_dev_sha1={env:CEPH_DEV_SHA1:latest} toxinidir={toxinidir}" # prepare nodes for testing with testinfra ansible-playbook -vv -i {changedir}/hosts {envdir}/tmp/ceph-ansible/tests/functional/setup.yml diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/create/hosts b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/create/hosts index f6a265ab3..e1c1de6f8 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/create/hosts +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/create/hosts @@ -3,3 +3,6 @@ mon0 [osds] osd0 + +[mgrs] +mon0 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/dmcrypt/hosts b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/dmcrypt/hosts index f6a265ab3..e1c1de6f8 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/dmcrypt/hosts +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/dmcrypt/hosts @@ -3,3 +3,6 @@ mon0 [osds] osd0 + +[mgrs] +mon0 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/dmcrypt/test.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/dmcrypt/test.yml index f93ff2304..b6db0ac2f 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/dmcrypt/test.yml +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/bluestore/dmcrypt/test.yml @@ -81,3 +81,8 @@ command: "ceph-volume lvm activate --all" environment: CEPH_VOLUME_DEBUG: 1 + + - name: list all OSDs + command: "ceph-volume lvm list" + environment: + CEPH_VOLUME_DEBUG: 1 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/create/hosts b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/create/hosts index f6a265ab3..e1c1de6f8 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/create/hosts +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/create/hosts @@ -3,3 +3,6 @@ mon0 [osds] osd0 + +[mgrs] +mon0 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/dmcrypt/hosts b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/dmcrypt/hosts index f6a265ab3..e1c1de6f8 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/dmcrypt/hosts +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/dmcrypt/hosts @@ -3,3 +3,6 @@ mon0 [osds] osd0 + +[mgrs] +mon0 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/dmcrypt/test.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/dmcrypt/test.yml index 49f37d20a..5dc67ade1 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/dmcrypt/test.yml +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/xenial/filestore/dmcrypt/test.yml @@ -65,3 +65,8 @@ command: "ceph-volume lvm activate --filestore --all" environment: CEPH_VOLUME_DEBUG: 1 + + - name: list all OSDs + command: "ceph-volume lvm list" + environment: + CEPH_VOLUME_DEBUG: 1 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/playbooks/deploy.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/playbooks/deploy.yml new file mode 100644 index 000000000..ff3954b21 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/playbooks/deploy.yml @@ -0,0 +1,123 @@ +--- +# Defines deployment design and assigns role to server groups + +- hosts: + - mons + - osds + - mgrs + + gather_facts: false + any_errors_fatal: true + become: true + + tags: + - always + + vars: + delegate_facts_host: True + + pre_tasks: + # If we can't get python2 installed before any module is used we will fail + # so just try what we can to get it installed + - name: check for python2 + stat: + path: /usr/bin/python + ignore_errors: yes + register: systempython2 + + - name: install python2 for debian based systems + raw: sudo apt-get -y install python-simplejson + ignore_errors: yes + when: + - systempython2.stat is undefined or systempython2.stat.exists == false + + - name: install python2 for fedora + raw: sudo dnf -y install python creates=/usr/bin/python + ignore_errors: yes + when: + - systempython2.stat is undefined or systempython2.stat.exists == false + + - name: install python2 for opensuse + raw: sudo zypper -n install python-base creates=/usr/bin/python2.7 + ignore_errors: yes + when: + - systempython2.stat is undefined or systempython2.stat.exists == false + + - name: gather facts + setup: + when: + - not delegate_facts_host | bool + + - name: gather and delegate facts + setup: + delegate_to: "{{ item }}" + delegate_facts: True + with_items: "{{ groups['all'] }}" + run_once: true + when: + - delegate_facts_host | bool + + - name: install required packages for fedora > 23 + raw: sudo dnf -y install python2-dnf libselinux-python ntp + when: + - ansible_distribution == 'Fedora' + - ansible_distribution_major_version|int >= 23 + + roles: + - ceph-defaults + - ceph-validate + +- hosts: + - mons + - osds + - mgrs + gather_facts: false + become: True + roles: + - role: ceph-defaults + tags: ['ceph_update_config'] + - role: ceph-common + - role: ceph-config + tags: ['ceph_update_config'] + +- hosts: mons + gather_facts: false + become: True + roles: + - role: ceph-defaults + - role: ceph-common + - role: ceph-mon + +- hosts: mgrs + gather_facts: false + become: True + roles: + - role: ceph-defaults + - role: ceph-common + - role: ceph-mgr + +- hosts: osds + gather_facts: false + become: True + tasks: + - name: rsync ceph-volume to test nodes on centos + synchronize: + src: "{{ toxinidir}}/../../../../ceph_volume" + dest: "/usr/lib/python2.7/site-packages" + use_ssh_args: true + when: ansible_os_family == "RedHat" + + - name: rsync ceph-volume to test nodes on ubuntu + synchronize: + src: "{{ toxinidir}}/../../../../ceph_volume" + dest: "/usr/lib/python2.7/dist-packages" + use_ssh_args: true + when: ansible_os_family == "Debian" + +- hosts: osds + gather_facts: false + become: True + roles: + - role: ceph-defaults + - role: ceph-common + - role: ceph-osd diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/scripts/vagrant_up.sh b/ceph/src/ceph-volume/ceph_volume/tests/functional/scripts/vagrant_up.sh new file mode 100644 index 000000000..2f9a15f84 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/scripts/vagrant_up.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +retries=0 +until [ $retries -ge 5 ] +do + echo "Attempting to start VMs. Attempts: $retries" + timeout 10m vagrant up "$@" && break + retries=$[$retries+1] + sleep 5 +done + +sleep 10 diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/simple/tox.ini b/ceph/src/ceph-volume/ceph_volume/tests/functional/simple/tox.ini index d5e9c33ea..1e813cedf 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/functional/simple/tox.ini +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/simple/tox.ini @@ -8,6 +8,7 @@ whitelist_externals = bash git sleep + cp passenv=* setenv= ANSIBLE_SSH_ARGS = -F {changedir}/vagrant_ssh_config @@ -18,8 +19,8 @@ setenv= VAGRANT_CWD = {changedir} CEPH_VOLUME_DEBUG = 1 deps= - ansible==2.4.1 - testinfra==1.7.1 + ansible~=2.6,<2.7 + testinfra pytest-xdist notario>=0.0.13 changedir= @@ -41,11 +42,13 @@ commands= # but the master branch doesn't pin dependencies so we can't guarantee to work correctly #pip install -r {envdir}/tmp/ceph-ansible/requirements.txt - vagrant up --no-provision {posargs:--provider=virtualbox} + bash {toxinidir}/../scripts/vagrant_up.sh {env:VAGRANT_UP_FLAGS:"--no-provision"} {posargs:--provider=virtualbox} bash {toxinidir}/../scripts/generate_ssh_config.sh {changedir} + cp {toxinidir}/../playbooks/deploy.yml {envdir}/tmp/ceph-ansible + # use ceph-ansible to deploy a ceph cluster on the vms - ansible-playbook -vv -i {changedir}/hosts {envdir}/tmp/ceph-ansible/site.yml.sample --extra-vars "fetch_directory={changedir}/fetch ceph_dev_branch={env:CEPH_DEV_BRANCH:master} ceph_dev_sha1={env:CEPH_DEV_SHA1:latest}" + ansible-playbook -vv -i {changedir}/hosts {envdir}/tmp/ceph-ansible/deploy.yml --extra-vars "fetch_directory={changedir}/fetch ceph_dev_branch={env:CEPH_DEV_BRANCH:master} ceph_dev_sha1={env:CEPH_DEV_SHA1:latest} toxinidir={toxinidir}" # prepare nodes for testing with testinfra ansible-playbook -vv -i {changedir}/hosts {envdir}/tmp/ceph-ansible/tests/functional/setup.yml diff --git a/ceph/src/ceph-volume/ceph_volume/tests/test_process.py b/ceph/src/ceph-volume/ceph_volume/tests/test_process.py index d38927ae2..c9dfaeebf 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/test_process.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/test_process.py @@ -66,3 +66,21 @@ class TestCall(object): assert 'ls' in log_lines assert 'stderr' in log_lines assert out == '' + + +class TestFunctionalCall(object): + + def test_stdin(self): + process.call(['xargs', 'ls'], stdin="echo '/'") + + def test_unicode_encoding(self): + process.call(['echo', u'\xd0']) + + def test_unicode_encoding_stdin(self): + process.call(['echo'], stdin=u'\xd0'.encode('utf-8')) + + +class TestFunctionalRun(object): + + def test_log_descriptors(self): + process.run(['ls', '-l']) diff --git a/ceph/src/ceph-volume/ceph_volume/tests/util/test_arg_validators.py b/ceph/src/ceph-volume/ceph_volume/tests/util/test_arg_validators.py index f989599c8..9f20edbf7 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/util/test_arg_validators.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/util/test_arg_validators.py @@ -96,3 +96,17 @@ class TestExcludeGroupOptions(object): ) stdout, stderr = capsys.readouterr() assert 'Cannot use --filestore (filestore) with --bluestore (bluestore)' in stdout + + +class TestValidDevice(object): + + def setup(self): + self.validator = arg_validators.ValidDevice() + + def test_path_is_valid(self, fake_call): + result = self.validator('/') + assert result.abspath == '/' + + def test_path_is_invalid(self, fake_call): + with pytest.raises(argparse.ArgumentError): + self.validator('/device/does/not/exist') diff --git a/ceph/src/ceph-volume/ceph_volume/tests/util/test_device.py b/ceph/src/ceph-volume/ceph_volume/tests/util/test_device.py new file mode 100644 index 000000000..225b06031 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/util/test_device.py @@ -0,0 +1,66 @@ +from ceph_volume.util import device +from ceph_volume.api import lvm as api + + +class TestDevice(object): + + def test_sys_api(self, device_info): + data = {"/dev/sda": {"foo": "bar"}} + device_info(devices=data) + disk = device.Device("/dev/sda") + assert disk.sys_api + assert "foo" in disk.sys_api + + def test_is_lv(self, device_info): + data = {"lv_path": "vg/lv"} + device_info(lv=data) + disk = device.Device("vg/lv") + assert disk.is_lv + + def test_is_device(self, device_info): + data = {"/dev/sda": {"foo": "bar"}} + lsblk = {"TYPE": "device"} + device_info(devices=data, lsblk=lsblk) + disk = device.Device("/dev/sda") + assert disk.is_device + + def test_is_partition(self, device_info, pvolumes): + data = {"/dev/sda": {"foo": "bar"}} + lsblk = {"TYPE": "part"} + device_info(devices=data, lsblk=lsblk) + disk = device.Device("/dev/sda") + assert disk.is_partition + + def test_is_not_lvm_memeber(self, device_info, pvolumes): + data = {"/dev/sda": {"foo": "bar"}} + lsblk = {"TYPE": "part"} + device_info(devices=data, lsblk=lsblk) + disk = device.Device("/dev/sda") + assert not disk.is_lvm_member + + def test_is_lvm_memeber(self, device_info, pvolumes): + data = {"/dev/sda": {"foo": "bar"}} + lsblk = {"TYPE": "part"} + device_info(devices=data, lsblk=lsblk) + disk = device.Device("/dev/sda") + assert not disk.is_lvm_member + + def test_is_mapper_device(self, device_info): + device_info() + disk = device.Device("/dev/mapper/foo") + assert disk.is_mapper + + def test_is_not_mapper_device(self, device_info): + device_info() + disk = device.Device("/dev/sda") + assert not disk.is_mapper + + def test_pv_api(self, device_info, pvolumes, monkeypatch): + FooPVolume = api.PVolume(pv_name='/dev/sda', pv_uuid="0000", pv_tags={}, vg_name="vg") + pvolumes.append(FooPVolume) + monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) + data = {"/dev/sda": {"foo": "bar"}} + lsblk = {"TYPE": "part"} + device_info(devices=data, lsblk=lsblk) + disk = device.Device("/dev/sda") + assert disk.pvs_api diff --git a/ceph/src/ceph-volume/ceph_volume/tests/util/test_disk.py b/ceph/src/ceph-volume/ceph_volume/tests/util/test_disk.py index b81db81bc..ae5fbe508 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/util/test_disk.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/util/test_disk.py @@ -1,3 +1,5 @@ +import os +import pytest from ceph_volume.util import disk @@ -39,3 +41,464 @@ class TestDeviceFamily(object): result = disk.device_family('sdaa5') for parsed in result: assert parsed['NAME'] in names + + +class TestMapDevPaths(object): + + def test_errors_return_empty_mapping(self, tmpdir): + bad_dir = os.path.join(str(tmpdir), 'nonexisting') + assert disk._map_dev_paths(bad_dir) == {} + + def test_base_name_and_abspath(self, tmpfile): + sda_path = tmpfile(name='sda', contents='') + directory = os.path.dirname(sda_path) + result = disk._map_dev_paths(directory) + assert len(result.keys()) == 1 + assert result['sda'] == sda_path + + def test_abspath_included(self, tmpfile): + sda_path = tmpfile(name='sda', contents='') + directory = os.path.dirname(sda_path) + result = disk._map_dev_paths(directory, include_abspath=True) + assert sorted(result.keys()) == sorted(['sda', sda_path]) + assert result['sda'] == sda_path + assert result[sda_path] == 'sda' + + def test_realpath_included(self, tmpfile): + sda_path = tmpfile(name='sda', contents='') + directory = os.path.dirname(sda_path) + dm_path = os.path.join(directory, 'dm-0') + os.symlink(sda_path, os.path.join(directory, 'dm-0')) + result = disk._map_dev_paths(directory, include_realpath=True) + assert sorted(result.keys()) == sorted(['sda', 'dm-0']) + assert result['sda'] == dm_path + assert result['dm-0'] == dm_path + + def test_absolute_and_realpath_included(self, tmpfile): + dm_path = tmpfile(name='dm-0', contents='') + directory = os.path.dirname(dm_path) + sda_path = os.path.join(directory, 'sda') + os.symlink(sda_path, os.path.join(directory, 'sda')) + result = disk._map_dev_paths(directory, include_realpath=True, include_abspath=True) + assert sorted(result.keys()) == sorted([dm_path, sda_path, 'sda', 'dm-0']) + assert result['sda'] == sda_path + assert result['dm-0'] == dm_path + assert result[sda_path] == sda_path + assert result[dm_path] == 'dm-0' + + +class TestGetBlockDevs(object): + + def test_loop_devices_are_missing(self, tmpfile): + path = os.path.dirname(tmpfile(name='loop0', contents='')) + result = disk.get_block_devs(sys_block_path=path) + assert result == [] + + def test_loop_devices_are_included(self, tmpfile): + path = os.path.dirname(tmpfile(name='loop0', contents='')) + result = disk.get_block_devs(sys_block_path=path, skip_loop=False) + assert len(result) == 1 + assert result == ['loop0'] + + +class TestGetDevDevs(object): + + def test_abspaths_are_included(self, tmpfile): + sda_path = tmpfile(name='sda', contents='') + directory = os.path.dirname(sda_path) + result = disk.get_dev_devs(directory) + assert sorted(result.keys()) == sorted(['sda', sda_path]) + assert result['sda'] == sda_path + assert result[sda_path] == 'sda' + + +class TestGetMapperDevs(object): + + def test_abspaths_and_realpaths_are_included(self, tmpfile): + dm_path = tmpfile(name='dm-0', contents='') + directory = os.path.dirname(dm_path) + sda_path = os.path.join(directory, 'sda') + os.symlink(sda_path, os.path.join(directory, 'sda')) + result = disk.get_mapper_devs(directory) + assert sorted(result.keys()) == sorted([dm_path, sda_path, 'sda', 'dm-0']) + assert result['sda'] == sda_path + assert result['dm-0'] == dm_path + assert result[sda_path] == sda_path + assert result[dm_path] == 'dm-0' + + +class TestHumanReadableSize(object): + + def test_bytes(self): + result = disk.human_readable_size(800) + assert result == '800.00 B' + + def test_kilobytes(self): + result = disk.human_readable_size(800*1024) + assert result == '800.00 KB' + + def test_megabytes(self): + result = disk.human_readable_size(800*1024*1024) + assert result == '800.00 MB' + + def test_gigabytes(self): + result = disk.human_readable_size(8.19*1024*1024*1024) + assert result == '8.19 GB' + + def test_terabytes(self): + result = disk.human_readable_size(81.2*1024*1024*1024*1024) + assert result == '81.20 TB' + + +class TestGetDevices(object): + + def setup_paths(self, tmpdir): + paths = [] + for directory in ['block', 'dev', 'mapper']: + path = os.path.join(str(tmpdir), directory) + paths.append(path) + os.makedirs(path) + return paths + + def test_no_devices_are_found(self, tmpdir): + result = disk.get_devices( + _sys_block_path=str(tmpdir), + _dev_path=str(tmpdir), + _mapper_path=str(tmpdir)) + assert result == {} + + def test_sda_block_is_found(self, tmpfile, tmpdir): + block_path, dev_path, mapper_path = self.setup_paths(tmpdir) + dev_sda_path = os.path.join(dev_path, 'sda') + os.makedirs(os.path.join(block_path, 'sda')) + os.makedirs(dev_sda_path) + result = disk.get_devices( + _sys_block_path=block_path, + _dev_path=dev_path, + _mapper_path=mapper_path) + assert len(result.keys()) == 1 + assert result[dev_sda_path]['human_readable_size'] == '0.00 B' + assert result[dev_sda_path]['model'] == '' + assert result[dev_sda_path]['partitions'] == {} + + def test_sda_is_removable_gets_skipped(self, tmpfile, tmpdir): + block_path, dev_path, mapper_path = self.setup_paths(tmpdir) + dev_sda_path = os.path.join(dev_path, 'sda') + block_sda_path = os.path.join(block_path, 'sda') + os.makedirs(block_sda_path) + os.makedirs(dev_sda_path) + + tmpfile('removable', contents='1', directory=block_sda_path) + result = disk.get_devices( + _sys_block_path=block_path, + _dev_path=dev_path, + _mapper_path=mapper_path) + assert result == {} + + def test_dm_device_is_not_used(self, monkeypatch, tmpdir): + # the link to the mapper is used instead + monkeypatch.setattr(disk.lvm, 'is_lv', lambda: True) + block_path, dev_path, mapper_path = self.setup_paths(tmpdir) + dev_dm_path = os.path.join(dev_path, 'dm-0') + ceph_data_path = os.path.join(mapper_path, 'ceph-data') + os.symlink(dev_dm_path, ceph_data_path) + block_dm_path = os.path.join(block_path, 'dm-0') + os.makedirs(block_dm_path) + + result = disk.get_devices( + _sys_block_path=block_path, + _dev_path=dev_path, + _mapper_path=mapper_path) + result = list(result.keys()) + assert len(result) == 1 + assert result == [ceph_data_path] + + def test_sda_size(self, tmpfile, tmpdir): + block_path, dev_path, mapper_path = self.setup_paths(tmpdir) + block_sda_path = os.path.join(block_path, 'sda') + dev_sda_path = os.path.join(dev_path, 'sda') + os.makedirs(block_sda_path) + os.makedirs(dev_sda_path) + tmpfile('size', '1024', directory=block_sda_path) + result = disk.get_devices( + _sys_block_path=block_path, + _dev_path=dev_path, + _mapper_path=mapper_path) + assert list(result.keys()) == [dev_sda_path] + assert result[dev_sda_path]['human_readable_size'] == '512.00 KB' + + def test_sda_sectorsize_fallsback(self, tmpfile, tmpdir): + # if no sectorsize, it will use queue/hw_sector_size + block_path, dev_path, mapper_path = self.setup_paths(tmpdir) + block_sda_path = os.path.join(block_path, 'sda') + sda_queue_path = os.path.join(block_sda_path, 'queue') + dev_sda_path = os.path.join(dev_path, 'sda') + os.makedirs(block_sda_path) + os.makedirs(sda_queue_path) + os.makedirs(dev_sda_path) + tmpfile('hw_sector_size', contents='1024', directory=sda_queue_path) + result = disk.get_devices( + _sys_block_path=block_path, + _dev_path=dev_path, + _mapper_path=mapper_path) + assert list(result.keys()) == [dev_sda_path] + assert result[dev_sda_path]['sectorsize'] == '1024' + + def test_sda_sectorsize_from_logical_block(self, tmpfile, tmpdir): + block_path, dev_path, mapper_path = self.setup_paths(tmpdir) + block_sda_path = os.path.join(block_path, 'sda') + sda_queue_path = os.path.join(block_sda_path, 'queue') + dev_sda_path = os.path.join(dev_path, 'sda') + os.makedirs(block_sda_path) + os.makedirs(sda_queue_path) + os.makedirs(dev_sda_path) + tmpfile('logical_block_size', contents='99', directory=sda_queue_path) + result = disk.get_devices( + _sys_block_path=block_path, + _dev_path=dev_path, + _mapper_path=mapper_path) + assert result[dev_sda_path]['sectorsize'] == '99' + + def test_sda_sectorsize_does_not_fallback(self, tmpfile, tmpdir): + block_path, dev_path, mapper_path = self.setup_paths(tmpdir) + block_sda_path = os.path.join(block_path, 'sda') + sda_queue_path = os.path.join(block_sda_path, 'queue') + dev_sda_path = os.path.join(dev_path, 'sda') + os.makedirs(block_sda_path) + os.makedirs(sda_queue_path) + os.makedirs(dev_sda_path) + tmpfile('logical_block_size', contents='99', directory=sda_queue_path) + tmpfile('hw_sector_size', contents='1024', directory=sda_queue_path) + result = disk.get_devices( + _sys_block_path=block_path, + _dev_path=dev_path, + _mapper_path=mapper_path) + assert result[dev_sda_path]['sectorsize'] == '99' + + def test_is_rotational(self, tmpfile, tmpdir): + block_path, dev_path, mapper_path = self.setup_paths(tmpdir) + block_sda_path = os.path.join(block_path, 'sda') + sda_queue_path = os.path.join(block_sda_path, 'queue') + dev_sda_path = os.path.join(dev_path, 'sda') + os.makedirs(block_sda_path) + os.makedirs(sda_queue_path) + os.makedirs(dev_sda_path) + tmpfile('rotational', contents='1', directory=sda_queue_path) + result = disk.get_devices( + _sys_block_path=block_path, + _dev_path=dev_path, + _mapper_path=mapper_path) + assert result[dev_sda_path]['rotational'] == '1' + + +class TestSizeCalculations(object): + + @pytest.mark.parametrize('aliases', [ + ('b', 'bytes'), + ('kb', 'kilobytes'), + ('mb', 'megabytes'), + ('gb', 'gigabytes'), + ('tb', 'terabytes'), + ]) + def test_aliases(self, aliases): + short_alias, long_alias = aliases + s = disk.Size(b=1) + short_alias = getattr(s, short_alias) + long_alias = getattr(s, long_alias) + assert short_alias == long_alias + + @pytest.mark.parametrize('values', [ + ('b', 857619069665.28), + ('kb', 837518622.72), + ('mb', 817889.28), + ('gb', 798.72), + ('tb', 0.78), + ]) + def test_terabytes(self, values): + # regardless of the input value, all the other values correlate to each + # other the same, every time + unit, value = values + s = disk.Size(**{unit: value}) + assert s.b == 857619069665.28 + assert s.kb == 837518622.72 + assert s.mb == 817889.28 + assert s.gb == 798.72 + assert s.tb == 0.78 + + +class TestSizeOperators(object): + + @pytest.mark.parametrize('larger', [1025, 1024.1, 1024.001]) + def test_gigabytes_is_smaller(self, larger): + assert disk.Size(gb=1) < disk.Size(mb=larger) + + @pytest.mark.parametrize('smaller', [1023, 1023.9, 1023.001]) + def test_gigabytes_is_larger(self, smaller): + assert disk.Size(gb=1) > disk.Size(mb=smaller) + + @pytest.mark.parametrize('larger', [1025, 1024.1, 1024.001, 1024]) + def test_gigabytes_is_smaller_or_equal(self, larger): + assert disk.Size(gb=1) <= disk.Size(mb=larger) + + @pytest.mark.parametrize('smaller', [1023, 1023.9, 1023.001, 1024]) + def test_gigabytes_is_larger_or_equal(self, smaller): + assert disk.Size(gb=1) >= disk.Size(mb=smaller) + + @pytest.mark.parametrize('values', [ + ('b', 857619069665.28), + ('kb', 837518622.72), + ('mb', 817889.28), + ('gb', 798.72), + ('tb', 0.78), + ]) + def test_equality(self, values): + unit, value = values + s = disk.Size(**{unit: value}) + # both tb and b, since b is always calculated regardless, and is useful + # when testing tb + assert disk.Size(tb=0.78) == s + assert disk.Size(b=857619069665.28) == s + + @pytest.mark.parametrize('values', [ + ('b', 857619069665.28), + ('kb', 837518622.72), + ('mb', 817889.28), + ('gb', 798.72), + ('tb', 0.78), + ]) + def test_inequality(self, values): + unit, value = values + s = disk.Size(**{unit: value}) + # both tb and b, since b is always calculated regardless, and is useful + # when testing tb + assert disk.Size(tb=1) != s + assert disk.Size(b=100) != s + + +class TestSizeOperations(object): + + def test_assignment_addition_with_size_objects(self): + result = disk.Size(mb=256) + disk.Size(gb=1) + assert result.gb == 1.25 + assert result.gb.as_int() == 1 + assert result.gb.as_float() == 1.25 + + def test_self_addition_with_size_objects(self): + base = disk.Size(mb=256) + base += disk.Size(gb=1) + assert base.gb == 1.25 + + def test_self_addition_does_not_alter_state(self): + base = disk.Size(mb=256) + base + disk.Size(gb=1) + assert base.mb == 256 + + def test_addition_with_non_size_objects(self): + with pytest.raises(TypeError): + disk.Size(mb=100) + 4 + + def test_assignment_subtraction_with_size_objects(self): + base = disk.Size(gb=1) + base -= disk.Size(mb=256) + assert base.mb == 768 + + def test_self_subtraction_does_not_alter_state(self): + base = disk.Size(gb=1) + base - disk.Size(mb=256) + assert base.gb == 1 + + def test_subtraction_with_size_objects(self): + result = disk.Size(gb=1) - disk.Size(mb=256) + assert result.mb == 768 + + def test_subtraction_with_non_size_objects(self): + with pytest.raises(TypeError): + disk.Size(mb=100) - 4 + + def test_multiplication_with_size_objects(self): + with pytest.raises(TypeError): + disk.Size(mb=100) * disk.Size(mb=1) + + def test_multiplication_with_non_size_objects(self): + base = disk.Size(gb=1) + result = base * 2 + assert result.gb == 2 + assert result.gb.as_int() == 2 + + def test_division_with_size_objects(self): + result = disk.Size(gb=1) / disk.Size(mb=1) + assert int(result) == 1024 + + def test_division_with_non_size_objects(self): + base = disk.Size(gb=1) + base / 2 + assert base.mb == 512 + assert base.mb.as_int() == 512 + + +class TestSizeAttributes(object): + + def test_attribute_does_not_exist(self): + with pytest.raises(AttributeError): + disk.Size(mb=1).exabytes + + +class TestSizeFormatting(object): + + def test_default_formatting_tb_to_b(self): + size = disk.Size(tb=0.0000000001) + result = "%s" % size + assert result == "109.95 B" + + def test_default_formatting_tb_to_kb(self): + size = disk.Size(tb=0.00000001) + result = "%s" % size + assert result == "10.74 KB" + + def test_default_formatting_tb_to_mb(self): + size = disk.Size(tb=0.000001) + result = "%s" % size + assert result == "1.05 MB" + + def test_default_formatting_tb_to_gb(self): + size = disk.Size(tb=0.001) + result = "%s" % size + assert result == "1.02 GB" + + def test_default_formatting_tb_to_tb(self): + size = disk.Size(tb=10) + result = "%s" % size + assert result == "10.00 TB" + + +class TestSizeSpecificFormatting(object): + + def test_formatting_b(self): + size = disk.Size(b=2048) + result = "%s" % size.b + assert "%s" % size.b == "%s" % size.bytes + assert result == "2048.00 B" + + def test_formatting_kb(self): + size = disk.Size(kb=5700) + result = "%s" % size.kb + assert "%s" % size.kb == "%s" % size.kilobytes + assert result == "5700.00 KB" + + def test_formatting_mb(self): + size = disk.Size(mb=4000) + result = "%s" % size.mb + assert "%s" % size.mb == "%s" % size.megabytes + assert result == "4000.00 MB" + + def test_formatting_gb(self): + size = disk.Size(gb=77777) + result = "%s" % size.gb + assert "%s" % size.gb == "%s" % size.gigabytes + assert result == "77777.00 GB" + + def test_formatting_tb(self): + size = disk.Size(tb=1027) + result = "%s" % size.tb + assert "%s" % size.tb == "%s" % size.terabytes + assert result == "1027.00 TB" diff --git a/ceph/src/ceph-volume/ceph_volume/tests/util/test_encryption.py b/ceph/src/ceph-volume/ceph_volume/tests/util/test_encryption.py index 3d84e5539..8cca42689 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/util/test_encryption.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/util/test_encryption.py @@ -17,3 +17,19 @@ class TestStatus(object): out = ['some line here', ' '] stub_call((out, '', 0)) assert encryption.status('/dev/sdc1') == {} + + +class TestDmcryptClose(object): + + def test_mapper_exists(self, fake_run, tmpfile): + file_name = tmpfile(name='mapper-device') + encryption.dmcrypt_close(file_name) + arguments = fake_run.calls[0]['args'][0] + assert arguments[0] == 'cryptsetup' + assert arguments[1] == 'remove' + assert arguments[2].startswith('/') + + def test_mapper_does_not_exist(self, fake_run): + file_name = '/path/does/not/exist' + encryption.dmcrypt_close(file_name) + assert fake_run.calls == [] diff --git a/ceph/src/ceph-volume/ceph_volume/tests/util/test_prepare.py b/ceph/src/ceph-volume/ceph_volume/tests/util/test_prepare.py index e702e052d..c611480ca 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/util/test_prepare.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/util/test_prepare.py @@ -7,24 +7,24 @@ from ceph_volume import conf from ceph_volume.tests.conftest import Factory -class TestCheckID(object): +class TestOSDIDAvailable(object): def test_false_if_id_is_none(self): - assert not prepare.check_id(None) + assert not prepare.osd_id_available(None) def test_returncode_is_not_zero(self, monkeypatch): monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: ('', '', 1)) with pytest.raises(RuntimeError): - prepare.check_id(1) + prepare.osd_id_available(1) - def test_id_does_exist(self, monkeypatch): + def test_id_does_exist_but_not_available(self, monkeypatch): stdout = dict(nodes=[ - dict(id=0), + dict(id=0, status="up"), ]) stdout = ['', json.dumps(stdout)] monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: (stdout, '', 0)) - result = prepare.check_id(0) - assert result + result = prepare.osd_id_available(0) + assert not result def test_id_does_not_exist(self, monkeypatch): stdout = dict(nodes=[ @@ -32,7 +32,7 @@ class TestCheckID(object): ]) stdout = ['', json.dumps(stdout)] monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: (stdout, '', 0)) - result = prepare.check_id(1) + result = prepare.osd_id_available(1) assert not result def test_invalid_osd_id(self, monkeypatch): @@ -41,9 +41,18 @@ class TestCheckID(object): ]) stdout = ['', json.dumps(stdout)] monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: (stdout, '', 0)) - result = prepare.check_id("foo") + result = prepare.osd_id_available("foo") assert not result + def test_returns_true_when_id_is_destroyed(self, monkeypatch): + stdout = dict(nodes=[ + dict(id=0, status="destroyed"), + ]) + stdout = ['', json.dumps(stdout)] + monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: (stdout, '', 0)) + result = prepare.osd_id_available(0) + assert result + class TestFormatDevice(object): @@ -116,6 +125,38 @@ class TestFormatDevice(object): assert expected == fake_run.calls[0]['args'][0] +mkfs_filestore_flags = [ + 'ceph-osd', + '--cluster', + '--osd-objectstore', 'filestore', + '--mkfs', + '-i', + '--monmap', + '--keyfile', '-', # goes through stdin + '--osd-data', + '--osd-journal', + '--osd-uuid', + '--setuser', 'ceph', + '--setgroup', 'ceph' +] + + +class TestOsdMkfsFilestore(object): + + @pytest.mark.parametrize('flag', mkfs_filestore_flags) + def test_keyring_is_used(self, fake_call, monkeypatch, flag): + monkeypatch.setattr(prepare, '__release__', 'mimic') + monkeypatch.setattr(system, 'chown', lambda path: True) + prepare.osd_mkfs_filestore(1, 'asdf', keyring='secret') + assert flag in fake_call.calls[0]['args'][0] + + def test_keyring_is_used_luminous(self, fake_call, monkeypatch): + monkeypatch.setattr(prepare, '__release__', 'luminous') + monkeypatch.setattr(system, 'chown', lambda path: True) + prepare.osd_mkfs_filestore(1, 'asdf', keyring='secret') + assert '--keyfile' not in fake_call.calls[0]['args'][0] + + class TestOsdMkfsBluestore(object): def test_keyring_is_added(self, fake_call, monkeypatch): @@ -128,6 +169,12 @@ class TestOsdMkfsBluestore(object): prepare.osd_mkfs_bluestore(1, 'asdf') assert '--keyfile' not in fake_call.calls[0]['args'][0] + def test_keyring_is_not_added_luminous(self, fake_call, monkeypatch): + monkeypatch.setattr(system, 'chown', lambda path: True) + prepare.osd_mkfs_bluestore(1, 'asdf') + monkeypatch.setattr(prepare, '__release__', 'luminous') + assert '--keyfile' not in fake_call.calls[0]['args'][0] + def test_wal_is_added(self, fake_call, monkeypatch): monkeypatch.setattr(system, 'chown', lambda path: True) prepare.osd_mkfs_bluestore(1, 'asdf', wal='/dev/smm1') @@ -290,3 +337,57 @@ class TestMkfsBluestore(object): '--osd-uuid', 'asdf-1234', '--setuser', 'ceph', '--setgroup', 'ceph']) assert expected in str(error) + + +class TestGetJournalSize(object): + + def test_undefined_size_fallbacks_formatted(self, conf_ceph_stub): + conf_ceph_stub(dedent(""" + [global] + fsid = a25d19a6-7d57-4eda-b006-78e35d2c4d9f + """)) + result = prepare.get_journal_size() + assert result == '5G' + + def test_undefined_size_fallbacks_unformatted(self, conf_ceph_stub): + conf_ceph_stub(dedent(""" + [global] + fsid = a25d19a6-7d57-4eda-b006-78e35d2c4d9f + """)) + result = prepare.get_journal_size(lv_format=False) + assert result.gb.as_int() == 5 + + def test_defined_size_unformatted(self, conf_ceph_stub): + conf_ceph_stub(dedent(""" + [global] + fsid = a25d19a6-7d57-4eda-b006-78e35d2c4d9f + + [osd] + osd journal size = 10240 + """)) + result = prepare.get_journal_size(lv_format=False) + assert result.gb.as_int() == 10 + + def test_defined_size_formatted(self, conf_ceph_stub): + conf_ceph_stub(dedent(""" + [global] + fsid = a25d19a6-7d57-4eda-b006-78e35d2c4d9f + + [osd] + osd journal size = 10240 + """)) + result = prepare.get_journal_size() + assert result == '10G' + + def test_refuse_tiny_journals(self, conf_ceph_stub): + conf_ceph_stub(dedent(""" + [global] + fsid = a25d19a6-7d57-4eda-b006-78e35d2c4d9f + + [osd] + osd journal size = 1024 + """)) + with pytest.raises(RuntimeError) as error: + prepare.get_journal_size() + assert 'journal sizes must be larger' in str(error) + assert 'detected: 1024.00 MB' in str(error) diff --git a/ceph/src/ceph-volume/ceph_volume/tests/util/test_system.py b/ceph/src/ceph-volume/ceph_volume/tests/util/test_system.py index 690147cb2..bf71e8746 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/util/test_system.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/util/test_system.py @@ -168,6 +168,30 @@ class TestIsBinary(object): assert system.is_binary(binary_path) is False +class TestGetFileContents(object): + + def test_path_does_not_exist(self, tmpdir): + filepath = os.path.join(str(tmpdir), 'doesnotexist') + assert system.get_file_contents(filepath, 'default') == 'default' + + def test_path_has_contents(self, tmpfile): + interesting_file = tmpfile(contents="1") + result = system.get_file_contents(interesting_file) + assert result == "1" + + def test_path_has_multiline_contents(self, tmpfile): + interesting_file = tmpfile(contents="0\n1") + result = system.get_file_contents(interesting_file) + assert result == "0\n1" + + def test_exception_returns_default(self, tmpfile): + interesting_file = tmpfile(contents="0") + # remove read, causes IOError + os.chmod(interesting_file, 0o000) + result = system.get_file_contents(interesting_file) + assert result == '' + + class TestWhich(object): def test_executable_exists_but_is_not_file(self, monkeypatch): diff --git a/ceph/src/ceph-volume/ceph_volume/tests/util/test_util.py b/ceph/src/ceph-volume/ceph_volume/tests/util/test_util.py new file mode 100644 index 000000000..82f2ef27f --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/tests/util/test_util.py @@ -0,0 +1,96 @@ +import pytest +from ceph_volume import util + + +class TestAsBytes(object): + + def test_bytes_just_gets_returned(self): + bytes_string = "contents".encode('utf-8') + assert util.as_bytes(bytes_string) == bytes_string + + def test_string_gets_converted_to_bytes(self): + result = util.as_bytes('contents') + assert isinstance(result, bytes) + + +class TestStrToInt(object): + + def test_passing_a_float_str(self): + result = util.str_to_int("1.99") + assert result == 1 + + def test_passing_a_float_does_not_round(self): + result = util.str_to_int("1.99", round_down=False) + assert result == 2 + + def test_text_is_not_an_integer_like(self): + with pytest.raises(RuntimeError) as error: + util.str_to_int("1.4GB") + assert str(error.value) == "Unable to convert to integer: '1.4GB'" + + +def true_responses(upper_casing=False): + if upper_casing: + return ['Y', 'YES', ''] + return ['y', 'yes', ''] + + +def false_responses(upper_casing=False): + if upper_casing: + return ['N', 'NO'] + return ['n', 'no'] + + +def invalid_responses(): + return [9, 0.1, 'h', [], {}, None] + + +class TestStrToBool(object): + + @pytest.mark.parametrize('response', true_responses()) + def test_trueish(self, response): + assert util.str_to_bool(response) is True + + @pytest.mark.parametrize('response', false_responses()) + def test_falseish(self, response): + assert util.str_to_bool(response) is False + + @pytest.mark.parametrize('response', true_responses(True)) + def test_trueish_upper(self, response): + assert util.str_to_bool(response) is True + + @pytest.mark.parametrize('response', false_responses(True)) + def test_falseish_upper(self, response): + assert util.str_to_bool(response) is False + + @pytest.mark.parametrize('response', invalid_responses()) + def test_invalid(self, response): + with pytest.raises(ValueError): + util.str_to_bool(response) + + +class TestPromptBool(object): + + @pytest.mark.parametrize('response', true_responses()) + def test_trueish(self, response): + fake_input = lambda x: response + qx = 'what the what?' + assert util.prompt_bool(qx, _raw_input=fake_input) is True + + @pytest.mark.parametrize('response', false_responses()) + def test_falseish(self, response): + fake_input = lambda x: response + qx = 'what the what?' + assert util.prompt_bool(qx, _raw_input=fake_input) is False + + def test_try_again_true(self): + responses = ['g', 'h', 'y'] + fake_input = lambda x: responses.pop(0) + qx = 'what the what?' + assert util.prompt_bool(qx, _raw_input=fake_input) is True + + def test_try_again_false(self): + responses = ['g', 'h', 'n'] + fake_input = lambda x: responses.pop(0) + qx = 'what the what?' + assert util.prompt_bool(qx, _raw_input=fake_input) is False diff --git a/ceph/src/ceph-volume/ceph_volume/util/__init__.py b/ceph/src/ceph-volume/ceph_volume/util/__init__.py index 3b8c30906..cdcf3a5b0 100644 --- a/ceph/src/ceph-volume/ceph_volume/util/__init__.py +++ b/ceph/src/ceph-volume/ceph_volume/util/__init__.py @@ -1,3 +1,10 @@ +import logging +from math import floor +from ceph_volume import terminal + + +logger = logging.getLogger(__name__) + def as_string(string): """ @@ -8,3 +15,71 @@ def as_string(string): # we really ignore here if we can't properly decode with utf-8 return string.decode('utf-8', 'ignore') return string + + +def as_bytes(string): + """ + Ensure that whatever type of string is incoming, it is returned as bytes, + encoding to utf-8 otherwise + """ + if isinstance(string, bytes): + return string + return string.encode('utf-8', errors='ignore') + + +def str_to_int(string, round_down=True): + """ + Parses a string number into an integer, optionally converting to a float + and rounding down. + """ + error_msg = "Unable to convert to integer: '%s'" % str(string) + try: + integer = float(string) + except (TypeError, ValueError): + logger.exception(error_msg) + raise RuntimeError(error_msg) + + if round_down: + integer = floor(integer) + else: + integer = round(integer) + return int(integer) + + +def str_to_bool(val): + """ + Convert a string representation of truth to True or False + + True values are 'y', 'yes', or ''; case-insensitive + False values are 'n', or 'no'; case-insensitive + Raises ValueError if 'val' is anything else. + """ + true_vals = ['yes', 'y', ''] + false_vals = ['no', 'n'] + try: + val = val.lower() + except AttributeError: + val = str(val).lower() + if val in true_vals: + return True + elif val in false_vals: + return False + else: + raise ValueError("Invalid input value: %s" % val) + + +def prompt_bool(question, _raw_input=None): + """ + Interface to prompt a boolean (or boolean-like) response from a user. + Usually a confirmation. + """ + input_prompt = _raw_input or raw_input + prompt_format = '--> {question} '.format(question=question) + response = input_prompt(prompt_format) + try: + return str_to_bool(response) + except ValueError: + terminal.error('Valid true responses are: y, yes, ') + terminal.error('Valid false responses are: n, no') + terminal.error('That response was invalid, please try again') + return prompt_bool(question, _raw_input=input_prompt) diff --git a/ceph/src/ceph-volume/ceph_volume/util/arg_validators.py b/ceph/src/ceph-volume/ceph_volume/util/arg_validators.py index 3866027ef..d0144fc58 100644 --- a/ceph/src/ceph-volume/ceph_volume/util/arg_validators.py +++ b/ceph/src/ceph-volume/ceph_volume/util/arg_validators.py @@ -3,6 +3,7 @@ import os from ceph_volume import terminal from ceph_volume import decorators from ceph_volume.util import disk +from ceph_volume.util.device import Device class LVPath(object): @@ -41,6 +42,18 @@ class LVPath(object): return string +class ValidDevice(object): + + def __call__(self, string): + device = Device(string) + if not device.exists: + raise argparse.ArgumentError( + None, "Unable to proceed with non-existing device: %s" % string + ) + + return device + + class OSDPath(object): """ Validate path exists and it looks like an OSD directory. diff --git a/ceph/src/ceph-volume/ceph_volume/util/device.py b/ceph/src/ceph-volume/ceph_volume/util/device.py new file mode 100644 index 000000000..7dca60a49 --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/util/device.py @@ -0,0 +1,98 @@ +import os +from ceph_volume import sys_info +from ceph_volume.api import lvm +from ceph_volume.util import disk + + +class Device(object): + + def __init__(self, path): + self.path = path + # LVs can have a vg/lv path, while disks will have /dev/sda + self.abspath = path + self.lv_api = None + self.pvs_api = [] + self.disk_api = {} + self.sys_api = {} + self._exists = None + self._is_lvm_member = None + self._parse() + + def _parse(self): + # start with lvm since it can use an absolute or relative path + lv = lvm.get_lv_from_argument(self.path) + if lv: + self.lv_api = lv + self.abspath = lv.lv_path + else: + dev = disk.lsblk(self.path) + self.disk_api = dev + device_type = dev.get('TYPE', '') + # always check is this is an lvm member + if device_type in ['part', 'disk']: + self._set_lvm_membership() + + if not sys_info.devices: + sys_info.devices = disk.get_devices() + self.sys_api = sys_info.devices.get(self.abspath, {}) + + def __repr__(self): + prefix = 'Unknown' + if self.is_lv: + prefix = 'LV' + elif self.is_partition: + prefix = 'Partition' + elif self.is_device: + prefix = 'Raw Device' + return '<%s: %s>' % (prefix, self.abspath) + + def _set_lvm_membership(self): + if self._is_lvm_member is None: + # check if there was a pv created with the + # name of device + pvs = lvm.PVolumes() + pvs.filter(pv_name=self.abspath) + if not pvs: + self._is_lvm_member = False + return self._is_lvm_member + has_vgs = [pv.vg_name for pv in pvs if pv.vg_name] + if has_vgs: + self._is_lvm_member = True + self.pvs_api = pvs + else: + # this is contentious, if a PV is recognized by LVM but has no + # VGs, should we consider it as part of LVM? We choose not to + # here, because most likely, we need to use VGs from this PV. + self._is_lvm_member = False + + return self._is_lvm_member + + @property + def exists(self): + return os.path.exists(self.abspath) + + @property + def is_lvm_member(self): + if self._is_lvm_member is None: + self._set_lvm_membership() + return self._is_lvm_member + + @property + def is_mapper(self): + return self.path.startswith('/dev/mapper') + + @property + def is_lv(self): + return self.lv_api is not None + + @property + def is_partition(self): + if self.disk_api: + return self.disk_api['TYPE'] == 'part' + return False + + @property + def is_device(self): + if self.disk_api: + return self.disk_api['TYPE'] == 'device' + return False diff --git a/ceph/src/ceph-volume/ceph_volume/util/disk.py b/ceph/src/ceph-volume/ceph_volume/util/disk.py index 434723340..053338972 100644 --- a/ceph/src/ceph-volume/ceph_volume/util/disk.py +++ b/ceph/src/ceph-volume/ceph_volume/util/disk.py @@ -1,6 +1,13 @@ +import logging import os +import re import stat from ceph_volume import process +from ceph_volume.api import lvm +from ceph_volume.util.system import get_file_contents + + +logger = logging.getLogger(__name__) # The blkid CLI tool has some oddities which prevents having one common call @@ -221,3 +228,431 @@ def is_partition(dev): if os.path.exists('/sys/dev/block/%d:%d/partition' % (major, minor)): return True return False + + +def _map_dev_paths(_path, include_abspath=False, include_realpath=False): + """ + Go through all the items in ``_path`` and map them to their absolute path:: + + {'sda': '/dev/sda'} + + If ``include_abspath`` is set, then a reverse mapping is set as well:: + + {'sda': '/dev/sda', '/dev/sda': 'sda'} + + If ``include_realpath`` is set then the same operation is done for any + links found when listing, these are *not* reversed to avoid clashing on + existing keys, but both abspath and basename can be included. For example:: + + { + 'ceph-data': '/dev/mapper/ceph-data', + '/dev/mapper/ceph-data': 'ceph-data', + '/dev/dm-0': '/dev/mapper/ceph-data', + 'dm-0': '/dev/mapper/ceph-data' + } + + + In case of possible exceptions the mapping is returned empty, and the + exception is logged. + """ + mapping = {} + try: + dev_names = os.listdir(_path) + except (OSError, IOError): + logger.exception('unable to list block devices from: %s' % _path) + return {} + + for dev_name in dev_names: + mapping[dev_name] = os.path.join(_path, dev_name) + + if include_abspath: + for k, v in list(mapping.items()): + mapping[v] = k + + if include_realpath: + for abspath in list(mapping.values()): + if not os.path.islink(abspath): + continue + + realpath = os.path.realpath(abspath) + basename = os.path.basename(realpath) + mapping[basename] = abspath + if include_abspath: + mapping[realpath] = abspath + + return mapping + + +def get_block_devs(sys_block_path="/sys/block", skip_loop=True): + """ + Go through all the items in /sys/block and return them as a list. + + The ``sys_block_path`` argument is set for easier testing and is not + required for proper operation. + """ + devices = _map_dev_paths(sys_block_path).keys() + if skip_loop: + return [d for d in devices if not d.startswith('loop')] + return list(devices) + + +def get_dev_devs(dev_path="/dev"): + """ + Go through all the items in /dev and return them as a list. + + The ``dev_path`` argument is set for easier testing and is not + required for proper operation. + """ + return _map_dev_paths(dev_path, include_abspath=True) + + +def get_mapper_devs(mapper_path="/dev/mapper"): + """ + Go through all the items in /dev and return them as a list. + + The ``dev_path`` argument is set for easier testing and is not + required for proper operation. + """ + return _map_dev_paths(mapper_path, include_abspath=True, include_realpath=True) + + +class BaseFloatUnit(float): + """ + Base class to support float representations of size values. Suffix is + computed on child classes by inspecting the class name + """ + + def __repr__(self): + return "<%s(%s)>" % (self.__class__.__name__, self.__float__()) + + def __str__(self): + return "{size:.2f} {suffix}".format( + size=self.__float__(), + suffix=self.__class__.__name__.split('Float')[-1] + ) + + def as_int(self): + return int(self.real) + + def as_float(self): + return self.real + + +class FloatB(BaseFloatUnit): + pass + + +class FloatMB(BaseFloatUnit): + pass + + +class FloatGB(BaseFloatUnit): + pass + + +class FloatKB(BaseFloatUnit): + pass + + +class FloatTB(BaseFloatUnit): + pass + + +class Size(object): + """ + Helper to provide an interface for different sizes given a single initial + input. Allows for comparison between different size objects, which avoids + the need to convert sizes before comparison (e.g. comparing megabytes + against gigabytes). + + Common comparison operators are supported:: + + >>> hd1 = Size(gb=400) + >>> hd2 = Size(gb=500) + >>> hd1 > hd2 + False + >>> hd1 < hd2 + True + >>> hd1 == hd2 + False + >>> hd1 == Size(gb=400) + True + + The Size object can also be multiplied or divided:: + + >>> hd1 + + >>> hd1 * 2 + + >>> hd1 + + + Additions and subtractions are only supported between Size objects:: + + >>> Size(gb=224) - Size(gb=100) + + >>> Size(gb=1) + Size(mb=300) + + + Can also display a human-readable representation, with automatic detection + on best suited unit, or alternatively, specific unit representation:: + + >>> s = Size(mb=2211) + >>> s + + >>> s.mb + + >>> print "Total size: %s" % s.mb + Total size: 2211.00 MB + >>> print "Total size: %s" % s + Total size: 2.16 GB + """ + + def __init__(self, multiplier=1024, **kw): + self._multiplier = multiplier + # create a mapping of units-to-multiplier, skip bytes as that is + # calculated initially always and does not need to convert + aliases = [ + [('kb', 'kilobytes'), self._multiplier], + [('mb', 'megabytes'), self._multiplier ** 2], + [('gb', 'gigabytes'), self._multiplier ** 3], + [('tb', 'terabytes'), self._multiplier ** 4], + ] + # and mappings for units-to-formatters, including bytes and aliases for + # each + format_aliases = [ + [('b', 'bytes'), FloatB], + [('kb', 'kilobytes'), FloatKB], + [('mb', 'megabytes'), FloatMB], + [('gb', 'gigabytes'), FloatGB], + [('tb', 'terabytes'), FloatTB], + ] + self._formatters = {} + for key, value in format_aliases: + for alias in key: + self._formatters[alias] = value + self._factors = {} + for key, value in aliases: + for alias in key: + self._factors[alias] = value + + for k, v in kw.items(): + self._convert(v, k) + # only pursue the first occurence + break + + def _convert(self, size, unit): + """ + Convert any size down to bytes so that other methods can rely on bytes + being available always, regardless of what they pass in, avoiding the + need for a mapping of every permutation. + """ + if unit in ['b', 'bytes']: + self._b = size + return + factor = self._factors[unit] + self._b = float(size * factor) + + def _get_best_format(self): + """ + Go through all the supported units, and use the first one that is less + than 1024. This allows to represent size in the most readable format + available + """ + for unit in ['b', 'kb', 'mb', 'gb', 'tb']: + if getattr(self, unit) > 1024: + continue + return getattr(self, unit) + + def __repr__(self): + return "" % self._get_best_format() + + def __str__(self): + return "%s" % self._get_best_format() + + def __lt__(self, other): + return self._b < other._b + + def __le__(self, other): + return self._b <= other._b + + def __eq__(self, other): + return self._b == other._b + + def __ne__(self, other): + return self._b != other._b + + def __ge__(self, other): + return self._b >= other._b + + def __gt__(self, other): + return self._b > other._b + + def __add__(self, other): + if isinstance(other, Size): + _b = self._b + other._b + return Size(b=_b) + raise TypeError('Cannot add "Size" object with int') + + def __sub__(self, other): + if isinstance(other, Size): + _b = self._b - other._b + return Size(b=_b) + raise TypeError('Cannot subtract "Size" object from int') + + def __mul__(self, other): + if isinstance(other, Size): + raise TypeError('Cannot multiply with "Size" object') + _b = self._b * other + return Size(b=_b) + + def __truediv__(self, other): + if isinstance(other, Size): + return self._b / other._b + self._b = self._b / other + return self + + def __div__(self, other): + if isinstance(other, Size): + return self._b / other._b + self._b = self._b / other + return self + + def __getattr__(self, unit): + """ + Calculate units on the fly, relies on the fact that ``bytes`` has been + converted at instantiation. Units that don't exist will trigger an + ``AttributeError`` + """ + try: + formatter = self._formatters[unit] + except KeyError: + raise AttributeError('Size object has not attribute "%s"' % unit) + if unit in ['b', 'bytes']: + return formatter(self._b) + try: + factor = self._factors[unit] + except KeyError: + raise AttributeError('Size object has not attribute "%s"' % unit) + return formatter(float(self._b) / factor) + + +def human_readable_size(size): + """ + Take a size in bytes, and transform it into a human readable size with up + to two decimals of precision. + """ + suffixes = ['B', 'KB', 'MB', 'GB', 'TB'] + suffix_index = 0 + while size > 1024: + suffix_index += 1 + size = size / 1024.0 + return "{size:.2f} {suffix}".format( + size=size, + suffix=suffixes[suffix_index]) + + +def get_partitions_facts(sys_block_path): + partition_metadata = {} + for folder in os.listdir(sys_block_path): + folder_path = os.path.join(sys_block_path, folder) + if os.path.exists(os.path.join(folder_path, 'partition')): + contents = get_file_contents(os.path.join(folder_path, 'partition')) + if '1' in contents: + part = {} + partname = folder + part_sys_block_path = os.path.join(sys_block_path, partname) + + part['start'] = get_file_contents(part_sys_block_path + "/start", 0) + part['sectors'] = get_file_contents(part_sys_block_path + "/size", 0) + + part['sectorsize'] = get_file_contents( + part_sys_block_path + "/queue/logical_block_size") + if not part['sectorsize']: + part['sectorsize'] = get_file_contents( + part_sys_block_path + "/queue/hw_sector_size", 512) + part['size'] = human_readable_size(float(part['sectors']) * 512) + + partition_metadata[partname] = part + return partition_metadata + + +def is_mapper_device(device_name): + return device_name.startswith(('/dev/mapper', '/dev/dm-')) + + +def get_devices(_sys_block_path='/sys/block', _dev_path='/dev', _mapper_path='/dev/mapper'): + """ + Captures all available devices from /sys/block/, including its partitions, + along with interesting metadata like sectors, size, vendor, + solid/rotational, etc... + + Returns a dictionary, where keys are the full paths to devices. + + ..note:: dmapper devices get their path updated to what they link from, if + /dev/dm-0 is linked by /dev/mapper/ceph-data, then the latter gets + used as the key. + + ..note:: loop devices, removable media, and logical volumes are never included. + """ + # Portions of this detection process are inspired by some of the fact + # gathering done by Ansible in module_utils/facts/hardware/linux.py. The + # processing of metadata and final outcome *is very different* and fully + # imcompatible. There are ignored devices, and paths get resolved depending + # on dm devices, loop, and removable media + + device_facts = {} + + block_devs = get_block_devs(_sys_block_path) + dev_devs = get_dev_devs(_dev_path) + mapper_devs = get_mapper_devs(_mapper_path) + + for block in block_devs: + sysdir = os.path.join(_sys_block_path, block) + metadata = {} + + # Ensure that the diskname is an absolute path and that it never points + # to a /dev/dm-* device + diskname = mapper_devs.get(block) or dev_devs.get(block) + + # If the mapper device is a logical volume it gets excluded + if is_mapper_device(diskname): + if lvm.is_lv(diskname): + continue + + # If the device reports itself as 'removable', get it excluded + metadata['removable'] = get_file_contents(os.path.join(sysdir, 'removable')) + if metadata['removable'] == '1': + continue + + for key in ['vendor', 'model', 'sas_address', 'sas_device_handle']: + metadata[key] = get_file_contents(sysdir + "/device/" + key) + + for key in ['sectors', 'size']: + metadata[key] = get_file_contents(os.path.join(sysdir, key), 0) + + for key, _file in [('support_discard', '/queue/discard_granularity')]: + metadata[key] = get_file_contents(os.path.join(sysdir, _file)) + + metadata['partitions'] = get_partitions_facts(sysdir) + + metadata['rotational'] = get_file_contents(sysdir + "/queue/rotational") + metadata['scheduler_mode'] = "" + scheduler = get_file_contents(sysdir + "/queue/scheduler") + if scheduler is not None: + m = re.match(r".*?(\[(.*)\])", scheduler) + if m: + metadata['scheduler_mode'] = m.group(2) + + if not metadata['sectors']: + metadata['sectors'] = 0 + size = metadata['sectors'] or metadata['size'] + metadata['sectorsize'] = get_file_contents(sysdir + "/queue/logical_block_size") + if not metadata['sectorsize']: + metadata['sectorsize'] = get_file_contents(sysdir + "/queue/hw_sector_size", 512) + metadata['human_readable_size'] = human_readable_size(float(size) * 512) + metadata['size'] = float(size) * 512 + metadata['path'] = diskname + + device_facts[diskname] = metadata + return device_facts diff --git a/ceph/src/ceph-volume/ceph_volume/util/encryption.py b/ceph/src/ceph-volume/ceph_volume/util/encryption.py index 0abe9b6c1..cc594a07e 100644 --- a/ceph/src/ceph-volume/ceph_volume/util/encryption.py +++ b/ceph/src/ceph-volume/ceph_volume/util/encryption.py @@ -96,6 +96,10 @@ def dmcrypt_close(mapping): :param mapping: """ + if not os.path.exists(mapping): + logger.debug('device mapper path does not exist %s' % mapping) + logger.debug('will skip cryptsetup removal') + return process.run(['cryptsetup', 'remove', mapping]) diff --git a/ceph/src/ceph-volume/ceph_volume/util/prepare.py b/ceph/src/ceph-volume/ceph_volume/util/prepare.py index d1cddf073..687a5892b 100644 --- a/ceph/src/ceph-volume/ceph_volume/util/prepare.py +++ b/ceph/src/ceph-volume/ceph_volume/util/prepare.py @@ -7,10 +7,11 @@ the single-call helper import os import logging import json -from ceph_volume import process, conf -from ceph_volume.util import system, constants +from ceph_volume import process, conf, __release__, terminal +from ceph_volume.util import system, constants, str_to_int, disk logger = logging.getLogger(__name__) +mlogger = terminal.MultiLogger(__name__) def create_key(): @@ -47,6 +48,28 @@ def write_keyring(osd_id, secret, keyring_name='keyring', name=None): system.chown(osd_keyring) +def get_journal_size(lv_format=True): + """ + Helper to retrieve the size (defined in megabytes in ceph.conf) to create + the journal logical volume, it "translates" the string into a float value, + then converts that into gigabytes, and finally (optionally) it formats it + back as a string so that it can be used for creating the LV. + + :param lv_format: Return a string to be used for ``lv_create``. A 5 GB size + would result in '5G', otherwise it will return a ``Size`` object. + """ + conf_journal_size = conf.ceph.get_safe('osd', 'osd_journal_size', '5120') + logger.debug('osd_journal_size set to %s' % conf_journal_size) + journal_size = disk.Size(mb=str_to_int(conf_journal_size)) + + if journal_size < disk.Size(gb=2): + mlogger.error('Refusing to continue with configured size for journal') + raise RuntimeError('journal sizes must be larger than 2GB, detected: %s' % journal_size) + if lv_format: + return '%sG' % journal_size.gb.as_int() + return journal_size + + def create_id(fsid, json_secrets, osd_id=None): """ :param fsid: The osd fsid to create, always required @@ -64,8 +87,11 @@ def create_id(fsid, json_secrets, osd_id=None): '-i', '-', 'osd', 'new', fsid ] - if check_id(osd_id): - cmd.append(osd_id) + if osd_id is not None: + if osd_id_available(osd_id): + cmd.append(osd_id) + else: + raise RuntimeError("The osd ID {} is already in use or does not exist.".format(osd_id)) stdout, stderr, returncode = process.call( cmd, stdin=json_secrets, @@ -76,10 +102,10 @@ def create_id(fsid, json_secrets, osd_id=None): return ' '.join(stdout).strip() -def check_id(osd_id): +def osd_id_available(osd_id): """ - Checks to see if an osd ID exists or not. Returns True - if it does exist, False if it doesn't. + Checks to see if an osd ID exists and if it's available for + reuse. Returns True if it is, False if it isn't. :param osd_id: The osd ID to check """ @@ -103,7 +129,10 @@ def check_id(osd_id): output = json.loads(''.join(stdout).strip()) osds = output['nodes'] - return any([str(osd['id']) == str(osd_id) for osd in osds]) + osd = [osd for osd in osds if str(osd['id']) == str(osd_id)] + if osd and osd[0].get('status') == "destroyed": + return True + return False def mount_tmpfs(path): @@ -114,6 +143,9 @@ def mount_tmpfs(path): path ]) + # Restore SELinux context + system.set_context(path) + def create_osd_path(osd_id, tmpfs=False): path = '/var/lib/ceph/osd/%s-%s' % (conf.cluster, osd_id) @@ -213,6 +245,9 @@ def mount_osd(device, osd_id, **kw): command.append(destination) process.run(command) + # Restore SELinux context + system.set_context(destination) + def _link_device(device, device_type, osd_id): """ @@ -327,7 +362,7 @@ def osd_mkfs_bluestore(osd_id, fsid, keyring=None, wal=False, db=False): raise RuntimeError('Command failed with exit code %s: %s' % (returncode, ' '.join(command))) -def osd_mkfs_filestore(osd_id, fsid): +def osd_mkfs_filestore(osd_id, fsid, keyring): """ Create the files for the OSD to function. A normal call will look like: @@ -347,7 +382,7 @@ def osd_mkfs_filestore(osd_id, fsid): system.chown(journal) system.chown(path) - process.run([ + command = [ 'ceph-osd', '--cluster', conf.cluster, # undocumented flag, sets the `type` file to contain 'filestore' @@ -355,9 +390,22 @@ def osd_mkfs_filestore(osd_id, fsid): '--mkfs', '-i', osd_id, '--monmap', monmap, + ] + + if __release__ != 'luminous': + # goes through stdin + command.extend(['--keyfile', '-']) + + command.extend([ '--osd-data', path, '--osd-journal', journal, '--osd-uuid', fsid, '--setuser', 'ceph', '--setgroup', 'ceph' ]) + + _, _, returncode = process.call( + command, stdin=keyring, terminal_verbose=True, show_command=True + ) + if returncode != 0: + raise RuntimeError('Command failed with exit code %s: %s' % (returncode, ' '.join(command))) diff --git a/ceph/src/ceph-volume/ceph_volume/util/system.py b/ceph/src/ceph-volume/ceph_volume/util/system.py index b4b7d17c4..b637f023a 100644 --- a/ceph/src/ceph-volume/ceph_volume/util/system.py +++ b/ceph/src/ceph-volume/ceph_volume/util/system.py @@ -65,6 +65,19 @@ def get_ceph_user_ids(): return user[2], user[3] +def get_file_contents(path, default=''): + contents = default + if not os.path.exists(path): + return contents + try: + with open(path, 'r') as open_file: + contents = open_file.read().strip() + except Exception: + logger.exception('Failed to read contents from: %s' % path) + + return contents + + def mkdir_p(path, chown=True): """ A `mkdir -p` that defaults to chown the path to the ceph user @@ -260,3 +273,12 @@ def get_mounts(devices=False, paths=False, realpath=False): return devices_mounted else: return paths_mounted + + +def set_context(path, recursive = False): + # restore selinux context to default policy values + if which('restorecon').startswith('/'): + if recursive: + process.run(['restorecon', '-R', path]) + else: + process.run(['restorecon', path]) diff --git a/ceph/src/ceph-volume/ceph_volume/util/templates.py b/ceph/src/ceph-volume/ceph_volume/util/templates.py new file mode 100644 index 000000000..90858d62d --- /dev/null +++ b/ceph/src/ceph-volume/ceph_volume/util/templates.py @@ -0,0 +1,25 @@ + +osd_header = """ +{:-^100}""".format('') + + +osd_component_titles = """ + Type Path LV Size % of device""" + + +osd_component = """ + {_type: <15} {path: <55} {size: <15} {percent}%""" + + +total_osds = """ +Total OSDs: {total_osds} +""" + +ssd_volume_group = """ +Solid State VG: + Targets: {target: <25} Total size: {total_lv_size: <25} + Total LVs: {total_lvs: <25} Size per LV: {lv_size: <25} + Devices: {block_db_devices} +""" + + diff --git a/ceph/src/ceph_mds.cc b/ceph/src/ceph_mds.cc index b6cff8348..7f68e5b52 100644 --- a/ceph/src/ceph_mds.cc +++ b/ceph/src/ceph_mds.cc @@ -88,11 +88,7 @@ static void handle_mds_signal(int signum) mds->handle_signal(signum); } -#ifdef BUILDING_FOR_EMBEDDED -extern "C" int cephd_mds(int argc, const char **argv) -#else int main(int argc, const char **argv) -#endif { ceph_pthread_setname(pthread_self(), "ceph-mds"); diff --git a/ceph/src/ceph_mon.cc b/ceph/src/ceph_mon.cc index 41a6ee0eb..c1d0b041f 100644 --- a/ceph/src/ceph_mon.cc +++ b/ceph/src/ceph_mon.cc @@ -181,12 +181,7 @@ static void usage() generic_server_usage(); } -#ifdef BUILDING_FOR_EMBEDDED -void cephd_preload_embedded_plugins(); -extern "C" int cephd_mon(int argc, const char **argv) -#else int main(int argc, const char **argv) -#endif { int err; @@ -475,7 +470,7 @@ int main(int argc, const char **argv) if (stats.avail_percent <= g_conf->mon_data_avail_crit) { derr << "error: monitor data filesystem reached concerning levels of" << " available storage space (available: " - << stats.avail_percent << "% " << prettybyte_t(stats.byte_avail) + << stats.avail_percent << "% " << byte_u_t(stats.byte_avail) << ")\nyou may adjust 'mon data avail crit' to a lower value" << " to make this go away (default: " << g_conf->mon_data_avail_crit << "%)\n" << dendl; @@ -505,12 +500,8 @@ int main(int argc, const char **argv) } common_init_finish(g_ceph_context); global_init_chdir(g_ceph_context); -#ifndef BUILDING_FOR_EMBEDDED if (global_init_preload_erasure_code(g_ceph_context) < 0) prefork.exit(1); -#else - cephd_preload_embedded_plugins(); -#endif } MonitorDBStore *store = new MonitorDBStore(g_conf->mon_data); diff --git a/ceph/src/ceph_osd.cc b/ceph/src/ceph_osd.cc index 1cfda9c1d..b2c845065 100644 --- a/ceph/src/ceph_osd.cc +++ b/ceph/src/ceph_osd.cc @@ -96,13 +96,7 @@ static void usage() generic_server_usage(); } -#ifdef BUILDING_FOR_EMBEDDED -void cephd_preload_embedded_plugins(); -void cephd_preload_rados_classes(OSD *osd); -extern "C" int cephd_osd(int argc, const char **argv) -#else int main(int argc, const char **argv) -#endif { vector args; argv_to_vec(argc, argv, args); @@ -262,9 +256,6 @@ int main(int argc, const char **argv) return -ENODEV; } -#ifdef BUILDING_FOR_EMBEDDED - cephd_preload_embedded_plugins(); -#endif if (mkkey) { common_init_finish(g_ceph_context); @@ -594,10 +585,8 @@ flushjournal_out: return -1; global_init_chdir(g_ceph_context); -#ifndef BUILDING_FOR_EMBEDDED if (global_init_preload_erasure_code(g_ceph_context) < 0) return -1; -#endif srand(time(NULL) + getpid()); @@ -638,10 +627,6 @@ flushjournal_out: return 1; } -#ifdef BUILDING_FOR_EMBEDDED - cephd_preload_rados_classes(osd); -#endif - // install signal handlers init_async_signal_handler(); register_async_signal_handler(SIGHUP, sighup_handler); diff --git a/ceph/src/client/Client.cc b/ceph/src/client/Client.cc index 41eff1b4b..7043f9058 100644 --- a/ceph/src/client/Client.cc +++ b/ceph/src/client/Client.cc @@ -448,6 +448,10 @@ void Client::dump_status(Formatter *f) f->dump_int("dentry_count", lru.lru_get_size()); f->dump_int("dentry_pinned_count", lru.lru_get_num_pinned()); f->dump_int("id", get_nodeid().v); + entity_inst_t inst(messenger->get_myname(), messenger->get_myaddr()); + f->dump_object("inst", inst); + f->dump_stream("inst_str") << inst; + f->dump_stream("addr_str") << inst.addr; f->dump_int("inode_count", inode_map.size()); f->dump_int("mds_epoch", mdsmap->get_epoch()); f->dump_int("osd_epoch", osd_epoch); @@ -674,33 +678,11 @@ void Client::trim_dentry(Dentry *dn) } -void Client::update_inode_file_bits(Inode *in, - uint64_t truncate_seq, uint64_t truncate_size, - uint64_t size, uint64_t change_attr, - uint64_t time_warp_seq, utime_t ctime, - utime_t mtime, - utime_t atime, - version_t inline_version, - bufferlist& inline_data, - int issued) +void Client::update_inode_file_size(Inode *in, int issued, uint64_t size, + uint64_t truncate_seq, uint64_t truncate_size) { - bool warn = false; - ldout(cct, 10) << "update_inode_file_bits " << *in << " " << ccap_string(issued) - << " mtime " << mtime << dendl; - ldout(cct, 25) << "truncate_seq: mds " << truncate_seq << " local " - << in->truncate_seq << " time_warp_seq: mds " << time_warp_seq - << " local " << in->time_warp_seq << dendl; uint64_t prior_size = in->size; - if (inline_version > in->inline_version) { - in->inline_data = inline_data; - in->inline_version = inline_version; - } - - /* always take a newer change attr */ - if (change_attr > in->change_attr) - in->change_attr = change_attr; - if (truncate_seq > in->truncate_seq || (truncate_seq == in->truncate_seq && size > in->size)) { ldout(cct, 10) << "size " << in->size << " -> " << size << dendl; @@ -736,7 +718,20 @@ void Client::update_inode_file_bits(Inode *in, ldout(cct, 0) << "Hmmm, truncate_seq && truncate_size changed on non-file inode!" << dendl; } } - +} + +void Client::update_inode_file_time(Inode *in, int issued, uint64_t time_warp_seq, + utime_t ctime, utime_t mtime, utime_t atime) +{ + ldout(cct, 10) << __func__ << " " << *in << " " << ccap_string(issued) + << " ctime " << ctime << " mtime " << mtime << dendl; + + if (time_warp_seq > in->time_warp_seq) + ldout(cct, 10) << " mds time_warp_seq " << time_warp_seq + << " is higher than local time_warp_seq " + << in->time_warp_seq << dendl; + + int warn = false; // be careful with size, mtime, atime if (issued & (CEPH_CAP_FILE_EXCL| CEPH_CAP_FILE_WR| @@ -747,9 +742,6 @@ void Client::update_inode_file_bits(Inode *in, if (ctime > in->ctime) in->ctime = ctime; if (time_warp_seq > in->time_warp_seq) { - ldout(cct, 10) << "mds time_warp_seq " << time_warp_seq << " on inode " << *in - << " is higher than local time_warp_seq " - << in->time_warp_seq << dendl; //the mds updated times, so take those! in->mtime = mtime; in->atime = atime; @@ -834,53 +826,59 @@ Inode * Client::add_update_inode(InodeStat *st, utime_t from, if (in->is_symlink()) in->symlink = st->symlink; - if (was_new) - ldout(cct, 12) << "add_update_inode adding " << *in << " caps " << ccap_string(st->cap.caps) << dendl; - - if (!st->cap.caps) - return in; // as with readdir returning indoes in different snaprealms (no caps!) - // only update inode if mds info is strictly newer, or it is the same and projected (odd). - bool updating_inode = false; - int issued = 0; - if (st->version == 0 || - (in->version & ~1) < st->version) { - updating_inode = true; + bool new_version = false; + if (in->version == 0 || + ((st->cap.flags & CEPH_CAP_FLAG_AUTH) && + (in->version & ~1) < st->version)) + new_version = true; - int implemented = 0; - issued = in->caps_issued(&implemented) | in->caps_dirty(); - issued |= implemented; + int issued; + in->caps_issued(&issued); + issued |= in->caps_dirty(); + int new_issued = ~issued & (int)st->cap.caps; - in->version = st->version; + if ((new_version || (new_issued & CEPH_CAP_AUTH_SHARED)) && + !(issued & CEPH_CAP_AUTH_EXCL)) { + in->mode = st->mode; + in->uid = st->uid; + in->gid = st->gid; + in->btime = st->btime; + } - if ((issued & CEPH_CAP_AUTH_EXCL) == 0) { - in->mode = st->mode; - in->uid = st->uid; - in->gid = st->gid; - in->btime = st->btime; - } + if ((new_version || (new_issued & CEPH_CAP_LINK_SHARED)) && + !(issued & CEPH_CAP_LINK_EXCL)) { + in->nlink = st->nlink; + } - if ((issued & CEPH_CAP_LINK_EXCL) == 0) { - in->nlink = st->nlink; - } + if (new_version || (new_issued & CEPH_CAP_ANY_RD)) { + update_inode_file_time(in, issued, st->time_warp_seq, + st->ctime, st->mtime, st->atime); + } - in->dirstat = st->dirstat; - in->rstat = st->rstat; - in->quota = st->quota; + if (new_version || + (new_issued & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR))) { in->layout = st->layout; + update_inode_file_size(in, issued, st->size, st->truncate_seq, st->truncate_size); + } - if (in->is_dir()) { + if (in->is_dir()) { + if (new_version || (new_issued & CEPH_CAP_FILE_SHARED)) { + in->dirstat = st->dirstat; + } + // dir_layout/rstat/quota are not tracked by capability, update them only if + // the inode stat is from auth mds + if (new_version || (st->cap.flags & CEPH_CAP_FLAG_AUTH)) { in->dir_layout = st->dir_layout; ldout(cct, 20) << " dir hash is " << (int)in->dir_layout.dl_dir_hash << dendl; + in->rstat = st->rstat; + in->quota = st->quota; + } + // move me if/when version reflects fragtree changes. + if (in->dirfragtree != st->dirfragtree) { + in->dirfragtree = st->dirfragtree; + _fragmap_remove_non_leaves(in); } - - update_inode_file_bits(in, st->truncate_seq, st->truncate_size, st->size, - st->change_attr, st->time_warp_seq, st->ctime, - st->mtime, st->atime, st->inline_version, - st->inline_data, issued); - } else if (st->inline_version > in->inline_version) { - in->inline_data = st->inline_data; - in->inline_version = st->inline_version; } if ((in->xattr_version == 0 || !(issued & CEPH_CAP_XATTR_EXCL)) && @@ -891,12 +889,24 @@ Inode * Client::add_update_inode(InodeStat *st, utime_t from, in->xattr_version = st->xattr_version; } - // move me if/when version reflects fragtree changes. - if (in->dirfragtree != st->dirfragtree) { - in->dirfragtree = st->dirfragtree; - _fragmap_remove_non_leaves(in); + if (st->inline_version > in->inline_version) { + in->inline_data = st->inline_data; + in->inline_version = st->inline_version; } + /* always take a newer change attr */ + if (st->change_attr > in->change_attr) + in->change_attr = st->change_attr; + + if (st->version > in->version) + in->version = st->version; + + if (was_new) + ldout(cct, 12) << __func__ << " adding " << *in << " caps " << ccap_string(st->cap.caps) << dendl; + + if (!st->cap.caps) + return in; // as with readdir returning indoes in different snaprealms (no caps!) + if (in->snapid == CEPH_NOSNAP) { add_update_cap(in, session, st->cap.cap_id, st->cap.caps, st->cap.seq, st->cap.mseq, inodeno_t(st->cap.realm), st->cap.flags, @@ -905,30 +915,28 @@ Inode * Client::add_update_inode(InodeStat *st, utime_t from, in->max_size = st->max_size; in->rstat = st->rstat; } - } else - in->snap_caps |= st->cap.caps; - // setting I_COMPLETE needs to happen after adding the cap - if (updating_inode && - in->is_dir() && - (st->cap.caps & CEPH_CAP_FILE_SHARED) && - (issued & CEPH_CAP_FILE_EXCL) == 0 && - in->dirstat.nfiles == 0 && - in->dirstat.nsubdirs == 0) { - ldout(cct, 10) << " marking (I_COMPLETE|I_DIR_ORDERED) on empty dir " << *in << dendl; - in->flags |= I_COMPLETE | I_DIR_ORDERED; - if (in->dir) { - ldout(cct, 10) << " dir is open on empty dir " << in->ino << " with " - << in->dir->dentries.size() << " entries, marking all dentries null" << dendl; - in->dir->readdir_cache.clear(); - for (auto p = in->dir->dentries.begin(); - p != in->dir->dentries.end(); - ++p) { - unlink(p->second, true, true); // keep dir, keep dentry + // setting I_COMPLETE needs to happen after adding the cap + if (in->is_dir() && + (st->cap.caps & CEPH_CAP_FILE_SHARED) && + (issued & CEPH_CAP_FILE_EXCL) == 0 && + in->dirstat.nfiles == 0 && + in->dirstat.nsubdirs == 0) { + ldout(cct, 10) << " marking (I_COMPLETE|I_DIR_ORDERED) on empty dir " << *in << dendl; + in->flags |= I_COMPLETE | I_DIR_ORDERED; + if (in->dir) { + ldout(cct, 10) << " dir is open on empty dir " << in->ino << " with " + << in->dir->dentries.size() << " entries, marking all dentries null" << dendl; + in->dir->readdir_cache.clear(); + for (const auto& p : in->dir->dentries) { + unlink(p.second, true, true); // keep dir, keep dentry + } + if (in->dir->dentries.empty()) + close_dir(in->dir); } - if (in->dir->dentries.empty()) - close_dir(in->dir); } + } else { + in->snap_caps |= st->cap.caps; } return in; @@ -1506,6 +1514,10 @@ void Client::connect_mds_targets(mds_rank_t mds) void Client::dump_mds_sessions(Formatter *f) { f->dump_int("id", get_nodeid().v); + entity_inst_t inst(messenger->get_myname(), messenger->get_myaddr()); + f->dump_object("inst", inst); + f->dump_stream("inst_str") << inst; + f->dump_stream("addr_str") << inst.addr; f->open_array_section("sessions"); for (map::const_iterator p = mds_sessions.begin(); p != mds_sessions.end(); ++p) { f->open_object_section("session"); @@ -3794,7 +3806,7 @@ bool Client::_flush(Inode *in, Context *onfinish) } if (objecter->osdmap_pool_full(in->layout.pool_id)) { - ldout(cct, 1) << __func__ << ": FULL, purging for ENOSPC" << dendl; + ldout(cct, 8) << __func__ << ": FULL, purging for ENOSPC" << dendl; objectcacher->purge_set(&in->oset); if (onfinish) { onfinish->complete(-ENOSPC); @@ -4839,13 +4851,11 @@ void Client::handle_cap_trunc(MetaSession *session, Inode *in, MClientCaps *m) << " size " << in->size << " -> " << m->get_size() << dendl; - int implemented = 0; - int issued = in->caps_issued(&implemented) | in->caps_dirty(); - issued |= implemented; - update_inode_file_bits(in, m->get_truncate_seq(), m->get_truncate_size(), - m->get_size(), m->get_change_attr(), m->get_time_warp_seq(), - m->get_ctime(), m->get_mtime(), m->get_atime(), - m->inline_version, m->inline_data, issued); + int issued; + in->caps_issued(&issued); + issued |= in->caps_dirty(); + update_inode_file_size(in, issued, m->get_size(), + m->get_truncate_seq(), m->get_truncate_size()); m->put(); } @@ -5041,27 +5051,27 @@ void Client::handle_cap_grant(MetaSession *session, Inode *in, Cap *cap, MClient cap->seq = m->get_seq(); cap->gen = session->cap_gen; - in->layout = m->get_layout(); - // update inode - int implemented = 0; - int issued = in->caps_issued(&implemented) | in->caps_dirty(); - issued |= implemented; + int issued; + in->caps_issued(&issued); + issued |= in->caps_dirty(); - if ((issued & CEPH_CAP_AUTH_EXCL) == 0) { + if ((new_caps & CEPH_CAP_AUTH_SHARED) && + !(issued & CEPH_CAP_AUTH_EXCL)) { in->mode = m->head.mode; in->uid = m->head.uid; in->gid = m->head.gid; in->btime = m->btime; } bool deleted_inode = false; - if ((issued & CEPH_CAP_LINK_EXCL) == 0) { + if ((new_caps & CEPH_CAP_LINK_SHARED) && + !(issued & CEPH_CAP_LINK_EXCL)) { in->nlink = m->head.nlink; if (in->nlink == 0 && (new_caps & (CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL))) deleted_inode = true; } - if ((issued & CEPH_CAP_XATTR_EXCL) == 0 && + if (!(issued & CEPH_CAP_XATTR_EXCL) && m->xattrbl.length() && m->head.xattr_version > in->xattr_version) { bufferlist::iterator p = m->xattrbl.begin(); @@ -5074,14 +5084,30 @@ void Client::handle_cap_grant(MetaSession *session, Inode *in, Cap *cap, MClient in->dirstat.nsubdirs = m->get_nsubdirs(); } - update_inode_file_bits(in, m->get_truncate_seq(), m->get_truncate_size(), m->get_size(), - m->get_change_attr(), m->get_time_warp_seq(), m->get_ctime(), - m->get_mtime(), m->get_atime(), - m->inline_version, m->inline_data, issued); + if (new_caps & CEPH_CAP_ANY_RD) { + update_inode_file_time(in, issued, m->get_time_warp_seq(), + m->get_ctime(), m->get_mtime(), m->get_atime()); + } + + if (new_caps & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR)) { + in->layout = m->get_layout(); + update_inode_file_size(in, issued, m->get_size(), + m->get_truncate_seq(), m->get_truncate_size()); + } + + if (m->inline_version > in->inline_version) { + in->inline_data = m->inline_data; + in->inline_version = m->inline_version; + } + + /* always take a newer change attr */ + if (m->get_change_attr() > in->change_attr) + in->change_attr = m->get_change_attr(); // max_size if (cap == in->auth_cap && - m->get_max_size() != in->max_size) { + (new_caps & CEPH_CAP_ANY_FILE_WR) && + (m->get_max_size() != in->max_size)) { ldout(cct, 10) << "max_size " << in->max_size << " -> " << m->get_max_size() << dendl; in->max_size = m->get_max_size(); if (in->max_size > in->wanted_max_size) { @@ -5185,7 +5211,7 @@ int Client::xattr_permission(Inode *in, const char *name, unsigned want, r = inode_permission(in, perms, want); } out: - ldout(cct, 3) << __func__ << " " << in << " = " << r << dendl; + ldout(cct, 5) << __func__ << " " << in << " = " << r << dendl; return r; } @@ -7441,7 +7467,7 @@ int Client::_opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms) return -ENOTDIR; *dirpp = new dir_result_t(in, perms); opened_dirs.insert(*dirpp); - ldout(cct, 3) << "_opendir(" << in->ino << ") = " << 0 << " (" << *dirpp << ")" << dendl; + ldout(cct, 8) << "_opendir(" << in->ino << ") = " << 0 << " (" << *dirpp << ")" << dendl; return 0; } @@ -8243,10 +8269,9 @@ int Client::lookup_hash(inodeno_t ino, inodeno_t dirino, const char *name, * the resulting Inode object in one operation, so that caller * can safely assume inode will still be there after return. */ -int Client::lookup_ino(inodeno_t ino, const UserPerm& perms, Inode **inode) +int Client::_lookup_ino(inodeno_t ino, const UserPerm& perms, Inode **inode) { - Mutex::Locker lock(client_lock); - ldout(cct, 3) << "lookup_ino enter(" << ino << ")" << dendl; + ldout(cct, 8) << "lookup_ino enter(" << ino << ")" << dendl; if (unmounting) return -ENOTCONN; @@ -8263,21 +8288,24 @@ int Client::lookup_ino(inodeno_t ino, const UserPerm& perms, Inode **inode) *inode = p->second; _ll_get(*inode); } - ldout(cct, 3) << "lookup_ino exit(" << ino << ") = " << r << dendl; + ldout(cct, 8) << "lookup_ino exit(" << ino << ") = " << r << dendl; return r; } - +int Client::lookup_ino(inodeno_t ino, const UserPerm& perms, Inode **inode) +{ + Mutex::Locker lock(client_lock); + return _lookup_ino(ino, perms, inode); +} /** * Find the parent inode of `ino` and insert it into * our cache. Conditionally also set `parent` to a referenced * Inode* if caller provides non-NULL value. */ -int Client::lookup_parent(Inode *ino, const UserPerm& perms, Inode **parent) +int Client::_lookup_parent(Inode *ino, const UserPerm& perms, Inode **parent) { - Mutex::Locker lock(client_lock); - ldout(cct, 3) << "lookup_parent enter(" << ino->ino << ")" << dendl; + ldout(cct, 8) << "lookup_parent enter(" << ino->ino << ")" << dendl; if (unmounting) return -ENOTCONN; @@ -8285,13 +8313,13 @@ int Client::lookup_parent(Inode *ino, const UserPerm& perms, Inode **parent) if (!ino->dn_set.empty()) { // if we exposed the parent here, we'd need to check permissions, // but right now we just rely on the MDS doing so in make_request - ldout(cct, 3) << "lookup_parent dentry already present" << dendl; + ldout(cct, 8) << "lookup_parent dentry already present" << dendl; return 0; } if (ino->is_root()) { *parent = NULL; - ldout(cct, 3) << "ino is root, no parent" << dendl; + ldout(cct, 8) << "ino is root, no parent" << dendl; return -EINVAL; } @@ -8306,25 +8334,28 @@ int Client::lookup_parent(Inode *ino, const UserPerm& perms, Inode **parent) if (r == 0) { *parent = target.get(); _ll_get(*parent); - ldout(cct, 3) << "lookup_parent found parent " << (*parent)->ino << dendl; + ldout(cct, 8) << "lookup_parent found parent " << (*parent)->ino << dendl; } else { *parent = NULL; } } - ldout(cct, 3) << "lookup_parent exit(" << ino->ino << ") = " << r << dendl; + ldout(cct, 8) << "lookup_parent exit(" << ino->ino << ") = " << r << dendl; return r; } +int Client::lookup_parent(Inode *ino, const UserPerm& perms, Inode **parent) +{ + Mutex::Locker lock(client_lock); + return _lookup_parent(ino, perms, parent); +} /** * Populate the parent dentry for `ino`, provided it is * a child of `parent`. */ -int Client::lookup_name(Inode *ino, Inode *parent, const UserPerm& perms) +int Client::_lookup_name(Inode *ino, Inode *parent, const UserPerm& perms) { assert(parent->is_dir()); - - Mutex::Locker lock(client_lock); ldout(cct, 3) << "lookup_name enter(" << ino->ino << ")" << dendl; if (unmounting) @@ -8340,6 +8371,11 @@ int Client::lookup_name(Inode *ino, Inode *parent, const UserPerm& perms) return r; } +int Client::lookup_name(Inode *ino, Inode *parent, const UserPerm& perms) +{ + Mutex::Locker lock(client_lock); + return _lookup_name(ino, parent, perms); +} Fh *Client::_create_fh(Inode *in, int flags, int cmode, const UserPerm& perms) { @@ -8383,7 +8419,7 @@ int Client::_release_fh(Fh *f) //ldout(cct, 3) << "op: client->close(open_files[ " << fh << " ]);" << dendl; //ldout(cct, 3) << "op: open_files.erase( " << fh << " );" << dendl; Inode *in = f->inode.get(); - ldout(cct, 5) << "_release_fh " << f << " mode " << f->mode << " on " << *in << dendl; + ldout(cct, 8) << "_release_fh " << f << " mode " << f->mode << " on " << *in << dendl; in->unset_deleg(f); @@ -8477,7 +8513,7 @@ int Client::_open(Inode *in, int flags, mode_t mode, Fh **fhp, result = get_caps(in, need, want, &have, -1); if (result < 0) { - ldout(cct, 1) << "Unable to get caps after open of inode " << *in << + ldout(cct, 8) << "Unable to get caps after open of inode " << *in << " . Denying open: " << cpp_strerror(result) << dendl; in->put_open_ref(cmode); @@ -8608,7 +8644,7 @@ loff_t Client::_lseek(Fh *f, loff_t offset, int whence) ceph_abort(); } - ldout(cct, 3) << "_lseek(" << f << ", " << offset << ", " << whence << ") = " << f->pos << dendl; + ldout(cct, 8) << "_lseek(" << f << ", " << offset << ", " << whence << ") = " << f->pos << dendl; return f->pos; } @@ -9416,14 +9452,14 @@ int Client::fsync(int fd, bool syncdataonly) // The IOs in this fsync were okay, but maybe something happened // in the background that we shoudl be reporting? r = f->take_async_err(); - ldout(cct, 3) << "fsync(" << fd << ", " << syncdataonly + ldout(cct, 5) << "fsync(" << fd << ", " << syncdataonly << ") = 0, async_err = " << r << dendl; } else { // Assume that an error we encountered during fsync, even reported // synchronously, would also have applied the error to the Fh, and we // should clear it here to avoid returning the same error again on next // call. - ldout(cct, 3) << "fsync(" << fd << ", " << syncdataonly << ") = " + ldout(cct, 5) << "fsync(" << fd << ", " << syncdataonly << ") = " << r << dendl; f->take_async_err(); } @@ -9440,7 +9476,7 @@ int Client::_fsync(Inode *in, bool syncdataonly) ceph_tid_t flush_tid = 0; InodeRef tmp_ref; - ldout(cct, 3) << "_fsync on " << *in << " " << (syncdataonly ? "(dataonly)":"(data+metadata)") << dendl; + ldout(cct, 8) << "_fsync on " << *in << " " << (syncdataonly ? "(dataonly)":"(data+metadata)") << dendl; if (cct->_conf->client_oc) { object_cacher_completion = new C_SafeCond(&lock, &cond, &done, &r); @@ -9490,7 +9526,7 @@ int Client::_fsync(Inode *in, bool syncdataonly) ldout(cct, 10) << "ino " << in->ino << " has no uncommitted writes" << dendl; } else { - ldout(cct, 1) << "ino " << in->ino << " failed to commit to disk! " + ldout(cct, 8) << "ino " << in->ino << " failed to commit to disk! " << cpp_strerror(-r) << dendl; } @@ -9499,7 +9535,7 @@ int Client::_fsync(Inode *in, bool syncdataonly) int Client::_fsync(Fh *f, bool syncdataonly) { - ldout(cct, 3) << "_fsync(" << f << ", " << (syncdataonly ? "dataonly)":"data+metadata)") << dendl; + ldout(cct, 8) << "_fsync(" << f << ", " << (syncdataonly ? "dataonly)":"data+metadata)") << dendl; return _fsync(f->inode.get(), syncdataonly); } @@ -9519,7 +9555,7 @@ int Client::fstat(int fd, struct stat *stbuf, const UserPerm& perms, int mask) if (r < 0) return r; fill_stat(f->inode, stbuf, NULL); - ldout(cct, 3) << "fstat(" << fd << ", " << stbuf << ") = " << r << dendl; + ldout(cct, 5) << "fstat(" << fd << ", " << stbuf << ") = " << r << dendl; return r; } @@ -10325,6 +10361,51 @@ int Client::ll_lookup(Inode *parent, const char *name, struct stat *attr, return r; } +int Client::ll_lookup_inode( + struct inodeno_t ino, + const UserPerm& perms, + Inode **inode) +{ + Mutex::Locker lock(client_lock); + ldout(cct, 3) << "ll_lookup_inode " << ino << dendl; + + // Num1: get inode and *inode + int r = _lookup_ino(ino, perms, inode); + if (r) { + return r; + } + assert(inode != NULL); + assert(*inode != NULL); + + // Num2: Request the parent inode, so that we can look up the name + Inode *parent; + r = _lookup_parent(*inode, perms, &parent); + if (r && r != -EINVAL) { + // Unexpected error + _ll_forget(*inode, 1); + return r; + } else if (r == -EINVAL) { + // EINVAL indicates node without parents (root), drop out now + // and don't try to look up the non-existent dentry. + return 0; + } + // FIXME: I don't think this works; lookup_parent() returns 0 if the parent + // is already in cache + assert(parent != NULL); + + // Num3: Finally, get the name (dentry) of the requested inode + r = _lookup_name(*inode, parent, perms); + if (r) { + // Unexpected error + _ll_forget(parent, 1); + _ll_forget(*inode, 1); + return r; + } + + _ll_forget(parent, 1); + return 0; +} + int Client::ll_lookupx(Inode *parent, const char *name, Inode **out, struct ceph_statx *stx, unsigned want, unsigned flags, const UserPerm& perms) @@ -10431,6 +10512,7 @@ int Client::_ll_put(Inode *in, int num) void Client::_ll_drop_pins() { ldout(cct, 10) << "_ll_drop_pins" << dendl; + std::set to_be_put; //this set will be deconstructed item by item when exit ceph::unordered_map::iterator next; for (ceph::unordered_map::iterator it = inode_map.begin(); it != inode_map.end(); @@ -10438,17 +10520,18 @@ void Client::_ll_drop_pins() Inode *in = it->second; next = it; ++next; - if (in->ll_ref) + if (in->ll_ref){ + to_be_put.insert(in); _ll_put(in, in->ll_ref); + } } } -bool Client::ll_forget(Inode *in, int count) +bool Client::_ll_forget(Inode *in, int count) { - Mutex::Locker lock(client_lock); inodeno_t ino = _get_inodeno(in); - ldout(cct, 3) << "ll_forget " << ino << " " << count << dendl; + ldout(cct, 8) << "ll_forget " << ino << " " << count << dendl; tout(cct) << "ll_forget" << std::endl; tout(cct) << ino.val << std::endl; tout(cct) << count << std::endl; @@ -10473,6 +10556,12 @@ bool Client::ll_forget(Inode *in, int count) return last; } +bool Client::ll_forget(Inode *in, int count) +{ + Mutex::Locker lock(client_lock); + return _ll_forget(in, count); +} + bool Client::ll_put(Inode *in) { /* ll_forget already takes the lock */ @@ -10520,7 +10609,7 @@ int Client::_ll_getattr(Inode *in, int caps, const UserPerm& perms) { vinodeno_t vino = _get_vino(in); - ldout(cct, 3) << "ll_getattr " << vino << dendl; + ldout(cct, 8) << "ll_getattr " << vino << dendl; tout(cct) << "ll_getattr" << std::endl; tout(cct) << vino.ino.val << std::endl; @@ -10570,7 +10659,7 @@ int Client::_ll_setattrx(Inode *in, struct ceph_statx *stx, int mask, { vinodeno_t vino = _get_vino(in); - ldout(cct, 3) << "ll_setattrx " << vino << " mask " << hex << mask << dec + ldout(cct, 8) << "ll_setattrx " << vino << " mask " << hex << mask << dec << dendl; tout(cct) << "ll_setattrx" << std::endl; tout(cct) << vino.ino.val << std::endl; @@ -10878,7 +10967,7 @@ int Client::_getxattr(Inode *in, const char *name, void *value, size_t size, } } out: - ldout(cct, 3) << "_getxattr(" << in->ino << ", \"" << name << "\", " << size << ") = " << r << dendl; + ldout(cct, 8) << "_getxattr(" << in->ino << ", \"" << name << "\", " << size << ") = " << r << dendl; return r; } @@ -10958,7 +11047,7 @@ int Client::_listxattr(Inode *in, char *name, size_t size, r = -ERANGE; } } - ldout(cct, 3) << "_listxattr(" << in->ino << ", " << size << ") = " << r << dendl; + ldout(cct, 8) << "_listxattr(" << in->ino << ", " << size << ") = " << r << dendl; return r; } @@ -11198,7 +11287,7 @@ int Client::_removexattr(Inode *in, const char *name, const UserPerm& perms) int res = make_request(req, perms); trim_cache(); - ldout(cct, 3) << "_removexattr(" << in->ino << ", \"" << name << "\") = " << res << dendl; + ldout(cct, 8) << "_removexattr(" << in->ino << ", \"" << name << "\") = " << res << dendl; return res; } @@ -11495,7 +11584,7 @@ int Client::ll_readlink(Inode *in, char *buf, size_t buflen, const UserPerm& per int Client::_mknod(Inode *dir, const char *name, mode_t mode, dev_t rdev, const UserPerm& perms, InodeRef *inp) { - ldout(cct, 3) << "_mknod(" << dir->ino << " " << name << ", 0" << oct + ldout(cct, 8) << "_mknod(" << dir->ino << " " << name << ", 0" << oct << mode << dec << ", " << rdev << ", uid " << perms.uid() << ", gid " << perms.gid() << ")" << dendl; @@ -11538,7 +11627,7 @@ int Client::_mknod(Inode *dir, const char *name, mode_t mode, dev_t rdev, trim_cache(); - ldout(cct, 3) << "mknod(" << path << ", 0" << oct << mode << dec << ") = " << res << dendl; + ldout(cct, 8) << "mknod(" << path << ", 0" << oct << mode << dec << ") = " << res << dendl; return res; fail: @@ -11627,7 +11716,7 @@ int Client::_create(Inode *dir, const char *name, int flags, mode_t mode, int object_size, const char *data_pool, bool *created, const UserPerm& perms) { - ldout(cct, 3) << "_create(" << dir->ino << " " << name << ", 0" << oct << + ldout(cct, 8) << "_create(" << dir->ino << " " << name << ", 0" << oct << mode << dec << ")" << dendl; if (strlen(name) > NAME_MAX) @@ -11703,7 +11792,7 @@ int Client::_create(Inode *dir, const char *name, int flags, mode_t mode, reply_error: trim_cache(); - ldout(cct, 3) << "create(" << path << ", 0" << oct << mode << dec + ldout(cct, 8) << "create(" << path << ", 0" << oct << mode << dec << " layout " << stripe_unit << ' ' << stripe_count << ' ' << object_size @@ -11719,7 +11808,7 @@ int Client::_create(Inode *dir, const char *name, int flags, mode_t mode, int Client::_mkdir(Inode *dir, const char *name, mode_t mode, const UserPerm& perm, InodeRef *inp) { - ldout(cct, 3) << "_mkdir(" << dir->ino << " " << name << ", 0" << oct + ldout(cct, 8) << "_mkdir(" << dir->ino << " " << name << ", 0" << oct << mode << dec << ", uid " << perm.uid() << ", gid " << perm.gid() << ")" << dendl; @@ -11764,7 +11853,7 @@ int Client::_mkdir(Inode *dir, const char *name, mode_t mode, const UserPerm& pe trim_cache(); - ldout(cct, 3) << "_mkdir(" << path << ", 0" << oct << mode << dec << ") = " << res << dendl; + ldout(cct, 8) << "_mkdir(" << path << ", 0" << oct << mode << dec << ") = " << res << dendl; return res; fail: @@ -11849,7 +11938,7 @@ int Client::ll_mkdirx(Inode *parent, const char *name, mode_t mode, Inode **out, int Client::_symlink(Inode *dir, const char *name, const char *target, const UserPerm& perms, InodeRef *inp) { - ldout(cct, 3) << "_symlink(" << dir->ino << " " << name << ", " << target + ldout(cct, 8) << "_symlink(" << dir->ino << " " << name << ", " << target << ", uid " << perms.uid() << ", gid " << perms.gid() << ")" << dendl; @@ -11883,7 +11972,7 @@ int Client::_symlink(Inode *dir, const char *name, const char *target, res = make_request(req, perms, inp); trim_cache(); - ldout(cct, 3) << "_symlink(\"" << path << "\", \"" << target << "\") = " << + ldout(cct, 8) << "_symlink(\"" << path << "\", \"" << target << "\") = " << res << dendl; return res; @@ -11967,7 +12056,7 @@ int Client::ll_symlinkx(Inode *parent, const char *name, const char *value, int Client::_unlink(Inode *dir, const char *name, const UserPerm& perm) { - ldout(cct, 3) << "_unlink(" << dir->ino << " " << name + ldout(cct, 8) << "_unlink(" << dir->ino << " " << name << " uid " << perm.uid() << " gid " << perm.gid() << ")" << dendl; @@ -12007,7 +12096,7 @@ int Client::_unlink(Inode *dir, const char *name, const UserPerm& perm) res = make_request(req, perm); trim_cache(); - ldout(cct, 3) << "unlink(" << path << ") = " << res << dendl; + ldout(cct, 8) << "unlink(" << path << ") = " << res << dendl; return res; fail: @@ -12039,7 +12128,7 @@ int Client::ll_unlink(Inode *in, const char *name, const UserPerm& perm) int Client::_rmdir(Inode *dir, const char *name, const UserPerm& perms) { - ldout(cct, 3) << "_rmdir(" << dir->ino << " " << name << " uid " + ldout(cct, 8) << "_rmdir(" << dir->ino << " " << name << " uid " << perms.uid() << " gid " << perms.gid() << ")" << dendl; if (dir->snapid != CEPH_NOSNAP && dir->snapid != CEPH_SNAPDIR) { @@ -12083,7 +12172,7 @@ int Client::_rmdir(Inode *dir, const char *name, const UserPerm& perms) res = make_request(req, perms); trim_cache(); - ldout(cct, 3) << "rmdir(" << path << ") = " << res << dendl; + ldout(cct, 8) << "rmdir(" << path << ") = " << res << dendl; return res; fail: @@ -12116,7 +12205,7 @@ int Client::ll_rmdir(Inode *in, const char *name, const UserPerm& perms) int Client::_rename(Inode *fromdir, const char *fromname, Inode *todir, const char *toname, const UserPerm& perm) { - ldout(cct, 3) << "_rename(" << fromdir->ino << " " << fromname << " to " + ldout(cct, 8) << "_rename(" << fromdir->ino << " " << fromname << " to " << todir->ino << " " << toname << " uid " << perm.uid() << " gid " << perm.gid() << ")" << dendl; @@ -12211,7 +12300,7 @@ int Client::_rename(Inode *fromdir, const char *fromname, Inode *todir, const ch // renamed item from our cache trim_cache(); - ldout(cct, 3) << "_rename(" << from << ", " << to << ") = " << res << dendl; + ldout(cct, 8) << "_rename(" << from << ", " << to << ") = " << res << dendl; return res; fail: @@ -12252,7 +12341,7 @@ int Client::ll_rename(Inode *parent, const char *name, Inode *newparent, int Client::_link(Inode *in, Inode *dir, const char *newname, const UserPerm& perm, InodeRef *inp) { - ldout(cct, 3) << "_link(" << in->ino << " to " << dir->ino << " " << newname + ldout(cct, 8) << "_link(" << in->ino << " to " << dir->ino << " " << newname << " uid " << perm.uid() << " gid " << perm.gid() << ")" << dendl; if (strlen(newname) > NAME_MAX) @@ -12287,7 +12376,7 @@ int Client::_link(Inode *in, Inode *dir, const char *newname, const UserPerm& pe ldout(cct, 10) << "link result is " << res << dendl; trim_cache(); - ldout(cct, 3) << "link(" << existing << ", " << path << ") = " << res << dendl; + ldout(cct, 8) << "link(" << existing << ", " << path << ") = " << res << dendl; return res; fail: @@ -12527,7 +12616,7 @@ int Client::_ll_create(Inode *parent, const char *name, mode_t mode, vinodeno_t vparent = _get_vino(parent); - ldout(cct, 3) << "_ll_create " << vparent << " " << name << " 0" << oct << + ldout(cct, 8) << "_ll_create " << vparent << " " << name << " 0" << oct << mode << dec << " " << ceph_flags_sys2wire(flags) << ", uid " << perms.uid() << ", gid " << perms.gid() << dendl; tout(cct) << "ll_create" << std::endl; @@ -12594,7 +12683,7 @@ out: tout(cct) << (unsigned long)*fhp << std::endl; tout(cct) << ino << std::endl; - ldout(cct, 3) << "_ll_create " << vparent << " " << name << " 0" << oct << + ldout(cct, 8) << "_ll_create " << vparent << " " << name << " 0" << oct << mode << dec << " " << ceph_flags_sys2wire(flags) << " = " << r << " (" << *fhp << " " << hex << ino << dec << ")" << dendl; diff --git a/ceph/src/client/Client.h b/ceph/src/client/Client.h index 9c076214a..2616f6d71 100644 --- a/ceph/src/client/Client.h +++ b/ceph/src/client/Client.h @@ -713,10 +713,11 @@ protected: void clear_dir_complete_and_ordered(Inode *diri, bool complete); void insert_readdir_results(MetaRequest *request, MetaSession *session, Inode *diri); Inode* insert_trace(MetaRequest *request, MetaSession *session); - void update_inode_file_bits(Inode *in, uint64_t truncate_seq, uint64_t truncate_size, uint64_t size, - uint64_t change_attr, uint64_t time_warp_seq, utime_t ctime, - utime_t mtime, utime_t atime, version_t inline_version, - bufferlist& inline_data, int issued); + void update_inode_file_size(Inode *in, int issued, uint64_t size, + uint64_t truncate_seq, uint64_t truncate_size); + void update_inode_file_time(Inode *in, int issued, uint64_t time_warp_seq, + utime_t ctime, utime_t mtime, utime_t atime); + Inode *add_update_inode(InodeStat *st, utime_t ttl, MetaSession *session, const UserPerm& request_perms); Dentry *insert_dentry_inode(Dir *dir, const string& dname, LeaseStat *dlease, @@ -945,6 +946,10 @@ private: mds_rank_t _get_random_up_mds() const; int _ll_getattr(Inode *in, int caps, const UserPerm& perms); + int _lookup_parent(Inode *in, const UserPerm& perms, Inode **parent=NULL); + int _lookup_name(Inode *in, Inode *parent, const UserPerm& perms); + int _lookup_ino(inodeno_t ino, const UserPerm& perms, Inode **inode=NULL); + bool _ll_forget(Inode *in, int count); public: int mount(const std::string &mount_root, const UserPerm& perms, @@ -1142,6 +1147,7 @@ public: Inode *ll_get_inode(vinodeno_t vino); int ll_lookup(Inode *parent, const char *name, struct stat *attr, Inode **out, const UserPerm& perms); + int ll_lookup_inode(struct inodeno_t ino, const UserPerm& perms, Inode **inode); int ll_lookupx(Inode *parent, const char *name, Inode **out, struct ceph_statx *stx, unsigned want, unsigned flags, const UserPerm& perms); diff --git a/ceph/src/client/Inode.h b/ceph/src/client/Inode.h index 614d84a30..63a7f50aa 100644 --- a/ceph/src/client/Inode.h +++ b/ceph/src/client/Inode.h @@ -255,7 +255,6 @@ struct Inode { fcntl_locks(NULL), flock_locks(NULL) { memset(&dir_layout, 0, sizeof(dir_layout)); - memset("a, 0, sizeof(quota)); } ~Inode(); diff --git a/ceph/src/cls/CMakeLists.txt b/ceph/src/cls/CMakeLists.txt index 1c36c1fd0..e4474c019 100644 --- a/ceph/src/cls/CMakeLists.txt +++ b/ceph/src/cls/CMakeLists.txt @@ -1,7 +1,6 @@ ## Rados object classes set(cls_dir ${CMAKE_INSTALL_LIBDIR}/rados-classes) -set(cls_embedded_srcs) # cls_sdk add_library(cls_sdk SHARED sdk/cls_sdk.cc) @@ -16,7 +15,6 @@ set_target_properties(cls_hello PROPERTIES SOVERSION "1" INSTALL_RPATH "") install(TARGETS cls_hello DESTINATION ${cls_dir}) -list(APPEND cls_embedded_srcs ${cls_hello_srcs}) # cls_numops set(cls_numops_srcs numops/cls_numops.cc) @@ -30,7 +28,6 @@ install(TARGETS cls_numops DESTINATION ${cls_dir}) set(cls_numops_client_srcs numops/cls_numops_client.cc) add_library(cls_numops_client STATIC ${cls_numops_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_numops_srcs} ${cls_numops_client_srcs}) # cls_rbd if (WITH_RBD) @@ -46,7 +43,6 @@ if (WITH_RBD) add_library(cls_rbd_client STATIC ${cls_rbd_client_srcs}) target_link_libraries(cls_rbd_client cls_lock_client) - list(APPEND cls_embedded_srcs ${cls_rbd_srcs} ${cls_rbd_client_srcs}) endif (WITH_RBD) # cls_lock @@ -64,7 +60,6 @@ set(cls_lock_client_srcs lock/cls_lock_ops.cc) add_library(cls_lock_client STATIC ${cls_lock_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_lock_srcs} ${cls_lock_client_srcs}) # cls_refcount set(cls_refcount_srcs @@ -84,7 +79,6 @@ set(cls_refcount_client_srcs refcount/cls_refcount_ops.cc) add_library(cls_refcount_client STATIC ${cls_refcount_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_refcount_srcs} ${cls_refcount_client_srcs}) # cls_version set(cls_version_srcs version/cls_version.cc) @@ -100,7 +94,6 @@ set(cls_version_client_srcs version/cls_version_types.cc) add_library(cls_version_client STATIC ${cls_version_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_version_srcs} ${cls_version_client_srcs}) # cls_log set(cls_log_srcs log/cls_log.cc) @@ -114,7 +107,6 @@ install(TARGETS cls_log DESTINATION ${cls_dir}) set(cls_log_client_srcs log/cls_log_client.cc) add_library(cls_log_client STATIC ${cls_log_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_log_srcs} ${cls_log_client_srcs}) # cls_statelog set(cls_statelog_srcs statelog/cls_statelog.cc) @@ -128,7 +120,6 @@ install(TARGETS cls_statelog DESTINATION ${cls_dir}) set(cls_statelog_client_srcs statelog/cls_statelog_client.cc) add_library(cls_statelog_client STATIC ${cls_statelog_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_statelog_srcs} ${cls_statelog_client_srcs}) # cls_timeindex set(cls_timeindex_srcs timeindex/cls_timeindex.cc) @@ -142,7 +133,6 @@ install(TARGETS cls_timeindex DESTINATION ${cls_dir}) set(cls_timeindex_client_srcs timeindex/cls_timeindex_client.cc) add_library(cls_timeindex_client STATIC ${cls_timeindex_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_timeindex_srcs} ${cls_timeindex_client_srcs}) # cls_replica_log set(cls_replica_log_srcs replica_log/cls_replica_log.cc) @@ -176,7 +166,6 @@ set(cls_user_client_srcs user/cls_user_ops.cc) add_library(cls_user_client STATIC ${cls_user_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_user_srcs} ${cls_user_client_srcs}) # cls_journal set(cls_journal_srcs @@ -194,7 +183,6 @@ set(cls_journal_client_srcs journal/cls_journal_types.cc) add_library(cls_journal_client STATIC ${cls_journal_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_journal_srcs} ${cls_journal_client_srcs}) # cls_rgw if (WITH_RADOSGW) @@ -217,7 +205,6 @@ if (WITH_RADOSGW) rgw/cls_rgw_ops.cc) add_library(cls_rgw_client STATIC ${cls_rgw_client_srcs}) - list(APPEND cls_embedded_srcs ${cls_rgw_srcs} ${cls_rgw_client_srcs}) endif (WITH_RADOSGW) # cls_cephfs @@ -235,7 +222,6 @@ if (WITH_CEPHFS) cephfs/cls_cephfs_client.cc) add_library(cls_cephfs_client STATIC ${cls_cephfs_client_srcs}) - list(APPEND cls_embedded_srcs ${cls_cephfs_srcs} ${cls_cephfs_client_srcs}) endif (WITH_CEPHFS) # cls_lua @@ -256,15 +242,3 @@ set(cls_lua_client_srcs lua/cls_lua_client.cc) add_library(cls_lua_client STATIC ${cls_lua_client_srcs}) -list(APPEND cls_embedded_srcs ${cls_lua_srcs} ${cls_lua_client_srcs}) - -if(WITH_EMBEDDED) - include(MergeStaticLibraries) - list(REMOVE_DUPLICATES cls_embedded_srcs) - add_library(cephd_cls_base STATIC ${cls_embedded_srcs}) - # while not necessary this seems to bring in the lua's include directories - # so that cls_lua srcs build correctly - target_link_libraries(cephd_cls_base liblua) - set_target_properties(cephd_cls_base PROPERTIES COMPILE_DEFINITIONS BUILDING_FOR_EMBEDDED) - merge_static_libraries(cephd_cls cephd_cls_base liblua) -endif() diff --git a/ceph/src/cls/rgw/cls_rgw.cc b/ceph/src/cls/rgw/cls_rgw.cc index d7deb78f8..cc47818a8 100644 --- a/ceph/src/cls/rgw/cls_rgw.cc +++ b/ceph/src/cls/rgw/cls_rgw.cc @@ -154,43 +154,37 @@ static int get_obj_vals(cls_method_context_t hctx, const string& start, const st if (pkeys->empty()) return 0; - map::reverse_iterator last_element = pkeys->rbegin(); + auto last_element = pkeys->rbegin(); if ((unsigned char)last_element->first[0] < BI_PREFIX_CHAR) { /* nothing to see here, move along */ return 0; } - map::iterator first_element = pkeys->begin(); + auto first_element = pkeys->begin(); if ((unsigned char)first_element->first[0] > BI_PREFIX_CHAR) { return 0; } /* let's rebuild the list, only keep entries we're interested in */ - map old_keys; - old_keys.swap(*pkeys); + auto comp = [](const pair& l, const string &r) { return l.first < r; }; + string new_start = {static_cast(BI_PREFIX_CHAR + 1)}; - for (map::iterator iter = old_keys.begin(); iter != old_keys.end(); ++iter) { - if ((unsigned char)iter->first[0] != BI_PREFIX_CHAR) { - (*pkeys)[iter->first] = iter->second; - } - } + auto lower = pkeys->lower_bound(string{static_cast(BI_PREFIX_CHAR)}); + auto upper = std::lower_bound(lower, pkeys->end(), new_start, comp); + pkeys->erase(lower, upper); if (num_entries == (int)pkeys->size()) return 0; map new_keys; - char c[] = { (char)(BI_PREFIX_CHAR + 1), 0 }; - string new_start = c; /* now get some more keys */ ret = cls_cxx_map_get_vals(hctx, new_start, filter_prefix, num_entries - pkeys->size(), &new_keys, pmore); if (ret < 0) return ret; - for (map::iterator iter = new_keys.begin(); iter != new_keys.end(); ++iter) { - (*pkeys)[iter->first] = iter->second; - } - + pkeys->insert(std::make_move_iterator(new_keys.begin()), + std::make_move_iterator(new_keys.end())); return 0; } @@ -1905,6 +1899,10 @@ int rgw_dir_suggest_changes(cls_method_context_t hctx, if (ret < 0 && ret != -ENOENT) return -EINVAL; + if (ret == -ENOENT) { + continue; + } + if (cur_disk_bl.length()) { bufferlist::iterator cur_disk_iter = cur_disk_bl.begin(); try { @@ -1959,18 +1957,6 @@ int rgw_dir_suggest_changes(cls_method_context_t hctx, } break; case CEPH_RGW_UPDATE: - if (!cur_disk.exists) { - // this update would only have been sent by the rgw client - // if the rgw_bucket_dir_entry existed, however between that - // check and now the entry has diappeared, so we were likely - // in the midst of a delete op, and we will not recreate the - // entry - CLS_LOG(10, - "CEPH_RGW_UPDATE not applied because rgw_bucket_dir_entry" - " no longer exists\n"); - break; - } - CLS_LOG(10, "CEPH_RGW_UPDATE name=%s instance=%s total_entries: %" PRId64 " -> %" PRId64 "\n", cur_change.key.name.c_str(), cur_change.key.instance.c_str(), stats.num_entries, stats.num_entries + 1); @@ -2555,7 +2541,7 @@ static int rgw_bi_list_op(cls_method_context_t hctx, bufferlist *in, bufferlist } op_ret.is_truncated = (count >= max) || more; - while (count >= max) { + while (count > max) { op_ret.entries.pop_back(); count--; } @@ -3326,8 +3312,11 @@ static int gc_iterate_entries(cls_method_context_t hctx, const string& marker, b CLS_LOG(10, "gc_iterate_entries key=%s\n", key.c_str()); - if (!end_key.empty() && key.compare(end_key) >= 0) + if (!end_key.empty() && key.compare(end_key) >= 0) { + if (truncated) + *truncated = false; return 0; + } if (!key_in_index(key, GC_OBJ_TIME_INDEX)) return 0; diff --git a/ceph/src/cls/rgw/cls_rgw_ops.h b/ceph/src/cls/rgw/cls_rgw_ops.h index 07ebeea12..52b37c55c 100644 --- a/ceph/src/cls/rgw/cls_rgw_ops.h +++ b/ceph/src/cls/rgw/cls_rgw_ops.h @@ -413,8 +413,7 @@ struct rgw_cls_list_op }; WRITE_CLASS_ENCODER(rgw_cls_list_op) -struct rgw_cls_list_ret -{ +struct rgw_cls_list_ret { rgw_bucket_dir dir; bool is_truncated; diff --git a/ceph/src/cls/rgw/cls_rgw_types.cc b/ceph/src/cls/rgw/cls_rgw_types.cc index 191723562..51ee342e9 100644 --- a/ceph/src/cls/rgw/cls_rgw_types.cc +++ b/ceph/src/cls/rgw/cls_rgw_types.cc @@ -613,7 +613,21 @@ void cls_rgw_reshard_entry::generate_test_instances(list void cls_rgw_bucket_instance_entry::dump(Formatter *f) const { - encode_json("reshard_status", (int)reshard_status, f); + string status_str; + switch(reshard_status) { + case CLS_RGW_RESHARD_NONE: + status_str= "none"; + break; + case CLS_RGW_RESHARD_IN_PROGRESS: + status_str = "in-progress"; + break; + case CLS_RGW_RESHARD_DONE: + status_str = "done"; + break; + default: + status_str = "invalid"; + } + encode_json("reshard_status", status_str, f); encode_json("new_bucket_instance_id", new_bucket_instance_id, f); encode_json("num_shards", num_shards, f); diff --git a/ceph/src/common/DecayCounter.cc b/ceph/src/common/DecayCounter.cc index 26a552d43..823249487 100644 --- a/ceph/src/common/DecayCounter.cc +++ b/ceph/src/common/DecayCounter.cc @@ -38,6 +38,7 @@ void DecayCounter::decode(const utime_t &t, bufferlist::iterator &p) ::decode(val, p); ::decode(delta, p); ::decode(vel, p); + last_decay = t; DECODE_FINISH(p); } diff --git a/ceph/src/common/TrackedOp.cc b/ceph/src/common/TrackedOp.cc index bd605e54c..4ed2fa48b 100644 --- a/ceph/src/common/TrackedOp.cc +++ b/ceph/src/common/TrackedOp.cc @@ -341,6 +341,10 @@ bool OpTracker::check_ops_in_flight(std::vector &warning_vector, int *sl auto i = sdata->ops_in_flight_sharded.begin(); while (i != sdata->ops_in_flight_sharded.end() && i->get_initiated() < too_old) { + + if (!i->warn_interval_multiplier) + continue; + (*slow)++; // exponential backoff of warning intervals diff --git a/ceph/src/common/TrackedOp.h b/ceph/src/common/TrackedOp.h index 9435a1913..dea88df4c 100644 --- a/ceph/src/common/TrackedOp.h +++ b/ceph/src/common/TrackedOp.h @@ -288,6 +288,10 @@ public: void mark_event(const char *event, utime_t stamp=ceph_clock_now()); + void mark_nowarn() { + warn_interval_multiplier = 0; + } + virtual const char *state_string() const { Mutex::Locker l(lock); return events.rbegin()->c_str(); diff --git a/ceph/src/common/config.cc b/ceph/src/common/config.cc index 3cbb27e34..88e3da9b5 100644 --- a/ceph/src/common/config.cc +++ b/ceph/src/common/config.cc @@ -391,6 +391,17 @@ void md_config_t::show_config(Formatter *f) _show_config(NULL, f); } +void md_config_t::config_options(Formatter *f) +{ + Mutex::Locker l(lock); + f->open_array_section("options"); + for (const auto& i: schema) { + const Option &opt = i.second; + opt.dump(f); + } + f->close_section(); +} + void md_config_t::_show_config(std::ostream *out, Formatter *f) { if (out) { diff --git a/ceph/src/common/config.h b/ceph/src/common/config.h index 1e573f5e1..612f083d8 100644 --- a/ceph/src/common/config.h +++ b/ceph/src/common/config.h @@ -180,6 +180,9 @@ public: void show_config(std::ostream& out); /// dump all config values to a formatter void show_config(Formatter *f); + + /// dump all config settings to a formatter + void config_options(Formatter *f); /// obtain a diff between our config values and another md_config_t values void diff(const md_config_t *other, diff --git a/ceph/src/common/hobject.h b/ceph/src/common/hobject.h index 9b3f38f44..24eeb9754 100644 --- a/ceph/src/common/hobject.h +++ b/ceph/src/common/hobject.h @@ -300,8 +300,8 @@ WRITE_CLASS_ENCODER(hobject_t) namespace std { template<> struct hash { size_t operator()(const hobject_t &r) const { - static rjhash I; - return r.get_hash() ^ I(r.snap); + static rjhash RJ; + return RJ(r.get_hash() ^ r.snap); } }; } // namespace std @@ -473,8 +473,12 @@ WRITE_CLASS_ENCODER(ghobject_t) namespace std { template<> struct hash { size_t operator()(const ghobject_t &r) const { - static rjhash I; - return r.hobj.get_hash() ^ I(r.hobj.snap); + static rjhash RJ; + static hash HO; + size_t hash = HO(r.hobj); + hash = RJ(hash ^ r.generation); + hash = hash ^ r.shard_id.id; + return hash; } }; } // namespace std diff --git a/ceph/src/common/legacy_config_opts.h b/ceph/src/common/legacy_config_opts.h index e2ac86444..38b36a60c 100644 --- a/ceph/src/common/legacy_config_opts.h +++ b/ceph/src/common/legacy_config_opts.h @@ -1375,6 +1375,7 @@ OPTION(rgw_cross_domain_policy, OPT_STR) OPTION(rgw_healthcheck_disabling_path, OPT_STR) // path that existence causes the healthcheck to respond 503 OPTION(rgw_s3_auth_use_rados, OPT_BOOL) // should we try to use the internal credentials for s3? OPTION(rgw_s3_auth_use_keystone, OPT_BOOL) // should we try to use keystone for s3? +OPTION(rgw_s3_auth_order, OPT_STR) // s3 authentication order to try OPTION(rgw_barbican_url, OPT_STR) // url for barbican server /* OpenLDAP-style LDAP parameter strings */ @@ -1473,6 +1474,8 @@ OPTION(rgw_md_log_max_shards, OPT_INT) // max shards for metadata log OPTION(rgw_num_zone_opstate_shards, OPT_INT) // max shards for keeping inter-region copy progress info OPTION(rgw_opstate_ratelimit_sec, OPT_INT) // min time between opstate updates on a single upload (0 for disabling ratelimit) OPTION(rgw_curl_wait_timeout_ms, OPT_INT) // timeout for certain curl calls +OPTION(rgw_curl_low_speed_limit, OPT_INT) // low speed limit for certain curl calls +OPTION(rgw_curl_low_speed_time, OPT_INT) // low speed time for certain curl calls OPTION(rgw_copy_obj_progress, OPT_BOOL) // should dump progress during long copy operations? OPTION(rgw_copy_obj_progress_every_bytes, OPT_INT) // min bytes between copy progress output OPTION(rgw_obj_tombstone_cache_size, OPT_INT) // how many objects in tombstone cache, which is used in multi-zone sync to keep diff --git a/ceph/src/common/options.cc b/ceph/src/common/options.cc index 5c83f9527..1ed027c9b 100644 --- a/ceph/src/common/options.cc +++ b/ceph/src/common/options.cc @@ -4068,7 +4068,7 @@ std::vector