From 33c7a0ef2143973309014ab28861a6fa401a5aa5 Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Fri, 24 Jun 2022 08:48:13 +0200 Subject: [PATCH] import ceph quincy 17.2.1 Signed-off-by: Thomas Lamprecht --- ceph/.github/CODEOWNERS | 85 + ceph/.github/labeler.yml | 1 + ceph/.github/workflows/pr-triage.yml | 10 +- ceph/CMakeLists.txt | 16 +- ceph/PendingReleaseNotes | 19 + ceph/admin/doc-requirements.txt | 1 + ceph/ceph.spec | 37 +- ceph/ceph.spec.in | 31 +- ceph/changelog.upstream | 6 + ceph/cmake/modules/AddCephTest.cmake | 2 - ceph/cmake/modules/Distutils.cmake | 11 + ceph/debian/ceph-mgr.install | 1 + ceph/debian/control | 8 +- ceph/debian/rules | 1 + ceph/doc/cephadm/adoption.rst | 2 +- ceph/doc/cephadm/install.rst | 15 +- ceph/doc/cephadm/operations.rst | 10 +- ceph/doc/cephadm/services/index.rst | 16 +- ceph/doc/cephadm/services/monitoring.rst | 20 + ceph/doc/cephadm/services/osd.rst | 6 + ceph/doc/cephadm/upgrade.rst | 97 + ceph/doc/cephfs/add-remove-mds.rst | 9 +- ceph/doc/cephfs/fs-volumes.rst | 84 +- ceph/doc/dev/cephadm/developing-cephadm.rst | 13 +- .../dev/developer_guide/basic-workflow.rst | 355 ++- ceph/doc/man/8/ceph.rst | 4 +- ceph/doc/man/8/cephfs-shell.rst | 4 +- ceph/doc/mgr/dashboard.rst | 4 +- ceph/doc/rados/operations/control.rst | 15 +- .../doc/rados/operations/placement-groups.rst | 2 +- ceph/doc/radosgw/pools.rst | 2 +- .../rbd/rbd-persistent-write-log-cache.rst | 57 +- ceph/doc/releases/index.rst | 6 +- ceph/doc/releases/quincy.rst | 445 +++ ceph/doc/releases/releases.yml | 6 + ceph/doc/start/hardware-recommendations.rst | 35 +- ceph/doc/start/intro.rst | 4 +- ceph/doc/start/os-recommendations.rst | 123 +- ceph/qa/.teuthology_branch | 1 - ceph/qa/config/rados.yaml | 1 + .../3-upgrade-with-workload.yaml | 2 +- .../suites/orch/cephadm/upgrade/3-upgrade/.qa | 1 + .../simple.yaml} | 0 .../cephadm/upgrade/3-upgrade/staggered.yaml | 111 + .../suites/orch/cephadm/upgrade/4-wait.yaml | 2 +- .../orch/cephadm/upgrade/5-upgrade-ls.yaml | 2 +- .../thrashers/mapgap.yaml | 1 + .../thrashers/pggrow.yaml | 1 + .../basic/tasks/test/rgw_ec_s3tests.yaml | 2 +- .../smoke/basic/tasks/test/rgw_s3tests.yaml | 2 +- ceph/qa/tasks/cephadm_cases/test_cli.py | 16 +- ceph/qa/tasks/cephfs/filesystem.py | 18 +- ceph/qa/tasks/cephfs/test_cephfs_shell.py | 102 +- ceph/qa/tasks/cephfs/test_mds_metrics.py | 119 +- ceph/qa/tasks/cephfs/test_mirroring.py | 13 +- ceph/qa/tasks/cephfs/test_misc.py | 144 + ceph/qa/tasks/cephfs/test_nfs.py | 70 +- ceph/qa/tasks/cephfs/test_volumes.py | 2418 +++++++++++++---- ceph/qa/tox.ini | 2 +- ceph/qa/workunits/cephadm/test_cephadm.sh | 20 + ceph/qa/workunits/cephtool/test.sh | 17 +- ceph/qa/workunits/rgw/olh_noname_key | 1 + ceph/qa/workunits/rgw/olh_noname_val | Bin 0 -> 71 bytes ceph/qa/workunits/rgw/test_rgw_reshard.py | 31 + ceph/run-make-check.sh | 5 +- ceph/src/.git_version | 4 +- ceph/src/CMakeLists.txt | 6 - .../ceph_volume/devices/lvm/batch.py | 2 +- .../ceph_volume/devices/lvm/common.py | 3 +- .../ceph_volume/devices/lvm/zap.py | 4 +- .../ceph_volume/devices/raw/common.py | 8 +- .../tests/devices/lvm/test_batch.py | 2 + .../tests/devices/lvm/test_create.py | 10 +- .../tests/devices/lvm/test_listing.py | 4 +- .../tests/devices/lvm/test_prepare.py | 23 +- .../ceph_volume/tests/devices/lvm/test_zap.py | 6 +- .../tests/devices/raw/test_prepare.py | 4 +- .../ceph_volume/tests/devices/test_zap.py | 14 +- .../batch/playbooks/setup_mixed_type.yml | 4 +- .../ceph_volume/tests/test_inventory.py | 17 +- .../tests/util/test_arg_validators.py | 245 +- .../ceph_volume/tests/util/test_device.py | 124 +- .../ceph_volume/util/arg_validators.py | 107 +- .../ceph-volume/ceph_volume/util/device.py | 14 +- ceph/src/ceph-volume/tox.ini | 3 +- ceph/src/ceph.in | 3 +- ceph/src/cephadm/box/Dockerfile | 2 +- ceph/src/cephadm/box/box.py | 114 +- ceph/src/cephadm/box/host.py | 86 +- ceph/src/cephadm/box/osd.py | 74 +- ceph/src/cephadm/box/util.py | 96 +- ceph/src/cephadm/cephadm | 1063 ++++++-- ceph/src/cephadm/tests/fixtures.py | 9 + ceph/src/cephadm/tests/test_cephadm.py | 562 +++- ceph/src/cephadm/tests/test_networks.py | 15 +- ceph/src/cephadm/tox.ini | 4 +- ceph/src/client/Client.cc | 142 +- ceph/src/client/Client.h | 1 + ceph/src/client/MetaSession.h | 1 + ceph/src/client/fuse_ll.cc | 107 + ceph/src/cls/rgw/cls_rgw.cc | 15 +- ceph/src/common/options/global.yaml.in | 11 + ceph/src/common/options/mds-client.yaml.in | 13 + ceph/src/common/options/mds.yaml.in | 8 + ceph/src/common/options/osd.yaml.in | 6 + ceph/src/common/options/rbd.yaml.in | 7 - ceph/src/common/options/rgw.yaml.in | 9 - ceph/src/include/ceph_features.h | 3 +- ceph/src/kv/KeyValueDB.h | 9 +- ceph/src/kv/RocksDBStore.cc | 126 +- ceph/src/kv/RocksDBStore.h | 26 +- ceph/src/kv/rocksdb_cache/BinnedLRUCache.cc | 2 +- ceph/src/kv/rocksdb_cache/BinnedLRUCache.h | 2 +- ceph/src/kv/rocksdb_cache/ShardedCache.cc | 2 +- ceph/src/kv/rocksdb_cache/ShardedCache.h | 4 +- ceph/src/librbd/cache/Types.h | 2 +- ceph/src/librbd/cache/pwl/AbstractWriteLog.cc | 110 +- ceph/src/librbd/cache/pwl/AbstractWriteLog.h | 5 +- ceph/src/librbd/cache/pwl/DiscardRequest.cc | 12 +- ceph/src/librbd/cache/pwl/ImageCacheState.cc | 146 +- ceph/src/librbd/cache/pwl/ImageCacheState.h | 35 +- ceph/src/librbd/cache/pwl/InitRequest.cc | 4 +- ceph/src/librbd/cache/pwl/LogEntry.cc | 12 +- ceph/src/librbd/cache/pwl/LogEntry.h | 10 +- ceph/src/librbd/cache/pwl/Request.cc | 2 +- ceph/src/librbd/cache/pwl/ShutdownRequest.cc | 2 +- ceph/src/librbd/cache/pwl/Types.cc | 43 +- ceph/src/librbd/cache/pwl/Types.h | 94 +- ceph/src/librbd/cache/pwl/rwl/WriteLog.cc | 14 +- ceph/src/librbd/cache/pwl/ssd/LogOperation.cc | 2 +- ceph/src/librbd/cache/pwl/ssd/Types.h | 9 +- ceph/src/librbd/cache/pwl/ssd/WriteLog.cc | 14 +- ceph/src/mds/CDir.cc | 50 +- ceph/src/mds/MDCache.cc | 48 +- ceph/src/mds/MDCache.h | 2 +- ceph/src/mds/MDSRank.cc | 16 +- ceph/src/mds/MDSRank.h | 6 +- ceph/src/mds/OpenFileTable.cc | 4 +- ceph/src/mds/Server.cc | 62 +- ceph/src/mds/Server.h | 3 +- ceph/src/mds/journal.cc | 26 +- ceph/src/mgr/ActivePyModules.cc | 11 + ceph/src/mgr/ActivePyModules.h | 1 + ceph/src/mgr/BaseMgrModule.cc | 10 + ceph/src/mgr/DaemonServer.cc | 5 + ceph/src/mgr/DaemonServer.h | 1 + ceph/src/mgr/MDSPerfMetricCollector.cc | 8 + ceph/src/mgr/MDSPerfMetricCollector.h | 2 + ceph/src/mgr/MDSPerfMetricTypes.h | 1 + ceph/src/mgr/MetricCollector.cc | 6 + ceph/src/mgr/MetricCollector.h | 2 + ceph/src/mon/LogMonitor.cc | 4 + ceph/src/mon/LogMonitor.h | 5 + ceph/src/mon/MonCommands.h | 1 + ceph/src/mon/Monitor.cc | 1 + ceph/src/mon/OSDMonitor.cc | 141 +- ceph/src/mount.fuse.ceph | 8 +- ceph/src/mount/conf.cc | 4 +- ceph/src/msg/msg_types.cc | 13 + ceph/src/msg/msg_types.h | 9 +- ceph/src/os/bluestore/BlueStore.cc | 26 +- ceph/src/osd/OSD.cc | 14 +- ceph/src/osd/OSDMap.cc | 160 +- ceph/src/osd/OSDMap.h | 36 +- ceph/src/osd/PG.cc | 2 - ceph/src/osd/PeeringState.cc | 2 + ceph/src/osd/PrimaryLogPG.cc | 14 +- ceph/src/osd/scrubber/pg_scrubber.cc | 24 +- ceph/src/osd/scrubber/pg_scrubber.h | 3 + ceph/src/osd/scrubber/scrub_machine.cc | 9 + ceph/src/osd/scrubber/scrub_machine.h | 4 +- ceph/src/osd/scrubber/scrub_machine_lstnr.h | 3 + ceph/src/osd/scrubber_common.h | 2 + ceph/src/osdc/Objecter.cc | 10 +- ceph/src/pybind/mgr/CMakeLists.txt | 2 +- ceph/src/pybind/mgr/alerts/module.py | 7 +- ceph/src/pybind/mgr/ceph_module.pyi | 1 + ceph/src/pybind/mgr/cephadm/agent.py | 134 +- ceph/src/pybind/mgr/cephadm/module.py | 144 +- .../src/pybind/mgr/cephadm/offline_watcher.py | 60 + ceph/src/pybind/mgr/cephadm/schedule.py | 17 + ceph/src/pybind/mgr/cephadm/serve.py | 64 +- .../mgr/cephadm/services/cephadmservice.py | 51 +- .../pybind/mgr/cephadm/services/monitoring.py | 108 +- ceph/src/pybind/mgr/cephadm/services/osd.py | 103 +- ceph/src/pybind/mgr/cephadm/ssh.py | 29 +- .../services/alertmanager/alertmanager.yml.j2 | 5 + .../services/grafana/ceph-dashboard.yml.j2 | 12 + .../templates/services/grafana/grafana.ini.j2 | 2 + .../cephadm/templates/services/loki.yml.j2 | 28 + .../templates/services/promtail.yml.j2 | 21 + ceph/src/pybind/mgr/cephadm/tests/fixtures.py | 11 +- .../pybind/mgr/cephadm/tests/test_agent.py | 157 ++ .../pybind/mgr/cephadm/tests/test_cephadm.py | 114 +- .../mgr/cephadm/tests/test_scheduling.py | 76 + .../pybind/mgr/cephadm/tests/test_services.py | 169 +- .../pybind/mgr/cephadm/tests/test_upgrade.py | 245 +- ceph/src/pybind/mgr/cephadm/upgrade.py | 795 ++++-- ceph/src/pybind/mgr/cephadm/utils.py | 4 +- .../mgr/dashboard/controllers/grafana.py | 13 +- .../pybind/mgr/dashboard/controllers/home.py | 8 + .../cypress/integration/block/mirroring.po.ts | 5 +- .../integration/cluster/hosts.e2e-spec.ts | 4 - .../cypress/integration/cluster/hosts.po.ts | 30 +- .../integration/cluster/services.po.ts | 39 +- .../workflow/09-services.e2e-spec.ts | 37 +- .../cypress/integration/page-helper.po.ts | 8 +- .../dist/en-US/281.919d718adfcdc2881381.js | 1 - .../dist/en-US/281.cd14092ccedeaf2d7d79.js | 1 + .../dist/en-US/330.070e111fc5b7315b4eac.js | 1 - .../dist/en-US/330.4192d10f1b1db19145cc.js | 1 + .../dist/en-US/585.062485a57141de14ef7c.js | 1 - .../dist/en-US/585.764bfab2e2f489fdfd7f.js | 1 + .../dashboard/frontend/dist/en-US/index.html | 4 +- .../dist/en-US/main.17cdabb5dfb516cc695c.js | 3 - .../dist/en-US/main.30fafaca6a3d4e1868e0.js | 3 + ...0a6.js => runtime.d9a3c3d3ac8fa3cc7c93.js} | 2 +- ...a2.css => styles.e6093c94066da7ab35c7.css} | 2 +- .../iscsi-setting.component.html | 2 +- ...target-image-settings-modal.component.html | 2 +- .../bootstrap-import-modal.component.html | 2 +- .../pool-edit-mode-modal.component.html | 2 +- .../block/rbd-form/rbd-form.component.html | 10 +- .../rbd-namespace-form-modal.component.html | 2 +- .../rbd-trash-purge-modal.component.html | 2 +- .../configuration-form.component.html | 2 +- .../host-details/host-details.component.html | 3 +- .../ceph/cluster/hosts/hosts.component.html | 12 +- .../cluster/hosts/hosts.component.spec.ts | 81 + .../app/ceph/cluster/hosts/hosts.component.ts | 34 +- .../app/ceph/cluster/logs/logs.component.html | 8 +- .../app/ceph/cluster/logs/logs.component.ts | 3 +- .../mgr-module-form.component.html | 2 +- .../osd-recv-speed-modal.component.html | 2 +- .../silence-form.component.spec.ts | 22 +- .../silence-form/silence-form.component.ts | 14 +- .../service-daemon-list.component.html | 58 +- .../service-daemon-list.component.spec.ts | 8 + .../service-daemon-list.component.ts | 70 +- .../service-form/service-form.component.html | 16 +- .../service-form.component.spec.ts | 24 + .../service-form/service-form.component.ts | 8 + .../nfs-form-client.component.html | 4 +- .../ceph/nfs/nfs-form/nfs-form.component.html | 10 +- .../crush-rule-form-modal.component.html | 6 +- ...ure-code-profile-form-modal.component.html | 14 +- .../pool/pool-form/pool-form.component.html | 12 +- .../rgw-bucket-form.component.html | 6 +- .../rgw-user-capability-modal.component.html | 4 +- .../rgw-user-form.component.html | 2 +- .../rgw-user-s3-key-modal.component.html | 2 +- .../rgw-user-subuser-modal.component.html | 2 +- .../shared/feedback/feedback.component.html | 4 +- .../ceph/shared/pg-category.service.spec.ts | 1 + .../app/ceph/shared/pg-category.service.ts | 2 +- ... smart_data_version_1_0_ata_response.json} | 0 .../smart_data_version_1_0_scsi_response.json | 208 ++ .../smart-list/smart-list.component.html | 6 +- .../smart-list/smart-list.component.spec.ts | 40 +- .../shared/smart-list/smart-list.component.ts | 57 +- .../login-layout/login-layout.component.html | 9 +- .../login-layout/login-layout.component.scss | 18 +- .../api/custom-login-banner.service.spec.ts | 35 + .../shared/api/custom-login-banner.service.ts | 15 + .../src/app/shared/api/host.service.ts | 4 +- .../app/shared/api/rgw-daemon.service.spec.ts | 13 +- .../src/app/shared/api/rgw-daemon.service.ts | 5 +- .../shared/components/components.module.ts | 7 +- .../custom-login-banner.component.html | 2 + .../custom-login-banner.component.scss | 5 + .../custom-login-banner.component.spec.ts | 25 + .../custom-login-banner.component.ts | 20 + .../form-modal/form-modal.component.html | 2 +- .../language-selector.component.html | 1 + .../refresh-selector.component.html | 2 +- .../usage-bar/usage-bar.component.html | 2 +- .../usage-bar/usage-bar.component.ts | 18 +- .../datatable/table/table.component.spec.ts | 31 +- .../shared/datatable/table/table.component.ts | 25 +- .../frontend/src/app/shared/models/smart.ts | 26 +- .../src/app/shared/pipes/empty.pipe.ts | 7 +- .../src/app/shared/pipes/pipes.module.ts | 7 +- .../pipes/search-highlight.pipe.spec.ts | 41 + .../app/shared/pipes/search-highlight.pipe.ts | 26 + .../src/styles/ceph-custom/_basics.scss | 5 + ceph/src/pybind/mgr/dashboard/module.py | 31 +- .../src/pybind/mgr/dashboard/requirements.txt | 1 + .../mgr/dashboard/services/ceph_service.py | 2 +- .../mgr/dashboard/services/custom_banner.py | 27 + .../mgr/dashboard/services/exception.py | 11 + .../mgr/dashboard/tests/test_grafana.py | 8 + ceph/src/pybind/mgr/devicehealth/module.py | 2 +- ceph/src/pybind/mgr/mds_autoscaler/module.py | 10 +- ceph/src/pybind/mgr/mgr_module.py | 44 +- ceph/src/pybind/mgr/nfs/export.py | 79 +- .../nfs/{export_utils.py => ganesha_conf.py} | 138 +- ceph/src/pybind/mgr/nfs/tests/test_nfs.py | 27 +- ceph/src/pybind/mgr/object_format.py | 531 ++++ .../src/pybind/mgr/orchestrator/_interface.py | 33 +- ceph/src/pybind/mgr/orchestrator/module.py | 117 +- .../orchestrator/tests/test_orchestrator.py | 61 + ceph/src/pybind/mgr/prometheus/module.py | 50 +- ceph/src/pybind/mgr/requirements-required.txt | 1 + ceph/src/pybind/mgr/rook/module.py | 2 +- ceph/src/pybind/mgr/stats/fs/perf_stats.py | 73 +- ceph/src/pybind/mgr/stats/module.py | 6 +- ceph/src/pybind/mgr/telemetry/module.py | 77 +- .../mgr/test_orchestrator/dummy_data.json | 4 + .../pybind/mgr/tests/test_object_format.py | 422 +++ ceph/src/pybind/mgr/tox.ini | 2 +- .../src/pybind/mgr/volumes/fs/async_cloner.py | 14 +- .../mgr/volumes/fs/operations/template.py | 54 +- .../operations/versions/metadata_manager.py | 15 +- .../fs/operations/versions/subvolume_base.py | 61 + .../fs/operations/versions/subvolume_v1.py | 25 +- .../fs/operations/versions/subvolume_v2.py | 3 +- ceph/src/pybind/mgr/volumes/fs/volume.py | 148 + ceph/src/pybind/mgr/volumes/module.py | 152 +- .../ceph/deployment/drive_group.py | 7 +- .../deployment/drive_selection/selector.py | 7 + .../ceph/deployment/inventory.py | 35 +- .../ceph/deployment/service_spec.py | 78 +- .../ceph/deployment/translate.py | 68 +- .../ceph/tests/c-v-inventory.json | 4 +- .../ceph/tests/test_drive_group.py | 84 +- .../ceph/tests/test_service_spec.py | 4 +- ceph/src/rgw/CMakeLists.txt | 2 +- ceph/src/rgw/rgw_coroutine.cc | 16 +- ceph/src/rgw/rgw_log.cc | 15 +- ceph/src/rgw/rgw_log.h | 5 +- ceph/src/rgw/rgw_reshard.cc | 7 + ceph/src/rgw/rgw_tools.cc | 11 - ceph/src/test/cli/rbd/help.t | 57 +- ceph/src/test/cls_rgw/test_cls_rgw.cc | 67 +- ceph/src/test/encoding/check-generated.sh | 2 +- ceph/src/test/librados/tier_cxx.cc | 68 + .../cache/pwl/test_mock_ReplicatedWriteLog.cc | 42 +- .../librbd/cache/pwl/test_mock_SSDWriteLog.cc | 42 +- ceph/src/test/objectstore/store_test.cc | 26 +- ceph/src/test/osd/TestOSDMap.cc | 207 ++ ceph/src/test/pybind/CMakeLists.txt | 4 +- ceph/src/test/pybind/test_ceph_argparse.py | 1376 +++++----- ceph/src/test/pybind/test_ceph_daemon.py | 14 +- .../rbd_mirror/test_mock_ImageReplayer.cc | 2 + ceph/src/tools/ceph_objectstore_tool.cc | 96 +- ceph/src/tools/cephfs/cephfs-shell | 69 +- ceph/src/tools/rbd/CMakeLists.txt | 2 +- ceph/src/tools/rbd/Utils.cc | 6 +- ceph/src/tools/rbd/Utils.h | 2 + ceph/src/tools/rbd/action/ImageCache.cc | 71 - ceph/src/tools/rbd/action/PersistentCache.cc | 122 + ceph/src/tools/rbd/action/Status.cc | 138 +- 352 files changed, 13759 insertions(+), 3937 deletions(-) create mode 100644 ceph/doc/releases/quincy.rst delete mode 100644 ceph/qa/.teuthology_branch create mode 120000 ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/.qa rename ceph/qa/suites/orch/cephadm/upgrade/{3-start-upgrade.yaml => 3-upgrade/simple.yaml} (100%) create mode 100644 ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/staggered.yaml create mode 100644 ceph/qa/workunits/rgw/olh_noname_key create mode 100644 ceph/qa/workunits/rgw/olh_noname_val create mode 100644 ceph/src/pybind/mgr/cephadm/offline_watcher.py create mode 100644 ceph/src/pybind/mgr/cephadm/templates/services/loki.yml.j2 create mode 100644 ceph/src/pybind/mgr/cephadm/templates/services/promtail.yml.j2 create mode 100644 ceph/src/pybind/mgr/cephadm/tests/test_agent.py delete mode 100644 ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/281.919d718adfcdc2881381.js create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/281.cd14092ccedeaf2d7d79.js delete mode 100644 ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/330.070e111fc5b7315b4eac.js create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/330.4192d10f1b1db19145cc.js delete mode 100644 ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/585.062485a57141de14ef7c.js create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/585.764bfab2e2f489fdfd7f.js delete mode 100644 ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/main.17cdabb5dfb516cc695c.js create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/main.30fafaca6a3d4e1868e0.js rename ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/{runtime.27fa8b70ed754072a0a6.js => runtime.d9a3c3d3ac8fa3cc7c93.js} (52%) rename ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/{styles.013d05bfdb2ad24949a2.css => styles.e6093c94066da7ab35c7.css} (98%) rename ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/{smart_data_version_1_0_hdd_response.json => smart_data_version_1_0_ata_response.json} (100%) create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/smart_data_version_1_0_scsi_response.json create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/api/custom-login-banner.service.spec.ts create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/api/custom-login-banner.service.ts create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/components/custom-login-banner/custom-login-banner.component.html create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/components/custom-login-banner/custom-login-banner.component.scss create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/components/custom-login-banner/custom-login-banner.component.spec.ts create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/components/custom-login-banner/custom-login-banner.component.ts create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/search-highlight.pipe.spec.ts create mode 100644 ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/search-highlight.pipe.ts create mode 100644 ceph/src/pybind/mgr/dashboard/services/custom_banner.py rename ceph/src/pybind/mgr/nfs/{export_utils.py => ganesha_conf.py} (86%) create mode 100644 ceph/src/pybind/mgr/object_format.py create mode 100644 ceph/src/pybind/mgr/tests/test_object_format.py delete mode 100644 ceph/src/tools/rbd/action/ImageCache.cc create mode 100644 ceph/src/tools/rbd/action/PersistentCache.cc diff --git a/ceph/.github/CODEOWNERS b/ceph/.github/CODEOWNERS index 88c6da6c6..f80f4b748 100644 --- a/ceph/.github/CODEOWNERS +++ b/ceph/.github/CODEOWNERS @@ -54,3 +54,88 @@ COPYING* @ceph/doc-writers /doc/ @ceph/doc-writers README* @ceph/doc-writers *.rst @ceph/doc-writers + +# core +/doc/man/8/ceph-authtool.rst @ceph/core +/doc/man/8/ceph-conf.rst @ceph/core +/doc/man/8/ceph-create-keys.rst @ceph/core +/doc/man/8/ceph-kvstore-tool.rst @ceph/core +/doc/man/8/ceph-mon.rst @ceph/core +/doc/man/8/ceph-objectstore-tool.rst @ceph/core +/doc/man/8/ceph-osd.rst @ceph/core +/doc/man/8/ceph.rst @ceph/core +/doc/man/8/crushtool.rst @ceph/core +/doc/man/8/monmaptool.rst @ceph/core +/doc/man/8/rados.rst @ceph/core +/doc/rados @ceph/core +/qa/standalone @ceph/core +/qa/suites/rados @ceph/core +/qa/workunits/erasure-code @ceph/core +/qa/workunits/mgr @ceph/core +/qa/workunits/mon @ceph/core +/qa/workunits/objectstore @ceph/core +/qa/workunits/rados @ceph/core +/src/ceph.in @ceph/core +/src/ceph_osd.cc @ceph/core +/src/ceph_mon.cc @ceph/core +/src/blk @ceph/core +/src/crush @ceph/core +/src/erasure-code @ceph/core +/src/kv @ceph/core +/src/librados @ceph/core +/src/mgr @ceph/core +/src/mon @ceph/core +/src/msg @ceph/core +/src/os @ceph/core +/src/osd @ceph/core +/src/tools/rados @ceph/core +/src/test/osd @ceph/core + +# rbd +/doc/dev/rbd* @ceph/rbd +/doc/man/8/ceph-rbdnamer.rst @ceph/rbd +/doc/man/8/rbd* @ceph/rbd +/doc/rbd @ceph/rbd +/doc/start/quick-rbd.rst @ceph/rbd +/qa/rbd @ceph/rbd +/qa/run_xfstests* @ceph/rbd +/qa/suites/krbd @ceph/rbd +/qa/suites/rbd @ceph/rbd +/qa/tasks/ceph_iscsi_client.py @ceph/rbd +/qa/tasks/metadata.yaml @ceph/rbd +/qa/tasks/qemu.py @ceph/rbd +/qa/tasks/rbd* @ceph/rbd +/qa/tasks/userdata* @ceph/rbd +/qa/workunits/cls/test_cls_journal.sh @ceph/rbd +/qa/workunits/cls/test_cls_lock.sh @ceph/rbd +/qa/workunits/cls/test_cls_rbd.sh @ceph/rbd +/qa/workunits/rbd @ceph/rbd +/src/ceph-rbdnamer @ceph/rbd +/src/cls/journal @ceph/rbd +/src/cls/lock @ceph/rbd +/src/cls/rbd @ceph/rbd +/src/common/options/rbd* @ceph/rbd +/src/etc-rbdmap @ceph/rbd +/src/include/krbd.h @ceph/rbd +/src/include/rbd* @ceph/rbd +/src/journal @ceph/rbd +/src/krbd.cc @ceph/rbd +/src/librbd @ceph/rbd +/src/ocf @ceph/rbd +/src/pybind/mgr/rbd_support @ceph/rbd +/src/pybind/rbd @ceph/rbd +/src/rbd* @ceph/rbd +/src/test/cli/rbd @ceph/rbd +/src/test/cli-integration/rbd @ceph/rbd +/src/test/cls_journal @ceph/rbd +/src/test/cls_lock @ceph/rbd +/src/test/cls_rbd @ceph/rbd +/src/test/journal @ceph/rbd +/src/test/librbd @ceph/rbd +/src/test/pybind/test_rbd.py @ceph/rbd +/src/test/rbd* @ceph/rbd +/src/test/run-rbd* @ceph/rbd +/src/test/test_rbd* @ceph/rbd +/src/tools/rbd* @ceph/rbd +/systemd/rbdmap.service.in @ceph/rbd +/udev/50-rbd.rules @ceph/rbd diff --git a/ceph/.github/labeler.yml b/ceph/.github/labeler.yml index c2cea0742..bf8c4d316 100644 --- a/ceph/.github/labeler.yml +++ b/ceph/.github/labeler.yml @@ -54,6 +54,7 @@ mgr: - src/pybind/mgr/ceph_module.pyi - src/pybind/mgr/mgr_module.py - src/pybind/mgr/mgr_util.py + - src/pybind/mgr/object_format.py - src/pybind/mgr/requirements.txt - src/pybind/mgr/tox.ini - src/test/mgr/** diff --git a/ceph/.github/workflows/pr-triage.yml b/ceph/.github/workflows/pr-triage.yml index 77fcff462..481f75331 100644 --- a/ceph/.github/workflows/pr-triage.yml +++ b/ceph/.github/workflows/pr-triage.yml @@ -12,13 +12,13 @@ jobs: with: sync-labels: '' repo-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Assign to Dashboard project + uses: srggrs/assign-one-project-github-action@65a8ddab497df42ef268001e67bbf976f8fd39e1 + if: contains(github.event.pull_request.labels.*.name, 'dashboard') + with: + project: https://github.com/ceph/ceph/projects/6 - name: Assign milestone based on target brach name uses: iyu/actions-milestone@dbf7e5348844c9ddc6b803a5721b85fa70fe3bb9 with: configuration-path: .github/milestone.yml repo-token: "${{ secrets.GITHUB_TOKEN }}" - - name: Assign to Dashboard project - uses: srggrs/assign-one-project-github-action@65a8ddab497df42ef268001e67bbf976f8fd39e1 - if: contains(github.event.pull_request.labels.*.name, 'dashboard') - with: - project: https://github.com/ceph/ceph/projects/6 diff --git a/ceph/CMakeLists.txt b/ceph/CMakeLists.txt index e95019ced..39bec5896 100644 --- a/ceph/CMakeLists.txt +++ b/ceph/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) project(ceph - VERSION 17.2.0 + VERSION 17.2.1 LANGUAGES CXX C ASM) cmake_policy(SET CMP0028 NEW) @@ -20,6 +20,12 @@ endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/") +if(NOT CMAKE_BUILD_TYPE AND EXISTS "${CMAKE_SOURCE_DIR}/.git") + set(default_build_type "Debug") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE + STRING "Default BUILD_TYPE is Debug, other options are: RelWithDebInfo, Release, and MinSizeRel." FORCE) +endif() + if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(LINUX ON) FIND_PACKAGE(Threads) @@ -403,7 +409,7 @@ option(WITH_RADOSGW_DBSTORE "DBStore backend for Rados Gateway" ON) option(WITH_RADOSGW_SELECT_PARQUET "Support for s3 select on parquet objects" ON) option(WITH_SYSTEM_ARROW "Use system-provided arrow" OFF) -option(WITH_SYSTEM_UTF8PROC "Use system-provided utf8proc" ON) +option(WITH_SYSTEM_UTF8PROC "Use system-provided utf8proc" OFF) if(WITH_RADOSGW) find_package(EXPAT REQUIRED) @@ -464,12 +470,8 @@ option(WITH_CEPHFS "CephFS is enabled" ON) if(NOT WIN32) # Please specify 3.[0-7] if you want to build with a certain version of python3. set(WITH_PYTHON3 "3" CACHE STRING "build with specified python3 version") -if(NOT WITH_PYTHON3 STREQUAL "3") - set(find_python3_exact "EXACT") -endif() -find_package(Python3 ${WITH_PYTHON3} ${find_python3_exact} REQUIRED +find_package(Python3 ${WITH_PYTHON3} EXACT REQUIRED COMPONENTS Interpreter Development) -unset(find_python3_exact) option(WITH_MGR "ceph-mgr is enabled" ON) if(WITH_MGR) diff --git a/ceph/PendingReleaseNotes b/ceph/PendingReleaseNotes index 547c99e53..3854ccb56 100644 --- a/ceph/PendingReleaseNotes +++ b/ceph/PendingReleaseNotes @@ -1,3 +1,22 @@ +>=17.2.1 + +* The "BlueStore zero block detection" feature (first introduced to Quincy in +https://github.com/ceph/ceph/pull/43337) has been turned off by default with a +new global configuration called `bluestore_zero_block_detection`. This feature, +intended for large-scale synthetic testing, does not interact well with some RBD +and CephFS features. Any side effects experienced in previous Quincy versions +would no longer occur, provided that the configuration remains set to false. +Relevant tracker: https://tracker.ceph.com/issues/55521 + +* telemetry: Added new Rook metrics to the 'basic' channel to report Rook's + version, Kubernetes version, node metrics, etc. + See a sample report with `ceph telemetry preview`. + Opt-in with `ceph telemetry on`. + + For more details, see: + + https://docs.ceph.com/en/latest/mgr/telemetry/ + >=17.0.0 * Filestore has been deprecated in Quincy, considering that BlueStore has been diff --git a/ceph/admin/doc-requirements.txt b/ceph/admin/doc-requirements.txt index a9c97a85d..4e4a501e6 100644 --- a/ceph/admin/doc-requirements.txt +++ b/ceph/admin/doc-requirements.txt @@ -16,3 +16,4 @@ typed-ast sphinxcontrib-openapi sphinxcontrib-seqdiag mistune < 2.0.0 +natsort diff --git a/ceph/ceph.spec b/ceph/ceph.spec index 66d9ea58d..14a54db8e 100644 --- a/ceph/ceph.spec +++ b/ceph/ceph.spec @@ -36,7 +36,11 @@ %bcond_with rbd_rwl_cache %endif %if 0%{?fedora} || 0%{?rhel} +%if 0%{?rhel} < 9 %bcond_with system_pmdk +%else +%bcond_without system_pmdk +%endif %bcond_without selinux %if 0%{?rhel} >= 8 %bcond_with cephfs_java @@ -141,11 +145,18 @@ %endif %endif +%if 0%{with seastar} +# disable -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1, as gcc-toolset-{9,10}-annobin +# do not provide gcc-annobin.so anymore, despite that they provide annobin.so. but +# redhat-rpm-config still passes -fplugin=gcc-annobin to the compiler. +%undefine _annotated_build +%endif + ################################################################################# # main package definition ################################################################################# Name: ceph -Version: 17.2.0 +Version: 17.2.1 Release: 0%{?dist} %if 0%{?fedora} || 0%{?rhel} Epoch: 2 @@ -161,7 +172,7 @@ License: LGPL-2.1 and LGPL-3.0 and CC-BY-SA-3.0 and GPL-2.0 and BSL-1.0 and BSD- Group: System/Filesystems %endif URL: http://ceph.com/ -Source0: %{?_remote_tarball_prefix}ceph-17.2.0.tar.bz2 +Source0: %{?_remote_tarball_prefix}ceph-17.2.1.tar.bz2 %if 0%{?suse_version} # _insert_obs_source_lines_here ExclusiveArch: x86_64 aarch64 ppc64le s390x @@ -251,7 +262,6 @@ BuildRequires: hostname BuildRequires: jq BuildRequires: libuuid-devel BuildRequires: python%{python3_pkgversion}-bcrypt -BuildRequires: python%{python3_pkgversion}-nose BuildRequires: python%{python3_pkgversion}-pecan BuildRequires: python%{python3_pkgversion}-requests BuildRequires: python%{python3_pkgversion}-dateutil @@ -262,7 +272,11 @@ BuildRequires: socat %if 0%{with zbd} BuildRequires: libzbd-devel %endif +%if 0%{?suse_version} +BuildRequires: libthrift-devel >= 0.13.0 +%else BuildRequires: thrift-devel >= 0.13.0 +%endif BuildRequires: re2-devel %if 0%{with jaeger} BuildRequires: bison @@ -321,6 +335,7 @@ BuildRequires: libbz2-devel BuildRequires: mozilla-nss-devel BuildRequires: keyutils-devel BuildRequires: libopenssl-devel +BuildRequires: ninja BuildRequires: openldap2-devel #BuildRequires: krb5 #BuildRequires: krb5-devel @@ -340,6 +355,7 @@ BuildRequires: nss-devel BuildRequires: keyutils-libs-devel BuildRequires: libibverbs-devel BuildRequires: librdmacm-devel +BuildRequires: ninja-build BuildRequires: openldap-devel #BuildRequires: krb5-devel BuildRequires: openssl-devel @@ -356,7 +372,6 @@ BuildRequires: golang %if 0%{?fedora} || 0%{?rhel} BuildRequires: golang-github-prometheus BuildRequires: libtool-ltdl-devel -BuildRequires: ninja-build BuildRequires: xmlsec1 BuildRequires: xmlsec1-devel %ifarch x86_64 @@ -376,7 +391,6 @@ BuildRequires: golang-github-prometheus-prometheus BuildRequires: libxmlsec1-1 BuildRequires: libxmlsec1-nss1 BuildRequires: libxmlsec1-openssl1 -BuildRequires: ninja BuildRequires: python%{python3_pkgversion}-CherryPy BuildRequires: python%{python3_pkgversion}-PyJWT BuildRequires: python%{python3_pkgversion}-Routes @@ -666,6 +680,7 @@ Group: System/Filesystems %endif Requires: ceph-mgr = %{_epoch_prefix}%{version}-%{release} Requires: python%{python3_pkgversion}-asyncssh +Requires: python%{python3_pkgversion}-natsort Requires: cephadm = %{_epoch_prefix}%{version}-%{release} %if 0%{?suse_version} Requires: openssh @@ -1224,7 +1239,7 @@ This package provides Ceph default alerts for Prometheus. # common ################################################################################# %prep -%autosetup -p1 -n ceph-17.2.0 +%autosetup -p1 -n ceph-17.2.1 %build # Disable lto on systems that do not support symver attribute @@ -1351,8 +1366,8 @@ cmake .. \ %if 0%{with system_arrow} -DWITH_SYSTEM_ARROW:BOOL=ON \ %endif -%if 0%{without system_utf8proc} - -DWITH_SYSTEM_UTF8PROC:BOOL=OFF \ +%if 0%{with system_utf8proc} + -DWITH_SYSTEM_UTF8PROC:BOOL=ON \ %endif -DWITH_GRAFANA:BOOL=ON @@ -1466,6 +1481,9 @@ install -m 644 -D monitoring/ceph-mixin/prometheus_alerts.yml %{buildroot}/etc/p %clean rm -rf %{buildroot} +# built binaries are no longer necessary at this point, +# but are consuming ~17GB of disk in the build environment +rm -rf %{_vpath_builddir} ################################################################################# # files and systemd scriptlets @@ -1566,7 +1584,7 @@ exit 0 %{_mandir}/man8/cephadm.8* %attr(0700,cephadm,cephadm) %dir %{_sharedstatedir}/cephadm %attr(0700,cephadm,cephadm) %dir %{_sharedstatedir}/cephadm/.ssh -%attr(0600,cephadm,cephadm) %{_sharedstatedir}/cephadm/.ssh/authorized_keys +%config(noreplace) %attr(0600,cephadm,cephadm) %{_sharedstatedir}/cephadm/.ssh/authorized_keys %files common %dir %{_docdir}/ceph @@ -1716,6 +1734,7 @@ fi %dir %{_datadir}/ceph/mgr %{_datadir}/ceph/mgr/mgr_module.* %{_datadir}/ceph/mgr/mgr_util.* +%{_datadir}/ceph/mgr/object_format.* %{_unitdir}/ceph-mgr@.service %{_unitdir}/ceph-mgr.target %attr(750,ceph,ceph) %dir %{_localstatedir}/lib/ceph/mgr diff --git a/ceph/ceph.spec.in b/ceph/ceph.spec.in index cb3fa2d36..9f522a750 100644 --- a/ceph/ceph.spec.in +++ b/ceph/ceph.spec.in @@ -36,7 +36,11 @@ %bcond_with rbd_rwl_cache %endif %if 0%{?fedora} || 0%{?rhel} +%if 0%{?rhel} < 9 %bcond_with system_pmdk +%else +%bcond_without system_pmdk +%endif %bcond_without selinux %if 0%{?rhel} >= 8 %bcond_with cephfs_java @@ -141,6 +145,13 @@ %endif %endif +%if 0%{with seastar} +# disable -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1, as gcc-toolset-{9,10}-annobin +# do not provide gcc-annobin.so anymore, despite that they provide annobin.so. but +# redhat-rpm-config still passes -fplugin=gcc-annobin to the compiler. +%undefine _annotated_build +%endif + ################################################################################# # main package definition ################################################################################# @@ -251,7 +262,6 @@ BuildRequires: hostname BuildRequires: jq BuildRequires: libuuid-devel BuildRequires: python%{python3_pkgversion}-bcrypt -BuildRequires: python%{python3_pkgversion}-nose BuildRequires: python%{python3_pkgversion}-pecan BuildRequires: python%{python3_pkgversion}-requests BuildRequires: python%{python3_pkgversion}-dateutil @@ -262,7 +272,11 @@ BuildRequires: socat %if 0%{with zbd} BuildRequires: libzbd-devel %endif +%if 0%{?suse_version} +BuildRequires: libthrift-devel >= 0.13.0 +%else BuildRequires: thrift-devel >= 0.13.0 +%endif BuildRequires: re2-devel %if 0%{with jaeger} BuildRequires: bison @@ -321,6 +335,7 @@ BuildRequires: libbz2-devel BuildRequires: mozilla-nss-devel BuildRequires: keyutils-devel BuildRequires: libopenssl-devel +BuildRequires: ninja BuildRequires: openldap2-devel #BuildRequires: krb5 #BuildRequires: krb5-devel @@ -340,6 +355,7 @@ BuildRequires: nss-devel BuildRequires: keyutils-libs-devel BuildRequires: libibverbs-devel BuildRequires: librdmacm-devel +BuildRequires: ninja-build BuildRequires: openldap-devel #BuildRequires: krb5-devel BuildRequires: openssl-devel @@ -356,7 +372,6 @@ BuildRequires: golang %if 0%{?fedora} || 0%{?rhel} BuildRequires: golang-github-prometheus BuildRequires: libtool-ltdl-devel -BuildRequires: ninja-build BuildRequires: xmlsec1 BuildRequires: xmlsec1-devel %ifarch x86_64 @@ -376,7 +391,6 @@ BuildRequires: golang-github-prometheus-prometheus BuildRequires: libxmlsec1-1 BuildRequires: libxmlsec1-nss1 BuildRequires: libxmlsec1-openssl1 -BuildRequires: ninja BuildRequires: python%{python3_pkgversion}-CherryPy BuildRequires: python%{python3_pkgversion}-PyJWT BuildRequires: python%{python3_pkgversion}-Routes @@ -666,6 +680,7 @@ Group: System/Filesystems %endif Requires: ceph-mgr = %{_epoch_prefix}%{version}-%{release} Requires: python%{python3_pkgversion}-asyncssh +Requires: python%{python3_pkgversion}-natsort Requires: cephadm = %{_epoch_prefix}%{version}-%{release} %if 0%{?suse_version} Requires: openssh @@ -1351,8 +1366,8 @@ cmake .. \ %if 0%{with system_arrow} -DWITH_SYSTEM_ARROW:BOOL=ON \ %endif -%if 0%{without system_utf8proc} - -DWITH_SYSTEM_UTF8PROC:BOOL=OFF \ +%if 0%{with system_utf8proc} + -DWITH_SYSTEM_UTF8PROC:BOOL=ON \ %endif -DWITH_GRAFANA:BOOL=ON @@ -1466,6 +1481,9 @@ install -m 644 -D monitoring/ceph-mixin/prometheus_alerts.yml %{buildroot}/etc/p %clean rm -rf %{buildroot} +# built binaries are no longer necessary at this point, +# but are consuming ~17GB of disk in the build environment +rm -rf %{_vpath_builddir} ################################################################################# # files and systemd scriptlets @@ -1566,7 +1584,7 @@ exit 0 %{_mandir}/man8/cephadm.8* %attr(0700,cephadm,cephadm) %dir %{_sharedstatedir}/cephadm %attr(0700,cephadm,cephadm) %dir %{_sharedstatedir}/cephadm/.ssh -%attr(0600,cephadm,cephadm) %{_sharedstatedir}/cephadm/.ssh/authorized_keys +%config(noreplace) %attr(0600,cephadm,cephadm) %{_sharedstatedir}/cephadm/.ssh/authorized_keys %files common %dir %{_docdir}/ceph @@ -1716,6 +1734,7 @@ fi %dir %{_datadir}/ceph/mgr %{_datadir}/ceph/mgr/mgr_module.* %{_datadir}/ceph/mgr/mgr_util.* +%{_datadir}/ceph/mgr/object_format.* %{_unitdir}/ceph-mgr@.service %{_unitdir}/ceph-mgr.target %attr(750,ceph,ceph) %dir %{_localstatedir}/lib/ceph/mgr diff --git a/ceph/changelog.upstream b/ceph/changelog.upstream index 93b0064bb..b048b8e4f 100644 --- a/ceph/changelog.upstream +++ b/ceph/changelog.upstream @@ -1,3 +1,9 @@ +ceph (17.2.1-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 23 Jun 2022 14:41:32 +0000 + ceph (17.2.0-1) stable; urgency=medium * New upstream release diff --git a/ceph/cmake/modules/AddCephTest.cmake b/ceph/cmake/modules/AddCephTest.cmake index 0df7125b5..46d3a1b4c 100644 --- a/ceph/cmake/modules/AddCephTest.cmake +++ b/ceph/cmake/modules/AddCephTest.cmake @@ -38,7 +38,6 @@ if(WITH_GTEST_PARALLEL) BUILD_COMMAND "" INSTALL_COMMAND "") add_dependencies(tests gtest-parallel_ext) - find_package(Python3 QUIET REQUIRED) set(GTEST_PARALLEL_COMMAND ${Python3_EXECUTABLE} ${gtest_parallel_source_dir}/gtest-parallel) endif() @@ -70,7 +69,6 @@ function(add_tox_test name) list(APPEND tox_envs py3) endif() string(REPLACE ";" "," tox_envs "${tox_envs}") - find_package(Python3 QUIET REQUIRED) add_test( NAME setup-venv-for-${name} COMMAND ${CMAKE_SOURCE_DIR}/src/tools/setup-virtualenv.sh --python=${Python3_EXECUTABLE} ${venv_path} diff --git a/ceph/cmake/modules/Distutils.cmake b/ceph/cmake/modules/Distutils.cmake index 2c0df43de..191636338 100644 --- a/ceph/cmake/modules/Distutils.cmake +++ b/ceph/cmake/modules/Distutils.cmake @@ -1,5 +1,16 @@ include(CMakeParseArguments) +# ensure that we are using the exact python version specified by +# 'WITH_PYTHON3', in case some included 3rd party libraries call +# 'find_package(Python3 ...) without specifying the exact version number. if +# the building host happens to have a higher version of python3, that version +# would be picked up instead by find_package(Python3). and that is not want we +# expect. +find_package(Python3 ${WITH_PYTHON3} EXACT + QUIET + REQUIRED + COMPONENTS Interpreter) + function(distutils_install_module name) set(py_srcs setup.py README.rst requirements.txt test-requirements.txt bin ${name}) foreach(src ${py_srcs}) diff --git a/ceph/debian/ceph-mgr.install b/ceph/debian/ceph-mgr.install index fd7b68e82..9535b1e60 100644 --- a/ceph/debian/ceph-mgr.install +++ b/ceph/debian/ceph-mgr.install @@ -2,3 +2,4 @@ lib/systemd/system/ceph-mgr* usr/bin/ceph-mgr usr/share/ceph/mgr/mgr_module.* usr/share/ceph/mgr/mgr_util.* +usr/share/ceph/mgr/object_format.* diff --git a/ceph/debian/control b/ceph/debian/control index 2f68285c7..af0f351ee 100644 --- a/ceph/debian/control +++ b/ceph/debian/control @@ -78,15 +78,15 @@ Build-Depends: automake, libxmlsec1-nss , libxmlsec1-openssl , libxmlsec1-dev , - ninja-build , + ninja-build, nlohmann-json3-dev , patch, pkg-config, prometheus , python3-all-dev, python3-cherrypy3, + python3-natsort, python3-jwt , - python3-nose , python3-pecan , python3-bcrypt , tox , @@ -228,6 +228,7 @@ Depends: ceph-base (= ${binary:Version}), ceph-mgr-modules-core (= ${binary:Version}), python3-bcrypt, python3-cherrypy3, + python3-natsort, python3-distutils, python3-jwt, python3-openssl, @@ -348,7 +349,8 @@ Depends: ceph-mgr (= ${binary:Version}), ${python:Depends}, openssh-client, python3-jinja2, - python3-cherrypy3 + python3-cherrypy3, + python3-natsort Description: cephadm orchestrator module for ceph-mgr Ceph is a massively scalable, open-source, distributed storage system that runs on commodity hardware and delivers object, diff --git a/ceph/debian/rules b/ceph/debian/rules index 890434275..9f49deabb 100755 --- a/ceph/debian/rules +++ b/ceph/debian/rules @@ -25,6 +25,7 @@ ifneq ($(filter pkg.ceph.arrow,$(DEB_BUILD_PROFILES)),) extraopts += -DWITH_SYSTEM_ARROW=ON endif +extraopts += -DWITH_SYSTEM_UTF8PROC=ON extraopts += -DWITH_OCF=ON -DWITH_LTTNG=ON extraopts += -DWITH_MGR_DASHBOARD_FRONTEND=OFF extraopts += -DWITH_PYTHON3=3 diff --git a/ceph/doc/cephadm/adoption.rst b/ceph/doc/cephadm/adoption.rst index 1130b4e11..6d9ec1251 100644 --- a/ceph/doc/cephadm/adoption.rst +++ b/ceph/doc/cephadm/adoption.rst @@ -34,7 +34,7 @@ Preparation latest stable release of Ceph is the default. You might be upgrading from an earlier Ceph release at the same time that you're performing this conversion; if you are upgrading from an earlier release, make sure to - follow any upgrade-releated instructions for that release. + follow any upgrade-related instructions for that release. Pass the image to cephadm with the following command: diff --git a/ceph/doc/cephadm/install.rst b/ceph/doc/cephadm/install.rst index e9272a55f..845297fa2 100644 --- a/ceph/doc/cephadm/install.rst +++ b/ceph/doc/cephadm/install.rst @@ -110,6 +110,15 @@ that case, you can install cephadm directly. For example: apt install -y cephadm + In CentOS Stream: + + .. prompt:: bash # + :substitutions: + + dnf search release-ceph + dnf install --assumeyes centos-release-ceph-|stable-release| + dnf install --assumeyes cephadm + In Fedora: .. prompt:: bash # @@ -363,10 +372,10 @@ To configure a Ceph cluster to run on a single host, use the ``--single-host-def The ``--single-host-defaults`` flag sets the following configuration options:: - global/osd_crush_choose_leaf_type = 0 + global/osd_crush_chooseleaf_type = 0 global/osd_pool_default_size = 2 mgr/mgr_standby_modules = False - + For more information on these options, see :ref:`one-node-cluster` and ``mgr_standby_modules`` in :ref:`mgr-administrator-guide`. Deployment in an isolated environment @@ -393,4 +402,4 @@ Then run bootstrap using the ``--image`` flag with your container image. For exa cephadm --image **:5000/ceph/ceph bootstrap --mon-ip ** -.. _cluster network: ../rados/configuration/network-config-ref#cluster-network \ No newline at end of file +.. _cluster network: ../rados/configuration/network-config-ref#cluster-network diff --git a/ceph/doc/cephadm/operations.rst b/ceph/doc/cephadm/operations.rst index ec6e8887a..fb7d5481d 100644 --- a/ceph/doc/cephadm/operations.rst +++ b/ceph/doc/cephadm/operations.rst @@ -323,7 +323,7 @@ To list all the configuration checks and their current states, run the following NAME HEALTHCHECK STATUS DESCRIPTION kernel_security CEPHADM_CHECK_KERNEL_LSM enabled checks SELINUX/Apparmor profiles are consistent across cluster hosts os_subscription CEPHADM_CHECK_SUBSCRIPTION enabled checks subscription states are consistent for all cluster hosts - public_network CEPHADM_CHECK_PUBLIC_MEMBERSHIP enabled check that all hosts have a NIC on the Ceph public_netork + public_network CEPHADM_CHECK_PUBLIC_MEMBERSHIP enabled check that all hosts have a NIC on the Ceph public_network osd_mtu_size CEPHADM_CHECK_MTU enabled check that OSD hosts share a common MTU setting osd_linkspeed CEPHADM_CHECK_LINKSPEED enabled check that OSD hosts share a common linkspeed network_missing CEPHADM_CHECK_NETWORK_MISSING enabled checks that the cluster/public networks defined exist on the Ceph hosts @@ -524,18 +524,18 @@ Purging a cluster .. danger:: THIS OPERATION WILL DESTROY ALL DATA STORED IN THIS CLUSTER -In order to destroy a cluster and delete all data stored in this cluster, pause -cephadm to avoid deploying new daemons. +In order to destroy a cluster and delete all data stored in this cluster, disable +cephadm to stop all orchestration operations (so we avoid deploying new daemons). .. prompt:: bash # - ceph orch pause + ceph mgr module disable cephadm Then verify the FSID of the cluster: .. prompt:: bash # - ceph fsid + ceph fsid Purge ceph daemons from all hosts in the cluster diff --git a/ceph/doc/cephadm/services/index.rst b/ceph/doc/cephadm/services/index.rst index efc8ed8f2..be9b3661a 100644 --- a/ceph/doc/cephadm/services/index.rst +++ b/ceph/doc/cephadm/services/index.rst @@ -252,7 +252,7 @@ Daemons can be explicitly placed on hosts by simply specifying them: .. prompt:: bash # - orch apply prometheus --placement="host1 host2 host3" + ceph orch apply prometheus --placement="host1 host2 host3" Or in YAML: @@ -269,7 +269,7 @@ MONs and other services may require some enhanced network specifications: .. prompt:: bash # - orch daemon add mon --placement="myhost:[v2:1.2.3.4:3300,v1:1.2.3.4:6789]=name" + ceph orch daemon add mon --placement="myhost:[v2:1.2.3.4:3300,v1:1.2.3.4:6789]=name" where ``[v2:1.2.3.4:3300,v1:1.2.3.4:6789]`` is the network address of the monitor and ``=name`` specifies the name of the new monitor. @@ -315,7 +315,7 @@ this command: .. prompt:: bash # - orch apply prometheus --placement="label:mylabel" + ceph orch apply prometheus --placement="label:mylabel" Or in YAML: @@ -334,7 +334,7 @@ Daemons can be placed on hosts as well: .. prompt:: bash # - orch apply prometheus --placement='myhost[1-3]' + ceph orch apply prometheus --placement='myhost[1-3]' Or in YAML: @@ -348,7 +348,7 @@ To place a service on *all* hosts, use ``"*"``: .. prompt:: bash # - orch apply node-exporter --placement='*' + ceph orch apply node-exporter --placement='*' Or in YAML: @@ -366,19 +366,19 @@ By specifying ``count``, only the number of daemons specified will be created: .. prompt:: bash # - orch apply prometheus --placement=3 + ceph orch apply prometheus --placement=3 To deploy *daemons* on a subset of hosts, specify the count: .. prompt:: bash # - orch apply prometheus --placement="2 host1 host2 host3" + ceph orch apply prometheus --placement="2 host1 host2 host3" If the count is bigger than the amount of hosts, cephadm deploys one per host: .. prompt:: bash # - orch apply prometheus --placement="3 host1 host2" + ceph orch apply prometheus --placement="3 host1 host2" The command immediately above results in two Prometheus daemons. diff --git a/ceph/doc/cephadm/services/monitoring.rst b/ceph/doc/cephadm/services/monitoring.rst index 5cb1537db..a17beba6d 100644 --- a/ceph/doc/cephadm/services/monitoring.rst +++ b/ceph/doc/cephadm/services/monitoring.rst @@ -387,6 +387,26 @@ added to the default receivers' ```` configuration. Run ``reconfig`` on the service to update its configuration: +.. prompt:: bash # + + ceph orch reconfig alertmanager + +Turn on Certificate Validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are using certificates for alertmanager and want to make sure +these certs are verified, you should set the "secure" option to +true in your alertmanager spec (this defaults to false). + +.. code-block:: yaml + + service_type: alertmanager + spec: + secure: true + +If you already had alertmanager daemons running before applying the spec +you must reconfigure them to update their configuration + .. prompt:: bash # ceph orch reconfig alertmanager diff --git a/ceph/doc/cephadm/services/osd.rst b/ceph/doc/cephadm/services/osd.rst index c96d8a510..c4260b84d 100644 --- a/ceph/doc/cephadm/services/osd.rst +++ b/ceph/doc/cephadm/services/osd.rst @@ -138,6 +138,12 @@ There are a few ways to create new OSDs: ceph orch daemon add osd host1:/dev/sdb + Advanced OSD creation from specific devices on a specific host: + + .. prompt:: bash # + + ceph orch daemon add osd host1:data_devices=/dev/sda,/dev/sdb,db_devices=/dev/sdc,osds_per_device=2 + * You can use :ref:`drivegroups` to categorize device(s) based on their properties. This might be useful in forming a clearer picture of which devices are available to consume. Properties include device type (SSD or diff --git a/ceph/doc/cephadm/upgrade.rst b/ceph/doc/cephadm/upgrade.rst index c6f58ad1f..b7f60b7fd 100644 --- a/ceph/doc/cephadm/upgrade.rst +++ b/ceph/doc/cephadm/upgrade.rst @@ -188,3 +188,100 @@ you need. For example, the following command upgrades to a development build: ceph orch upgrade start --image quay.io/ceph-ci/ceph:recent-git-branch-name For more information about available container images, see :ref:`containers`. + +Staggered Upgrade +================= + +Some users may prefer to upgrade components in phases rather than all at once. +The upgrade command, starting in 16.2.10 and 17.2.1 allows parameters +to limit which daemons are upgraded by a single upgrade command. The options in +include ``daemon_types``, ``services``, ``hosts`` and ``limit``. ``daemon_types`` +takes a comma-separated list of daemon types and will only upgrade daemons of those +types. ``services`` is mutually exclusive with ``daemon_types``, only takes services +of one type at a time (e.g. can't provide an OSD and RGW service at the same time), and +will only upgrade daemons belonging to those services. ``hosts`` can be combined +with ``daemon_types`` or ``services`` or provided on its own. The ``hosts`` parameter +follows the same format as the command line options for :ref:`orchestrator-cli-placement-spec`. +``limit`` takes an integer > 0 and provides a numerical limit on the number of +daemons cephadm will upgrade. ``limit`` can be combined with any of the other +parameters. For example, if you specify to upgrade daemons of type osd on host +Host1 with ``limit`` set to 3, cephadm will upgrade (up to) 3 osd daemons on +Host1. + +Example: specifying daemon types and hosts: + +.. prompt:: bash # + + ceph orch upgrade start --image --daemon-types mgr,mon --hosts host1,host2 + +Example: specifying services and using limit: + +.. prompt:: bash # + + ceph orch upgrade start --image --services rgw.example1,rgw.example2 --limit 2 + +.. note:: + + Cephadm strictly enforces an order to the upgrade of daemons that is still present + in staggered upgrade scenarios. The current upgrade ordering is + ``mgr -> mon -> crash -> osd -> mds -> rgw -> rbd-mirror -> cephfs-mirror -> iscsi -> nfs``. + If you specify parameters that would upgrade daemons out of order, the upgrade + command will block and note which daemons will be missed if you proceed. + +.. note:: + + Upgrade commands with limiting parameters will validate the options before beginning the + upgrade, which may require pulling the new container image. Do not be surprised + if the upgrade start command takes a while to return when limiting parameters are provided. + +.. note:: + + In staggered upgrade scenarios (when a limiting parameter is provided) monitoring + stack daemons including Prometheus and node-exporter are refreshed after the Manager + daemons have been upgraded. Do not be surprised if Manager upgrades thus take longer + than expected. Note that the versions of monitoring stack daemons may not change between + Ceph releases, in which case they are only redeployed. + +Upgrading to a version that supports staggered upgrade from one that doesn't +---------------------------------------------------------------------------- + +While upgrading from a version that already supports staggered upgrades the process +simply requires providing the necessary arguments. However, if you wish to upgrade +to a version that supports staggered upgrade from one that does not, there is a +workaround. It requires first manually upgrading the Manager daemons and then passing +the limiting parameters as usual. + +.. warning:: + Make sure you have multiple running mgr daemons before attempting this procedure. + +To start with, determine which Manager is your active one and which are standby. This +can be done in a variety of ways such as looking at the ``ceph -s`` output. Then, +manually upgrade each standby mgr daemon with: + +.. prompt:: bash # + + ceph orch daemon redeploy mgr.example1.abcdef --image + +.. note:: + + If you are on a very early version of cephadm (early Octopus) the ``orch daemon redeploy`` + command may not have the ``--image`` flag. In that case, you must manually set the + Manager container image ``ceph config set mgr container_image `` and then + redeploy the Manager ``ceph orch daemon redeploy mgr.example1.abcdef`` + +At this point, a Manager fail over should allow us to have the active Manager be one +running the new version. + +.. prompt:: bash # + + ceph mgr fail + +Verify the active Manager is now one running the new version. To complete the Manager +upgrading: + +.. prompt:: bash # + + ceph orch upgrade start --image --daemon-types mgr + +You should now have all your Manager daemons on the new version and be able to +specify the limiting parameters for the rest of the upgrade. diff --git a/ceph/doc/cephfs/add-remove-mds.rst b/ceph/doc/cephfs/add-remove-mds.rst index 6a6bc08de..12e4576a0 100644 --- a/ceph/doc/cephfs/add-remove-mds.rst +++ b/ceph/doc/cephfs/add-remove-mds.rst @@ -1,3 +1,10 @@ +.. note:: + It is highly recommended to use :doc:`/cephadm/index` or another Ceph + orchestrator for setting up the ceph cluster. Use this approach only if you + are setting up the ceph cluster manually. If one still intends to use the + manual way for deploying MDS daemons, :doc:`/cephadm/services/mds/` can + also be used. + ============================ Deploying Metadata Servers ============================ @@ -62,7 +69,7 @@ means limiting its cache size. Adding an MDS ============= -#. Create an mds data point ``/var/lib/ceph/mds/ceph-${id}``. The daemon only uses this directory to store its keyring. +#. Create an mds directory ``/var/lib/ceph/mds/ceph-${id}``. The daemon only uses this directory to store its keyring. #. Create the authentication key, if you use CephX: :: diff --git a/ceph/doc/cephfs/fs-volumes.rst b/ceph/doc/cephfs/fs-volumes.rst index 6ef08a2e9..eb33b1f93 100644 --- a/ceph/doc/cephfs/fs-volumes.rst +++ b/ceph/doc/cephfs/fs-volumes.rst @@ -212,7 +212,7 @@ Fetch the absolute path of a subvolume using:: $ ceph fs subvolume getpath [--group_name ] -Fetch the metadata of a subvolume using:: +Fetch the information of a subvolume using:: $ ceph fs subvolume info [--group_name ] @@ -260,6 +260,31 @@ List subvolumes using:: .. note:: subvolumes that are removed but have snapshots retained, are also listed. +Set custom metadata on the subvolume as a key-value pair using:: + + $ ceph fs subvolume metadata set [--group_name ] + +.. note:: If the key_name already exists then the old value will get replaced by the new value. + +.. note:: key_name and value should be a string of ASCII characters (as specified in python's string.printable). key_name is case-insensitive and always stored in lower case. + +.. note:: Custom metadata on a subvolume is not preserved when snapshotting the subvolume, and hence, is also not preserved when cloning the subvolume snapshot. + +Get custom metadata set on the subvolume using the metadata key:: + + $ ceph fs subvolume metadata get [--group_name ] + +List custom metadata (key-value pairs) set on the subvolume using:: + + $ ceph fs subvolume metadata ls [--group_name ] + +Remove custom metadata set on the subvolume using the metadata key:: + + $ ceph fs subvolume metadata rm [--group_name ] [--force] + +Using the '--force' flag allows the command to succeed that would otherwise +fail if the metadata key did not exist. + Create a snapshot of a subvolume using:: $ ceph fs subvolume snapshot create [--group_name ] @@ -278,7 +303,7 @@ List snapshots of a subvolume using:: $ ceph fs subvolume snapshot ls [--group_name ] -Fetch the metadata of a snapshot using:: +Fetch the information of a snapshot using:: $ ceph fs subvolume snapshot info [--group_name ] @@ -289,6 +314,31 @@ The output format is json and contains fields as follows. * has_pending_clones: "yes" if snapshot clone is in progress otherwise "no" * size: snapshot size in bytes +Set custom metadata on the snapshot as a key-value pair using:: + + $ ceph fs subvolume snapshot metadata set [--group_name ] + +.. note:: If the key_name already exists then the old value will get replaced by the new value. + +.. note:: The key_name and value should be a string of ASCII characters (as specified in python's string.printable). The key_name is case-insensitive and always stored in lower case. + +.. note:: Custom metadata on a snapshots is not preserved when snapshotting the subvolume, and hence, is also not preserved when cloning the subvolume snapshot. + +Get custom metadata set on the snapshot using the metadata key:: + + $ ceph fs subvolume snapshot metadata get [--group_name ] + +List custom metadata (key-value pairs) set on the snapshot using:: + + $ ceph fs subvolume snapshot metadata ls [--group_name ] + +Remove custom metadata set on the snapshot using the metadata key:: + + $ ceph fs subvolume snapshot metadata rm [--group_name ] [--force] + +Using the '--force' flag allows the command to succeed that would otherwise +fail if the metadata key did not exist. + Cloning Snapshots ----------------- @@ -340,8 +390,14 @@ A clone can be in one of the following states: #. `in-progress` : Clone operation is in progress #. `complete` : Clone operation has successfully finished #. `failed` : Clone operation has failed +#. `canceled` : Clone operation is cancelled by user + +The reason for a clone failure is shown as below: -Sample output from an `in-progress` clone operation:: +#. `errno` : error number +#. `error_msg` : failure error string + +Sample output of an `in-progress` clone operation:: $ ceph fs subvolume snapshot clone cephfs subvol1 snap1 clone1 $ ceph fs clone status cephfs clone1 @@ -356,6 +412,28 @@ Sample output from an `in-progress` clone operation:: } } +.. note:: The `failure` section will be shown only if the clone is in failed or cancelled state + +Sample output of a `failed` clone operation:: + + $ ceph fs subvolume snapshot clone cephfs subvol1 snap1 clone1 + $ ceph fs clone status cephfs clone1 + { + "status": { + "state": "failed", + "source": { + "volume": "cephfs", + "subvolume": "subvol1", + "snapshot": "snap1" + "size": "104857600" + }, + "failure": { + "errno": "122", + "errstr": "Disk quota exceeded" + } + } + } + (NOTE: since `subvol1` is in default group, `source` section in `clone status` does not include group name) .. note:: Cloned subvolumes are accessible only after the clone operation has successfully completed. diff --git a/ceph/doc/dev/cephadm/developing-cephadm.rst b/ceph/doc/dev/cephadm/developing-cephadm.rst index 5b9ab747c..64f29c9a1 100644 --- a/ceph/doc/dev/cephadm/developing-cephadm.rst +++ b/ceph/doc/dev/cephadm/developing-cephadm.rst @@ -264,9 +264,18 @@ In order to setup Cephadm's box run:: .. note:: It is recommended to run box with verbose (-v). -After getting all needed images we can run:: +After getting all needed images we can create a simple cluster without osds and hosts with:: - sudo box -v cluster start --osds 3 --hosts 3 + sudo box -v cluster start + +If you want to deploy the cluster with more osds and hosts:: + # 3 osds and 3 hosts by default + sudo box -v cluster start --extended + # explicitly change number of hosts and osds + sudo box -v cluster start --extended --osds 5 --hosts 5 + +Without the extended option, explicitly adding either more hosts or osds won't change the state +of the cluster. .. note:: Cluster start will try to setup even if cluster setup was not called. .. note:: Osds are created with loopback devices and hence, sudo is needed to diff --git a/ceph/doc/dev/developer_guide/basic-workflow.rst b/ceph/doc/dev/developer_guide/basic-workflow.rst index ecaad5982..6bd98ebb4 100644 --- a/ceph/doc/dev/developer_guide/basic-workflow.rst +++ b/ceph/doc/dev/developer_guide/basic-workflow.rst @@ -29,50 +29,79 @@ The following chart illustrates the basic Ceph development workflow: \--------------/ This page assumes that you are a new contributor with an idea for a bugfix or -enhancement, but do not know how to proceed. Watch the `Getting Started with -Ceph Development `_ video for a -practical summary of this workflow. +an enhancement, but you do not know how to proceed. Watch the `Getting Started +with Ceph Development `_ video for +a practical summary of this workflow. Updating the tracker -------------------- -Before you start, you should know the :ref:`issue-tracker` (Redmine) number -of the bug you intend to fix. If there is no tracker issue, now is the time to -create one for code changes. Straightforward documentation cleanup does -not necessarily require a corresponding tracker issue. However, an issue -(ticket) should be created if one is adding new documentation chapters or -files, or for other substantial changes. +Find the :ref:`issue-tracker` (Redmine) number of the bug you intend to fix. If +no tracker issue exists, create one. There is only one case in which you do not +have to create a Redmine tracker issue: the case of minor documentation changes. -The tracker ticket serves to explain the issue (bug) to your fellow Ceph -developers and keep them informed as you make progress toward resolution. To -this end, please provide a descriptive title and write appropriate information -and details into the description. When composing the ticket's title, consider "If I -want to search for this ticket two years from now, what keywords will I search -for?" +Simple documentation cleanup does not require a corresponding tracker issue. +Major documenatation changes do require a tracker issue. Major documentation +changes include adding new documentation chapters or files, and making +substantial changes to the structure or content of the documentation. -If you have sufficient tracker permissions, assign the bug to yourself by -setting the ``Assignee`` field. If your tracker permissions have not been -elevated, simply add a comment with a short message like "I am working on this -issue". +A (Redmine) tracker ticket explains the issue (bug) to other Ceph developers to +keep them informed as the bug nears resolution. Provide a useful, clear title +and include detailed information in the description. When composing the title +of the ticket, ask yourself "If I need to search for this ticket two years from +now, which keywords am I likely to search for?" Then include those keywords in +the title. -Forking and Cloning the Ceph Repository ---------------------------------------- +If your tracker permissions are elevated, assign the bug to yourself by setting +the ``Assignee`` field. If your tracker permissions have not been elevated, +just add a comment with a short message that says "I am working on this issue". -This section, and the ones that follow, correspond to nodes in the above chart. +Ceph Workflow Overview +---------------------- -The upstream code is found at https://github.com/ceph/ceph.git, which is known -as the "upstream repo", or simply "upstream". As the chart shows, we will make -a local copy of this repository, modify it, test our modifications, then submit -the modifications for review and merging. +Three repositories are involved in the Ceph workflow. They are: -A local copy of the upstream code is made by +1. The upstream repository (ceph/ceph) +2. Your fork of the upstream repository (your_github_id/ceph) +3. Your local working copy of the repository (on your workstation) -1. Forking the upstream repo on GitHub, and -2. Cloning your fork to make a local working copy +The procedure for making changes to the Ceph repository is as follows: +#. Configure your local environment -Forking The Ceph Repository -^^^^^^^^^^^^^^^^^^^^^^^^^^^ + #. :ref:`Create a fork` of the "upstream Ceph" + repository. + + #. :ref:`Clone the fork` to your local filesystem. + +#. Fix the bug + + #. :ref:`Synchronize local master with upstream master`. + + #. :ref:`Create a bugfix branch` in your local working copy. + + #. :ref:`Make alterations to the local working copy of the repository in your + local filesystem`. + + #. :ref:`Push the changes in your local working copy to your fork`. + +#. Create a Pull Request to push the change upstream + + #. Create a Pull Request that asks for your changes to be added into the + "upstream Ceph" repository. + +Preparing Your Local Working Copy of the Ceph Repository +-------------------------------------------------------- + +The procedures in this section, "Preparing Your Local Working Copy of the Ceph +Repository", must be followed only when you are first setting up your local +environment. If this is your first time working with the Ceph project, then +these commands are necessary and are the first commands that you should run. + +.. _forking: + +Creating a Fork of the Ceph Repository +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ See the `GitHub documentation `_ for @@ -80,58 +109,81 @@ detailed instructions on forking. In short, if your GitHub username is "mygithubaccount", your fork of the upstream repo will appear at ``https://github.com/mygithubaccount/ceph``. +.. _cloning: + Cloning Your Fork ^^^^^^^^^^^^^^^^^ -Once you have created your fork, clone it by running: +After you have created your fork, clone it by running the following command: .. prompt:: bash $ git clone https://github.com/mygithubaccount/ceph -You must fork the Ceph repository before you clone it. Without forking, you cannot -open a `GitHub pull request +You must fork the Ceph repository before you clone it. If you fail to fork, +you cannot open a `GitHub pull request `_. For more information on using GitHub, refer to `GitHub Help `_. Configuring Your Local Environment ----------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In the local environment created in the previous step, you now have a copy of -the ``master`` branch in ``remotes/origin/master``. This fork -(https://github.com/mygithubaccount/ceph.git) is frozen in time and the -upstream repo (https://github.com/ceph/ceph.git, typically abbreviated to -``ceph/ceph.git``) is updated frequently by other contributors. This means that -you must sync your fork periodically. Failure to synchronize your fork may -result in your commits and pull requests failing to merge because they refer to -file contents that have changed since you last synchronized your fork. +The commands in this section configure your local git environment so that it +generates "Signed-off-by:" tags. They also set up your local environment so +that it can stay synchronized with the upstream repository. -Configure your local git environment with your name and email address. +These commands are necessary only during the initial setup of your local +working copy. Another way to say that is "These commands are necessary +only the first time that you are working with the Ceph repository. They are, +however, unavoidable, and if you fail to run them then you will not be able +to work on the Ceph repository.". -.. prompt:: bash $ +1. Configure your local git environment with your name and email address. - git config user.name "FIRST_NAME LAST_NAME" - git config user.email "MY_NAME@example.com" + .. prompt:: bash $ -Add the upstream repo as a "remote" and fetch it: + git config user.name "FIRST_NAME LAST_NAME" + git config user.email "MY_NAME@example.com" -.. prompt:: bash $ +2. Add the upstream repo as a "remote" and fetch it: - git remote add ceph https://github.com/ceph/ceph.git - git fetch ceph + .. prompt:: bash $ + + git remote add ceph https://github.com/ceph/ceph.git + git fetch ceph + + These commands fetch all the branches and commits from ``ceph/ceph.git`` to + the local git repo as ``remotes/ceph/$BRANCH_NAME`` and can be referenced as + ``ceph/$BRANCH_NAME`` in local git commands. + +Fixing the Bug +-------------- + +.. _synchronizing: + +Synchronizing Local Master with Upstream Master +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -These commands fetch all the branches and commits from ``ceph/ceph.git`` to the -local git repo as ``remotes/ceph/$BRANCH_NAME`` and can be referenced as -``ceph/$BRANCH_NAME`` in local git commands. +In your local git environment, there is a copy of the ``master`` branch in +``remotes/origin/master``. This is called "local master". This copy of the +master branch (https://github.com/your_github_id/ceph.git) is "frozen in time" +at the moment that you cloned it, but the upstream repo +(https://github.com/ceph/ceph.git, typically abbreviated to ``ceph/ceph.git``) +that it was forked from is not frozen in time: the upstream repo is still being +updated by other contributors. +Because upstream master is continually receiving updates from other +contributors, your fork will drift farther and farther from the state of the +upstream repo when you cloned it. -Resetting Local Master to Upstream Master -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +You must keep your fork's master branch synchronized with upstream master in +order to reduce drift between your fork's master branch and the upstream master +branch. -Your local ``master`` branch can be reset to the upstream Ceph ``master`` -branch by running the following commands: +Here are the commands for keeping your fork synchronized with the +upstream repository: .. prompt:: bash $ @@ -143,8 +195,10 @@ branch by running the following commands: This procedure should be followed often, in order to keep your local ``master`` in sync with upstream ``master``. +.. _bugfix_branch: + Creating a Bugfix branch ------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ Create a branch for your bugfix: @@ -154,73 +208,122 @@ Create a branch for your bugfix: git checkout -b fix_1 git push -u origin fix_1 -This creates a local branch called ``fix_1`` in our GitHub fork. At this point, -the ``fix_1`` branch is identical to the ``master`` branch, but not for long! -You are now ready to modify the code. Be careful to always run `git checkout -master` first, otherwise you may find commits from an unrelated branch mixed -with your new work. +The first command (git checkout master) makes sure that the bugfix branch +"fix_1" is created from the most recent state of the master branch of the +upstream repository. -Fixing the bug locally ----------------------- +The second command (git checkout -b fix_1) creates a "bugfix branch" called +"fix_1" in your local working copy of the repository. The changes that you make +in order to fix the bug will be commited to this branch. -In the `Ceph issue tracker `_, change the status of -the tracker issue to "In progress". This communicates to other Ceph -contributors that you have begun working on a fix, which helps to avoid -duplication of effort. If you don't have permission to change that field, your -previous comment that you are working on the issue is sufficient. +The third command (git push -u origin fix_1) pushes the bugfix branch from +your local working repository to your fork of the upstream repository. -Your fix may be very simple and require only minimal testing. But that's not -likely. It is more likely that the process of fixing your bug will be iterative -and will involve trial and error, as well as skill. An explanation of how to -fix bugs is beyond the scope of this document. Instead, we focus on the -mechanics of the process in the context of the Ceph project. +.. _fixing_bug_locally: -For a detailed discussion of the tools available for validating bugfixes, -see the chapters on testing. +Fixing the bug in the local working copy +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For now, let us assume that you have finished work on the bugfix, that you have -tested the bugfix , and that you believe that it works. Commit the changes to -your local branch using the ``--signoff`` option (here represented as the `s` -portion of the `-as` flag): +#. Updating the tracker -.. prompt:: bash $ + In the `Ceph issue tracker `_, change the status + of the tracker issue to "In progress". This communicates to other Ceph + contributors that you have begun working on a fix, which helps to avoid + duplication of effort. If you don't have permission to change that field, + your comment that you are working on the issue is sufficient. - git commit -as +#. Fixing the bug itself -Push the changes to your fork: + This guide cannot tell you how to fix the bug that you have chosen to fix. + This guide assumes that you know what required improvement, and that you + know what to do to provide that improvement. -.. prompt:: bash $ + It might be that your fix is simple and requires only minimal testing. But + that's unlikely. It is more likely that the process of fixing your bug will + be iterative and will involve trial, error, skill, and patience. + + For a detailed discussion of the tools available for validating bugfixes, + see the chapters on testing. + +Pushing the Fix to Your Fork +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You have finished work on the bugfix. You have tested the bugfix, and you +believe that it works. + +#. Commit the changes to your local working copy. + + Commit the changes to the `fix_1` branch of your local working copy by using + the ``--signoff`` option (here represented as the `s` portion of the `-as` + flag): + + .. prompt:: bash $ + + git commit -as + + .. _push_changes: + +#. Push the changes to your fork: + + Push the changes from the `fix_1` branch of your local working copy to the + `fix_1` branch of your fork of the upstream repository: + + .. prompt:: bash $ + + git push origin fix_1 + + .. note:: + + In the command `git push origin fix_1`, `origin` is the name of your fork + of the upstream Ceph repository, and can be thought of as a nickname for + `git@github.com:username/ceph.git`, where `username` is your GitHub + username. + + It is possible that `origin` is not the name of your fork. Discover the + name of your fork by running `git remote -v`, as shown here: + + .. code-block:: bash + + $ git remote -v + ceph https://github.com/ceph/ceph.git (fetch) + ceph https://github.com/ceph/ceph.git (push) + origin git@github.com:username/ceph.git (fetch) + origin git@github.com:username/ceph.git (push) + + The line "origin git@github.com:username/ceph.git (fetch)" and the line + "origin git@github.com:username/ceph.git (push)" provide the information + that "origin" is the name of your fork of the Ceph repository. - git push origin fix_1 Opening a GitHub pull request ----------------------------- -The next step is to open a GitHub pull request (PR). This makes your bugfix -visible to the community of Ceph contributors. They will review it and may -perform additional testing and / or request changes. +After you have pushed the bugfix to your fork, open a GitHub pull request +(PR). This makes your bugfix visible to the community of Ceph contributors. +They will review it. They may perform additional testing on your bugfix, and +they might request changes to the bugfix. -This is the point where you "go public" with your modifications. Be prepared -to receive suggestions and constructive criticism in the form of comments -within the PR. Don't worry! The Ceph project is a friendly place! +Be prepared to receive suggestions and constructive criticism in the form of +comments within the PR. -If you are uncertain how to create and manage pull requests, you may read -`this GitHub pull request tutorial`_. +If you don't know how to create and manage pull requests, read `this GitHub +pull request tutorial`_. .. _`this GitHub pull request tutorial`: https://help.github.com/articles/using-pull-requests/ -For ideas on what constitutes a "good" pull request, see +To learn what constitutes a "good" pull request, see the `Git Commit Good Practice`_ article at the `OpenStack Project Wiki`_. .. _`Git Commit Good Practice`: https://wiki.openstack.org/wiki/GitCommitMessages .. _`OpenStack Project Wiki`: https://wiki.openstack.org/wiki/Main_Page -and our own `Submitting Patches `_ document. +See also our own `Submitting Patches +`_ document. -Once your pull request (PR) is opened, update the :ref:`issue-tracker` by -adding a comment directing other contributors to your PR. The comment can be -as simple as:: +After your pull request (PR) has been opened, update the :ref:`issue-tracker` +by adding a comment directing other contributors to your PR. The comment can be +as simple as this:: *PR*: https://github.com/ceph/ceph/pull/$NUMBER_OF_YOUR_PULL_REQUEST @@ -229,8 +332,8 @@ Understanding Automated PR validation When you create or update your PR, the Ceph project's `Continuous Integration (CI) `_ infrastructure -automatically tests it. At the time of this writing (September 2020), the -automated CI testing included five tests: +automatically tests it. At the time of this writing (May 2022), the automated +CI testing included many tests. These five are among them: #. a test to check that the commits are properly signed (see :ref:`submitting-patches`): #. a test to check that the documentation builds @@ -238,15 +341,15 @@ automated CI testing included five tests: #. a test to check that the API is in order #. a :ref:`make check` test -Additional tests may be performed depending on which files your PR modifies. +Additional tests may be run depending on which files your PR modifies. -The :ref:`make check` test builds the PR and runs it through a battery of -tests. These tests run on servers operated by the Ceph Continuous -Integration (CI) team. When the tests complete, the result will be shown -on GitHub in the pull request itself. +The :ref:`make check` test builds the PR and runs it through a +battery of tests. These tests run on servers that are operated by the Ceph +Continuous Integration (CI) team. When the tests have completed their run, the +result is shown on GitHub in the pull request itself. -You should test your modifications before you open a PR. -Refer to the chapters on testing for details. +Test your modifications before you open a PR. Refer to the chapters +on testing for details. Notes on PR make check test ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -254,26 +357,28 @@ Notes on PR make check test The GitHub :ref:`make check` test is driven by a Jenkins instance. Jenkins merges your PR branch into the latest version of the base branch before -starting tests. This means that you don't have to rebase the PR to pick up any fixes. +it starts any tests. This means that you don't have to rebase the PR in order +to pick up any fixes. You can trigger PR tests at any time by adding a comment to the PR - the -comment should contain the string "test this please". Since a human subscribed -to the PR might interpret that as a request for him or her to test the PR, we -recommend that you address Jenkins directly. For example, write "jenkins retest -this please". For efficiency a single re-test can also be requested with -e.g. "jenkins test signed". For reference, a list of these requests is -automatically added to the end of each new PR's description. +comment should contain the string "test this please". Since a human who is +subscribed to the PR might interpret that as a request for him or her to test +the PR, you must address Jenkins directly. For example, write "jenkins retest +this please". If you need to run only one of the tests, you can request it with +a command like "jenkins test signed". A list of these requests is automatically +added to the end of each new PR's description, so check there to find the +single test you need. If there is a build failure and you aren't sure what caused it, check the -:ref:`make check` log. To access it, click on the "details" (next -to the :ref:`make check` test in the PR) link to enter the Jenkins web -GUI. Then click on "Console Output" (on the left). - -Jenkins is configured to search logs for strings known to have been associated -with :ref:`make check` failures in the past. However, there is no -guarantee that these known strings are associated with any given -:ref:`make check` failure. You'll have to read through the log to determine the -cause of your specific failure. +:ref:`make check` log. To access the make check log, click the +"details" (next to the :ref:`make check` test in the PR) link to +enter the Jenkins web GUI. Then click "Console Output" (on the left). + +Jenkins is configured to search logs for strings that are known to have been +associated with :ref:`make check` failures in the past. However, +there is no guarantee that these known strings are associated with any given +:ref:`make check` failure. You'll have to read through the log to +determine the cause of your specific failure. Integration tests AKA ceph-qa-suite ----------------------------------- diff --git a/ceph/doc/man/8/ceph.rst b/ceph/doc/man/8/ceph.rst index 639d85e56..77ecd1fab 100644 --- a/ceph/doc/man/8/ceph.rst +++ b/ceph/doc/man/8/ceph.rst @@ -1615,9 +1615,9 @@ Options Make less verbose. -.. option:: -f {json,json-pretty,xml,xml-pretty,plain}, --format +.. option:: -f {json,json-pretty,xml,xml-pretty,plain,yaml}, --format - Format of output. + Format of output. Note: yaml is only valid for orch commands. .. option:: --connect-timeout CLUSTER_TIMEOUT diff --git a/ceph/doc/man/8/cephfs-shell.rst b/ceph/doc/man/8/cephfs-shell.rst index 65b344fa9..be09f0b19 100644 --- a/ceph/doc/man/8/cephfs-shell.rst +++ b/ceph/doc/man/8/cephfs-shell.rst @@ -84,7 +84,7 @@ Copy a file/directory to Ceph File System from Local File System. Usage : - put [options] [target_path] + put [options] * source_path - local file/directory path to be copied to cephfs. * if `.` copies all the file/directories in the local working directory. @@ -104,7 +104,7 @@ Copy a file from Ceph File System to Local File System. Usage : - get [options] [target_path] + get [options] * source_path - remote file/directory path which is to be copied to local file system. * if `.` copies all the file/directories in the remote working directory. diff --git a/ceph/doc/mgr/dashboard.rst b/ceph/doc/mgr/dashboard.rst index 60fca266e..5bfcced4c 100644 --- a/ceph/doc/mgr/dashboard.rst +++ b/ceph/doc/mgr/dashboard.rst @@ -581,7 +581,7 @@ running/deployed:: $ ceph dashboard set-grafana-api-url # default: '' -The format of url is : `::` +The format of the URL : `://:` .. note:: @@ -596,7 +596,7 @@ The format of url is : `::` If you are using a self-signed certificate for Grafana, disable certificate verification in the dashboard to avoid refused connections, which can be a result of certificates signed by an unknown CA or that do not -matchn the host name:: +match the host name:: $ ceph dashboard set-grafana-api-ssl-verify False diff --git a/ceph/doc/rados/operations/control.rst b/ceph/doc/rados/operations/control.rst index c5f911f81..ebe38a1d2 100644 --- a/ceph/doc/rados/operations/control.rst +++ b/ceph/doc/rados/operations/control.rst @@ -243,18 +243,23 @@ Deployments utilizing Nautilus (or later revisions of Luminous and Mimic) that have no pre-Luminous cients may instead wish to instead enable the `balancer`` module for ``ceph-mgr``. -Add/remove an IP address to/from the blocklist. When adding an address, +Add/remove an IP address or CIDR range to/from the blocklist. +When adding to the blocklist, you can specify how long it should be blocklisted in seconds; otherwise, it will default to 1 hour. A blocklisted address is prevented from -connecting to any OSD. Blocklisting is most often used to prevent a -lagging metadata server from making bad changes to data on the OSDs. +connecting to any OSD. If you blocklist an IP or range containing an OSD, be aware +that OSD will also be prevented from performing operations on its peers where it +acts as a client. (This includes tiering and copy-from functionality.) + +If you want to blocklist a range (in CIDR format), you may do so by +including the ``range`` keyword. These commands are mostly only useful for failure testing, as blocklists are normally maintained automatically and shouldn't need manual intervention. :: - ceph osd blocklist add ADDRESS[:source_port] [TIME] - ceph osd blocklist rm ADDRESS[:source_port] + ceph osd blocklist ["range"] add ADDRESS[:source_port][/netmask_bits] [TIME] + ceph osd blocklist ["range"] rm ADDRESS[:source_port][/netmask_bits] Creates/deletes a snapshot of a pool. :: diff --git a/ceph/doc/rados/operations/placement-groups.rst b/ceph/doc/rados/operations/placement-groups.rst index 5b8e07a0d..d8d1a532b 100644 --- a/ceph/doc/rados/operations/placement-groups.rst +++ b/ceph/doc/rados/operations/placement-groups.rst @@ -724,4 +724,4 @@ entirely. To mark the "unfound" objects as "lost", execute the following:: .. _Create a Pool: ../pools#createpool .. _Mapping PGs to OSDs: ../../../architecture#mapping-pgs-to-osds -.. _pgcalc: http://ceph.com/pgcalc/ +.. _pgcalc: https://old.ceph.com/pgcalc/ diff --git a/ceph/doc/radosgw/pools.rst b/ceph/doc/radosgw/pools.rst index a9b00eac1..bb1246c1f 100644 --- a/ceph/doc/radosgw/pools.rst +++ b/ceph/doc/radosgw/pools.rst @@ -17,7 +17,7 @@ exist, it will create that pool with the default values from are sufficient for some pools, but others (especially those listed in ``placement_pools`` for the bucket index and data) will require additional tuning. We recommend using the `Ceph Placement Group’s per Pool -Calculator `__ to calculate a suitable number of +Calculator `__ to calculate a suitable number of placement groups for these pools. See `Pools `__ for details on pool creation. diff --git a/ceph/doc/rbd/rbd-persistent-write-log-cache.rst b/ceph/doc/rbd/rbd-persistent-write-log-cache.rst index ae3809ba3..d1814a923 100644 --- a/ceph/doc/rbd/rbd-persistent-write-log-cache.rst +++ b/ceph/doc/rbd/rbd-persistent-write-log-cache.rst @@ -60,10 +60,6 @@ Here are some cache configuration settings: - ``rbd_persistent_cache_size`` The cache size per image. The minimum cache size is 1 GB. -- ``rbd_persistent_cache_log_periodic_stats`` This is a debug option. It is - used to emit periodic perf stats to the debug log if ``debug rbd pwl`` is - set to ``1`` or higher. - The above configurations can be set per-host, per-pool, per-image etc. Eg, to set per-host, add the overrides to the appropriate `section`_ in the host's ``ceph.conf`` file. To set per-pool, per-image, etc, please refer to the @@ -79,30 +75,59 @@ users may use the command ``rbd status``. :: rbd status {pool-name}/{image-name} The status of the cache is shown, including present, clean, cache size and the -location. Currently the status is updated only at the time the cache is opened -and closed and therefore may appear to be out of date (e.g. show that the cache -is clean when it is actually dirty). +location as well as some basic metrics. For example:: $ rbd status rbd/foo - Watchers: none - Image cache state: {"present":"true","empty":"false","clean":"true","cache_type":"ssd","pwl_host":"sceph9","pwl_path":"/tmp/rbd-pwl.rbd.abcdef123456.pool","pwl_size":1073741824} + Watchers: + watcher=10.10.0.102:0/1061883624 client.25496 cookie=140338056493088 + Persistent cache state: + host: sceph9 + path: /mnt/nvme0/rbd-pwl.rbd.101e5824ad9a.pool + size: 1 GiB + mode: ssd + stats_timestamp: Sun Apr 10 13:26:32 2022 + present: true empty: false clean: false + allocated: 509 MiB + cached: 501 MiB + dirty: 338 MiB + free: 515 MiB + hits_full: 1450 / 61% + hits_partial: 0 / 0% + misses: 924 + hit_bytes: 192 MiB / 66% + miss_bytes: 97 MiB + +Flush Cache +----------- + +To flush a cache file with ``rbd``, specify the ``persistent-cache flush`` +command, the pool name and the image name. :: + + rbd persistent-cache flush {pool-name}/{image-name} + +If the application dies unexpectedly, this command can also be used to flush +the cache back to OSDs. + +For example:: + + $ rbd persistent-cache flush rbd/foo -Discard Cache -------------- +Invalidate Cache +---------------- -To discard a cache file with ``rbd``, specify the ``image-cache invalidate`` -option, the pool name and the image name. :: +To invalidate (discard) a cache file with ``rbd``, specify the +``persistent-cache invalidate`` command, the pool name and the image name. :: - rbd image-cache invalidate {pool-name}/{image-name} + rbd persistent-cache invalidate {pool-name}/{image-name} -The command removes the cache metadata of the corresponding image, disable +The command removes the cache metadata of the corresponding image, disables the cache feature and deletes the local cache file if it exists. For example:: - $ rbd image-cache invalidate rbd/foo + $ rbd persistent-cache invalidate rbd/foo .. _section: ../../rados/configuration/ceph-conf/#configuration-sections .. _commands: ../../man/8/rbd#commands diff --git a/ceph/doc/releases/index.rst b/ceph/doc/releases/index.rst index d662d7076..ac9bc4d0b 100644 --- a/ceph/doc/releases/index.rst +++ b/ceph/doc/releases/index.rst @@ -19,6 +19,7 @@ security fixes. :maxdepth: 1 :hidden: + Quincy (v17.2.*) Pacific (v16.2.*) Octopus (v15.2.*) @@ -55,7 +56,10 @@ receive bug fixes or backports). Release timeline ---------------- -.. ceph_timeline:: releases.yml pacific octopus nautilus +.. ceph_timeline:: releases.yml quincy pacific octopus + +.. _Quincy: quincy +.. _17.2.0: quincy#v17-2-0-quincy .. _Pacific: pacific diff --git a/ceph/doc/releases/quincy.rst b/ceph/doc/releases/quincy.rst new file mode 100644 index 000000000..c56f104e4 --- /dev/null +++ b/ceph/doc/releases/quincy.rst @@ -0,0 +1,445 @@ +====== +Quincy +====== + +Quincy is the 17th stable release of Ceph. It is named after Squidward +Quincy Tentacles from Spongebob Squarepants. + +v17.2.0 Quincy +============== + +This is the first stable release of Ceph Quincy. + +Major Changes from Pacific +-------------------------- + +General +~~~~~~~ + +* Filestore has been deprecated in Quincy. BlueStore is Ceph's default object + store. + +* The `ceph-mgr-modules-core` debian package no longer recommends + `ceph-mgr-rook`. `ceph-mgr-rook` depends on `python3-numpy`, which + cannot be imported in different Python sub-interpreters multiple times + when the version of `python3-numpy` is older than 1.19. Because + `apt-get` installs the `Recommends` packages by default, `ceph-mgr-rook` + was always installed along with the `ceph-mgr` debian package as an + indirect dependency. If your workflow depends on this behavior, you + might want to install `ceph-mgr-rook` separately. + +* The ``device_health_metrics`` pool has been renamed ``.mgr``. It is now + used as a common store for all ``ceph-mgr`` modules. After upgrading to + Quincy, the ``device_health_metrics`` pool will be renamed to ``.mgr`` + on existing clusters. + +* The ``ceph pg dump`` command now prints three additional columns: + `LAST_SCRUB_DURATION` shows the duration (in seconds) of the last completed + scrub; + `SCRUB_SCHEDULING` conveys whether a PG is scheduled to be scrubbed at a + specified time, whether it is queued for scrubbing, or whether it is being + scrubbed; + `OBJECTS_SCRUBBED` shows the number of objects scrubbed in a PG after a + scrub begins. + +* A health warning is now reported if the ``require-osd-release`` flag + is not set to the appropriate release after a cluster upgrade. + +* LevelDB support has been removed. ``WITH_LEVELDB`` is no longer a supported + build option. Users *should* migrate their monitors and OSDs to RocksDB + before upgrading to Quincy. + +* Cephadm: ``osd_memory_target_autotune`` is enabled by default, which sets + ``mgr/cephadm/autotune_memory_target_ratio`` to ``0.7`` of total RAM. This + is unsuitable for hyperconverged infrastructures. For hyperconverged Ceph, + please refer to the documentation or set + ``mgr/cephadm/autotune_memory_target_ratio`` to ``0.2``. + +* telemetry: Improved the opt-in flow so that users can keep sharing the same + data, even when new data collections are available. A new 'perf' channel that + collects various performance metrics is now avaiable to opt into with: + `ceph telemetry on` + `ceph telemetry enable channel perf` + See a sample report with `ceph telemetry preview`. + Note that generating a telemetry report with 'perf' channel data might + take a few moments in big clusters. + For more details, see: + https://docs.ceph.com/en/quincy/mgr/telemetry/ + +* MGR: The progress module disables the pg recovery event by default since the + event is expensive and has interrupted other services when there are OSDs + being marked in/out from the the cluster. However, the user can still enable + this event anytime. For more detail, see: + + https://docs.ceph.com/en/quincy/mgr/progress/ + +* https://tracker.ceph.com/issues/55383 is a known issue - + ``mon_cluster_log_to_journald`` needs to be set to false, when + ``mon_cluster_log_to_file`` is set to true to continue to log cluster + log messages to file, after log rotation. + +Cephadm +------- + +* SNMP Support +* Colocation of Daemons (mgr, mds, rgw) +* osd memory autotuning +* Integration with new NFS mgr module +* Ability to zap osds as they are removed +* cephadm agent for increased performance/scalability + +Dashboard +~~~~~~~~~ +* Day 1: the new "Cluster Expansion Wizard" will guide users through post-install steps: + adding new hosts, storage devices or services. +* NFS: the Dashboard now allows users to fully manage all NFS exports from a single place. +* New mgr module (feedback): users can quickly report Ceph tracker issues + or suggestions directly from the Dashboard or the CLI. +* New "Message of the Day": cluster admins can publish a custom message in a banner. +* Cephadm integration improvements: + * Host management: maintenance, specs and labelling, + * Service management: edit and display logs, + * Daemon management (start, stop, restart, reload), + * New services supported: ingress (HAProxy) and SNMP-gateway. +* Monitoring and alerting: + * 43 new alerts have been added (totalling 68) improving observability of events affecting: + cluster health, monitors, storage devices, PGs and CephFS. + * Alerts can now be sent externally as SNMP traps via the new SNMP gateway service + (the MIB is provided). + * Improved integrated full/nearfull event notifications. + * Grafana Dashboards now use grafonnet format (though they're still available + in JSON format). + * Stack update: images for monitoring containers have been updated. + Grafana 8.3.5, Prometheus 2.33.4, Alertmanager 0.23.0 and Node Exporter 1.3.1. + This reduced exposure to several Grafana vulnerabilities (CVE-2021-43798, + CVE-2021-39226, CVE-2021-43798, CVE-2020-29510, CVE-2020-29511). + +RADOS +~~~~~ + +* OSD: Ceph now uses `mclock_scheduler` for BlueStore OSDs as its default + `osd_op_queue` to provide QoS. The 'mclock_scheduler' is not supported + for Filestore OSDs. Therefore, the default 'osd_op_queue' is set to `wpq` + for Filestore OSDs and is enforced even if the user attempts to change it. + For more details on configuring mclock see, + + https://docs.ceph.com/en/quincy/rados/configuration/mclock-config-ref/ + + An outstanding issue exists during runtime where the mclock config options + related to reservation, weight and limit cannot be modified after switching + to the `custom` mclock profile using the `ceph config set ...` command. + This is tracked by: https://tracker.ceph.com/issues/55153. Until the issue + is fixed, users are advised to avoid using the 'custom' profile or use the + workaround mentioned in the tracker. + +* MGR: The pg_autoscaler can now be turned `on` and `off` globally + with the `noautoscale` flag. By default, it is set to `on`, but this flag + can come in handy to prevent rebalancing triggered by autoscaling during + cluster upgrade and maintenance. Pools can now be created with the `--bulk` + flag, which allows the autoscaler to allocate more PGs to such pools. This + can be useful to get better out of the box performance for data-heavy pools. + + For more details about autoscaling, see: + https://docs.ceph.com/en/quincy/rados/operations/placement-groups/ + +* OSD: Support for on-wire compression for osd-osd communication, `off` by + default. + + For more details about compression modes, see: + https://docs.ceph.com/en/quincy/rados/configuration/msgr2/#compression-modes + +* OSD: Concise reporting of slow operations in the cluster log. The old + and more verbose logging behavior can be regained by setting + `osd_aggregated_slow_ops_logging` to false. + +* the "kvs" Ceph object class is not packaged anymore. The "kvs" Ceph + object class offers a distributed flat b-tree key-value store that + is implemented on top of the librados objects omap. Because there + are no existing internal users of this object class, it is not + packaged anymore. + +RBD block storage +~~~~~~~~~~~~~~~~~ + +* rbd-nbd: `rbd device attach` and `rbd device detach` commands added, + these allow for safe reattach after `rbd-nbd` daemon is restarted since + Linux kernel 5.14. + +* rbd-nbd: `notrim` map option added to support thick-provisioned images, + similar to krbd. + +* Large stabilization effort for client-side persistent caching on SSD + devices, also available in 16.2.8. For details on usage, see: + + https://docs.ceph.com/en/quincy/rbd/rbd-persistent-write-log-cache/ + +* Several bug fixes in diff calculation when using fast-diff image + feature + whole object (inexact) mode. In some rare cases these + long-standing issues could cause an incorrect `rbd export`. Also + fixed in 15.2.16 and 16.2.8. + +* Fix for a potential performance degradation when running Windows VMs + on krbd. For details, see `rxbounce` map option description: + + https://docs.ceph.com/en/quincy/man/8/rbd/#kernel-rbd-krbd-options + +RGW object storage +~~~~~~~~~~~~~~~~~~ + +* RGW now supports rate limiting by user and/or by bucket. With this + feature it is possible to limit user and/or bucket, the total operations + and/or bytes per minute can be delivered. This feature allows the + admin to limit only READ operations and/or WRITE operations. The + rate-limiting configuration could be applied on all users and all buckets + by using global configuration. + +* `radosgw-admin realm delete` has been renamed to `radosgw-admin realm + rm`. This is consistent with the help message. + +* S3 bucket notification events now contain an `eTag` key instead of + `etag`, and eventName values no longer carry the `s3:` prefix, fixing + deviations from the message format that is observed on AWS. + +* It is possible to specify ssl options and ciphers for beast frontend + now. The default ssl options setting is + "no_sslv2:no_sslv3:no_tlsv1:no_tlsv1_1". If you want to return to the old + behavior, add 'ssl_options=' (empty) to the ``rgw frontends`` configuration. + +* The behavior for Multipart Upload was modified so that only + CompleteMultipartUpload notification is sent at the end of the multipart + upload. The POST notification at the beginning of the upload and the PUT + notifications that were sent on each part are no longer sent. + + +CephFS distributed file system +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* fs: A file system can be created with a specific ID ("fscid"). This is + useful in certain recovery scenarios (for example, when a monitor + database has been lost and rebuilt, and the restored file system is + expected to have the same ID as before). + +* fs: A file system can be renamed using the `fs rename` command. Any cephx + credentials authorized for the old file system name will need to be + reauthorized to the new file system name. Since the operations of the clients + using these re-authorized IDs may be disrupted, this command requires the + "--yes-i-really-mean-it" flag. Also, mirroring is expected to be disabled + on the file system. + +* MDS upgrades no longer require all standby MDS daemons to be stoped before + upgrading a file systems's sole active MDS. + +* CephFS: Failure to replay the journal by a standby-replay daemon now + causes the rank to be marked "damaged". + +Upgrading from Octopus or Pacific +---------------------------------- + +Quincy does not support LevelDB. Please migrate your OSDs and monitors +to RocksDB before upgrading to Quincy. + +Before starting, make sure your cluster is stable and healthy (no down or +recovering OSDs). (This is optional, but recommended.) You can disable +the autoscaler for all pools during the upgrade using the noautoscale flag. + +.. note:: + + You can monitor the progress of your upgrade at each stage with the + ``ceph versions`` command, which will tell you what ceph version(s) are + running for each type of daemon. + +Upgrading cephadm clusters +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your cluster is deployed with cephadm (first introduced in Octopus), then +the upgrade process is entirely automated. To initiate the upgrade, + + .. prompt:: bash # + + ceph orch upgrade start --ceph-version 17.2.0 + +The same process is used to upgrade to future minor releases. + +Upgrade progress can be monitored with ``ceph -s`` (which provides a simple +progress bar) or more verbosely with + + .. prompt:: bash # + + ceph -W cephadm + +The upgrade can be paused or resumed with + + .. prompt:: bash # + + ceph orch upgrade pause # to pause + ceph orch upgrade resume # to resume + +or canceled with + + .. prompt:: bash # + + ceph orch upgrade stop + +Note that canceling the upgrade simply stops the process; there is no ability to +downgrade back to Octopus or Pacific. + + +Upgrading non-cephadm clusters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + If you cluster is running Octopus (15.2.x) or later, you might choose + to first convert it to use cephadm so that the upgrade to Quincy + is automated (see above). For more information, see + :ref:`cephadm-adoption`. + +#. Set the ``noout`` flag for the duration of the upgrade. (Optional, + but recommended.):: + + # ceph osd set noout + +#. Upgrade monitors by installing the new packages and restarting the + monitor daemons. For example, on each monitor host,:: + + # systemctl restart ceph-mon.target + + Once all monitors are up, verify that the monitor upgrade is + complete by looking for the ``quincy`` string in the mon + map. The command:: + + # ceph mon dump | grep min_mon_release + + should report:: + + min_mon_release 17 (quincy) + + If it doesn't, that implies that one or more monitors hasn't been + upgraded and restarted and/or the quorum does not include all monitors. + +#. Upgrade ``ceph-mgr`` daemons by installing the new packages and + restarting all manager daemons. For example, on each manager host,:: + + # systemctl restart ceph-mgr.target + + Verify the ``ceph-mgr`` daemons are running by checking ``ceph + -s``:: + + # ceph -s + + ... + services: + mon: 3 daemons, quorum foo,bar,baz + mgr: foo(active), standbys: bar, baz + ... + +#. Upgrade all OSDs by installing the new packages and restarting the + ceph-osd daemons on all OSD hosts:: + + # systemctl restart ceph-osd.target + +#. Upgrade all CephFS MDS daemons. For each CephFS file system, + + #. Disable standby_replay:: + + # ceph fs set allow_standby_replay false + + #. Reduce the number of ranks to 1. (Make note of the original + number of MDS daemons first if you plan to restore it later.):: + + # ceph status + # ceph fs set max_mds 1 + + #. Wait for the cluster to deactivate any non-zero ranks by + periodically checking the status:: + + # ceph status + + #. Take all standby MDS daemons offline on the appropriate hosts with:: + + # systemctl stop ceph-mds@ + + #. Confirm that only one MDS is online and is rank 0 for your FS:: + + # ceph status + + #. Upgrade the last remaining MDS daemon by installing the new + packages and restarting the daemon:: + + # systemctl restart ceph-mds.target + + #. Restart all standby MDS daemons that were taken offline:: + + # systemctl start ceph-mds.target + + #. Restore the original value of ``max_mds`` for the volume:: + + # ceph fs set max_mds + +#. Upgrade all radosgw daemons by upgrading packages and restarting + daemons on all hosts:: + + # systemctl restart ceph-radosgw.target + +#. Complete the upgrade by disallowing pre-Quincy OSDs and enabling + all new Quincy-only functionality:: + + # ceph osd require-osd-release quincy + +#. If you set ``noout`` at the beginning, be sure to clear it with:: + + # ceph osd unset noout + +#. Consider transitioning your cluster to use the cephadm deployment + and orchestration framework to simplify cluster management and + future upgrades. For more information on converting an existing + cluster to cephadm, see :ref:`cephadm-adoption`. + +Post-upgrade +~~~~~~~~~~~~ + +#. Verify the cluster is healthy with ``ceph health``. If your cluster is + running Filestore, a deprecation warning is expected. This warning can + be temporarily muted using the following command:: + + ceph health mute OSD_FILESTORE + +#. If you are upgrading from Mimic, or did not already do so when you + upgraded to Nautilus, we recommend you enable the new :ref:`v2 + network protocol `, issue the following command:: + + ceph mon enable-msgr2 + + This will instruct all monitors that bind to the old default port + 6789 for the legacy v1 protocol to also bind to the new 3300 v2 + protocol port. To see if all monitors have been updated,:: + + ceph mon dump + + and verify that each monitor has both a ``v2:`` and ``v1:`` address + listed. + +#. Consider enabling the :ref:`telemetry module ` to send + anonymized usage statistics and crash information to the Ceph + upstream developers. To see what would be reported (without actually + sending any information to anyone),:: + + ceph telemetry preview-all + + If you are comfortable with the data that is reported, you can opt-in to + automatically report the high-level cluster metadata with:: + + ceph telemetry on + + The public dashboard that aggregates Ceph telemetry can be found at + `https://telemetry-public.ceph.com/ `_. + + For more information about the telemetry module, see :ref:`the + documentation `. + + +Upgrading from pre-Octopus releases (like Nautilus) +--------------------------------------------------- + + +You *must* first upgrade to Octopus (15.2.z) or Pacific (16.2.z) before +upgrading to Quincy. diff --git a/ceph/doc/releases/releases.yml b/ceph/doc/releases/releases.yml index 9eb19a24c..975dc0d63 100644 --- a/ceph/doc/releases/releases.yml +++ b/ceph/doc/releases/releases.yml @@ -12,6 +12,12 @@ # If a version might represent an actual number (e.g. 0.80) quote it. # releases: + quincy: + target_eol: 2024-06-01 + releases: + - version: 17.2.0 + released: 2022-04-19 + pacific: target_eol: 2023-06-01 releases: diff --git a/ceph/doc/start/hardware-recommendations.rst b/ceph/doc/start/hardware-recommendations.rst index 10233d52f..b6c47502c 100644 --- a/ceph/doc/start/hardware-recommendations.rst +++ b/ceph/doc/start/hardware-recommendations.rst @@ -72,29 +72,31 @@ Memory ====== Bluestore uses its own memory to cache data rather than relying on the -operating system page cache. In bluestore you can adjust the amount of memory -the OSD attempts to consume with the ``osd_memory_target`` configuration -option. +operating system's page cache. In Bluestore you can adjust the amount of memory +that the OSD attempts to consume by changing the :confval:`osd_memory_target` +configuration option. -- Setting the osd_memory_target below 2GB is typically not recommended (it may - fail to keep the memory that low and may also cause extremely slow performance. +- Setting the :confval:`osd_memory_target` below 2GB is typically not + recommended (Ceph may fail to keep the memory consumption under 2GB and + this may cause extremely slow performance). - Setting the memory target between 2GB and 4GB typically works but may result in degraded performance as metadata may be read from disk during IO unless the active data set is relatively small. -- 4GB is the current default osd_memory_target size and was set that way to try - and balance memory requirements and OSD performance for typical use cases. +- 4GB is the current default :confval:`osd_memory_target` size. This default + was chosen for typical use cases, and is intended to balance memory + requirements and OSD performance. -- Setting the osd_memory_target higher than 4GB may improve performance when - there are many (small) objects or large (256GB/OSD or more) data sets being - processed. +- Setting the :confval:`osd_memory_target` higher than 4GB can improve + performance when there many (small) objects or when large (256GB/OSD + or more) data sets are processed. .. important:: The OSD memory autotuning is "best effort". While the OSD may unmap memory to allow the kernel to reclaim it, there is no guarantee that - the kernel will actually reclaim freed memory within any specific time - frame. This is especially true in older versions of Ceph where transparent - huge pages can prevent the kernel from reclaiming memory freed from + the kernel will actually reclaim freed memory within a specific time + frame. This applies especially in older versions of Ceph, where transparent + huge pages can prevent the kernel from reclaiming memory that was freed from fragmented huge pages. Modern versions of Ceph disable transparent huge pages at the application level to avoid this, though that still does not guarantee that the kernel will immediately reclaim unmapped memory. The OSD @@ -104,9 +106,10 @@ option. kernel. That value may be more or less than needed depending on the exact configuration of the system. -When using the legacy FileStore backend, the page cache is used for caching -data, so no tuning is normally needed, and the OSD memory consumption is -generally related to the number of PGs per daemon in the system. +When using the legacy FileStore back end, the page cache is used for caching +data, so no tuning is normally needed. When using the legacy FileStore backend, +the OSD memory consumption is related to the number of PGs per daemon in the +system. Data Storage diff --git a/ceph/doc/start/intro.rst b/ceph/doc/start/intro.rst index a0549ea98..1f1eb9b38 100644 --- a/ceph/doc/start/intro.rst +++ b/ceph/doc/start/intro.rst @@ -38,8 +38,8 @@ required when running Ceph File System clients. ``ceph-osd``) stores data, handles data replication, recovery, rebalancing, and provides some monitoring information to Ceph Monitors and Managers by checking other Ceph OSD Daemons for a - heartbeat. At least 3 Ceph OSDs are normally required for redundancy - and high availability. + heartbeat. At least three Ceph OSDs are normally required for + redundancy and high availability. - **MDSs**: A :term:`Ceph Metadata Server` (MDS, ``ceph-mds``) stores metadata on behalf of the :term:`Ceph File System` (i.e., Ceph Block diff --git a/ceph/doc/start/os-recommendations.rst b/ceph/doc/start/os-recommendations.rst index ee5c6442d..64887c925 100644 --- a/ceph/doc/start/os-recommendations.rst +++ b/ceph/doc/start/os-recommendations.rst @@ -38,112 +38,27 @@ Platforms The charts below show how Ceph's requirements map onto various Linux platforms. Generally speaking, there is very little dependence on -specific distributions aside from the kernel and system initialization +specific distributions outside of the kernel and system initialization package (i.e., sysvinit, systemd). -Octopus (15.2.z) ------------------ - -+----------+----------+--------------------+--------------+---------+------------+ -| Distro | Release | Code Name | Kernel | Notes | Testing | -+==========+==========+====================+==============+=========+============+ -| CentOS | 8 | N/A | linux-4.18 | | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ -| CentOS | 7 | N/A | linux-3.10.0 | 4, 5 | B, I | -+----------+----------+--------------------+--------------+---------+------------+ -| Debian | 10 | Buster | linux-4.19 | | B | -+----------+----------+--------------------+--------------+---------+------------+ -| RHEL | 8 | Ootpa | linux-4.18 | | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ -| RHEL | 7 | Maipo | linux-3.10.0 | | B, I | -+----------+----------+--------------------+--------------+---------+------------+ -| Ubuntu | 18.04 | Bionic Beaver | linux-4.15 | 4 | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ -| openSUSE | 15.2 | Leap | linux-5.3 | 6 | | -+----------+----------+--------------------+--------------+---------+------------+ -| openSUSE | | Tumbleweed | | | | -+----------+----------+--------------------+--------------+---------+------------+ - - -Nautilus (14.2.z) ------------------ - -+----------+----------+--------------------+--------------+---------+------------+ -| Distro | Release | Code Name | Kernel | Notes | Testing | -+==========+==========+====================+==============+=========+============+ -| CentOS | 7 | N/A | linux-3.10.0 | 3 | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ -| Debian | 8.0 | Jessie | linux-3.16.0 | 1, 2 | B, I | -+----------+----------+--------------------+--------------+---------+------------+ -| Debian | 9.0 | Stretch | linux-4.9 | 1, 2 | B, I | -+----------+----------+--------------------+--------------+---------+------------+ -| RHEL | 7 | Maipo | linux-3.10.0 | | B, I | -+----------+----------+--------------------+--------------+---------+------------+ -| Ubuntu | 14.04 | Trusty Tahr | linux-3.13.0 | | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ -| Ubuntu | 16.04 | Xenial Xerus | linux-4.4.0 | 3 | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ -| Ubuntu | 18.04 | Bionic Beaver | linux-4.15 | 3 | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ -| openSUSE | 15.1 | Leap | linux-4.12 | 6 | | -+----------+----------+--------------------+--------------+---------+------------+ - -Luminous (12.2.z) ------------------ - -+----------+----------+--------------------+--------------+---------+------------+ -| Distro | Release | Code Name | Kernel | Notes | Testing | -+==========+==========+====================+==============+=========+============+ -| CentOS | 7 | N/A | linux-3.10.0 | 3 | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ -| Debian | 8.0 | Jessie | linux-3.16.0 | 1, 2 | B, I | -+----------+----------+--------------------+--------------+---------+------------+ -| Debian | 9.0 | Stretch | linux-4.9 | 1, 2 | B, I | -+----------+----------+--------------------+--------------+---------+------------+ -| Fedora | 22 | N/A | linux-3.14.0 | | B, I | -+----------+----------+--------------------+--------------+---------+------------+ -| RHEL | 7 | Maipo | linux-3.10.0 | | B, I | -+----------+----------+--------------------+--------------+---------+------------+ -| Ubuntu | 14.04 | Trusty Tahr | linux-3.13.0 | | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ -| Ubuntu | 16.04 | Xenial Xerus | linux-4.4.0 | 3 | B, I, C | -+----------+----------+--------------------+--------------+---------+------------+ - -Notes ------ - -- **1**: The default kernel has an older version of ``Btrfs`` that we do not - recommend for ``ceph-osd`` storage nodes. We recommend using ``BlueStore`` - starting with Luminous, and ``XFS`` for previous releases with ``Filestore``. - -- **2**: The default kernel has an old Ceph client that we do not recommend - for kernel client (kernel RBD or the Ceph file system). Upgrade to a - recommended kernel. - -- **3**: The default kernel regularly fails in QA when the ``Btrfs`` - file system is used. We recommend using ``BlueStore`` starting from - Luminous, and ``XFS`` for previous releases with ``Filestore``. - -- **4**: ``btrfs`` is no longer tested on this release. We recommend - using ``bluestore``. - -- **5**: Some additional features related to dashboard are not available. - -- **6**: Packages are built regularly, but not distributed by upstream Ceph. - -Testing -------- - -- **B**: We build release packages for this platform. For some of these - platforms, we may also continuously build all Ceph branches and perform - basic unit tests. - -- **I**: We do basic installation and functionality tests of releases on this - platform. - -- **C**: We run a comprehensive functional, regression, and stress test suite - on this platform on a continuous basis. This includes development branches, - pre-release, and released code. ++--------------+--------+------------------------+--------------------------------+-------------------+-----------------+ +| Release Name | Tag | CentOS | Ubuntu | OpenSUSE :sup:`C` | Debian :sup:`C` | ++==============+========+========================+================================+===================+=================+ +| Quincy | 17.2.z | 8 :sup:`A` | 20.04 :sup:`A` | 15.3 | 11 | ++--------------+--------+------------------------+--------------------------------+-------------------+-----------------+ +| Pacific | 16.2.z | 8 :sup:`A` | 18.04 :sup:`C`, 20.04 :sup:`A` | 15.2 | 10, 11 | ++--------------+--------+------------------------+--------------------------------+-------------------+-----------------+ +| Octopus | 15.2.z | 7 :sup:`B` 8 :sup:`A` | 18.04 :sup:`C`, 20.04 :sup:`A` | 15.2 | 10 | ++--------------+--------+------------------------+--------------------------------+-------------------+-----------------+ + +- **A**: Ceph provides packages and has done comprehensive tests on the software in them. +- **B**: Ceph provides packages and has done basic tests on the software in them. +- **C**: Ceph provides packages only. No tests have been done on these releases. + +.. note:: + **For Centos 7 Users** + + ``Btrfs`` is no longer tested on Centos 7 in the Octopus release. We recommend using ``bluestore`` instead. .. _CRUSH Tunables: ../../rados/operations/crush-map#tunables diff --git a/ceph/qa/.teuthology_branch b/ceph/qa/.teuthology_branch deleted file mode 100644 index 1f7391f92..000000000 --- a/ceph/qa/.teuthology_branch +++ /dev/null @@ -1 +0,0 @@ -master diff --git a/ceph/qa/config/rados.yaml b/ceph/qa/config/rados.yaml index e468e126a..2f5779b0e 100644 --- a/ceph/qa/config/rados.yaml +++ b/ceph/qa/config/rados.yaml @@ -6,5 +6,6 @@ overrides: osd op queue cut off: debug_random osd debug verify missing on start: true osd debug verify cached snaps: true + bluestore zero block detection: true mon: mon scrub interval: 300 diff --git a/ceph/qa/suites/orch/cephadm/mgr-nfs-upgrade/3-upgrade-with-workload.yaml b/ceph/qa/suites/orch/cephadm/mgr-nfs-upgrade/3-upgrade-with-workload.yaml index 6d10b3576..362e03734 100644 --- a/ceph/qa/suites/orch/cephadm/mgr-nfs-upgrade/3-upgrade-with-workload.yaml +++ b/ceph/qa/suites/orch/cephadm/mgr-nfs-upgrade/3-upgrade-with-workload.yaml @@ -16,7 +16,7 @@ upgrade-tasks: - cephadm.shell: env: [sha1] host.a: - - while ceph orch upgrade status | jq '.in_progress' | grep true ; do ceph orch ps ; ceph versions ; sleep 30 ; done + - while ceph orch upgrade status | jq '.in_progress' | grep true && ! ceph orch upgrade status | jq '.message' | grep Error ; do ceph orch ps ; ceph versions ; ceph orch upgrade status ; sleep 30 ; done - ceph orch ps - ceph versions - echo "wait for servicemap items w/ changing names to refresh" diff --git a/ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/.qa b/ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/.qa new file mode 120000 index 000000000..a602a0353 --- /dev/null +++ b/ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/.qa @@ -0,0 +1 @@ +../.qa/ \ No newline at end of file diff --git a/ceph/qa/suites/orch/cephadm/upgrade/3-start-upgrade.yaml b/ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/simple.yaml similarity index 100% rename from ceph/qa/suites/orch/cephadm/upgrade/3-start-upgrade.yaml rename to ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/simple.yaml diff --git a/ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/staggered.yaml b/ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/staggered.yaml new file mode 100644 index 000000000..ebd06c92d --- /dev/null +++ b/ceph/qa/suites/orch/cephadm/upgrade/3-upgrade/staggered.yaml @@ -0,0 +1,111 @@ +tasks: +- cephadm.shell: + env: [sha1] + mon.a: + - radosgw-admin realm create --rgw-realm=r --default + - radosgw-admin zonegroup create --rgw-zonegroup=default --master --default + - radosgw-admin zone create --rgw-zonegroup=default --rgw-zone=z --master --default + - radosgw-admin period update --rgw-realm=r --commit + - ceph orch apply rgw r z --placement=2 --port=8000 + - sleep 180 + - ceph config set mon mon_warn_on_insecure_global_id_reclaim false --force + - ceph config set mon mon_warn_on_insecure_global_id_reclaim_allowed false --force + - ceph config set global log_to_journald false --force + # get some good info on the state of things pre-upgrade. Useful for debugging + - ceph orch ps + - ceph versions + - ceph -s + - ceph orch ls + # doing staggered upgrade requires mgr daemons being on a version that contains the staggered upgrade code + # until there is a stable version that contains it, we can test by manually upgrading a mgr daemon + - ceph config set mgr container_image quay.ceph.io/ceph-ci/ceph:$sha1 + - ceph orch daemon redeploy "mgr.$(ceph mgr dump -f json | jq .standbys | jq .[] | jq -r .name)" + - ceph orch ps --refresh + - sleep 180 + # gather more possible debugging info + - ceph orch ps + - ceph versions + - ceph -s + # check that there are two different versions found for mgr daemon (which implies we upgraded one) + - ceph versions | jq -e '.mgr | length == 2' + - ceph mgr fail + - sleep 180 + # now try upgrading the other mgr + # we should now have access to --image flag for the daemon redeploy command + - ceph orch daemon redeploy "mgr.$(ceph mgr dump -f json | jq .standbys | jq .[] | jq -r .name)" --image quay.ceph.io/ceph-ci/ceph:$sha1 + - ceph orch ps --refresh + - sleep 180 + # gather more possible debugging info + - ceph orch ps + - ceph versions + - ceph -s + - ceph mgr fail + - sleep 180 + # gather more debugging info + - ceph orch ps + - ceph versions + - ceph -s + # now that both mgrs should have been redeployed with the new version, we should be back on only 1 version for the mgrs + - ceph versions | jq -e '.mgr | length == 1' + - ceph mgr fail + - sleep 180 + # debugging info + - ceph orch ps + - ceph versions + # to make sure mgr daemons upgrade is fully completed, including being deployed by a mgr on new new version + # also serves as an early failure if manually upgrading the mgrs failed as --daemon-types won't be recognized + - ceph orch upgrade start --image quay.ceph.io/ceph-ci/ceph:$sha1 --daemon-types mgr + - while ceph orch upgrade status | jq '.in_progress' | grep true && ! ceph orch upgrade status | jq '.message' | grep Error ; do ceph orch ps ; ceph versions ; ceph orch upgrade status ; sleep 30 ; done + # verify only one version found for mgrs and that their version hash matches what we are upgrading to + - ceph versions | jq -e '.mgr | length == 1' + - ceph versions | jq -e '.mgr | keys' | grep $sha1 + # verify overall we still se two versions, basically to make sure --daemon-types wans't ignored and all daemons upgraded + - ceph versions | jq -e '.overall | length == 2' + # check that exactly two daemons have been upgraded to the new image (our 2 mgr daemons) + - ceph orch upgrade check quay.ceph.io/ceph-ci/ceph:$sha1 | jq -e '.up_to_date | length == 2' + # upgrade only the mons on one of the two hosts + - ceph orch upgrade start --image quay.ceph.io/ceph-ci/ceph:$sha1 --daemon-types mon --hosts $(ceph orch ps | grep mgr.x | awk '{print $2}') + - while ceph orch upgrade status | jq '.in_progress' | grep true && ! ceph orch upgrade status | jq '.message' | grep Error ; do ceph orch ps ; ceph versions ; ceph orch upgrade status ; sleep 30 ; done + - ceph orch ps + # verify tow different version seen for mons + - ceph versions | jq -e '.mon | length == 2' + # upgrade mons on the other hosts + - ceph orch upgrade start --image quay.ceph.io/ceph-ci/ceph:$sha1 --daemon-types mon --hosts $(ceph orch ps | grep mgr.y | awk '{print $2}') + - while ceph orch upgrade status | jq '.in_progress' | grep true && ! ceph orch upgrade status | jq '.message' | grep Error ; do ceph orch ps ; ceph versions ; ceph orch upgrade status ; sleep 30 ; done + - ceph orch ps + # verify all mons now on same version and version hash matches what we are upgrading to + - ceph versions | jq -e '.mon | length == 1' + - ceph versions | jq -e '.mon | keys' | grep $sha1 + # verify exactly 5 daemons are now upgraded (2 mgrs, 3 mons) + - ceph orch upgrade check quay.ceph.io/ceph-ci/ceph:$sha1 | jq -e '.up_to_date | length == 5' + # upgrade exactly 2 osd daemons + - ceph orch upgrade start --image quay.ceph.io/ceph-ci/ceph:$sha1 --daemon-types osd --limit 2 + - while ceph orch upgrade status | jq '.in_progress' | grep true && ! ceph orch upgrade status | jq '.message' | grep Error ; do ceph orch ps ; ceph versions ; ceph orch upgrade status ; sleep 30 ; done + - ceph orch ps + # verify two different versions now seen for osds + - ceph versions | jq -e '.osd | length == 2' + # verify exactly 7 daemons have been upgraded (2 mgrs, 3 mons, 2 osds) + - ceph orch upgrade check quay.ceph.io/ceph-ci/ceph:$sha1 | jq -e '.up_to_date | length == 7' + # upgrade one more osd + - ceph orch upgrade start --image quay.ceph.io/ceph-ci/ceph:$sha1 --daemon-types crash,osd --limit 1 + - while ceph orch upgrade status | jq '.in_progress' | grep true && ! ceph orch upgrade status | jq '.message' | grep Error ; do ceph orch ps ; ceph versions ; ceph orch upgrade status ; sleep 30 ; done + - ceph orch ps + - ceph versions | jq -e '.osd | length == 2' + # verify now 8 daemons have been upgraded + - ceph orch upgrade check quay.ceph.io/ceph-ci/ceph:$sha1 | jq -e '.up_to_date | length == 8' + # upgrade the rest of the osds + - ceph orch upgrade start --image quay.ceph.io/ceph-ci/ceph:$sha1 --daemon-types crash,osd + - while ceph orch upgrade status | jq '.in_progress' | grep true && ! ceph orch upgrade status | jq '.message' | grep Error ; do ceph orch ps ; ceph versions ; ceph orch upgrade status ; sleep 30 ; done + - ceph orch ps + # verify all osds are now on same version and version hash matches what we are upgrading to + - ceph versions | jq -e '.osd | length == 1' + - ceph versions | jq -e '.osd | keys' | grep $sha1 + # upgrade the rgw daemons using --services + - ceph orch upgrade start --image quay.ceph.io/ceph-ci/ceph:$sha1 --services rgw.r.z + - while ceph orch upgrade status | jq '.in_progress' | grep true && ! ceph orch upgrade status | jq '.message' | grep Error ; do ceph orch ps ; ceph versions ; ceph orch upgrade status ; sleep 30 ; done + - ceph orch ps + # verify all rgw daemons on same version and version hash matches what we are upgrading to + - ceph versions | jq -e '.rgw | length == 1' + - ceph versions | jq -e '.rgw | keys' | grep $sha1 + # run upgrade one more time with no filter parameters to make sure anything left gets upgraded + - ceph orch upgrade start --image quay.ceph.io/ceph-ci/ceph:$sha1 diff --git a/ceph/qa/suites/orch/cephadm/upgrade/4-wait.yaml b/ceph/qa/suites/orch/cephadm/upgrade/4-wait.yaml index dfac1847a..58afe00c5 100644 --- a/ceph/qa/suites/orch/cephadm/upgrade/4-wait.yaml +++ b/ceph/qa/suites/orch/cephadm/upgrade/4-wait.yaml @@ -2,7 +2,7 @@ tasks: - cephadm.shell: env: [sha1] mon.a: - - while ceph orch upgrade status | jq '.in_progress' | grep true ; do ceph orch ps ; ceph versions ; sleep 30 ; done + - while ceph orch upgrade status | jq '.in_progress' | grep true && ! ceph orch upgrade status | jq '.message' | grep Error ; do ceph orch ps ; ceph versions ; ceph orch upgrade status ; sleep 30 ; done - ceph orch ps - ceph versions - echo "wait for servicemap items w/ changing names to refresh" diff --git a/ceph/qa/suites/orch/cephadm/upgrade/5-upgrade-ls.yaml b/ceph/qa/suites/orch/cephadm/upgrade/5-upgrade-ls.yaml index 236ea6c6b..799458bc5 100644 --- a/ceph/qa/suites/orch/cephadm/upgrade/5-upgrade-ls.yaml +++ b/ceph/qa/suites/orch/cephadm/upgrade/5-upgrade-ls.yaml @@ -2,5 +2,5 @@ tasks: - cephadm.shell: mon.a: - ceph orch upgrade ls - - ceph orch upgrade ls --image quay.io/ceph/ceph | grep 16.2.0 + - ceph orch upgrade ls --image quay.io/ceph/ceph --show-all-versions | grep 16.2.0 - ceph orch upgrade ls --image quay.io/ceph/ceph --tags | grep v16.2.2 diff --git a/ceph/qa/suites/rados/thrash-erasure-code-big/thrashers/mapgap.yaml b/ceph/qa/suites/rados/thrash-erasure-code-big/thrashers/mapgap.yaml index 318b20266..18843d872 100644 --- a/ceph/qa/suites/rados/thrash-erasure-code-big/thrashers/mapgap.yaml +++ b/ceph/qa/suites/rados/thrash-erasure-code-big/thrashers/mapgap.yaml @@ -11,6 +11,7 @@ overrides: osd map cache size: 1 osd scrub min interval: 60 osd scrub max interval: 120 + osd max backfills: 6 tasks: - thrashosds: timeout: 1800 diff --git a/ceph/qa/suites/rados/thrash-erasure-code-big/thrashers/pggrow.yaml b/ceph/qa/suites/rados/thrash-erasure-code-big/thrashers/pggrow.yaml index 772f2698b..9cbb80dba 100644 --- a/ceph/qa/suites/rados/thrash-erasure-code-big/thrashers/pggrow.yaml +++ b/ceph/qa/suites/rados/thrash-erasure-code-big/thrashers/pggrow.yaml @@ -7,6 +7,7 @@ overrides: osd: osd scrub min interval: 60 osd scrub max interval: 120 + osd max backfills: 6 tasks: - thrashosds: timeout: 1200 diff --git a/ceph/qa/suites/smoke/basic/tasks/test/rgw_ec_s3tests.yaml b/ceph/qa/suites/smoke/basic/tasks/test/rgw_ec_s3tests.yaml index a57399a5d..0d843591b 100644 --- a/ceph/qa/suites/smoke/basic/tasks/test/rgw_ec_s3tests.yaml +++ b/ceph/qa/suites/smoke/basic/tasks/test/rgw_ec_s3tests.yaml @@ -7,7 +7,7 @@ tasks: - rgw: [client.0] - s3tests: client.0: - force-branch: ceph-master + force-branch: ceph-quincy rgw_server: client.0 overrides: ceph: diff --git a/ceph/qa/suites/smoke/basic/tasks/test/rgw_s3tests.yaml b/ceph/qa/suites/smoke/basic/tasks/test/rgw_s3tests.yaml index 189a2d573..dee65ac4b 100644 --- a/ceph/qa/suites/smoke/basic/tasks/test/rgw_s3tests.yaml +++ b/ceph/qa/suites/smoke/basic/tasks/test/rgw_s3tests.yaml @@ -4,7 +4,7 @@ tasks: - rgw: [client.0] - s3tests: client.0: - force-branch: ceph-master + force-branch: ceph-quincy rgw_server: client.0 overrides: ceph: diff --git a/ceph/qa/tasks/cephadm_cases/test_cli.py b/ceph/qa/tasks/cephadm_cases/test_cli.py index c05395673..ca40a8cdb 100644 --- a/ceph/qa/tasks/cephadm_cases/test_cli.py +++ b/ceph/qa/tasks/cephadm_cases/test_cli.py @@ -44,14 +44,14 @@ class TestCephadmCLI(MgrTestCase): def test_pause(self): self._orch_cmd('pause') - self.wait_for_health('CEPHADM_PAUSED', 30) + self.wait_for_health('CEPHADM_PAUSED', 60) self._orch_cmd('resume') - self.wait_for_health_clear(30) + self.wait_for_health_clear(60) def test_daemon_restart(self): self._orch_cmd('daemon', 'stop', 'osd.0') - self.wait_for_health('OSD_DOWN', 30) - with safe_while(sleep=1, tries=30) as proceed: + self.wait_for_health('OSD_DOWN', 60) + with safe_while(sleep=2, tries=30) as proceed: while proceed(): j = json.loads(self._orch_cmd('ps', '--format', 'json')) d = {d['daemon_name']: d for d in j} @@ -59,7 +59,7 @@ class TestCephadmCLI(MgrTestCase): break time.sleep(5) self._orch_cmd('daemon', 'start', 'osd.0') - self.wait_for_health_clear(90) + self.wait_for_health_clear(120) self._orch_cmd('daemon', 'restart', 'osd.0') def test_device_ls_wide(self): @@ -67,7 +67,7 @@ class TestCephadmCLI(MgrTestCase): def test_cephfs_mirror(self): self._orch_cmd('apply', 'cephfs-mirror') - self.wait_until_true(lambda: 'cephfs-mirror' in self._orch_cmd('ps'), 30) - self.wait_for_health_clear(30) + self.wait_until_true(lambda: 'cephfs-mirror' in self._orch_cmd('ps'), 60) + self.wait_for_health_clear(60) self._orch_cmd('rm', 'cephfs-mirror') - self.wait_until_true(lambda: 'cephfs-mirror' not in self._orch_cmd('ps'), 30) + self.wait_until_true(lambda: 'cephfs-mirror' not in self._orch_cmd('ps'), 60) diff --git a/ceph/qa/tasks/cephfs/filesystem.py b/ceph/qa/tasks/cephfs/filesystem.py index 65af4adaa..c8cdbedd1 100644 --- a/ceph/qa/tasks/cephfs/filesystem.py +++ b/ceph/qa/tasks/cephfs/filesystem.py @@ -263,18 +263,12 @@ class CephCluster(object): log.debug("_json_asok output empty") return None - def is_addr_blocklisted(self, addr=None): - if addr is None: - log.warn("Couldn't get the client address, so the blocklisted " - "status undetermined") - return False - - blocklist = json.loads(self.mon_manager.run_cluster_cmd( - args=["osd", "blocklist", "ls", "--format=json"], - stdout=StringIO()).stdout.getvalue()) - for b in blocklist: - if addr == b["addr"]: - return True + def is_addr_blocklisted(self, addr): + blocklist = json.loads(self.mon_manager.raw_cluster_cmd( + "osd", "dump", "--format=json"))['blocklist'] + if addr in blocklist: + return True + log.warn(f'The address {addr} is not blocklisted') return False diff --git a/ceph/qa/tasks/cephfs/test_cephfs_shell.py b/ceph/qa/tasks/cephfs/test_cephfs_shell.py index 1ee5c7f84..81cd2a9ad 100644 --- a/ceph/qa/tasks/cephfs/test_cephfs_shell.py +++ b/ceph/qa/tasks/cephfs/test_cephfs_shell.py @@ -309,38 +309,6 @@ class TestRmdir(TestCephFSShell): self.mount_a.stat(self.dir_name) class TestGetAndPut(TestCephFSShell): - def test_without_target_dir(self): - """ - Test put and get commands without target path. - """ - tempdir = self.mount_a.client_remote.mkdtemp() - tempdirname = path.basename(tempdir) - files = ('dump1', 'dump2', 'dump3', tempdirname) - - for i, file_ in enumerate(files[ : -1]): - size = i + 1 - ofarg = 'of=' + path.join(tempdir, file_) - bsarg = 'bs=' + str(size) + 'M' - self.mount_a.run_shell_payload(f"dd if=/dev/urandom {ofarg} {bsarg} count=1") - - self.run_cephfs_shell_cmd('put ' + tempdir) - for file_ in files: - if file_ == tempdirname: - self.mount_a.stat(path.join(self.mount_a.mountpoint, file_)) - else: - self.mount_a.stat(path.join(self.mount_a.mountpoint, - tempdirname, file_)) - - self.mount_a.run_shell_payload(f"rm -rf {tempdir}") - - self.run_cephfs_shell_cmd('get ' + tempdirname) - pwd = self.get_cephfs_shell_cmd_output('!pwd') - for file_ in files: - if file_ == tempdirname: - self.mount_a.run_shell_payload(f"stat {path.join(pwd, file_)}") - else: - self.mount_a.run_shell_payload(f"stat {path.join(pwd, tempdirname, file_)}") - def test_get_with_target_name(self): """ Test that get passes with target name @@ -354,7 +322,7 @@ class TestGetAndPut(TestCephFSShell): o = self.mount_a.stat('dump4') log.info("mount_a output:\n{}".format(o)) - o = self.get_cephfs_shell_cmd_output("get dump4 .") + o = self.get_cephfs_shell_cmd_output("get dump4 ./dump4") log.info("cephfs-shell output:\n{}".format(o)) o = self.get_cephfs_shell_cmd_output("!cat dump4") @@ -367,31 +335,36 @@ class TestGetAndPut(TestCephFSShell): def test_get_without_target_name(self): """ - Test that get passes with target name + Test that get should fail when there is no target name """ - s = 'D' * 1024 - o = self.get_cephfs_shell_cmd_output("put - dump5", stdin=s) - log.info("cephfs-shell output:\n{}".format(o)) - + s = 'Somedata' # put - dump5 should pass - o = self.mount_a.stat('dump5') - log.info("mount_a output:\n{}".format(o)) - - # get dump5 should fail - o = self.get_cephfs_shell_cmd_output("get dump5") - o = self.get_cephfs_shell_cmd_output("!stat dump5 || echo $?") - log.info("cephfs-shell output:\n{}".format(o)) - l = o.split('\n') - try: - ret = int(l[1]) - # verify that stat dump5 passes - # if ret == 1, then that implies the stat failed - # which implies that there was a problem with "get dump5" - assert(ret != 1) - except ValueError: - # we have a valid stat output; so this is good - # if the int() fails then that means there's a valid stat output - pass + self.get_cephfs_shell_cmd_output("put - dump5", stdin=s) + + self.mount_a.stat('dump5') + + # get dump5 should fail as there is no local_path mentioned + with self.assertRaises(CommandFailedError): + self.get_cephfs_shell_cmd_output("get dump5") + + # stat dump would return non-zero exit code as get dump failed + # cwd=None because we want to run it at CWD, not at cephfs mntpt. + r = self.mount_a.run_shell('stat dump5', cwd=None, + check_status=False).returncode + self.assertEqual(r, 1) + + def test_get_doesnt_create_dir(self): + # if get cmd is creating subdirs on its own then dump7 will be + # stored as ./dump7/tmp/dump7 and not ./dump7, therefore + # if doing `cat ./dump7` returns non-zero exit code(i.e. 1) then + # it implies that no such file exists at that location + dir_abspath = path.join(self.mount_a.mountpoint, 'tmp') + self.mount_a.run_shell_payload(f"mkdir {dir_abspath}") + self.mount_a.client_remote.write_file(path.join(dir_abspath, 'dump7'), + 'somedata') + self.get_cephfs_shell_cmd_output("get /tmp/dump7 ./dump7") + # test that dump7 exists + self.mount_a.run_shell("cat ./dump7", cwd=None) def test_get_to_console(self): """ @@ -416,6 +389,23 @@ class TestGetAndPut(TestCephFSShell): log.info("o_hash:{}".format(o_hash)) assert(s_hash == o_hash) + def test_put_without_target_name(self): + """ + put - should fail as the cmd expects both arguments are mandatory. + """ + with self.assertRaises(CommandFailedError): + self.get_cephfs_shell_cmd_output("put -") + + def test_put_validate_local_path(self): + """ + This test is intended to make sure local_path is validated before + trying to put the file from local fs to cephfs and the command + put ./dumpXYZ dump8 would fail as dumpXYX doesn't exist. + """ + with self.assertRaises(CommandFailedError): + o = self.get_cephfs_shell_cmd_output("put ./dumpXYZ dump8") + log.info("cephfs-shell output:\n{}".format(o)) + class TestSnapshots(TestCephFSShell): def test_snap(self): """ diff --git a/ceph/qa/tasks/cephfs/test_mds_metrics.py b/ceph/qa/tasks/cephfs/test_mds_metrics.py index be680bb86..727b80c6a 100644 --- a/ceph/qa/tasks/cephfs/test_mds_metrics.py +++ b/ceph/qa/tasks/cephfs/test_mds_metrics.py @@ -5,7 +5,7 @@ import random import logging import errno -from teuthology.contextutil import safe_while +from teuthology.contextutil import safe_while, MaxWhileTries from teuthology.exceptions import CommandFailedError from tasks.cephfs.cephfs_test_case import CephFSTestCase @@ -100,6 +100,24 @@ class TestMDSMetrics(CephFSTestCase): break return done, metrics + def _setup_fs(self, fs_name): + fs_a = self.mds_cluster.newfs(name=fs_name) + + self.mds_cluster.mds_restart() + + # Wait for filesystem to go healthy + fs_a.wait_for_daemons() + + # Reconfigure client auth caps + for mount in self.mounts: + self.mds_cluster.mon_manager.raw_cluster_cmd_result( + 'auth', 'caps', f"client.{mount.client_id}", + 'mds', 'allow', + 'mon', 'allow r', + 'osd', f'allow rw pool={fs_a.get_data_pool_name()}') + + return fs_a + # basic check to verify if we get back metrics from each active mds rank def test_metrics_from_rank(self): @@ -394,3 +412,102 @@ class TestMDSMetrics(CephFSTestCase): raise else: raise RuntimeError("expected the 'fs perf stat' command to fail for invalid client_ip") + + def test_perf_stats_stale_metrics(self): + """ + That `ceph fs perf stats` doesn't output stale metrics after the rank0 MDS failover + """ + # validate + valid, metrics = self._get_metrics(self.verify_mds_metrics( + active_mds_count=1, client_count=TestMDSMetrics.CLIENTS_REQUIRED), 30) + log.debug(f'metrics={metrics}') + self.assertTrue(valid) + + #mount_a and mount_b are the clients mounted for TestMDSMetrics. So get their + #entries from the global_metrics. + client_a_name = f'client.{self.mount_a.get_global_id()}' + client_b_name = f'client.{self.mount_b.get_global_id()}' + + global_metrics = metrics['global_metrics'] + client_a_metrics = global_metrics[client_a_name] + client_b_metrics = global_metrics[client_b_name] + + #fail rank0 mds + self.fs.rank_fail(rank=0) + + # Wait for 10 seconds for the failover to complete and + # the mgr to get initial metrics from the new rank0 mds. + time.sleep(10) + + fscid = self.fs.id + + # spread directory per rank + self._spread_directory_on_all_ranks(fscid) + + # spread some I/O + self._do_spread_io_all_clients(fscid) + + # wait a bit for mgr to get updated metrics + time.sleep(5) + + # validate + try: + valid, metrics_new = self._get_metrics(self.verify_mds_metrics( + active_mds_count=1, client_count=TestMDSMetrics.CLIENTS_REQUIRED), 30) + log.debug(f'metrics={metrics_new}') + self.assertTrue(valid) + + global_metrics = metrics_new['global_metrics'] + client_a_metrics_new = global_metrics[client_a_name] + client_b_metrics_new = global_metrics[client_b_name] + + #the metrics should be different for the test to succeed. + self.assertNotEqual(client_a_metrics, client_a_metrics_new) + self.assertNotEqual(client_b_metrics, client_b_metrics_new) + except MaxWhileTries: + raise RuntimeError("Failed to fetch `ceph fs perf stats` metrics") + finally: + # cleanup test directories + self._cleanup_test_dirs() + + def test_client_metrics_and_metadata(self): + self.mount_a.umount_wait() + self.mount_b.umount_wait() + + self.mds_cluster.mon_manager.raw_cluster_cmd("fs", "flag", "set", + "enable_multiple", "true", + "--yes-i-really-mean-it") + + #creating filesystem + fs_a = self._setup_fs(fs_name = "fs1") + + # Mount a client on fs_a + self.mount_a.mount_wait(cephfs_name=fs_a.name) + self.mount_a.write_n_mb("pad.bin", 1) + self.mount_a.write_n_mb("test.bin", 2) + self.mount_a.path_to_ino("test.bin") + self.mount_a.create_files() + + #creating another filesystem + fs_b = self._setup_fs(fs_name = "fs2") + + # Mount a client on fs_b + self.mount_b.mount_wait(cephfs_name=fs_b.name) + self.mount_b.write_n_mb("test.bin", 1) + self.mount_b.path_to_ino("test.bin") + self.mount_b.create_files() + + # validate + valid, metrics = self._get_metrics( + self.verify_mds_metrics(client_count=TestMDSMetrics.CLIENTS_REQUIRED), 30) + log.debug(f"metrics={metrics}") + self.assertTrue(valid) + + client_metadata = metrics['client_metadata'] + + for i in client_metadata: + if not (client_metadata[i]['hostname']): + raise RuntimeError("hostname not found!") + if not (client_metadata[i]['valid_metrics']): + raise RuntimeError("valid_metrics not found!") + diff --git a/ceph/qa/tasks/cephfs/test_mirroring.py b/ceph/qa/tasks/cephfs/test_mirroring.py index 91f07fdf4..a5f8cdac7 100644 --- a/ceph/qa/tasks/cephfs/test_mirroring.py +++ b/ceph/qa/tasks/cephfs/test_mirroring.py @@ -217,10 +217,6 @@ class TestMirroring(CephFSTestCase): 'fs', 'mirror', 'status', f'{fs_name}@{fs_id}') return res['rados_inst'] - def get_blocklisted_instances(self): - return json.loads(self.mds_cluster.mon_manager.raw_cluster_cmd( - "osd", "dump", "--format=json-pretty"))['blocklist'] - def mirror_daemon_command(self, cmd_label, *args): asok_path = self.get_daemon_admin_socket() try: @@ -451,8 +447,7 @@ class TestMirroring(CephFSTestCase): self.mount_a.run_shell(['kill', '-SIGCONT', pid]) # check if the rados addr is blocklisted - blocklist = self.get_blocklisted_instances() - self.assertTrue(rados_inst in blocklist) + self.assertTrue(self.mds_cluster.is_addr_blocklisted(rados_inst)) # wait enough so that the mirror daemon restarts blocklisted instances time.sleep(40) @@ -619,8 +614,7 @@ class TestMirroring(CephFSTestCase): self.mount_a.run_shell(['kill', '-SIGCONT', pid]) # check if the rados addr is blocklisted - blocklist = self.get_blocklisted_instances() - self.assertTrue(rados_inst in blocklist) + self.assertTrue(self.mds_cluster.is_addr_blocklisted(rados_inst)) time.sleep(500) self.check_peer_status(self.primary_fs_name, self.primary_fs_id, @@ -893,8 +887,7 @@ class TestMirroring(CephFSTestCase): time.sleep(40) # make sure the rados addr is blocklisted - blocklist = self.get_blocklisted_instances() - self.assertTrue(rados_inst in blocklist) + self.assertTrue(self.mds_cluster.is_addr_blocklisted(rados_inst)) # now we are sure that there are no "active" mirror daemons -- add a directory path. dir_path_p = "/d0/d1" diff --git a/ceph/qa/tasks/cephfs/test_misc.py b/ceph/qa/tasks/cephfs/test_misc.py index 0367c141f..e819dbbe1 100644 --- a/ceph/qa/tasks/cephfs/test_misc.py +++ b/ceph/qa/tasks/cephfs/test_misc.py @@ -3,10 +3,14 @@ from io import StringIO from tasks.cephfs.fuse_mount import FuseMount from tasks.cephfs.cephfs_test_case import CephFSTestCase from teuthology.exceptions import CommandFailedError +from textwrap import dedent +from threading import Thread import errno +import platform import time import json import logging +import os log = logging.getLogger(__name__) @@ -29,6 +33,24 @@ class TestMisc(CephFSTestCase): self.mount_a.umount_wait(force=True) p.wait() + def test_fuse_mount_on_already_mounted_path(self): + if platform.system() != "Linux": + self.skipTest("Require Linux platform") + + if not isinstance(self.mount_a, FuseMount): + self.skipTest("Require FUSE client") + + # Try to mount already mounted path + # expecting EBUSY error + try: + mount_cmd = ['sudo'] + self.mount_a._mount_bin + [self.mount_a.hostfs_mntpt] + self.mount_a.client_remote.run(args=mount_cmd, stderr=StringIO(), + stdout=StringIO(), timeout=60, omit_sudo=False) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.EBUSY) + else: + self.fail("Expected EBUSY") + def test_getattr_caps(self): """ Check if MDS recognizes the 'mask' parameter of open request. @@ -216,6 +238,128 @@ class TestMisc(CephFSTestCase): self.assertEqual(lsflags["allow_multimds_snaps"], True) self.assertEqual(lsflags["allow_standby_replay"], True) + def _test_sync_stuck_for_around_5s(self, dir_path, file_sync=False): + self.mount_a.run_shell(["mkdir", dir_path]) + + sync_dir_pyscript = dedent(""" + import os + + path = "{path}" + dfd = os.open(path, os.O_DIRECTORY) + os.fsync(dfd) + os.close(dfd) + """.format(path=dir_path)) + + # run create/delete directories and test the sync time duration + for i in range(300): + for j in range(5): + self.mount_a.run_shell(["mkdir", os.path.join(dir_path, f"{i}_{j}")]) + start = time.time() + if file_sync: + self.mount_a.run_shell(['python3', '-c', sync_dir_pyscript]) + else: + self.mount_a.run_shell(["sync"]) + duration = time.time() - start + log.info(f"sync mkdir i = {i}, duration = {duration}") + self.assertLess(duration, 4) + + for j in range(5): + self.mount_a.run_shell(["rm", "-rf", os.path.join(dir_path, f"{i}_{j}")]) + start = time.time() + if file_sync: + self.mount_a.run_shell(['python3', '-c', sync_dir_pyscript]) + else: + self.mount_a.run_shell(["sync"]) + duration = time.time() - start + log.info(f"sync rmdir i = {i}, duration = {duration}") + self.assertLess(duration, 4) + + self.mount_a.run_shell(["rm", "-rf", dir_path]) + + def test_filesystem_sync_stuck_for_around_5s(self): + """ + To check whether the fsync will be stuck to wait for the mdlog to be + flushed for at most 5 seconds. + """ + + dir_path = "filesystem_sync_do_not_wait_mdlog_testdir" + self._test_sync_stuck_for_around_5s(dir_path) + + def test_file_sync_stuck_for_around_5s(self): + """ + To check whether the filesystem sync will be stuck to wait for the + mdlog to be flushed for at most 5 seconds. + """ + + dir_path = "file_sync_do_not_wait_mdlog_testdir" + self._test_sync_stuck_for_around_5s(dir_path, True) + + def test_file_filesystem_sync_crash(self): + """ + To check whether the kernel crashes when doing the file/filesystem sync. + """ + + stop_thread = False + dir_path = "file_filesystem_sync_crash_testdir" + self.mount_a.run_shell(["mkdir", dir_path]) + + def mkdir_rmdir_thread(mount, path): + #global stop_thread + + log.info(" mkdir_rmdir_thread starting...") + num = 0 + while not stop_thread: + n = num + m = num + for __ in range(10): + mount.run_shell(["mkdir", os.path.join(path, f"{n}")]) + n += 1 + for __ in range(10): + mount.run_shell(["rm", "-rf", os.path.join(path, f"{m}")]) + m += 1 + num += 10 + log.info(" mkdir_rmdir_thread stopped") + + def filesystem_sync_thread(mount, path): + #global stop_thread + + log.info(" filesystem_sync_thread starting...") + while not stop_thread: + mount.run_shell(["sync"]) + log.info(" filesystem_sync_thread stopped") + + def file_sync_thread(mount, path): + #global stop_thread + + log.info(" file_sync_thread starting...") + pyscript = dedent(""" + import os + + path = "{path}" + dfd = os.open(path, os.O_DIRECTORY) + os.fsync(dfd) + os.close(dfd) + """.format(path=path)) + + while not stop_thread: + mount.run_shell(['python3', '-c', pyscript]) + log.info(" file_sync_thread stopped") + + td1 = Thread(target=mkdir_rmdir_thread, args=(self.mount_a, dir_path,)) + td2 = Thread(target=filesystem_sync_thread, args=(self.mount_a, dir_path,)) + td3 = Thread(target=file_sync_thread, args=(self.mount_a, dir_path,)) + + td1.start() + td2.start() + td3.start() + time.sleep(1200) # run 20 minutes + stop_thread = True + td1.join() + td2.join() + td3.join() + self.mount_a.run_shell(["rm", "-rf", dir_path]) + + class TestCacheDrop(CephFSTestCase): CLIENTS_REQUIRED = 1 diff --git a/ceph/qa/tasks/cephfs/test_nfs.py b/ceph/qa/tasks/cephfs/test_nfs.py index 97269a32c..47b3e63a6 100644 --- a/ceph/qa/tasks/cephfs/test_nfs.py +++ b/ceph/qa/tasks/cephfs/test_nfs.py @@ -70,20 +70,42 @@ class TestNFS(MgrTestCase): log.info("Disabling NFS") self._sys_cmd(['sudo', 'systemctl', 'disable', 'nfs-server', '--now']) - def _fetch_nfs_status(self): - return self._orch_cmd('ps', f'--service_name={self.expected_name}') + def _fetch_nfs_daemons_details(self, enable_json=False): + args = ('ps', f'--service_name={self.expected_name}') + if enable_json: + args = (*args, '--format=json') + return self._orch_cmd(*args) + + def _check_nfs_cluster_event(self, expected_event): + ''' + Check whether an event occured during the lifetime of the NFS service + :param expected_event: event that was expected to occur + ''' + event_occurred = False + # Wait few seconds for NFS daemons' status to be updated + with contextutil.safe_while(sleep=10, tries=12, _raise=False) as proceed: + while not event_occurred and proceed(): + daemons_details = json.loads( + self._fetch_nfs_daemons_details(enable_json=True)) + log.info('daemons details %s', daemons_details) + for event in daemons_details[0]['events']: + log.info('daemon event %s', event) + if expected_event in event: + event_occurred = True + break + return event_occurred def _check_nfs_cluster_status(self, expected_status, fail_msg): ''' - Tests if nfs cluster created or deleted successfully + Check the current status of the NFS service :param expected_status: Status to be verified :param fail_msg: Message to be printed if test failed ''' - # Wait for few seconds as ganesha daemon takes few seconds to be deleted/created + # Wait for two minutes as ganesha daemon takes some time to be deleted/created wait_time = 10 - while wait_time <= 60: + while wait_time <= 120: time.sleep(wait_time) - if expected_status in self._fetch_nfs_status(): + if expected_status in self._fetch_nfs_daemons_details(): return wait_time += 10 self.fail(fail_msg) @@ -300,8 +322,11 @@ class TestNFS(MgrTestCase): self._test_mnt(pseudo_path, port, ip) except CommandFailedError as e: # Write to cephfs export should fail for test to pass - if e.exitstatus != errno.EPERM: - raise + self.assertEqual( + e.exitstatus, errno.EPERM, + 'invalid error code on trying to write to read-only export') + else: + self.fail('expected write to a read-only export to fail') def test_create_and_delete_cluster(self): ''' @@ -596,12 +621,13 @@ class TestNFS(MgrTestCase): })) port, ip = self._get_port_ip_info() self._test_mnt(self.pseudo_path, port, ip) - self._check_nfs_cluster_status('running', 'NFS Ganesha cluster restart failed') + self._check_nfs_cluster_status( + 'running', 'NFS Ganesha cluster not running after new export was applied') self._test_delete_cluster() def test_update_export(self): ''' - Test update of exports + Test update of export's pseudo path and access type from rw to ro ''' self._create_default_export() port, ip = self._get_port_ip_info() @@ -613,10 +639,32 @@ class TestNFS(MgrTestCase): self.ctx.cluster.run(args=['ceph', 'nfs', 'export', 'apply', self.cluster_id, '-i', '-'], stdin=json.dumps(export_block)) - self._check_nfs_cluster_status('running', 'NFS Ganesha cluster restart failed') + if not self._check_nfs_cluster_event('restart'): + self.fail("updating export's pseudo path should trigger restart of NFS service") + self._check_nfs_cluster_status('running', 'NFS Ganesha cluster not running after restart') self._write_to_read_only_export(new_pseudo_path, port, ip) self._test_delete_cluster() + def test_update_export_ro_to_rw(self): + ''' + Test update of export's access level from ro to rw + ''' + self._test_create_cluster() + self._create_export( + export_id='1', create_fs=True, + extra_cmd=['--pseudo-path', self.pseudo_path, '--readonly']) + port, ip = self._get_port_ip_info() + self._write_to_read_only_export(self.pseudo_path, port, ip) + export_block = self._get_export() + export_block['access_type'] = 'RW' + self.ctx.cluster.run( + args=['ceph', 'nfs', 'export', 'apply', self.cluster_id, '-i', '-'], + stdin=json.dumps(export_block)) + if self._check_nfs_cluster_event('restart'): + self.fail("update of export's access type should not trigger NFS service restart") + self._test_mnt(self.pseudo_path, port, ip) + self._test_delete_cluster() + def test_update_export_with_invalid_values(self): ''' Test update of export with invalid values diff --git a/ceph/qa/tasks/cephfs/test_volumes.py b/ceph/qa/tasks/cephfs/test_volumes.py index e9d57a770..2ae4674db 100644 --- a/ceph/qa/tasks/cephfs/test_volumes.py +++ b/ceph/qa/tasks/cephfs/test_volumes.py @@ -9,6 +9,7 @@ import uuid import unittest from hashlib import md5 from textwrap import dedent +from io import StringIO from tasks.cephfs.cephfs_test_case import CephFSTestCase from tasks.cephfs.fuse_mount import FuseMount @@ -53,12 +54,23 @@ class TestVolumesHelper(CephFSTestCase): time.sleep(1) self.assertTrue(check < timo) + def _get_clone_status(self, clone, clone_group=None): + args = ["clone", "status", self.volname, clone] + if clone_group: + args.append(clone_group) + args = tuple(args) + result = json.loads(self._fs_cmd(*args)) + return result + def _wait_for_clone_to_complete(self, clone, clone_group=None, timo=120): self.__check_clone_state("complete", clone, clone_group, timo) def _wait_for_clone_to_fail(self, clone, clone_group=None, timo=120): self.__check_clone_state("failed", clone, clone_group, timo) + def _wait_for_clone_to_be_in_progress(self, clone, clone_group=None, timo=120): + self.__check_clone_state("in-progress", clone, clone_group, timo) + def _check_clone_canceled(self, clone, clone_group=None): self.__check_clone_state("canceled", clone, clone_group, timo=1) @@ -2437,428 +2449,535 @@ class TestSubvolumes(TestVolumesHelper): # verify trash dir is clean self._wait_for_trash_empty() - -class TestSubvolumeGroupSnapshots(TestVolumesHelper): - """Tests for FS subvolume group snapshot operations.""" - @unittest.skip("skipping subvolumegroup snapshot tests") - def test_nonexistent_subvolume_group_snapshot_rm(self): + def test_subvolume_retain_snapshot_rm_idempotency(self): + """ + ensure subvolume deletion of a subvolume which is already deleted with retain snapshots option passes. + After subvolume deletion with retain snapshots, the subvolume exists until the trash directory (resides inside subvolume) + is cleaned up. The subvolume deletion issued while the trash directory is not empty, should pass and should + not error out with EAGAIN. + """ subvolume = self._generate_random_subvolume_name() - group = self._generate_random_group_name() snapshot = self._generate_random_snapshot_name() - # create group - self._fs_cmd("subvolumegroup", "create", self.volname, group) + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode=777") - # create subvolume in group - self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) + # do some IO + self._do_subvolume_io(subvolume, number_of_files=256) - # snapshot group - self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) - # remove snapshot - self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot) + # remove with snapshot retention + self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") - # remove snapshot + # remove snapshots (removes retained volume) + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # remove subvolume (check idempotency) try: - self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot) + self._fs_cmd("subvolume", "rm", self.volname, subvolume) except CommandFailedError as ce: if ce.exitstatus != errno.ENOENT: - raise - else: - raise RuntimeError("expected the 'fs subvolumegroup snapshot rm' command to fail") - - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + self.fail(f"expected subvolume rm to pass with error: {os.strerror(ce.exitstatus)}") # verify trash dir is clean self._wait_for_trash_empty() - # remove group - self._fs_cmd("subvolumegroup", "rm", self.volname, group) - @unittest.skip("skipping subvolumegroup snapshot tests") - def test_subvolume_group_snapshot_create_and_rm(self): - subvolume = self._generate_random_subvolume_name() + def test_subvolume_user_metadata_set(self): + subvolname = self._generate_random_subvolume_name() group = self._generate_random_group_name() - snapshot = self._generate_random_snapshot_name() - # create group + # create group. self._fs_cmd("subvolumegroup", "create", self.volname, group) - # create subvolume in group - self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) - - # snapshot group - self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # remove snapshot - self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot) + # set metadata for subvolume. + key = "key" + value = "value" + try: + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata set' command to succeed") - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - # verify trash dir is clean + # verify trash dir is clean. self._wait_for_trash_empty() - # remove group - self._fs_cmd("subvolumegroup", "rm", self.volname, group) - - @unittest.skip("skipping subvolumegroup snapshot tests") - def test_subvolume_group_snapshot_idempotence(self): - subvolume = self._generate_random_subvolume_name() + def test_subvolume_user_metadata_set_idempotence(self): + subvolname = self._generate_random_subvolume_name() group = self._generate_random_group_name() - snapshot = self._generate_random_snapshot_name() - # create group + # create group. self._fs_cmd("subvolumegroup", "create", self.volname, group) - # create subvolume in group - self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) - - # snapshot group - self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) - - # try creating snapshot w/ same snapshot name -- shoule be idempotent - self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) - - # remove snapshot - self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + # set metadata for subvolume. + key = "key" + value = "value" + try: + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata set' command to succeed") - # verify trash dir is clean - self._wait_for_trash_empty() + # set same metadata again for subvolume. + try: + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata set' command to succeed because it is idempotent operation") - # remove group + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) self._fs_cmd("subvolumegroup", "rm", self.volname, group) - @unittest.skip("skipping subvolumegroup snapshot tests") - def test_subvolume_group_snapshot_ls(self): - # tests the 'fs subvolumegroup snapshot ls' command - - snapshots = [] + # verify trash dir is clean. + self._wait_for_trash_empty() - # create group + def test_subvolume_user_metadata_get(self): + subvolname = self._generate_random_subvolume_name() group = self._generate_random_group_name() + + # create group. self._fs_cmd("subvolumegroup", "create", self.volname, group) - # create subvolumegroup snapshots - snapshots = self._generate_random_snapshot_name(3) - for snapshot in snapshots: - self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - subvolgrpsnapshotls = json.loads(self._fs_cmd('subvolumegroup', 'snapshot', 'ls', self.volname, group)) - if len(subvolgrpsnapshotls) == 0: - raise RuntimeError("Expected the 'fs subvolumegroup snapshot ls' command to list the created subvolume group snapshots") - else: - snapshotnames = [snapshot['name'] for snapshot in subvolgrpsnapshotls] - if collections.Counter(snapshotnames) != collections.Counter(snapshots): - raise RuntimeError("Error creating or listing subvolume group snapshots") + # set metadata for subvolume. + key = "key" + value = "value" + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) - @unittest.skip("skipping subvolumegroup snapshot tests") - def test_subvolume_group_snapshot_rm_force(self): - # test removing non-existing subvolume group snapshot with --force - group = self._generate_random_group_name() - snapshot = self._generate_random_snapshot_name() - # remove snapshot + # get value for specified key. try: - self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot, "--force") + ret = self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group) except CommandFailedError: - raise RuntimeError("expected the 'fs subvolumegroup snapshot rm --force' command to succeed") - - def test_subvolume_group_snapshot_unsupported_status(self): - group = self._generate_random_group_name() - snapshot = self._generate_random_snapshot_name() + self.fail("expected the 'fs subvolume metadata get' command to succeed") - # create group - self._fs_cmd("subvolumegroup", "create", self.volname, group) + # remove '\n' from returned value. + ret = ret.strip('\n') - # snapshot group - try: - self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.ENOSYS, "invalid error code on subvolumegroup snapshot create") - else: - self.fail("expected subvolumegroup snapshot create command to fail") + # match received value with expected value. + self.assertEqual(value, ret) - # remove group + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) self._fs_cmd("subvolumegroup", "rm", self.volname, group) + # verify trash dir is clean. + self._wait_for_trash_empty() -class TestSubvolumeSnapshots(TestVolumesHelper): - """Tests for FS subvolume snapshot operations.""" - def test_nonexistent_subvolume_snapshot_rm(self): - subvolume = self._generate_random_subvolume_name() - snapshot = self._generate_random_snapshot_name() + def test_subvolume_user_metadata_get_for_nonexisting_key(self): + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() - # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) - # snapshot subvolume - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # remove snapshot - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + # set metadata for subvolume. + key = "key" + value = "value" + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) - # remove snapshot again + # try to get value for nonexisting key + # Expecting ENOENT exit status because key does not exist try: - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) - except CommandFailedError as ce: - if ce.exitstatus != errno.ENOENT: - raise + self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, "key_nonexist", "--group_name", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) else: - raise RuntimeError("expected the 'fs subvolume snapshot rm' command to fail") + self.fail("Expected ENOENT because 'key_nonexist' does not exist") - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - # verify trash dir is clean + # verify trash dir is clean. self._wait_for_trash_empty() - def test_subvolume_snapshot_create_and_rm(self): - subvolume = self._generate_random_subvolume_name() - snapshot = self._generate_random_snapshot_name() + def test_subvolume_user_metadata_get_for_nonexisting_section(self): + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() - # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) - # snapshot subvolume - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # remove snapshot - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + # try to get value for nonexisting key (as section does not exist) + # Expecting ENOENT exit status because key does not exist + try: + self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, "key", "--group_name", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because section does not exist") - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - # verify trash dir is clean + # verify trash dir is clean. self._wait_for_trash_empty() - def test_subvolume_snapshot_create_idempotence(self): - subvolume = self._generate_random_subvolume_name() - snapshot = self._generate_random_snapshot_name() + def test_subvolume_user_metadata_update(self): + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() - # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) - # snapshot subvolume - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # try creating w/ same subvolume snapshot name -- should be idempotent - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + # set metadata for subvolume. + key = "key" + value = "value" + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) - # remove snapshot - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + # update metadata against key. + new_value = "new_value" + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, new_value, "--group_name", group) - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume) + # get metadata for specified key of subvolume. + try: + ret = self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata get' command to succeed") - # verify trash dir is clean - self._wait_for_trash_empty() + # remove '\n' from returned value. + ret = ret.strip('\n') - def test_subvolume_snapshot_info(self): + # match received value with expected value. + self.assertEqual(new_value, ret) - """ - tests the 'fs subvolume snapshot info' command - """ + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - snap_md = ["created_at", "data_pool", "has_pending_clones", "size"] + # verify trash dir is clean. + self._wait_for_trash_empty() - subvolume = self._generate_random_subvolume_name() - snapshot, snap_missing = self._generate_random_snapshot_name(2) + def test_subvolume_user_metadata_list(self): + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() - # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode=777") + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) - # do some IO - self._do_subvolume_io(subvolume, number_of_files=1) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # snapshot subvolume - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + # set metadata for subvolume. + input_metadata_dict = {f'key_{i}' : f'value_{i}' for i in range(3)} - snap_info = json.loads(self._get_subvolume_snapshot_info(self.volname, subvolume, snapshot)) - for md in snap_md: - self.assertIn(md, snap_info, "'{0}' key not present in metadata of snapshot".format(md)) - self.assertEqual(snap_info["has_pending_clones"], "no") + for k, v in input_metadata_dict.items(): + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, k, v, "--group_name", group) - # snapshot info for non-existent snapshot + # list metadata try: - self._get_subvolume_snapshot_info(self.volname, subvolume, snap_missing) - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on snapshot info of non-existent snapshot") - else: - self.fail("expected snapshot info of non-existent snapshot to fail") + ret = self._fs_cmd("subvolume", "metadata", "ls", self.volname, subvolname, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata ls' command to succeed") - # remove snapshot - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + ret_dict = json.loads(ret) - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume) + # compare output with expected output + self.assertDictEqual(input_metadata_dict, ret_dict) - # verify trash dir is clean + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. self._wait_for_trash_empty() - def test_subvolume_snapshot_in_group(self): - subvolume = self._generate_random_subvolume_name() + def test_subvolume_user_metadata_list_if_no_metadata_set(self): + subvolname = self._generate_random_subvolume_name() group = self._generate_random_group_name() - snapshot = self._generate_random_snapshot_name() - # create group + # create group. self._fs_cmd("subvolumegroup", "create", self.volname, group) - # create subvolume in group - self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # snapshot subvolume in group - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot, group) + # list metadata + try: + ret = self._fs_cmd("subvolume", "metadata", "ls", self.volname, subvolname, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata ls' command to succeed") - # remove snapshot - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, group) + # remove '\n' from returned value. + ret = ret.strip('\n') - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + # compare output with expected output + # expecting empty json/dictionary + self.assertEqual(ret, "{}") - # verify trash dir is clean + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. self._wait_for_trash_empty() - # remove group - self._fs_cmd("subvolumegroup", "rm", self.volname, group) + def test_subvolume_user_metadata_remove(self): + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() - def test_subvolume_snapshot_ls(self): - # tests the 'fs subvolume snapshot ls' command + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) - snapshots = [] + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # create subvolume - subvolume = self._generate_random_subvolume_name() - self._fs_cmd("subvolume", "create", self.volname, subvolume) + # set metadata for subvolume. + key = "key" + value = "value" + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) - # create subvolume snapshots - snapshots = self._generate_random_snapshot_name(3) - for snapshot in snapshots: - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + # remove metadata against specified key. + try: + self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, key, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata rm' command to succeed") - subvolsnapshotls = json.loads(self._fs_cmd('subvolume', 'snapshot', 'ls', self.volname, subvolume)) - if len(subvolsnapshotls) == 0: - self.fail("Expected the 'fs subvolume snapshot ls' command to list the created subvolume snapshots") + # confirm key is removed by again fetching metadata + try: + self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) else: - snapshotnames = [snapshot['name'] for snapshot in subvolsnapshotls] - if collections.Counter(snapshotnames) != collections.Counter(snapshots): - self.fail("Error creating or listing subvolume snapshots") - - # remove snapshot - for snapshot in snapshots: - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + self.fail("Expected ENOENT because key does not exist") - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - # verify trash dir is clean + # verify trash dir is clean. self._wait_for_trash_empty() - def test_subvolume_inherited_snapshot_ls(self): - # tests the scenario where 'fs subvolume snapshot ls' command - # should not list inherited snapshots created as part of snapshot - # at ancestral level - - snapshots = [] - subvolume = self._generate_random_subvolume_name() + def test_subvolume_user_metadata_remove_for_nonexisting_key(self): + subvolname = self._generate_random_subvolume_name() group = self._generate_random_group_name() - snap_count = 3 - # create group + # create group. self._fs_cmd("subvolumegroup", "create", self.volname, group) - # create subvolume in group - self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # create subvolume snapshots - snapshots = self._generate_random_snapshot_name(snap_count) - for snapshot in snapshots: - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot, group) + # set metadata for subvolume. + key = "key" + value = "value" + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) - # Create snapshot at ancestral level - ancestral_snappath1 = os.path.join(".", "volumes", group, ".snap", "ancestral_snap_1") - ancestral_snappath2 = os.path.join(".", "volumes", group, ".snap", "ancestral_snap_2") - self.mount_a.run_shell(['mkdir', '-p', ancestral_snappath1, ancestral_snappath2], sudo=True) + # try to remove value for nonexisting key + # Expecting ENOENT exit status because key does not exist + try: + self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, "key_nonexist", "--group_name", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because 'key_nonexist' does not exist") - subvolsnapshotls = json.loads(self._fs_cmd('subvolume', 'snapshot', 'ls', self.volname, subvolume, group)) - self.assertEqual(len(subvolsnapshotls), snap_count) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - # remove ancestral snapshots - self.mount_a.run_shell(['rmdir', ancestral_snappath1, ancestral_snappath2], sudo=True) + # verify trash dir is clean. + self._wait_for_trash_empty() - # remove snapshot - for snapshot in snapshots: - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, group) + def test_subvolume_user_metadata_remove_for_nonexisting_section(self): + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) - # verify trash dir is clean + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) + + # try to remove value for nonexisting key (as section does not exist) + # Expecting ENOENT exit status because key does not exist + try: + self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, "key", "--group_name", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because section does not exist") + + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. self._wait_for_trash_empty() - # remove group + def test_subvolume_user_metadata_remove_force(self): + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) + + # set metadata for subvolume. + key = "key" + value = "value" + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) + + # remove metadata against specified key with --force option. + try: + self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, key, "--group_name", group, "--force") + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata rm' command to succeed") + + # confirm key is removed by again fetching metadata + try: + self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because key does not exist") + + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) self._fs_cmd("subvolumegroup", "rm", self.volname, group) - def test_subvolume_inherited_snapshot_info(self): - """ - tests the scenario where 'fs subvolume snapshot info' command - should fail for inherited snapshots created as part of snapshot - at ancestral level - """ + # verify trash dir is clean. + self._wait_for_trash_empty() - subvolume = self._generate_random_subvolume_name() + def test_subvolume_user_metadata_remove_force_for_nonexisting_key(self): + subvolname = self._generate_random_subvolume_name() group = self._generate_random_group_name() - # create group + # create group. self._fs_cmd("subvolumegroup", "create", self.volname, group) - # create subvolume in group - self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group) - # Create snapshot at ancestral level - ancestral_snap_name = "ancestral_snap_1" - ancestral_snappath1 = os.path.join(".", "volumes", group, ".snap", ancestral_snap_name) - self.mount_a.run_shell(['mkdir', '-p', ancestral_snappath1], sudo=True) + # set metadata for subvolume. + key = "key" + value = "value" + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) - # Validate existence of inherited snapshot - group_path = os.path.join(".", "volumes", group) - inode_number_group_dir = int(self.mount_a.run_shell(['stat', '-c' '%i', group_path]).stdout.getvalue().strip()) - inherited_snap = "_{0}_{1}".format(ancestral_snap_name, inode_number_group_dir) - inherited_snappath = os.path.join(".", "volumes", group, subvolume,".snap", inherited_snap) - self.mount_a.run_shell(['ls', inherited_snappath]) + # remove metadata against specified key. + try: + self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, key, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata rm' command to succeed") - # snapshot info on inherited snapshot + # confirm key is removed by again fetching metadata try: - self._get_subvolume_snapshot_info(self.volname, subvolume, inherited_snap, group) - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.EINVAL, "invalid error code on snapshot info of inherited snapshot") + self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) else: - self.fail("expected snapshot info of inherited snapshot to fail") + self.fail("Expected ENOENT because key does not exist") - # remove ancestral snapshots - self.mount_a.run_shell(['rmdir', ancestral_snappath1], sudo=True) + # again remove metadata against already removed key with --force option. + try: + self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, key, "--group_name", group, "--force") + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata rm' (with --force) command to succeed") - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--group_name", group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - # verify trash dir is clean + # verify trash dir is clean. self._wait_for_trash_empty() - # remove group + def test_subvolume_user_metadata_set_and_get_for_legacy_subvolume(self): + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + + # emulate a old-fashioned subvolume in a custom group + createpath = os.path.join(".", "volumes", group, subvolname) + self.mount_a.run_shell(['mkdir', '-p', createpath], sudo=True) + + # set metadata for subvolume. + key = "key" + value = "value" + try: + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata set' command to succeed") + + # get value for specified key. + try: + ret = self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata get' command to succeed") + + # remove '\n' from returned value. + ret = ret.strip('\n') + + # match received value with expected value. + self.assertEqual(value, ret) + + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) self._fs_cmd("subvolumegroup", "rm", self.volname, group) - def test_subvolume_inherited_snapshot_rm(self): - """ - tests the scenario where 'fs subvolume snapshot rm' command - should fail for inherited snapshots created as part of snapshot - at ancestral level - """ + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_user_metadata_list_and_remove_for_legacy_subvolume(self): + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + + # emulate a old-fashioned subvolume in a custom group + createpath = os.path.join(".", "volumes", group, subvolname) + self.mount_a.run_shell(['mkdir', '-p', createpath], sudo=True) + + # set metadata for subvolume. + input_metadata_dict = {f'key_{i}' : f'value_{i}' for i in range(3)} + + for k, v in input_metadata_dict.items(): + self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, k, v, "--group_name", group) + + # list metadata + try: + ret = self._fs_cmd("subvolume", "metadata", "ls", self.volname, subvolname, "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata ls' command to succeed") + + ret_dict = json.loads(ret) + + # compare output with expected output + self.assertDictEqual(input_metadata_dict, ret_dict) + + # remove metadata against specified key. + try: + self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, "key_1", "--group_name", group) + except CommandFailedError: + self.fail("expected the 'fs subvolume metadata rm' command to succeed") + + # confirm key is removed by again fetching metadata + try: + self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, "key_1", "--group_name", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because key_1 does not exist") + + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + # verify trash dir is clean. + self._wait_for_trash_empty() + +class TestSubvolumeGroupSnapshots(TestVolumesHelper): + """Tests for FS subvolume group snapshot operations.""" + @unittest.skip("skipping subvolumegroup snapshot tests") + def test_nonexistent_subvolume_group_snapshot_rm(self): subvolume = self._generate_random_subvolume_name() group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() # create group self._fs_cmd("subvolumegroup", "create", self.volname, group) @@ -2866,28 +2985,20 @@ class TestSubvolumeSnapshots(TestVolumesHelper): # create subvolume in group self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) - # Create snapshot at ancestral level - ancestral_snap_name = "ancestral_snap_1" - ancestral_snappath1 = os.path.join(".", "volumes", group, ".snap", ancestral_snap_name) - self.mount_a.run_shell(['mkdir', '-p', ancestral_snappath1], sudo=True) + # snapshot group + self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) - # Validate existence of inherited snap - group_path = os.path.join(".", "volumes", group) - inode_number_group_dir = int(self.mount_a.run_shell(['stat', '-c' '%i', group_path]).stdout.getvalue().strip()) - inherited_snap = "_{0}_{1}".format(ancestral_snap_name, inode_number_group_dir) - inherited_snappath = os.path.join(".", "volumes", group, subvolume,".snap", inherited_snap) - self.mount_a.run_shell(['ls', inherited_snappath]) + # remove snapshot + self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot) - # inherited snapshot should not be deletable + # remove snapshot try: - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, inherited_snap, "--group_name", group) + self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot) except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.EINVAL, msg="invalid error code when removing inherited snapshot") + if ce.exitstatus != errno.ENOENT: + raise else: - self.fail("expected removing inheirted snapshot to fail") - - # remove ancestral snapshots - self.mount_a.run_shell(['rmdir', ancestral_snappath1], sudo=True) + raise RuntimeError("expected the 'fs subvolumegroup snapshot rm' command to fail") # remove subvolume self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) @@ -2898,16 +3009,11 @@ class TestSubvolumeSnapshots(TestVolumesHelper): # remove group self._fs_cmd("subvolumegroup", "rm", self.volname, group) - def test_subvolume_subvolumegroup_snapshot_name_conflict(self): - """ - tests the scenario where creation of subvolume snapshot name - with same name as it's subvolumegroup snapshot name. This should - fail. - """ - + @unittest.skip("skipping subvolumegroup snapshot tests") + def test_subvolume_group_snapshot_create_and_rm(self): subvolume = self._generate_random_subvolume_name() group = self._generate_random_group_name() - group_snapshot = self._generate_random_snapshot_name() + snapshot = self._generate_random_snapshot_name() # create group self._fs_cmd("subvolumegroup", "create", self.volname, group) @@ -2915,23 +3021,11 @@ class TestSubvolumeSnapshots(TestVolumesHelper): # create subvolume in group self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) - # Create subvolumegroup snapshot - group_snapshot_path = os.path.join(".", "volumes", group, ".snap", group_snapshot) - self.mount_a.run_shell(['mkdir', '-p', group_snapshot_path], sudo=True) - - # Validate existence of subvolumegroup snapshot - self.mount_a.run_shell(['ls', group_snapshot_path]) - - # Creation of subvolume snapshot with it's subvolumegroup snapshot name should fail - try: - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, group_snapshot, "--group_name", group) - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.EINVAL, msg="invalid error code when creating subvolume snapshot with same name as subvolume group snapshot") - else: - self.fail("expected subvolume snapshot creation with same name as subvolumegroup snapshot to fail") + # snapshot group + self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) - # remove subvolumegroup snapshot - self.mount_a.run_shell(['rmdir', group_snapshot_path], sudo=True) + # remove snapshot + self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot) # remove subvolume self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) @@ -2942,352 +3036,1368 @@ class TestSubvolumeSnapshots(TestVolumesHelper): # remove group self._fs_cmd("subvolumegroup", "rm", self.volname, group) - def test_subvolume_retain_snapshot_invalid_recreate(self): - """ - ensure retained subvolume recreate does not leave any incarnations in the subvolume and trash - """ + @unittest.skip("skipping subvolumegroup snapshot tests") + def test_subvolume_group_snapshot_idempotence(self): subvolume = self._generate_random_subvolume_name() + group = self._generate_random_group_name() snapshot = self._generate_random_snapshot_name() - # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) + # create group + self._fs_cmd("subvolumegroup", "create", self.volname, group) - # snapshot subvolume - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + # create subvolume in group + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) - # remove with snapshot retention - self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + # snapshot group + self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) - # recreate subvolume with an invalid pool - data_pool = "invalid_pool" + # try creating snapshot w/ same snapshot name -- shoule be idempotent + self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) + + # remove snapshot + self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot) + + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + + # verify trash dir is clean + self._wait_for_trash_empty() + + # remove group + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + @unittest.skip("skipping subvolumegroup snapshot tests") + def test_subvolume_group_snapshot_ls(self): + # tests the 'fs subvolumegroup snapshot ls' command + + snapshots = [] + + # create group + group = self._generate_random_group_name() + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolumegroup snapshots + snapshots = self._generate_random_snapshot_name(3) + for snapshot in snapshots: + self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) + + subvolgrpsnapshotls = json.loads(self._fs_cmd('subvolumegroup', 'snapshot', 'ls', self.volname, group)) + if len(subvolgrpsnapshotls) == 0: + raise RuntimeError("Expected the 'fs subvolumegroup snapshot ls' command to list the created subvolume group snapshots") + else: + snapshotnames = [snapshot['name'] for snapshot in subvolgrpsnapshotls] + if collections.Counter(snapshotnames) != collections.Counter(snapshots): + raise RuntimeError("Error creating or listing subvolume group snapshots") + + @unittest.skip("skipping subvolumegroup snapshot tests") + def test_subvolume_group_snapshot_rm_force(self): + # test removing non-existing subvolume group snapshot with --force + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + # remove snapshot try: - self._fs_cmd("subvolume", "create", self.volname, subvolume, "--pool_layout", data_pool) + self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot, "--force") + except CommandFailedError: + raise RuntimeError("expected the 'fs subvolumegroup snapshot rm --force' command to succeed") + + def test_subvolume_group_snapshot_unsupported_status(self): + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # snapshot group + try: + self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot) except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.EINVAL, "invalid error code on recreate of subvolume with invalid poolname") + self.assertEqual(ce.exitstatus, errno.ENOSYS, "invalid error code on subvolumegroup snapshot create") else: - self.fail("expected recreate of subvolume with invalid poolname to fail") + self.fail("expected subvolumegroup snapshot create command to fail") - # fetch info - subvol_info = json.loads(self._fs_cmd("subvolume", "info", self.volname, subvolume)) - self.assertEqual(subvol_info["state"], "snapshot-retained", - msg="expected state to be 'snapshot-retained', found '{0}".format(subvol_info["state"])) + # remove group + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - # getpath + +class TestSubvolumeSnapshots(TestVolumesHelper): + """Tests for FS subvolume snapshot operations.""" + def test_nonexistent_subvolume_snapshot_rm(self): + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # remove snapshot again try: - self._fs_cmd("subvolume", "getpath", self.volname, subvolume) + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on getpath of subvolume with retained snapshots") + if ce.exitstatus != errno.ENOENT: + raise else: - self.fail("expected getpath of subvolume with retained snapshots to fail") + raise RuntimeError("expected the 'fs subvolume snapshot rm' command to fail") - # remove snapshot (should remove volume) - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume) # verify trash dir is clean self._wait_for_trash_empty() - def test_subvolume_retain_snapshot_recreate_subvolume(self): - """ - ensure a retained subvolume can be recreated and further snapshotted - """ - snap_md = ["created_at", "data_pool", "has_pending_clones", "size"] - + def test_subvolume_snapshot_create_and_rm(self): subvolume = self._generate_random_subvolume_name() - snapshot1, snapshot2 = self._generate_random_snapshot_name(2) + snapshot = self._generate_random_snapshot_name() # create subvolume self._fs_cmd("subvolume", "create", self.volname, subvolume) # snapshot subvolume - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot1) - - # remove with snapshot retention - self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) - # fetch info - subvol_info = json.loads(self._fs_cmd("subvolume", "info", self.volname, subvolume)) - self.assertEqual(subvol_info["state"], "snapshot-retained", - msg="expected state to be 'snapshot-retained', found '{0}".format(subvol_info["state"])) + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) - # recreate retained subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume) - # fetch info - subvol_info = json.loads(self._fs_cmd("subvolume", "info", self.volname, subvolume)) - self.assertEqual(subvol_info["state"], "complete", - msg="expected state to be 'snapshot-retained', found '{0}".format(subvol_info["state"])) + # verify trash dir is clean + self._wait_for_trash_empty() - # snapshot info (older snapshot) - snap_info = json.loads(self._get_subvolume_snapshot_info(self.volname, subvolume, snapshot1)) - for md in snap_md: - self.assertIn(md, snap_info, "'{0}' key not present in metadata of snapshot".format(md)) - self.assertEqual(snap_info["has_pending_clones"], "no") + def test_subvolume_snapshot_create_idempotence(self): + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() - # snap-create (new snapshot) - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot2) + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) - # remove with retain snapshots - self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) - # list snapshots - subvolsnapshotls = json.loads(self._fs_cmd('subvolume', 'snapshot', 'ls', self.volname, subvolume)) - self.assertEqual(len(subvolsnapshotls), 2, "Expected the 'fs subvolume snapshot ls' command to list the" - " created subvolume snapshots") - snapshotnames = [snapshot['name'] for snapshot in subvolsnapshotls] - for snap in [snapshot1, snapshot2]: - self.assertIn(snap, snapshotnames, "Missing snapshot '{0}' in snapshot list".format(snap)) + # try creating w/ same subvolume snapshot name -- should be idempotent + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) - # remove snapshots (should remove volume) - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot1) - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot2) + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) - # verify list subvolumes returns an empty list - subvolumels = json.loads(self._fs_cmd('subvolume', 'ls', self.volname)) - self.assertEqual(len(subvolumels), 0) + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume) # verify trash dir is clean self._wait_for_trash_empty() - def test_subvolume_retain_snapshot_with_snapshots(self): + def test_subvolume_snapshot_info(self): + """ - ensure retain snapshots based delete of a subvolume with snapshots retains the subvolume - also test allowed and dis-allowed operations on a retained subvolume + tests the 'fs subvolume snapshot info' command """ + snap_md = ["created_at", "data_pool", "has_pending_clones", "size"] subvolume = self._generate_random_subvolume_name() - snapshot = self._generate_random_snapshot_name() + snapshot, snap_missing = self._generate_random_snapshot_name(2) # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode=777") + + # do some IO + self._do_subvolume_io(subvolume, number_of_files=1) # snapshot subvolume self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) - # remove subvolume -- should fail with ENOTEMPTY since it has snapshots - try: - self._fs_cmd("subvolume", "rm", self.volname, subvolume) - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.ENOTEMPTY, "invalid error code on rm of retained subvolume with snapshots") - else: - self.fail("expected rm of subvolume with retained snapshots to fail") - - # remove with snapshot retention - self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") - - # fetch info - subvol_info = json.loads(self._fs_cmd("subvolume", "info", self.volname, subvolume)) - self.assertEqual(subvol_info["state"], "snapshot-retained", - msg="expected state to be 'snapshot-retained', found '{0}".format(subvol_info["state"])) - - ## test allowed ops in retained state - # ls - subvolumes = json.loads(self._fs_cmd('subvolume', 'ls', self.volname)) - self.assertEqual(len(subvolumes), 1, "subvolume ls count mismatch, expected '1', found {0}".format(len(subvolumes))) - self.assertEqual(subvolumes[0]['name'], subvolume, - "subvolume name mismatch in ls output, expected '{0}', found '{1}'".format(subvolume, subvolumes[0]['name'])) - - # snapshot info snap_info = json.loads(self._get_subvolume_snapshot_info(self.volname, subvolume, snapshot)) for md in snap_md: self.assertIn(md, snap_info, "'{0}' key not present in metadata of snapshot".format(md)) self.assertEqual(snap_info["has_pending_clones"], "no") - # rm --force (allowed but should fail) - try: - self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--force") - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.ENOTEMPTY, "invalid error code on rm of subvolume with retained snapshots") - else: - self.fail("expected rm of subvolume with retained snapshots to fail") - - # rm (allowed but should fail) - try: - self._fs_cmd("subvolume", "rm", self.volname, subvolume) - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.ENOTEMPTY, "invalid error code on rm of subvolume with retained snapshots") - else: - self.fail("expected rm of subvolume with retained snapshots to fail") - - ## test disallowed ops - # getpath - try: - self._fs_cmd("subvolume", "getpath", self.volname, subvolume) - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on getpath of subvolume with retained snapshots") - else: - self.fail("expected getpath of subvolume with retained snapshots to fail") - - # resize - nsize = self.DEFAULT_FILE_SIZE*1024*1024 - try: - self._fs_cmd("subvolume", "resize", self.volname, subvolume, str(nsize)) - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on resize of subvolume with retained snapshots") - else: - self.fail("expected resize of subvolume with retained snapshots to fail") - - # snap-create + # snapshot info for non-existent snapshot try: - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, "fail") + self._get_subvolume_snapshot_info(self.volname, subvolume, snap_missing) except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on snapshot create of subvolume with retained snapshots") + self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on snapshot info of non-existent snapshot") else: - self.fail("expected snapshot create of subvolume with retained snapshots to fail") + self.fail("expected snapshot info of non-existent snapshot to fail") - # remove snapshot (should remove volume) + # remove snapshot self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) - # verify list subvolumes returns an empty list - subvolumels = json.loads(self._fs_cmd('subvolume', 'ls', self.volname)) - self.assertEqual(len(subvolumels), 0) + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume) # verify trash dir is clean self._wait_for_trash_empty() - def test_subvolume_retain_snapshot_without_snapshots(self): + def test_subvolume_snapshot_in_group(self): + subvolume = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) + + # snapshot subvolume in group + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot, group) + + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, group) + + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + + # verify trash dir is clean + self._wait_for_trash_empty() + + # remove group + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + def test_subvolume_snapshot_ls(self): + # tests the 'fs subvolume snapshot ls' command + + snapshots = [] + + # create subvolume + subvolume = self._generate_random_subvolume_name() + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # create subvolume snapshots + snapshots = self._generate_random_snapshot_name(3) + for snapshot in snapshots: + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + subvolsnapshotls = json.loads(self._fs_cmd('subvolume', 'snapshot', 'ls', self.volname, subvolume)) + if len(subvolsnapshotls) == 0: + self.fail("Expected the 'fs subvolume snapshot ls' command to list the created subvolume snapshots") + else: + snapshotnames = [snapshot['name'] for snapshot in subvolsnapshotls] + if collections.Counter(snapshotnames) != collections.Counter(snapshots): + self.fail("Error creating or listing subvolume snapshots") + + # remove snapshot + for snapshot in snapshots: + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_subvolume_inherited_snapshot_ls(self): + # tests the scenario where 'fs subvolume snapshot ls' command + # should not list inherited snapshots created as part of snapshot + # at ancestral level + + snapshots = [] + subvolume = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snap_count = 3 + + # create group + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) + + # create subvolume snapshots + snapshots = self._generate_random_snapshot_name(snap_count) + for snapshot in snapshots: + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot, group) + + # Create snapshot at ancestral level + ancestral_snappath1 = os.path.join(".", "volumes", group, ".snap", "ancestral_snap_1") + ancestral_snappath2 = os.path.join(".", "volumes", group, ".snap", "ancestral_snap_2") + self.mount_a.run_shell(['mkdir', '-p', ancestral_snappath1, ancestral_snappath2], sudo=True) + + subvolsnapshotls = json.loads(self._fs_cmd('subvolume', 'snapshot', 'ls', self.volname, subvolume, group)) + self.assertEqual(len(subvolsnapshotls), snap_count) + + # remove ancestral snapshots + self.mount_a.run_shell(['rmdir', ancestral_snappath1, ancestral_snappath2], sudo=True) + + # remove snapshot + for snapshot in snapshots: + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, group) + + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + + # verify trash dir is clean + self._wait_for_trash_empty() + + # remove group + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + def test_subvolume_inherited_snapshot_info(self): + """ + tests the scenario where 'fs subvolume snapshot info' command + should fail for inherited snapshots created as part of snapshot + at ancestral level + """ + + subvolume = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + + # create group + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) + + # Create snapshot at ancestral level + ancestral_snap_name = "ancestral_snap_1" + ancestral_snappath1 = os.path.join(".", "volumes", group, ".snap", ancestral_snap_name) + self.mount_a.run_shell(['mkdir', '-p', ancestral_snappath1], sudo=True) + + # Validate existence of inherited snapshot + group_path = os.path.join(".", "volumes", group) + inode_number_group_dir = int(self.mount_a.run_shell(['stat', '-c' '%i', group_path]).stdout.getvalue().strip()) + inherited_snap = "_{0}_{1}".format(ancestral_snap_name, inode_number_group_dir) + inherited_snappath = os.path.join(".", "volumes", group, subvolume,".snap", inherited_snap) + self.mount_a.run_shell(['ls', inherited_snappath]) + + # snapshot info on inherited snapshot + try: + self._get_subvolume_snapshot_info(self.volname, subvolume, inherited_snap, group) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.EINVAL, "invalid error code on snapshot info of inherited snapshot") + else: + self.fail("expected snapshot info of inherited snapshot to fail") + + # remove ancestral snapshots + self.mount_a.run_shell(['rmdir', ancestral_snappath1], sudo=True) + + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--group_name", group) + + # verify trash dir is clean + self._wait_for_trash_empty() + + # remove group + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + def test_subvolume_inherited_snapshot_rm(self): + """ + tests the scenario where 'fs subvolume snapshot rm' command + should fail for inherited snapshots created as part of snapshot + at ancestral level + """ + + subvolume = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + + # create group + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) + + # Create snapshot at ancestral level + ancestral_snap_name = "ancestral_snap_1" + ancestral_snappath1 = os.path.join(".", "volumes", group, ".snap", ancestral_snap_name) + self.mount_a.run_shell(['mkdir', '-p', ancestral_snappath1], sudo=True) + + # Validate existence of inherited snap + group_path = os.path.join(".", "volumes", group) + inode_number_group_dir = int(self.mount_a.run_shell(['stat', '-c' '%i', group_path]).stdout.getvalue().strip()) + inherited_snap = "_{0}_{1}".format(ancestral_snap_name, inode_number_group_dir) + inherited_snappath = os.path.join(".", "volumes", group, subvolume,".snap", inherited_snap) + self.mount_a.run_shell(['ls', inherited_snappath]) + + # inherited snapshot should not be deletable + try: + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, inherited_snap, "--group_name", group) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.EINVAL, msg="invalid error code when removing inherited snapshot") + else: + self.fail("expected removing inheirted snapshot to fail") + + # remove ancestral snapshots + self.mount_a.run_shell(['rmdir', ancestral_snappath1], sudo=True) + + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + + # verify trash dir is clean + self._wait_for_trash_empty() + + # remove group + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + def test_subvolume_subvolumegroup_snapshot_name_conflict(self): + """ + tests the scenario where creation of subvolume snapshot name + with same name as it's subvolumegroup snapshot name. This should + fail. + """ + + subvolume = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + group_snapshot = self._generate_random_snapshot_name() + + # create group + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group) + + # Create subvolumegroup snapshot + group_snapshot_path = os.path.join(".", "volumes", group, ".snap", group_snapshot) + self.mount_a.run_shell(['mkdir', '-p', group_snapshot_path], sudo=True) + + # Validate existence of subvolumegroup snapshot + self.mount_a.run_shell(['ls', group_snapshot_path]) + + # Creation of subvolume snapshot with it's subvolumegroup snapshot name should fail + try: + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, group_snapshot, "--group_name", group) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.EINVAL, msg="invalid error code when creating subvolume snapshot with same name as subvolume group snapshot") + else: + self.fail("expected subvolume snapshot creation with same name as subvolumegroup snapshot to fail") + + # remove subvolumegroup snapshot + self.mount_a.run_shell(['rmdir', group_snapshot_path], sudo=True) + + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume, group) + + # verify trash dir is clean + self._wait_for_trash_empty() + + # remove group + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + def test_subvolume_retain_snapshot_invalid_recreate(self): + """ + ensure retained subvolume recreate does not leave any incarnations in the subvolume and trash + """ + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # remove with snapshot retention + self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + + # recreate subvolume with an invalid pool + data_pool = "invalid_pool" + try: + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--pool_layout", data_pool) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.EINVAL, "invalid error code on recreate of subvolume with invalid poolname") + else: + self.fail("expected recreate of subvolume with invalid poolname to fail") + + # fetch info + subvol_info = json.loads(self._fs_cmd("subvolume", "info", self.volname, subvolume)) + self.assertEqual(subvol_info["state"], "snapshot-retained", + msg="expected state to be 'snapshot-retained', found '{0}".format(subvol_info["state"])) + + # getpath + try: + self._fs_cmd("subvolume", "getpath", self.volname, subvolume) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on getpath of subvolume with retained snapshots") + else: + self.fail("expected getpath of subvolume with retained snapshots to fail") + + # remove snapshot (should remove volume) + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_subvolume_retain_snapshot_recreate_subvolume(self): + """ + ensure a retained subvolume can be recreated and further snapshotted + """ + snap_md = ["created_at", "data_pool", "has_pending_clones", "size"] + + subvolume = self._generate_random_subvolume_name() + snapshot1, snapshot2 = self._generate_random_snapshot_name(2) + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot1) + + # remove with snapshot retention + self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + + # fetch info + subvol_info = json.loads(self._fs_cmd("subvolume", "info", self.volname, subvolume)) + self.assertEqual(subvol_info["state"], "snapshot-retained", + msg="expected state to be 'snapshot-retained', found '{0}".format(subvol_info["state"])) + + # recreate retained subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # fetch info + subvol_info = json.loads(self._fs_cmd("subvolume", "info", self.volname, subvolume)) + self.assertEqual(subvol_info["state"], "complete", + msg="expected state to be 'snapshot-retained', found '{0}".format(subvol_info["state"])) + + # snapshot info (older snapshot) + snap_info = json.loads(self._get_subvolume_snapshot_info(self.volname, subvolume, snapshot1)) + for md in snap_md: + self.assertIn(md, snap_info, "'{0}' key not present in metadata of snapshot".format(md)) + self.assertEqual(snap_info["has_pending_clones"], "no") + + # snap-create (new snapshot) + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot2) + + # remove with retain snapshots + self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + + # list snapshots + subvolsnapshotls = json.loads(self._fs_cmd('subvolume', 'snapshot', 'ls', self.volname, subvolume)) + self.assertEqual(len(subvolsnapshotls), 2, "Expected the 'fs subvolume snapshot ls' command to list the" + " created subvolume snapshots") + snapshotnames = [snapshot['name'] for snapshot in subvolsnapshotls] + for snap in [snapshot1, snapshot2]: + self.assertIn(snap, snapshotnames, "Missing snapshot '{0}' in snapshot list".format(snap)) + + # remove snapshots (should remove volume) + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot1) + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot2) + + # verify list subvolumes returns an empty list + subvolumels = json.loads(self._fs_cmd('subvolume', 'ls', self.volname)) + self.assertEqual(len(subvolumels), 0) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_subvolume_retain_snapshot_with_snapshots(self): + """ + ensure retain snapshots based delete of a subvolume with snapshots retains the subvolume + also test allowed and dis-allowed operations on a retained subvolume + """ + snap_md = ["created_at", "data_pool", "has_pending_clones", "size"] + + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # remove subvolume -- should fail with ENOTEMPTY since it has snapshots + try: + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.ENOTEMPTY, "invalid error code on rm of retained subvolume with snapshots") + else: + self.fail("expected rm of subvolume with retained snapshots to fail") + + # remove with snapshot retention + self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + + # fetch info + subvol_info = json.loads(self._fs_cmd("subvolume", "info", self.volname, subvolume)) + self.assertEqual(subvol_info["state"], "snapshot-retained", + msg="expected state to be 'snapshot-retained', found '{0}".format(subvol_info["state"])) + + ## test allowed ops in retained state + # ls + subvolumes = json.loads(self._fs_cmd('subvolume', 'ls', self.volname)) + self.assertEqual(len(subvolumes), 1, "subvolume ls count mismatch, expected '1', found {0}".format(len(subvolumes))) + self.assertEqual(subvolumes[0]['name'], subvolume, + "subvolume name mismatch in ls output, expected '{0}', found '{1}'".format(subvolume, subvolumes[0]['name'])) + + # snapshot info + snap_info = json.loads(self._get_subvolume_snapshot_info(self.volname, subvolume, snapshot)) + for md in snap_md: + self.assertIn(md, snap_info, "'{0}' key not present in metadata of snapshot".format(md)) + self.assertEqual(snap_info["has_pending_clones"], "no") + + # rm --force (allowed but should fail) + try: + self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--force") + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.ENOTEMPTY, "invalid error code on rm of subvolume with retained snapshots") + else: + self.fail("expected rm of subvolume with retained snapshots to fail") + + # rm (allowed but should fail) + try: + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.ENOTEMPTY, "invalid error code on rm of subvolume with retained snapshots") + else: + self.fail("expected rm of subvolume with retained snapshots to fail") + + ## test disallowed ops + # getpath + try: + self._fs_cmd("subvolume", "getpath", self.volname, subvolume) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on getpath of subvolume with retained snapshots") + else: + self.fail("expected getpath of subvolume with retained snapshots to fail") + + # resize + nsize = self.DEFAULT_FILE_SIZE*1024*1024 + try: + self._fs_cmd("subvolume", "resize", self.volname, subvolume, str(nsize)) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on resize of subvolume with retained snapshots") + else: + self.fail("expected resize of subvolume with retained snapshots to fail") + + # snap-create + try: + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, "fail") + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.ENOENT, "invalid error code on snapshot create of subvolume with retained snapshots") + else: + self.fail("expected snapshot create of subvolume with retained snapshots to fail") + + # remove snapshot (should remove volume) + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # verify list subvolumes returns an empty list + subvolumels = json.loads(self._fs_cmd('subvolume', 'ls', self.volname)) + self.assertEqual(len(subvolumels), 0) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_subvolume_retain_snapshot_without_snapshots(self): + """ + ensure retain snapshots based delete of a subvolume with no snapshots, deletes the subbvolume + """ + subvolume = self._generate_random_subvolume_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # remove with snapshot retention (should remove volume, no snapshots to retain) + self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + + # verify list subvolumes returns an empty list + subvolumels = json.loads(self._fs_cmd('subvolume', 'ls', self.volname)) + self.assertEqual(len(subvolumels), 0) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_subvolume_retain_snapshot_trash_busy_recreate(self): + """ + ensure retained subvolume recreate fails if its trash is not yet purged + """ + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # remove with snapshot retention + self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + + # fake a trash entry + self._update_fake_trash(subvolume) + + # recreate subvolume + try: + self._fs_cmd("subvolume", "create", self.volname, subvolume) + except CommandFailedError as ce: + self.assertEqual(ce.exitstatus, errno.EAGAIN, "invalid error code on recreate of subvolume with purge pending") + else: + self.fail("expected recreate of subvolume with purge pending to fail") + + # clear fake trash entry + self._update_fake_trash(subvolume, create=False) + + # recreate subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_subvolume_rm_with_snapshots(self): + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # remove subvolume -- should fail with ENOTEMPTY since it has snapshots + try: + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + except CommandFailedError as ce: + if ce.exitstatus != errno.ENOTEMPTY: + raise RuntimeError("invalid error code returned when deleting subvolume with snapshots") + else: + raise RuntimeError("expected subvolume deletion to fail") + + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # remove subvolume + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_subvolume_snapshot_protect_unprotect_sanity(self): + """ + Snapshot protect/unprotect commands are deprecated. This test exists to ensure that + invoking the command does not cause errors, till they are removed from a subsequent release. + """ + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + clone = self._generate_random_clone_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode=777") + + # do some IO + self._do_subvolume_io(subvolume, number_of_files=64) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # now, protect snapshot + self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot) + + # schedule a clone + self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone) + + # check clone status + self._wait_for_clone_to_complete(clone) + + # now, unprotect snapshot + self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot) + + # verify clone + self._verify_clone(subvolume, snapshot, clone) + + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # remove subvolumes + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + self._fs_cmd("subvolume", "rm", self.volname, clone) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_subvolume_snapshot_rm_force(self): + # test removing non existing subvolume snapshot with --force + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + + # remove snapshot + try: + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, "--force") + except CommandFailedError: + raise RuntimeError("expected the 'fs subvolume snapshot rm --force' command to succeed") + + def test_subvolume_snapshot_metadata_set(self): + """ + Set custom metadata for subvolume snapshot. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # set metadata for snapshot. + key = "key" + value = "value" + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata set' command to succeed") + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_set_idempotence(self): + """ + Set custom metadata for subvolume snapshot (Idempotency). + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # set metadata for snapshot. + key = "key" + value = "value" + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata set' command to succeed") + + # set same metadata again for subvolume. + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata set' command to succeed because it is idempotent operation") + + # get value for specified key. + try: + ret = self._fs_cmd("subvolume", "snapshot", "metadata", "get", self.volname, subvolname, snapshot, key, group) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata get' command to succeed") + + # remove '\n' from returned value. + ret = ret.strip('\n') + + # match received value with expected value. + self.assertEqual(value, ret) + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_get(self): + """ + Get custom metadata for a specified key in subvolume snapshot metadata. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # set metadata for snapshot. + key = "key" + value = "value" + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) + + # get value for specified key. + try: + ret = self._fs_cmd("subvolume", "snapshot", "metadata", "get", self.volname, subvolname, snapshot, key, group) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata get' command to succeed") + + # remove '\n' from returned value. + ret = ret.strip('\n') + + # match received value with expected value. + self.assertEqual(value, ret) + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_get_for_nonexisting_key(self): + """ + Get custom metadata for subvolume snapshot if specified key not exist in metadata. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # set metadata for snapshot. + key = "key" + value = "value" + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) + + # try to get value for nonexisting key + # Expecting ENOENT exit status because key does not exist + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "get", self.volname, subvolname, snapshot, "key_nonexist", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because 'key_nonexist' does not exist") + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_get_for_nonexisting_section(self): + """ + Get custom metadata for subvolume snapshot if metadata is not added for subvolume snapshot. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # try to get value for nonexisting key (as section does not exist) + # Expecting ENOENT exit status because key does not exist + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "get", self.volname, subvolname, snapshot, "key", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because section does not exist") + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_update(self): + """ + Update custom metadata for a specified key in subvolume snapshot metadata. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # set metadata for snapshot. + key = "key" + value = "value" + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) + + # update metadata against key. + new_value = "new_value" + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, new_value, group) + + # get metadata for specified key of snapshot. + try: + ret = self._fs_cmd("subvolume", "snapshot", "metadata", "get", self.volname, subvolname, snapshot, key, group) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata get' command to succeed") + + # remove '\n' from returned value. + ret = ret.strip('\n') + + # match received value with expected value. + self.assertEqual(new_value, ret) + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_list(self): + """ + List custom metadata for subvolume snapshot. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # set metadata for subvolume. + input_metadata_dict = {f'key_{i}' : f'value_{i}' for i in range(3)} + + for k, v in input_metadata_dict.items(): + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, k, v, group) + + # list metadata + try: + ret_dict = json.loads(self._fs_cmd("subvolume", "snapshot", "metadata", "ls", self.volname, subvolname, snapshot, group)) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata ls' command to succeed") + + # compare output with expected output + self.assertDictEqual(input_metadata_dict, ret_dict) + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_list_if_no_metadata_set(self): + """ + List custom metadata for subvolume snapshot if metadata is not added for subvolume snapshot. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # list metadata + try: + ret_dict = json.loads(self._fs_cmd("subvolume", "snapshot", "metadata", "ls", self.volname, subvolname, snapshot, group)) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata ls' command to succeed") + + # compare output with expected output + empty_dict = {} + self.assertDictEqual(ret_dict, empty_dict) + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_remove(self): + """ + Remove custom metadata for a specified key in subvolume snapshot metadata. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # set metadata for snapshot. + key = "key" + value = "value" + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) + + # remove metadata against specified key. + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "rm", self.volname, subvolname, snapshot, key, group) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata rm' command to succeed") + + # confirm key is removed by again fetching metadata + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "get", self.volname, subvolname, key, snapshot, group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because key does not exist") + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_remove_for_nonexisting_key(self): + """ + Remove custom metadata for subvolume snapshot if specified key not exist in metadata. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() + + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) + + # set metadata for snapshot. + key = "key" + value = "value" + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) + + # try to remove value for nonexisting key + # Expecting ENOENT exit status because key does not exist + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "rm", self.volname, subvolname, snapshot, "key_nonexist", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because 'key_nonexist' does not exist") + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() + + def test_subvolume_snapshot_metadata_remove_for_nonexisting_section(self): """ - ensure retain snapshots based delete of a subvolume with no snapshots, deletes the subbvolume + Remove custom metadata for subvolume snapshot if metadata is not added for subvolume snapshot. """ - subvolume = self._generate_random_subvolume_name() + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() + snapshot = self._generate_random_snapshot_name() - # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) - # remove with snapshot retention (should remove volume, no snapshots to retain) - self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) - # verify list subvolumes returns an empty list - subvolumels = json.loads(self._fs_cmd('subvolume', 'ls', self.volname)) - self.assertEqual(len(subvolumels), 0) + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) - # verify trash dir is clean + # try to remove value for nonexisting key (as section does not exist) + # Expecting ENOENT exit status because key does not exist + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "rm", self.volname, subvolname, snapshot, "key", group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) + else: + self.fail("Expected ENOENT because section does not exist") + + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. self._wait_for_trash_empty() - def test_subvolume_retain_snapshot_trash_busy_recreate(self): + def test_subvolume_snapshot_metadata_remove_force(self): """ - ensure retained subvolume recreate fails if its trash is not yet purged + Forcefully remove custom metadata for a specified key in subvolume snapshot metadata. """ - subvolume = self._generate_random_subvolume_name() + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() snapshot = self._generate_random_snapshot_name() - # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) # snapshot subvolume - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) - # remove with snapshot retention - self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots") + # set metadata for snapshot. + key = "key" + value = "value" + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) - # fake a trash entry - self._update_fake_trash(subvolume) + # remove metadata against specified key with --force option. + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "rm", self.volname, subvolname, snapshot, key, group, "--force") + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata rm' command to succeed") - # recreate subvolume + # confirm key is removed by again fetching metadata try: - self._fs_cmd("subvolume", "create", self.volname, subvolume) - except CommandFailedError as ce: - self.assertEqual(ce.exitstatus, errno.EAGAIN, "invalid error code on recreate of subvolume with purge pending") + self._fs_cmd("subvolume", "snapshot", "metadata", "get", self.volname, subvolname, snapshot, key, group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) else: - self.fail("expected recreate of subvolume with purge pending to fail") - - # clear fake trash entry - self._update_fake_trash(subvolume, create=False) - - # recreate subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) - - # remove snapshot - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + self.fail("Expected ENOENT because key does not exist") - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume) + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - # verify trash dir is clean + # verify trash dir is clean. self._wait_for_trash_empty() - def test_subvolume_rm_with_snapshots(self): - subvolume = self._generate_random_subvolume_name() + def test_subvolume_snapshot_metadata_remove_force_for_nonexisting_key(self): + """ + Forcefully remove custom metadata for subvolume snapshot if specified key not exist in metadata. + """ + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() snapshot = self._generate_random_snapshot_name() - # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume) + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) + + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) # snapshot subvolume - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) - # remove subvolume -- should fail with ENOTEMPTY since it has snapshots + # set metadata for snapshot. + key = "key" + value = "value" + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) + + # remove metadata against specified key. try: - self._fs_cmd("subvolume", "rm", self.volname, subvolume) - except CommandFailedError as ce: - if ce.exitstatus != errno.ENOTEMPTY: - raise RuntimeError("invalid error code returned when deleting subvolume with snapshots") + self._fs_cmd("subvolume", "snapshot", "metadata", "rm", self.volname, subvolname, snapshot, key, group) + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata rm' command to succeed") + + # confirm key is removed by again fetching metadata + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "get", self.volname, subvolname, snapshot, key, group) + except CommandFailedError as e: + self.assertEqual(e.exitstatus, errno.ENOENT) else: - raise RuntimeError("expected subvolume deletion to fail") + self.fail("Expected ENOENT because key does not exist") - # remove snapshot - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + # again remove metadata against already removed key with --force option. + try: + self._fs_cmd("subvolume", "snapshot", "metadata", "rm", self.volname, subvolname, snapshot, key, group, "--force") + except CommandFailedError: + self.fail("expected the 'fs subvolume snapshot metadata rm' (with --force) command to succeed") - # remove subvolume - self._fs_cmd("subvolume", "rm", self.volname, subvolume) + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) - # verify trash dir is clean + # verify trash dir is clean. self._wait_for_trash_empty() - def test_subvolume_snapshot_protect_unprotect_sanity(self): + def test_subvolume_snapshot_metadata_after_snapshot_remove(self): """ - Snapshot protect/unprotect commands are deprecated. This test exists to ensure that - invoking the command does not cause errors, till they are removed from a subsequent release. + Verify metadata removal of subvolume snapshot after snapshot removal. """ - subvolume = self._generate_random_subvolume_name() + subvolname = self._generate_random_subvolume_name() + group = self._generate_random_group_name() snapshot = self._generate_random_snapshot_name() - clone = self._generate_random_clone_name() - # create subvolume - self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode=777") + # create group. + self._fs_cmd("subvolumegroup", "create", self.volname, group) - # do some IO - self._do_subvolume_io(subvolume, number_of_files=64) + # create subvolume in group. + self._fs_cmd("subvolume", "create", self.volname, subvolname, group) # snapshot subvolume - self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) - - # now, protect snapshot - self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot) - - # schedule a clone - self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone) + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolname, snapshot, group) - # check clone status - self._wait_for_clone_to_complete(clone) + # set metadata for snapshot. + key = "key" + value = "value" + self._fs_cmd("subvolume", "snapshot", "metadata", "set", self.volname, subvolname, snapshot, key, value, group) - # now, unprotect snapshot - self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot) + # get value for specified key. + ret = self._fs_cmd("subvolume", "snapshot", "metadata", "get", self.volname, subvolname, snapshot, key, group) - # verify clone - self._verify_clone(subvolume, snapshot, clone) + # remove '\n' from returned value. + ret = ret.strip('\n') - # remove snapshot - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + # match received value with expected value. + self.assertEqual(value, ret) - # remove subvolumes - self._fs_cmd("subvolume", "rm", self.volname, subvolume) - self._fs_cmd("subvolume", "rm", self.volname, clone) + # remove subvolume snapshot. + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolname, snapshot, group) - # verify trash dir is clean - self._wait_for_trash_empty() + # try to get metadata after removing snapshot. + # Expecting error ENOENT with error message of snapshot does not exist + cmd_ret = self.mgr_cluster.mon_manager.run_cluster_cmd( + args=["fs", "subvolume", "snapshot", "metadata", "get", self.volname, subvolname, snapshot, key, group], + check_status=False, stdout=StringIO(), stderr=StringIO()) + self.assertEqual(cmd_ret.returncode, errno.ENOENT, "Expecting ENOENT error") + self.assertIn(f"snapshot '{snapshot}' does not exist", cmd_ret.stderr.getvalue(), + f"Expecting message: snapshot '{snapshot}' does not exist ") - def test_subvolume_snapshot_rm_force(self): - # test removing non existing subvolume snapshot with --force - subvolume = self._generate_random_subvolume_name() - snapshot = self._generate_random_snapshot_name() + # confirm metadata is removed by searching section name in .meta file + meta_path = os.path.join(".", "volumes", group, subvolname, ".meta") + section_name = "SNAP_METADATA_" + snapshot - # remove snapshot try: - self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, "--force") - except CommandFailedError: - raise RuntimeError("expected the 'fs subvolume snapshot rm --force' command to succeed") + self.mount_a.run_shell(f"sudo grep {section_name} {meta_path}", omit_sudo=False) + except CommandFailedError as e: + self.assertNotEqual(e.exitstatus, 0) + else: + self.fail("Expected non-zero exist status because section should not exist") + self._fs_cmd("subvolume", "rm", self.volname, subvolname, group) + self._fs_cmd("subvolumegroup", "rm", self.volname, group) + + # verify trash dir is clean. + self._wait_for_trash_empty() class TestSubvolumeSnapshotClones(TestVolumesHelper): """ Tests for FS subvolume snapshot clone operations.""" @@ -3903,6 +5013,208 @@ class TestSubvolumeSnapshotClones(TestVolumesHelper): # verify trash dir is clean self._wait_for_trash_empty() + def test_clone_failure_status_pending_in_progress_complete(self): + """ + ensure failure status is not shown when clone is not in failed/cancelled state + """ + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + clone1 = self._generate_random_clone_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode=777") + + # do some IO + self._do_subvolume_io(subvolume, number_of_files=200) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # Insert delay at the beginning of snapshot clone + self.config_set('mgr', 'mgr/volumes/snapshot_clone_delay', 5) + + # schedule a clone1 + self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone1) + + # pending clone shouldn't show failure status + clone1_result = self._get_clone_status(clone1) + try: + clone1_result["status"]["failure"]["errno"] + except KeyError as e: + self.assertEqual(str(e), "'failure'") + else: + self.fail("clone status shouldn't show failure for pending clone") + + # check clone1 to be in-progress + self._wait_for_clone_to_be_in_progress(clone1) + + # in-progress clone1 shouldn't show failure status + clone1_result = self._get_clone_status(clone1) + try: + clone1_result["status"]["failure"]["errno"] + except KeyError as e: + self.assertEqual(str(e), "'failure'") + else: + self.fail("clone status shouldn't show failure for in-progress clone") + + # wait for clone1 to complete + self._wait_for_clone_to_complete(clone1) + + # complete clone1 shouldn't show failure status + clone1_result = self._get_clone_status(clone1) + try: + clone1_result["status"]["failure"]["errno"] + except KeyError as e: + self.assertEqual(str(e), "'failure'") + else: + self.fail("clone status shouldn't show failure for complete clone") + + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # remove subvolumes + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + self._fs_cmd("subvolume", "rm", self.volname, clone1) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_clone_failure_status_failed(self): + """ + ensure failure status is shown when clone is in failed state and validate the reason + """ + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + clone1 = self._generate_random_clone_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode=777") + + # do some IO + self._do_subvolume_io(subvolume, number_of_files=200) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # Insert delay at the beginning of snapshot clone + self.config_set('mgr', 'mgr/volumes/snapshot_clone_delay', 5) + + # schedule a clone1 + self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone1) + + # remove snapshot from backend to force the clone failure. + snappath = os.path.join(".", "volumes", "_nogroup", subvolume, ".snap", snapshot) + self.mount_a.run_shell(['rmdir', snappath], sudo=True) + + # wait for clone1 to fail. + self._wait_for_clone_to_fail(clone1) + + # check clone1 status + clone1_result = self._get_clone_status(clone1) + self.assertEqual(clone1_result["status"]["state"], "failed") + self.assertEqual(clone1_result["status"]["failure"]["errno"], "2") + self.assertEqual(clone1_result["status"]["failure"]["error_msg"], "snapshot '{0}' does not exist".format(snapshot)) + + # clone removal should succeed after failure, remove clone1 + self._fs_cmd("subvolume", "rm", self.volname, clone1, "--force") + + # remove subvolumes + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_clone_failure_status_pending_cancelled(self): + """ + ensure failure status is shown when clone is cancelled during pending state and validate the reason + """ + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + clone1 = self._generate_random_clone_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode=777") + + # do some IO + self._do_subvolume_io(subvolume, number_of_files=200) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # Insert delay at the beginning of snapshot clone + self.config_set('mgr', 'mgr/volumes/snapshot_clone_delay', 5) + + # schedule a clone1 + self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone1) + + # cancel pending clone1 + self._fs_cmd("clone", "cancel", self.volname, clone1) + + # check clone1 status + clone1_result = self._get_clone_status(clone1) + self.assertEqual(clone1_result["status"]["state"], "canceled") + self.assertEqual(clone1_result["status"]["failure"]["errno"], "4") + self.assertEqual(clone1_result["status"]["failure"]["error_msg"], "user interrupted clone operation") + + # clone removal should succeed with force after cancelled, remove clone1 + self._fs_cmd("subvolume", "rm", self.volname, clone1, "--force") + + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # remove subvolumes + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + + # verify trash dir is clean + self._wait_for_trash_empty() + + def test_clone_failure_status_in_progress_cancelled(self): + """ + ensure failure status is shown when clone is cancelled during in-progress state and validate the reason + """ + subvolume = self._generate_random_subvolume_name() + snapshot = self._generate_random_snapshot_name() + clone1 = self._generate_random_clone_name() + + # create subvolume + self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode=777") + + # do some IO + self._do_subvolume_io(subvolume, number_of_files=200) + + # snapshot subvolume + self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot) + + # Insert delay at the beginning of snapshot clone + self.config_set('mgr', 'mgr/volumes/snapshot_clone_delay', 5) + + # schedule a clone1 + self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone1) + + # wait for clone1 to be in-progress + self._wait_for_clone_to_be_in_progress(clone1) + + # cancel in-progess clone1 + self._fs_cmd("clone", "cancel", self.volname, clone1) + + # check clone1 status + clone1_result = self._get_clone_status(clone1) + self.assertEqual(clone1_result["status"]["state"], "canceled") + self.assertEqual(clone1_result["status"]["failure"]["errno"], "4") + self.assertEqual(clone1_result["status"]["failure"]["error_msg"], "user interrupted clone operation") + + # clone removal should succeed with force after cancelled, remove clone1 + self._fs_cmd("subvolume", "rm", self.volname, clone1, "--force") + + # remove snapshot + self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot) + + # remove subvolumes + self._fs_cmd("subvolume", "rm", self.volname, subvolume) + + # verify trash dir is clean + self._wait_for_trash_empty() + def test_subvolume_snapshot_clone(self): subvolume = self._generate_random_subvolume_name() snapshot = self._generate_random_snapshot_name() diff --git a/ceph/qa/tox.ini b/ceph/qa/tox.ini index 7fc94eb5a..82c1d704c 100644 --- a/ceph/qa/tox.ini +++ b/ceph/qa/tox.ini @@ -25,7 +25,7 @@ commands = mypy {posargs:.} [testenv:py3] basepython = python3 deps = - {env:TEUTHOLOGY_GIT:git+https://github.com/ceph/teuthology.git@master}#egg=teuthology[coverage,orchestra,test] + {env:TEUTHOLOGY_GIT:git+https://github.com/ceph/teuthology.git@main}#egg=teuthology[coverage,orchestra,test] httplib2 commands = pytest --assert=plain test_import.py diff --git a/ceph/qa/workunits/cephadm/test_cephadm.sh b/ceph/qa/workunits/cephadm/test_cephadm.sh index 643065b73..31ebf0a5d 100755 --- a/ceph/qa/workunits/cephadm/test_cephadm.sh +++ b/ceph/qa/workunits/cephadm/test_cephadm.sh @@ -77,6 +77,22 @@ function expect_false() if eval "$@"; then return 1; else return 0; fi } +# expect_return_code $expected_code $command ... +function expect_return_code() +{ + set -x + local expected_code="$1" + shift + local command="$@" + + set +e + eval "$command" + local return_code="$?" + set -e + + if [ ! "$return_code" -eq "$expected_code" ]; then return 1; else return 0; fi +} + function is_available() { local name="$1" @@ -370,6 +386,10 @@ $CEPHADM unit --fsid $FSID --name mon.a -- disable expect_false $CEPHADM unit --fsid $FSID --name mon.a -- is-enabled $CEPHADM unit --fsid $FSID --name mon.a -- enable $CEPHADM unit --fsid $FSID --name mon.a -- is-enabled +$CEPHADM unit --fsid $FSID --name mon.a -- status +$CEPHADM unit --fsid $FSID --name mon.a -- stop +expect_return_code 3 $CEPHADM unit --fsid $FSID --name mon.a -- status +$CEPHADM unit --fsid $FSID --name mon.a -- start ## shell $CEPHADM shell --fsid $FSID -- true diff --git a/ceph/qa/workunits/cephtool/test.sh b/ceph/qa/workunits/cephtool/test.sh index 1b4af1766..bac102c08 100755 --- a/ceph/qa/workunits/cephtool/test.sh +++ b/ceph/qa/workunits/cephtool/test.sh @@ -1426,11 +1426,20 @@ function test_mon_osd() ceph osd blocklist ls | grep $bl ceph osd blocklist rm $bl ceph osd blocklist ls | expect_false grep $bl - expect_false "ceph osd blocklist $bl/-1" - expect_false "ceph osd blocklist $bl/foo" + expect_false "ceph osd blocklist add $bl/-1" + expect_false "ceph osd blocklist add $bl/foo" - # test with wrong address - expect_false "ceph osd blocklist 1234.56.78.90/100" + # test with invalid address + expect_false "ceph osd blocklist add 1234.56.78.90/100" + + # test range blocklisting + bl=192.168.0.1:0/24 + ceph osd blocklist range add $bl + ceph osd blocklist ls | grep $bl + ceph osd blocklist range rm $bl + ceph osd blocklist ls | expect_false grep $bl + bad_bl=192.168.0.1/33 + expect_false ceph osd blocklist range add $bad_bl # Test `clear` ceph osd blocklist add $bl diff --git a/ceph/qa/workunits/rgw/olh_noname_key b/ceph/qa/workunits/rgw/olh_noname_key new file mode 100644 index 000000000..6138c57cd --- /dev/null +++ b/ceph/qa/workunits/rgw/olh_noname_key @@ -0,0 +1 @@ +€1001_04/57/0457f727ec113e418d5b16d206b200ed068c0533554883ce811df7c932a3df68/2018_12_11/2889999/3386469/metadata.gz \ No newline at end of file diff --git a/ceph/qa/workunits/rgw/olh_noname_val b/ceph/qa/workunits/rgw/olh_noname_val new file mode 100644 index 0000000000000000000000000000000000000000..ff442e1374c6e24735d85a9e6c9e4020c8fad591 GIT binary patch literal 71 zcmZQ%bYx&)U}WR~l5oHdXE87+0NEvJ6_o}BRRzUmnc0bXsi}D;mZqjvl}Q$fN%_S& I*_8~800e;y=l}o! literal 0 HcmV?d00001 diff --git a/ceph/qa/workunits/rgw/test_rgw_reshard.py b/ceph/qa/workunits/rgw/test_rgw_reshard.py index 61e92b990..400994e47 100755 --- a/ceph/qa/workunits/rgw/test_rgw_reshard.py +++ b/ceph/qa/workunits/rgw/test_rgw_reshard.py @@ -6,6 +6,7 @@ import subprocess import json import boto3 import botocore.exceptions +import os """ Rgw manual and dynamic resharding testing against a running instance @@ -30,6 +31,7 @@ SECRET_KEY = 'LnEsqNNqZIpkzauboDcLXLcYaWwLQ3Kop0zAnKIn' BUCKET_NAME1 = 'myfoo' BUCKET_NAME2 = 'mybar' VER_BUCKET_NAME = 'myver' +INDEX_POOL = 'default.rgw.buckets.index' def exec_cmd(cmd): @@ -219,6 +221,35 @@ def main(): new_ver_bucket_acl = connection.BucketAcl(VER_BUCKET_NAME).load() assert new_ver_bucket_acl == ver_bucket_acl + # TESTCASE 'check reshard removes olh entries with empty name' + log.debug(' test: reshard removes olh entries with empty name') + bucket1.objects.all().delete() + + # get name of shard 0 object, add a bogus olh entry with empty name + bucket_shard0 = '.dir.%s.0' % get_bucket_stats(BUCKET_NAME1).bucket_id + if 'CEPH_ROOT' in os.environ: + k = '%s/qa/workunits/rgw/olh_noname_key' % os.environ['CEPH_ROOT'] + v = '%s/qa/workunits/rgw/olh_noname_val' % os.environ['CEPH_ROOT'] + else: + k = 'olh_noname_key' + v = 'olh_noname_val' + exec_cmd('rados -p %s setomapval %s --omap-key-file %s < %s' % (INDEX_POOL, bucket_shard0, k, v)) + + # check that bi list has one entry with empty name + cmd = exec_cmd('radosgw-admin bi list --bucket %s' % BUCKET_NAME1) + json_op = json.loads(cmd.decode('utf-8', 'ignore')) # ignore utf-8 can't decode 0x80 + assert len(json_op) == 1 + assert json_op[0]['entry']['key']['name'] == '' + + # reshard to prune the bogus olh + cmd = exec_cmd('radosgw-admin bucket reshard --bucket %s --num-shards %s --yes-i-really-mean-it' % (BUCKET_NAME1, 1)) + + # get new name of shard 0 object, check that bi list has zero entries + bucket_shard0 = '.dir.%s.0' % get_bucket_stats(BUCKET_NAME1).bucket_id + cmd = exec_cmd('radosgw-admin bi list --bucket %s' % BUCKET_NAME1) + json_op = json.loads(cmd) + assert len(json_op) == 0 + # Clean up log.debug("Deleting bucket %s", BUCKET_NAME1) bucket1.objects.all().delete() diff --git a/ceph/run-make-check.sh b/ceph/run-make-check.sh index 85d58a3d3..abe1670f5 100755 --- a/ceph/run-make-check.sh +++ b/ceph/run-make-check.sh @@ -96,9 +96,10 @@ function main() { if [ $WITH_ZBD ]; then cmake_opts+=" -DWITH_ZBD=ON" fi - if [ $WITH_PMEM ]; then - cmake_opts+=" -DWITH_RBD_RWL=ON -DWITH_SYSTEM_PMDK=ON" + if [ $WITH_RBD_RWL ]; then + cmake_opts+=" -DWITH_RBD_RWL=ON" fi + cmake_opts+=" -DWITH_RBD_SSD_CACHE=ON" configure "$cmake_opts" "$@" build tests echo "make check: successful build on $(git rev-parse HEAD)" diff --git a/ceph/src/.git_version b/ceph/src/.git_version index 379440a3d..b7c2846fb 100644 --- a/ceph/src/.git_version +++ b/ceph/src/.git_version @@ -1,2 +1,2 @@ -43e2e60a7559d3f46c9d53f1ca875fd499a1e35e -17.2.0 +ec95624474b1871a821a912b8c3af68f8f8e7aa1 +17.2.1 diff --git a/ceph/src/CMakeLists.txt b/ceph/src/CMakeLists.txt index 30cf36054..83a90e4ec 100644 --- a/ceph/src/CMakeLists.txt +++ b/ceph/src/CMakeLists.txt @@ -158,12 +158,6 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang) endif() endif(CMAKE_CXX_COMPILER_ID STREQUAL GNU) -if(NOT CMAKE_BUILD_TYPE AND EXISTS "${CMAKE_SOURCE_DIR}/.git") - set(default_build_type "Debug") - set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE - STRING "Default BUILD_TYPE is Debug, other options are: RelWithDebInfo, Release, and MinSizeRel." FORCE) -endif() - if(WITH_CEPH_DEBUG_MUTEX) add_compile_options($<$:-DCEPH_DEBUG_MUTEX>) endif() diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/batch.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/batch.py index dace25530..6fa619d50 100644 --- a/ceph/src/ceph-volume/ceph_volume/devices/lvm/batch.py +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/batch.py @@ -196,7 +196,7 @@ class Batch(object): 'devices', metavar='DEVICES', nargs='*', - type=arg_validators.ValidBatchDevice(), + type=arg_validators.ValidBatchDataDevice(), default=[], help='Devices to provision OSDs', ) diff --git a/ceph/src/ceph-volume/ceph_volume/devices/lvm/common.py b/ceph/src/ceph-volume/ceph_volume/devices/lvm/common.py index 614be0af6..edc8e1cbc 100644 --- a/ceph/src/ceph-volume/ceph_volume/devices/lvm/common.py +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/common.py @@ -4,7 +4,6 @@ from ceph_volume import terminal from ceph_volume.devices.lvm.zap import Zap import argparse - def rollback_osd(args, osd_id=None): """ When the process of creating or preparing fails, the OSD needs to be @@ -40,7 +39,7 @@ common_args = { '--data': { 'help': 'OSD data path. A physical device or logical volume', 'required': True, - 'type': arg_validators.ValidDevice(as_string=True), + 'type': arg_validators.ValidDataDevice(as_string=True), #'default':, #'type':, }, 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 e0cbfb172..9f8141d54 100644 --- a/ceph/src/ceph-volume/ceph_volume/devices/lvm/zap.py +++ b/ceph/src/ceph-volume/ceph_volume/devices/lvm/zap.py @@ -168,6 +168,7 @@ class Zap(object): """ lv = api.get_single_lv(filters={'lv_name': device.lv_name, 'vg_name': device.vg_name}) + pv = api.get_single_pv(filters={'lv_uuid': lv.lv_uuid}) self.unmount_lv(lv) wipefs(device.abspath) @@ -182,6 +183,7 @@ class Zap(object): mlogger.info('Only 1 LV left in VG, will proceed to destroy ' 'volume group %s', device.vg_name) api.remove_vg(device.vg_name) + api.remove_pv(pv.pv_name) else: mlogger.info('More than 1 LV left in VG, will proceed to ' 'destroy LV only') @@ -362,7 +364,7 @@ class Zap(object): 'devices', metavar='DEVICES', nargs='*', - type=arg_validators.ValidDevice(gpt_ok=True), + type=arg_validators.ValidZapDevice(gpt_ok=True), default=[], help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)' ) diff --git a/ceph/src/ceph-volume/ceph_volume/devices/raw/common.py b/ceph/src/ceph-volume/ceph_volume/devices/raw/common.py index 54e77aca6..19de81fe5 100644 --- a/ceph/src/ceph-volume/ceph_volume/devices/raw/common.py +++ b/ceph/src/ceph-volume/ceph_volume/devices/raw/common.py @@ -14,7 +14,7 @@ def create_parser(prog, description): parser.add_argument( '--data', required=True, - type=arg_validators.ValidDevice(as_string=True), + type=arg_validators.ValidRawDevice(as_string=True), help='a raw device to use for the OSD', ) parser.add_argument( @@ -35,12 +35,14 @@ def create_parser(prog, description): parser.add_argument( '--block.db', dest='block_db', - help='Path to bluestore block.db block device' + help='Path to bluestore block.db block device', + type=arg_validators.ValidRawDevice(as_string=True) ) parser.add_argument( '--block.wal', dest='block_wal', - help='Path to bluestore block.wal block device' + help='Path to bluestore block.wal block device', + type=arg_validators.ValidRawDevice(as_string=True) ) parser.add_argument( '--dmcrypt', diff --git a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_batch.py b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_batch.py index 265a9b84e..96a5b5d74 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_batch.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_batch.py @@ -32,6 +32,8 @@ class TestBatch(object): def test_reject_partition(self, mocked_device): mocked_device.return_value = MagicMock( is_partition=True, + has_fs=False, + is_lvm_member=False, has_gpt_headers=False, has_partitions=False, ) diff --git a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_create.py b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_create.py index 994038f3b..1665d76c3 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_create.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_create.py @@ -1,5 +1,6 @@ import pytest from ceph_volume.devices import lvm +from mock import patch class TestCreate(object): @@ -17,7 +18,8 @@ class TestCreate(object): assert 'Use the bluestore objectstore' in stdout assert 'A physical device or logical' in stdout - def test_excludes_filestore_bluestore_flags(self, capsys, device_info): + @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False) + def test_excludes_filestore_bluestore_flags(self, m_has_bs_label, fake_call, capsys, device_info): device_info() with pytest.raises(SystemExit): lvm.create.Create(argv=['--data', '/dev/sdfoo', '--filestore', '--bluestore']).main() @@ -25,7 +27,8 @@ class TestCreate(object): expected = 'Cannot use --filestore (filestore) with --bluestore (bluestore)' assert expected in stderr - def test_excludes_other_filestore_bluestore_flags(self, capsys, device_info): + @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False) + def test_excludes_other_filestore_bluestore_flags(self, m_has_bs_label, fake_call, capsys, device_info): device_info() with pytest.raises(SystemExit): lvm.create.Create(argv=[ @@ -36,7 +39,8 @@ class TestCreate(object): expected = 'Cannot use --bluestore (bluestore) with --journal (filestore)' assert expected in stderr - def test_excludes_block_and_journal_flags(self, capsys, device_info): + @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False) + def test_excludes_block_and_journal_flags(self, m_has_bs_label, fake_call, capsys, device_info): device_info() with pytest.raises(SystemExit): lvm.create.Create(argv=[ 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 c5afe674d..7e4d963c8 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 @@ -62,7 +62,7 @@ class TestPrettyReport(object): class TestList(object): - def test_empty_full_json_zero_exit_status(self, is_root,factory,capsys): + def test_empty_full_json_zero_exit_status(self, fake_call, is_root, factory, capsys): args = factory(format='json', device=None) lvm.listing.List([]).list(args) stdout, stderr = capsys.readouterr() @@ -74,7 +74,7 @@ class TestList(object): stdout, stderr = capsys.readouterr() assert stdout == '{}\n' - def test_empty_full_zero_exit_status(self, is_root, factory): + def test_empty_full_zero_exit_status(self, fake_call, is_root, factory): args = factory(format='pretty', device=None) with pytest.raises(SystemExit): lvm.listing.List([]).list(args) diff --git a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_prepare.py b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_prepare.py index fcbc276f0..9f0a5e0bb 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_prepare.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_prepare.py @@ -1,7 +1,7 @@ import pytest from ceph_volume.devices import lvm from ceph_volume.api import lvm as api -from mock.mock import patch, Mock +from mock.mock import patch, Mock, MagicMock class TestLVM(object): @@ -66,7 +66,9 @@ class TestPrepare(object): assert 'Use the bluestore objectstore' in stdout assert 'A physical device or logical' in stdout - def test_excludes_filestore_bluestore_flags(self, capsys, device_info): + + @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False) + def test_excludes_filestore_bluestore_flags(self, m_has_bs_label, fake_call, capsys, device_info): device_info() with pytest.raises(SystemExit): lvm.prepare.Prepare(argv=['--data', '/dev/sdfoo', '--filestore', '--bluestore']).main() @@ -74,7 +76,9 @@ class TestPrepare(object): expected = 'Cannot use --filestore (filestore) with --bluestore (bluestore)' assert expected in stderr - def test_excludes_other_filestore_bluestore_flags(self, capsys, device_info): + + @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False) + def test_excludes_other_filestore_bluestore_flags(self, m_has_bs_label, fake_call, capsys, device_info): device_info() with pytest.raises(SystemExit): lvm.prepare.Prepare(argv=[ @@ -85,7 +89,8 @@ class TestPrepare(object): expected = 'Cannot use --bluestore (bluestore) with --journal (filestore)' assert expected in stderr - def test_excludes_block_and_journal_flags(self, capsys, device_info): + @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False) + def test_excludes_block_and_journal_flags(self, m_has_bs_label, fake_call, capsys, device_info): device_info() with pytest.raises(SystemExit): lvm.prepare.Prepare(argv=[ @@ -96,9 +101,15 @@ class TestPrepare(object): expected = 'Cannot use --block.db (bluestore) with --journal (filestore)' assert expected in stderr - def test_journal_is_required_with_filestore(self, is_root, monkeypatch, device_info): + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False) + def test_journal_is_required_with_filestore(self, m_has_bs_label, m_device, is_root, monkeypatch, device_info): + m_device.return_value = MagicMock(exists=True, + has_fs=False, + used_by_ceph=False, + has_partitions=False, + has_gpt_headers=False) monkeypatch.setattr("os.path.exists", lambda path: True) - device_info() with pytest.raises(SystemExit) as error: lvm.prepare.Prepare(argv=['--filestore', '--data', '/dev/sdfoo']).main() expected = '--journal is required when using --filestore' diff --git a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_zap.py b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_zap.py index eff187228..53c694633 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_zap.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_zap.py @@ -105,7 +105,7 @@ class TestEnsureAssociatedLVs(object): result = zap.ensure_associated_lvs(volumes) assert result == [] - def test_data_is_found(self): + def test_data_is_found(self, fake_call): tags = 'ceph.osd_id=0,ceph.osd_fsid=asdf-lkjh,ceph.journal_uuid=x,ceph.type=data' osd = api.Volume( lv_name='volume1', lv_uuid='y', vg_name='', lv_path='/dev/VolGroup/data', lv_tags=tags) @@ -114,7 +114,7 @@ class TestEnsureAssociatedLVs(object): result = zap.ensure_associated_lvs(volumes) assert result == ['/dev/VolGroup/data'] - def test_block_is_found(self): + def test_block_is_found(self, fake_call): tags = 'ceph.osd_id=0,ceph.osd_fsid=asdf-lkjh,ceph.journal_uuid=x,ceph.type=block' osd = api.Volume( lv_name='volume1', lv_uuid='y', vg_name='', lv_path='/dev/VolGroup/block', lv_tags=tags) @@ -150,7 +150,7 @@ class TestEnsureAssociatedLVs(object): assert '/dev/sdb1' in result assert '/dev/VolGroup/block' in result - def test_journal_is_found(self): + def test_journal_is_found(self, fake_call): tags = 'ceph.osd_id=0,ceph.osd_fsid=asdf-lkjh,ceph.journal_uuid=x,ceph.type=journal' osd = api.Volume( lv_name='volume1', lv_uuid='y', vg_name='', lv_path='/dev/VolGroup/lv', lv_tags=tags) diff --git a/ceph/src/ceph-volume/ceph_volume/tests/devices/raw/test_prepare.py b/ceph/src/ceph-volume/ceph_volume/tests/devices/raw/test_prepare.py index e4cf8ce11..f814bbf13 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/devices/raw/test_prepare.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/devices/raw/test_prepare.py @@ -41,7 +41,7 @@ class TestPrepare(object): assert 'Path to bluestore block.wal block device' in stdout assert 'Enable device encryption via dm-crypt' in stdout - @patch('ceph_volume.util.arg_validators.ValidDevice.__call__') + @patch('ceph_volume.util.arg_validators.ValidRawDevice.__call__') def test_prepare_dmcrypt_no_secret_passed(self, m_valid_device, capsys): m_valid_device.return_value = '/dev/foo' with pytest.raises(SystemExit): @@ -87,7 +87,7 @@ class TestPrepare(object): @patch('ceph_volume.devices.raw.prepare.rollback_osd') @patch('ceph_volume.devices.raw.prepare.Prepare.prepare') - @patch('ceph_volume.util.arg_validators.ValidDevice.__call__') + @patch('ceph_volume.util.arg_validators.ValidRawDevice.__call__') def test_safe_prepare_exception_raised(self, m_valid_device, m_prepare, m_rollback_osd): m_valid_device.return_value = '/dev/foo' m_prepare.side_effect=Exception('foo') 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 42c4940f1..745b58ae5 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 @@ -1,5 +1,6 @@ import pytest from ceph_volume.devices import lvm +from mock.mock import patch, MagicMock class TestZap(object): @@ -19,9 +20,18 @@ class TestZap(object): '/dev/mapper/foo', '/dev/dm-0', ]) - def test_can_not_zap_mapper_device(self, monkeypatch, device_info, capsys, is_root, device_name): + @patch('ceph_volume.util.arg_validators.Device') + def test_can_not_zap_mapper_device(self, mocked_device, monkeypatch, device_info, capsys, is_root, device_name): monkeypatch.setattr('os.path.exists', lambda x: True) - device_info() + mocked_device.return_value = MagicMock( + is_mapper=True, + is_mpath=False, + used_by_ceph=True, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False + ) with pytest.raises(SystemExit): lvm.zap.Zap(argv=[device_name]).main() stdout, stderr = capsys.readouterr() diff --git a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/setup_mixed_type.yml b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/setup_mixed_type.yml index 5460fdd0d..1fa9f66fc 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/setup_mixed_type.yml +++ b/ceph/src/ceph-volume/ceph_volume/tests/functional/batch/playbooks/setup_mixed_type.yml @@ -10,8 +10,8 @@ - name: tell lvm to ignore loop devices lineinfile: path: /etc/lvm/lvm.conf - line: ' global_filter = [ "r|loop|", "a|.*|" ]' - insertafter: '^devices {' + line: "\tfilter = [ 'r|loop.*|' ]" + insertafter: 'devices {' - name: lvm allow changes depsite duplicate PVIDs lineinfile: path: /etc/lvm/lvm.conf diff --git a/ceph/src/ceph-volume/ceph_volume/tests/test_inventory.py b/ceph/src/ceph-volume/ceph_volume/tests/test_inventory.py index 28654cec3..b00bd668d 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/test_inventory.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/test_inventory.py @@ -111,6 +111,7 @@ def device_data(device_info): class TestInventory(object): expected_keys = [ + 'ceph_device', 'path', 'rejected_reasons', 'sys_api', @@ -152,30 +153,30 @@ class TestInventory(object): 'errors', ] - def test_json_inventory_keys_unexpected(self, device_report_keys): + def test_json_inventory_keys_unexpected(self, fake_call, device_report_keys): for k in device_report_keys: assert k in self.expected_keys, "unexpected key {} in report".format(k) - def test_json_inventory_keys_missing(self, device_report_keys): + def test_json_inventory_keys_missing(self, fake_call, device_report_keys): for k in self.expected_keys: assert k in device_report_keys, "expected key {} in report".format(k) - def test_sys_api_keys_unexpected(self, device_sys_api_keys): + def test_sys_api_keys_unexpected(self, fake_call, device_sys_api_keys): for k in device_sys_api_keys: assert k in self.expected_sys_api_keys, "unexpected key {} in sys_api field".format(k) - def test_sys_api_keys_missing(self, device_sys_api_keys): + def test_sys_api_keys_missing(self, fake_call, device_sys_api_keys): for k in self.expected_sys_api_keys: assert k in device_sys_api_keys, "expected key {} in sys_api field".format(k) - def test_lsm_data_type_unexpected(self, device_data): + def test_lsm_data_type_unexpected(self, fake_call, device_data): assert isinstance(device_data['lsm_data'], dict), "lsm_data field must be of type dict" - def test_lsm_data_keys_unexpected(self, device_data): + def test_lsm_data_keys_unexpected(self, fake_call, device_data): for k in device_data['lsm_data'].keys(): assert k in self.expected_lsm_keys, "unexpected key {} in lsm_data field".format(k) - def test_lsm_data_keys_missing(self, device_data): + def test_lsm_data_keys_missing(self, fake_call, device_data): lsm_keys = device_data['lsm_data'].keys() assert lsm_keys for k in self.expected_lsm_keys: @@ -251,4 +252,4 @@ class TestLSM(object): def test_lsmdisk_led_fault(self, lsm_info): assert lsm_info.led_fault_state == 'Off' def test_lsmdisk_report(self, lsm_info): - assert isinstance(lsm_info.json_report(), dict) \ No newline at end of file + assert isinstance(lsm_info.json_report(), dict) 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 13dff80bf..19aaaa3bd 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 @@ -1,9 +1,9 @@ import argparse import pytest import os -from ceph_volume import exceptions +from ceph_volume import exceptions, process from ceph_volume.util import arg_validators -from mock.mock import patch, PropertyMock +from mock.mock import patch, MagicMock class TestOSDPath(object): @@ -81,21 +81,252 @@ class TestValidDevice(object): def setup(self): self.validator = arg_validators.ValidDevice() - def test_path_is_valid(self, fake_call, patch_bluestore_label): + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + def test_path_is_valid(self, m_has_bs_label, fake_call, patch_bluestore_label): result = self.validator('/') assert result.abspath == '/' - def test_path_is_invalid(self, fake_call, patch_bluestore_label): + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + def test_path_is_invalid(self, m_has_bs_label, fake_call, patch_bluestore_label): with pytest.raises(argparse.ArgumentError): self.validator('/device/does/not/exist') - @patch('ceph_volume.util.arg_validators.Device.has_partitions', new_callable=PropertyMock, return_value=True) - @patch('ceph_volume.util.arg_validators.Device.exists', new_callable=PropertyMock, return_value=True) + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) - def test_dev_has_partitions(self, m_get_single_lv, m_exists, m_has_partitions, fake_call): + def test_dev_has_partitions(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call): + mocked_device.return_value = MagicMock( + exists=True, + has_partitions=True, + ) + with pytest.raises(RuntimeError): + self.validator('/dev/foo') + +class TestValidZapDevice(object): + def setup(self): + self.validator = arg_validators.ValidZapDevice() + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_has_partition(self, m_get_single_lv, m_has_bs_label, mocked_device): + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=True, + has_gpt_headers=False, + has_fs=False + ) + self.validator.zap = False + with pytest.raises(RuntimeError): + assert self.validator('/dev/foo') + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_has_no_partition(self, m_get_single_lv, m_has_bs_label, mocked_device): + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False + ) + self.validator.zap = False + assert self.validator('/dev/foo') + +class TestValidDataDevice(object): + def setup(self): + self.validator = arg_validators.ValidDataDevice() + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_used_by_ceph(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call): + mocked_device.return_value = MagicMock( + used_by_ceph=True, + exists=True, + has_partitions=False, + has_gpt_headers=False + ) + with pytest.raises(SystemExit): + self.validator.zap = False + self.validator('/dev/foo') + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_has_fs(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call): + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=True + ) + with pytest.raises(RuntimeError): + self.validator.zap = False + self.validator('/dev/foo') + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=True) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_has_bs_signature(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call): + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False + ) + with pytest.raises(RuntimeError): + self.validator.zap = False + self.validator('/dev/foo') + +class TestValidRawDevice(object): + def setup(self): + self.validator = arg_validators.ValidRawDevice() + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.util.arg_validators.disk.blkid') + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_dmcrypt_device_already_prepared(self, m_get_single_lv, m_blkid, m_has_bs_label, mocked_device, fake_call, monkeypatch): + def mock_call(cmd, **kw): + return ('', '', 1) + monkeypatch.setattr(process, 'call', mock_call) + m_blkid.return_value = {'UUID': '8fd92779-ad78-437c-a06f-275f7170fa74', 'TYPE': 'crypto_LUKS'} + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False + ) + with pytest.raises(SystemExit): + self.validator.zap = False + self.validator('/dev/foo') + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_already_prepared(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call): + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False + ) + with pytest.raises(SystemExit): + self.validator.zap = False + self.validator('/dev/foo') + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_not_prepared(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call, monkeypatch): + def mock_call(cmd, **kw): + return ('', '', 1) + monkeypatch.setattr(process, 'call', mock_call) + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False + ) + self.validator.zap = False + assert self.validator('/dev/foo') + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_has_partition(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call, monkeypatch): + def mock_call(cmd, **kw): + return ('', '', 1) + monkeypatch.setattr(process, 'call', mock_call) + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=True, + has_gpt_headers=False, + has_fs=False + ) + self.validator.zap = False with pytest.raises(RuntimeError): + assert self.validator('/dev/foo') + +class TestValidBatchDevice(object): + def setup(self): + self.validator = arg_validators.ValidBatchDevice() + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_is_partition(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call): + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False, + is_partition=True + ) + with pytest.raises(argparse.ArgumentError): + self.validator.zap = False self.validator('/dev/foo') + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_is_not_partition(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call): + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False, + is_partition=False + ) + self.validator.zap = False + assert self.validator('/dev/foo') + +class TestValidBatchDataDevice(object): + def setup(self): + self.validator = arg_validators.ValidBatchDataDevice() + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_is_partition(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call): + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False, + is_partition=True + ) + with pytest.raises(argparse.ArgumentError): + self.validator.zap = False + assert self.validator('/dev/foo') + + @patch('ceph_volume.util.arg_validators.Device') + @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False) + @patch('ceph_volume.api.lvm.get_single_lv', return_value=None) + def test_device_is_not_partition(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call): + mocked_device.return_value = MagicMock( + used_by_ceph=False, + exists=True, + has_partitions=False, + has_gpt_headers=False, + has_fs=False, + is_partition=False + ) + self.validator.zap = False + assert self.validator('/dev/foo') + class TestValidFraction(object): 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 index 578f2fa5e..f6e439279 100644 --- a/ceph/src/ceph-volume/ceph_volume/tests/util/test_device.py +++ b/ceph/src/ceph-volume/ceph_volume/tests/util/test_device.py @@ -37,7 +37,7 @@ class TestDevice(object): disk = device.Device("/dev/sda") assert disk.lvm_size.gb == 4 - def test_lvm_size_rounds_down(self, device_info): + def test_lvm_size_rounds_down(self, fake_call, device_info): # 5.5GB in size data = {"/dev/sda": {"size": "5905580032"}} lsblk = {"TYPE": "disk"} @@ -45,14 +45,14 @@ class TestDevice(object): disk = device.Device("/dev/sda") assert disk.lvm_size.gb == 4 - def test_is_lv(self, device_info): + def test_is_lv(self, fake_call, device_info): data = {"lv_path": "vg/lv", "vg_name": "vg", "name": "lv"} lsblk = {"TYPE": "lvm"} device_info(lv=data,lsblk=lsblk) disk = device.Device("vg/lv") assert disk.is_lv - def test_vgs_is_empty(self, device_info, monkeypatch): + def test_vgs_is_empty(self, fake_call, device_info, monkeypatch): BarPVolume = api.PVolume(pv_name='/dev/sda', pv_uuid="0000", pv_tags={}) pvolumes = [] @@ -64,7 +64,7 @@ class TestDevice(object): disk = device.Device("/dev/nvme0n1") assert disk.vgs == [] - def test_vgs_is_not_empty(self, device_info, monkeypatch): + def test_vgs_is_not_empty(self, fake_call, device_info, monkeypatch): vg = api.VolumeGroup(vg_name='foo/bar', vg_free_count=6, vg_extent_size=1073741824) monkeypatch.setattr(api, 'get_device_vgs', lambda x: [vg]) @@ -73,42 +73,42 @@ class TestDevice(object): disk = device.Device("/dev/nvme0n1") assert len(disk.vgs) == 1 - def test_device_is_device(self, device_info): + def test_device_is_device(self, fake_call, 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 is True - def test_device_is_rotational(self, device_info): + def test_device_is_rotational(self, fake_call, device_info): data = {"/dev/sda": {"rotational": "1"}} lsblk = {"TYPE": "device"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/sda") assert disk.rotational - def test_device_is_not_rotational(self, device_info): + def test_device_is_not_rotational(self, fake_call, device_info): data = {"/dev/sda": {"rotational": "0"}} lsblk = {"TYPE": "device"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/sda") assert not disk.rotational - def test_device_is_rotational_lsblk(self, device_info): + def test_device_is_rotational_lsblk(self, fake_call, device_info): data = {"/dev/sda": {"foo": "bar"}} lsblk = {"TYPE": "device", "ROTA": "1"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/sda") assert disk.rotational - def test_device_is_not_rotational_lsblk(self, device_info): + def test_device_is_not_rotational_lsblk(self, fake_call, device_info): data = {"/dev/sda": {"rotational": "0"}} lsblk = {"TYPE": "device", "ROTA": "0"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/sda") assert not disk.rotational - def test_device_is_rotational_defaults_true(self, device_info): + def test_device_is_rotational_defaults_true(self, fake_call, device_info): # rotational will default true if no info from sys_api or lsblk is found data = {"/dev/sda": {"foo": "bar"}} lsblk = {"TYPE": "device", "foo": "bar"} @@ -116,54 +116,54 @@ class TestDevice(object): disk = device.Device("/dev/sda") assert disk.rotational - def test_disk_is_device(self, device_info): + def test_disk_is_device(self, fake_call, device_info): data = {"/dev/sda": {"foo": "bar"}} lsblk = {"TYPE": "disk"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/sda") assert disk.is_device is True - def test_is_partition(self, device_info): + def test_is_partition(self, fake_call, device_info): data = {"/dev/sda1": {"foo": "bar"}} lsblk = {"TYPE": "part", "PKNAME": "sda"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/sda1") assert disk.is_partition - def test_mpath_device_is_device(self, device_info): + def test_mpath_device_is_device(self, fake_call, device_info): data = {"/dev/foo": {"foo": "bar"}} lsblk = {"TYPE": "mpath"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/foo") assert disk.is_device is True - def test_is_not_lvm_member(self, device_info): + def test_is_not_lvm_member(self, fake_call, device_info): data = {"/dev/sda1": {"foo": "bar"}} lsblk = {"TYPE": "part", "PKNAME": "sda"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/sda1") assert not disk.is_lvm_member - def test_is_lvm_member(self, device_info): + def test_is_lvm_member(self, fake_call, device_info): data = {"/dev/sda1": {"foo": "bar"}} lsblk = {"TYPE": "part", "PKNAME": "sda"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/sda1") assert not disk.is_lvm_member - def test_is_mapper_device(self, device_info): + def test_is_mapper_device(self, fake_call, device_info): lsblk = {"TYPE": "lvm"} device_info(lsblk=lsblk) disk = device.Device("/dev/mapper/foo") assert disk.is_mapper - def test_dm_is_mapper_device(self, device_info): + def test_dm_is_mapper_device(self, fake_call, device_info): lsblk = {"TYPE": "lvm"} device_info(lsblk=lsblk) disk = device.Device("/dev/dm-4") assert disk.is_mapper - def test_is_not_mapper_device(self, device_info): + def test_is_not_mapper_device(self, fake_call, device_info): lsblk = {"TYPE": "disk"} device_info(lsblk=lsblk) disk = device.Device("/dev/sda") @@ -171,19 +171,19 @@ class TestDevice(object): @pytest.mark.usefixtures("lsblk_ceph_disk_member", "disable_kernel_queries") - def test_is_ceph_disk_lsblk(self, monkeypatch, patch_bluestore_label): + def test_is_ceph_disk_lsblk(self, fake_call, monkeypatch, patch_bluestore_label): disk = device.Device("/dev/sda") assert disk.is_ceph_disk_member @pytest.mark.usefixtures("blkid_ceph_disk_member", "disable_kernel_queries") - def test_is_ceph_disk_blkid(self, monkeypatch, patch_bluestore_label): + def test_is_ceph_disk_blkid(self, fake_call, monkeypatch, patch_bluestore_label): disk = device.Device("/dev/sda") assert disk.is_ceph_disk_member @pytest.mark.usefixtures("lsblk_ceph_disk_member", "disable_kernel_queries") - def test_is_ceph_disk_member_not_available_lsblk(self, monkeypatch, patch_bluestore_label): + def test_is_ceph_disk_member_not_available_lsblk(self, fake_call, monkeypatch, patch_bluestore_label): disk = device.Device("/dev/sda") assert disk.is_ceph_disk_member assert not disk.available @@ -191,20 +191,20 @@ class TestDevice(object): @pytest.mark.usefixtures("blkid_ceph_disk_member", "disable_kernel_queries") - def test_is_ceph_disk_member_not_available_blkid(self, monkeypatch, patch_bluestore_label): + def test_is_ceph_disk_member_not_available_blkid(self, fake_call, monkeypatch, patch_bluestore_label): disk = device.Device("/dev/sda") assert disk.is_ceph_disk_member assert not disk.available assert "Used by ceph-disk" in disk.rejected_reasons - def test_reject_removable_device(self, device_info): + def test_reject_removable_device(self, fake_call, device_info): data = {"/dev/sdb": {"removable": 1}} lsblk = {"TYPE": "disk"} device_info(devices=data,lsblk=lsblk) disk = device.Device("/dev/sdb") assert not disk.available - def test_reject_device_with_gpt_headers(self, device_info): + def test_reject_device_with_gpt_headers(self, fake_call, device_info): data = {"/dev/sdb": {"removable": 0, "size": 5368709120}} lsblk = {"TYPE": "disk"} blkid= {"PTTYPE": "gpt"} @@ -216,42 +216,42 @@ class TestDevice(object): disk = device.Device("/dev/sdb") assert not disk.available - def test_accept_non_removable_device(self, device_info): + def test_accept_non_removable_device(self, fake_call, device_info): data = {"/dev/sdb": {"removable": 0, "size": 5368709120}} lsblk = {"TYPE": "disk"} device_info(devices=data,lsblk=lsblk) disk = device.Device("/dev/sdb") assert disk.available - def test_reject_not_acceptable_device(self, device_info): + def test_reject_not_acceptable_device(self, fake_call, device_info): data = {"/dev/dm-0": {"foo": "bar"}} lsblk = {"TYPE": "mpath"} device_info(devices=data, lsblk=lsblk) disk = device.Device("/dev/dm-0") assert not disk.available - def test_reject_readonly_device(self, device_info): + def test_reject_readonly_device(self, fake_call, device_info): data = {"/dev/cdrom": {"ro": 1}} lsblk = {"TYPE": "disk"} device_info(devices=data,lsblk=lsblk) disk = device.Device("/dev/cdrom") assert not disk.available - def test_reject_smaller_than_5gb(self, device_info): + def test_reject_smaller_than_5gb(self, fake_call, device_info): data = {"/dev/sda": {"size": 5368709119}} lsblk = {"TYPE": "disk"} device_info(devices=data,lsblk=lsblk) disk = device.Device("/dev/sda") assert not disk.available, 'too small device is available' - def test_accept_non_readonly_device(self, device_info): + def test_accept_non_readonly_device(self, fake_call, device_info): data = {"/dev/sda": {"ro": 0, "size": 5368709120}} lsblk = {"TYPE": "disk"} device_info(devices=data,lsblk=lsblk) disk = device.Device("/dev/sda") assert disk.available - def test_reject_bluestore_device(self, monkeypatch, patch_bluestore_label, device_info): + def test_reject_bluestore_device(self, fake_call, monkeypatch, patch_bluestore_label, device_info): patch_bluestore_label.return_value = True lsblk = {"TYPE": "disk"} device_info(lsblk=lsblk) @@ -259,7 +259,7 @@ class TestDevice(object): assert not disk.available assert "Has BlueStore device label" in disk.rejected_reasons - def test_reject_device_with_oserror(self, monkeypatch, patch_bluestore_label, device_info): + def test_reject_device_with_oserror(self, fake_call, monkeypatch, patch_bluestore_label, device_info): patch_bluestore_label.side_effect = OSError('test failure') lsblk = {"TYPE": "disk"} device_info(lsblk=lsblk) @@ -269,11 +269,11 @@ class TestDevice(object): @pytest.mark.usefixtures("device_info_not_ceph_disk_member", "disable_kernel_queries") - def test_is_not_ceph_disk_member_lsblk(self, patch_bluestore_label): + def test_is_not_ceph_disk_member_lsblk(self, fake_call, patch_bluestore_label): disk = device.Device("/dev/sda") assert disk.is_ceph_disk_member is False - def test_existing_vg_available(self, monkeypatch, device_info): + def test_existing_vg_available(self, fake_call, monkeypatch, device_info): vg = api.VolumeGroup(vg_name='foo/bar', vg_free_count=1536, vg_extent_size=4194304) monkeypatch.setattr(api, 'get_device_vgs', lambda x: [vg]) @@ -285,7 +285,7 @@ class TestDevice(object): assert not disk.available assert not disk.available_raw - def test_existing_vg_too_small(self, monkeypatch, device_info): + def test_existing_vg_too_small(self, fake_call, monkeypatch, device_info): vg = api.VolumeGroup(vg_name='foo/bar', vg_free_count=4, vg_extent_size=1073741824) monkeypatch.setattr(api, 'get_device_vgs', lambda x: [vg]) @@ -297,7 +297,7 @@ class TestDevice(object): assert not disk.available assert not disk.available_raw - def test_multiple_existing_vgs(self, monkeypatch, device_info): + def test_multiple_existing_vgs(self, fake_call, monkeypatch, device_info): vg1 = api.VolumeGroup(vg_name='foo/bar', vg_free_count=1000, vg_extent_size=4194304) vg2 = api.VolumeGroup(vg_name='foo/bar', vg_free_count=536, @@ -312,7 +312,7 @@ class TestDevice(object): assert not disk.available_raw @pytest.mark.parametrize("ceph_type", ["data", "block"]) - def test_used_by_ceph(self, device_info, + def test_used_by_ceph(self, fake_call, device_info, monkeypatch, ceph_type): data = {"/dev/sda": {"foo": "bar"}} lsblk = {"TYPE": "part", "PKNAME": "sda"} @@ -337,7 +337,7 @@ class TestDevice(object): disk = device.Device("/dev/sda") assert disk.used_by_ceph - def test_not_used_by_ceph(self, device_info, monkeypatch): + def test_not_used_by_ceph(self, fake_call, device_info, monkeypatch): FooPVolume = api.PVolume(pv_name='/dev/sda', pv_uuid="0000", lv_uuid="0000", pv_tags={}, vg_name="vg") pvolumes = [] pvolumes.append(FooPVolume) @@ -350,7 +350,7 @@ class TestDevice(object): disk = device.Device("/dev/sda") assert not disk.used_by_ceph - def test_get_device_id(self, device_info): + def test_get_device_id(self, fake_call, device_info): udev = {k:k for k in ['ID_VENDOR', 'ID_MODEL', 'ID_SCSI_SERIAL']} lsblk = {"TYPE": "disk"} device_info(udevadm=udev,lsblk=lsblk) @@ -371,33 +371,33 @@ class TestDevice(object): class TestDeviceEncryption(object): - def test_partition_is_not_encrypted_lsblk(self, device_info): + def test_partition_is_not_encrypted_lsblk(self, fake_call, device_info): lsblk = {'TYPE': 'part', 'FSTYPE': 'xfs', 'PKNAME': 'sda'} device_info(lsblk=lsblk) disk = device.Device("/dev/sda") assert disk.is_encrypted is False - def test_partition_is_encrypted_lsblk(self, device_info): + def test_partition_is_encrypted_lsblk(self, fake_call, device_info): lsblk = {'TYPE': 'part', 'FSTYPE': 'crypto_LUKS', 'PKNAME': 'sda'} device_info(lsblk=lsblk) disk = device.Device("/dev/sda") assert disk.is_encrypted is True - def test_partition_is_not_encrypted_blkid(self, device_info): + def test_partition_is_not_encrypted_blkid(self, fake_call, device_info): lsblk = {'TYPE': 'part', 'PKNAME': 'sda'} blkid = {'TYPE': 'ceph data'} device_info(lsblk=lsblk, blkid=blkid) disk = device.Device("/dev/sda") assert disk.is_encrypted is False - def test_partition_is_encrypted_blkid(self, device_info): + def test_partition_is_encrypted_blkid(self, fake_call, device_info): lsblk = {'TYPE': 'part', 'PKNAME': 'sda'} blkid = {'TYPE': 'crypto_LUKS'} device_info(lsblk=lsblk, blkid=blkid) disk = device.Device("/dev/sda") assert disk.is_encrypted is True - def test_mapper_is_encrypted_luks1(self, device_info, monkeypatch): + def test_mapper_is_encrypted_luks1(self, fake_call, device_info, monkeypatch): status = {'type': 'LUKS1'} monkeypatch.setattr(device, 'encryption_status', lambda x: status) lsblk = {'FSTYPE': 'xfs', 'TYPE': 'lvm'} @@ -406,7 +406,7 @@ class TestDeviceEncryption(object): disk = device.Device("/dev/mapper/uuid") assert disk.is_encrypted is True - def test_mapper_is_encrypted_luks2(self, device_info, monkeypatch): + def test_mapper_is_encrypted_luks2(self, fake_call, device_info, monkeypatch): status = {'type': 'LUKS2'} monkeypatch.setattr(device, 'encryption_status', lambda x: status) lsblk = {'FSTYPE': 'xfs', 'TYPE': 'lvm'} @@ -415,7 +415,7 @@ class TestDeviceEncryption(object): disk = device.Device("/dev/mapper/uuid") assert disk.is_encrypted is True - def test_mapper_is_encrypted_plain(self, device_info, monkeypatch): + def test_mapper_is_encrypted_plain(self, fake_call, device_info, monkeypatch): status = {'type': 'PLAIN'} monkeypatch.setattr(device, 'encryption_status', lambda x: status) lsblk = {'FSTYPE': 'xfs', 'TYPE': 'lvm'} @@ -424,7 +424,7 @@ class TestDeviceEncryption(object): disk = device.Device("/dev/mapper/uuid") assert disk.is_encrypted is True - def test_mapper_is_not_encrypted_plain(self, device_info, monkeypatch): + def test_mapper_is_not_encrypted_plain(self, fake_call, device_info, monkeypatch): monkeypatch.setattr(device, 'encryption_status', lambda x: {}) lsblk = {'FSTYPE': 'xfs', 'TYPE': 'lvm'} blkid = {'TYPE': 'mapper'} @@ -432,7 +432,7 @@ class TestDeviceEncryption(object): disk = device.Device("/dev/mapper/uuid") assert disk.is_encrypted is False - def test_lv_is_encrypted_blkid(self, device_info): + def test_lv_is_encrypted_blkid(self, fake_call, device_info): lsblk = {'TYPE': 'lvm'} blkid = {'TYPE': 'crypto_LUKS'} device_info(lsblk=lsblk, blkid=blkid) @@ -440,7 +440,7 @@ class TestDeviceEncryption(object): disk.lv_api = {} assert disk.is_encrypted is True - def test_lv_is_not_encrypted_blkid(self, factory, device_info): + def test_lv_is_not_encrypted_blkid(self, fake_call, factory, device_info): lsblk = {'TYPE': 'lvm'} blkid = {'TYPE': 'xfs'} device_info(lsblk=lsblk, blkid=blkid) @@ -448,7 +448,7 @@ class TestDeviceEncryption(object): disk.lv_api = factory(encrypted=None) assert disk.is_encrypted is False - def test_lv_is_encrypted_lsblk(self, device_info): + def test_lv_is_encrypted_lsblk(self, fake_call, device_info): lsblk = {'FSTYPE': 'crypto_LUKS', 'TYPE': 'lvm'} blkid = {'TYPE': 'mapper'} device_info(lsblk=lsblk, blkid=blkid) @@ -456,7 +456,7 @@ class TestDeviceEncryption(object): disk.lv_api = {} assert disk.is_encrypted is True - def test_lv_is_not_encrypted_lsblk(self, factory, device_info): + def test_lv_is_not_encrypted_lsblk(self, fake_call, factory, device_info): lsblk = {'FSTYPE': 'xfs', 'TYPE': 'lvm'} blkid = {'TYPE': 'mapper'} device_info(lsblk=lsblk, blkid=blkid) @@ -464,7 +464,7 @@ class TestDeviceEncryption(object): disk.lv_api = factory(encrypted=None) assert disk.is_encrypted is False - def test_lv_is_encrypted_lvm_api(self, factory, device_info): + def test_lv_is_encrypted_lvm_api(self, fake_call, factory, device_info): lsblk = {'FSTYPE': 'xfs', 'TYPE': 'lvm'} blkid = {'TYPE': 'mapper'} device_info(lsblk=lsblk, blkid=blkid) @@ -472,7 +472,7 @@ class TestDeviceEncryption(object): disk.lv_api = factory(encrypted=True) assert disk.is_encrypted is True - def test_lv_is_not_encrypted_lvm_api(self, factory, device_info): + def test_lv_is_not_encrypted_lvm_api(self, fake_call, factory, device_info): lsblk = {'FSTYPE': 'xfs', 'TYPE': 'lvm'} blkid = {'TYPE': 'mapper'} device_info(lsblk=lsblk, blkid=blkid) @@ -491,7 +491,7 @@ class TestDeviceOrdering(object): "/dev/sdd": {"removable": 1}, # invalid } - def test_valid_before_invalid(self, device_info): + def test_valid_before_invalid(self, fake_call, device_info): lsblk = {"TYPE": "disk"} device_info(devices=self.data,lsblk=lsblk) sda = device.Device("/dev/sda") @@ -500,7 +500,7 @@ class TestDeviceOrdering(object): assert sda < sdb assert sdb > sda - def test_valid_alphabetical_ordering(self, device_info): + def test_valid_alphabetical_ordering(self, fake_call, device_info): lsblk = {"TYPE": "disk"} device_info(devices=self.data,lsblk=lsblk) sda = device.Device("/dev/sda") @@ -509,7 +509,7 @@ class TestDeviceOrdering(object): assert sda < sdc assert sdc > sda - def test_invalid_alphabetical_ordering(self, device_info): + def test_invalid_alphabetical_ordering(self, fake_call, device_info): lsblk = {"TYPE": "disk"} device_info(devices=self.data,lsblk=lsblk) sdb = device.Device("/dev/sdb") @@ -521,14 +521,14 @@ class TestDeviceOrdering(object): class TestCephDiskDevice(object): - def test_partlabel_lsblk(self, device_info): + def test_partlabel_lsblk(self, fake_call, device_info): lsblk = {"TYPE": "disk", "PARTLABEL": ""} device_info(lsblk=lsblk) disk = device.CephDiskDevice(device.Device("/dev/sda")) assert disk.partlabel == '' - def test_partlabel_blkid(self, device_info): + def test_partlabel_blkid(self, fake_call, device_info): blkid = {"TYPE": "disk", "PARTLABEL": "ceph data"} device_info(blkid=blkid) disk = device.CephDiskDevice(device.Device("/dev/sda")) @@ -537,21 +537,21 @@ class TestCephDiskDevice(object): @pytest.mark.usefixtures("blkid_ceph_disk_member", "disable_kernel_queries") - def test_is_member_blkid(self, monkeypatch, patch_bluestore_label): + def test_is_member_blkid(self, fake_call, monkeypatch, patch_bluestore_label): disk = device.CephDiskDevice(device.Device("/dev/sda")) assert disk.is_member is True @pytest.mark.usefixtures("lsblk_ceph_disk_member", "disable_kernel_queries") - def test_is_member_lsblk(self, patch_bluestore_label, device_info): + def test_is_member_lsblk(self, fake_call, patch_bluestore_label, device_info): lsblk = {"TYPE": "disk", "PARTLABEL": "ceph"} device_info(lsblk=lsblk) disk = device.CephDiskDevice(device.Device("/dev/sda")) assert disk.is_member is True - def test_unknown_type(self, device_info): + def test_unknown_type(self, fake_call, device_info): lsblk = {"TYPE": "disk", "PARTLABEL": "gluster"} device_info(lsblk=lsblk) disk = device.CephDiskDevice(device.Device("/dev/sda")) @@ -562,7 +562,7 @@ class TestCephDiskDevice(object): @pytest.mark.usefixtures("blkid_ceph_disk_member", "disable_kernel_queries") - def test_type_blkid(self, monkeypatch, device_info, ceph_partlabel): + def test_type_blkid(self, monkeypatch, fake_call, device_info, ceph_partlabel): disk = device.CephDiskDevice(device.Device("/dev/sda")) assert disk.type in self.ceph_types @@ -570,7 +570,7 @@ class TestCephDiskDevice(object): @pytest.mark.usefixtures("blkid_ceph_disk_member", "lsblk_ceph_disk_member", "disable_kernel_queries") - def test_type_lsblk(self, device_info, ceph_partlabel): + def test_type_lsblk(self, fake_call, device_info, ceph_partlabel): disk = device.CephDiskDevice(device.Device("/dev/sda")) assert disk.type in self.ceph_types 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 9793457d4..655f7cd55 100644 --- a/ceph/src/ceph-volume/ceph_volume/util/arg_validators.py +++ b/ceph/src/ceph-volume/ceph_volume/util/arg_validators.py @@ -1,10 +1,9 @@ import argparse import os import math -from ceph_volume import terminal -from ceph_volume import decorators -from ceph_volume.util import disk +from ceph_volume import terminal, decorators, process from ceph_volume.util.device import Device +from ceph_volume.util import disk def valid_osd_id(val): @@ -17,8 +16,13 @@ class ValidDevice(object): self.gpt_ok = gpt_ok def __call__(self, dev_path): - device = self._is_valid_device(dev_path) - return self._format_device(device) + self.get_device(dev_path) + self._validated_device = self._is_valid_device() + return self._format_device(self._validated_device) + + def get_device(self, dev_path): + self._device = Device(dev_path) + self.dev_path = dev_path def _format_device(self, device): if self.as_string: @@ -28,36 +32,101 @@ class ValidDevice(object): return device.path return device - def _is_valid_device(self, dev_path): - device = Device(dev_path) + def _is_valid_device(self): error = None - if not device.exists: - error = "Unable to proceed with non-existing device: %s" % dev_path + if not self._device.exists: + error = "Unable to proceed with non-existing device: %s" % self.dev_path # FIXME this is not a nice API, this validator was meant to catch any # non-existing devices upfront, not check for gpt headers. Now this # needs to optionally skip checking gpt headers which is beyond # verifying if the device exists. The better solution would be to # configure this with a list of checks that can be excluded/included on # __init__ - elif device.has_gpt_headers and not self.gpt_ok: - error = "GPT headers found, they must be removed on: %s" % dev_path - if device.has_partitions: - raise RuntimeError("Device {} has partitions.".format(dev_path)) + elif self._device.has_gpt_headers and not self.gpt_ok: + error = "GPT headers found, they must be removed on: %s" % self.dev_path + if self._device.has_partitions: + raise RuntimeError("Device {} has partitions.".format(self.dev_path)) if error: raise argparse.ArgumentError(None, error) - return device + return self._device -class ValidBatchDevice(ValidDevice): +class ValidZapDevice(ValidDevice): + def __call__(self, dev_path): + super().get_device(dev_path) + return self._format_device(self._is_valid_device()) + + def _is_valid_device(self, raise_sys_exit=True): + super()._is_valid_device() + return self._device + +class ValidDataDevice(ValidDevice): def __call__(self, dev_path): - dev = self._is_valid_device(dev_path) - if dev.is_partition: + super().get_device(dev_path) + return self._format_device(self._is_valid_device()) + + def _is_valid_device(self, raise_sys_exit=True): + super()._is_valid_device() + if self._device.used_by_ceph: + terminal.info('Device {} is already prepared'.format(self.dev_path)) + if raise_sys_exit: + raise SystemExit(0) + if self._device.has_fs and not self._device.used_by_ceph: + raise RuntimeError("Device {} has a filesystem.".format(self.dev_path)) + if self.dev_path[0] == '/' and disk.has_bluestore_label(self.dev_path): + raise RuntimeError("Device {} has bluestore signature.".format(self.dev_path)) + return self._device + +class ValidRawDevice(ValidDevice): + def __call__(self, dev_path): + super().get_device(dev_path) + return self._format_device(self._is_valid_device()) + + def _is_valid_device(self, raise_sys_exit=True): + out, err, rc = process.call([ + 'ceph-bluestore-tool', 'show-label', + '--dev', self.dev_path], verbose_on_failure=False) + if not rc: + terminal.info("Raw device {} is already prepared.".format(self.dev_path)) + raise SystemExit(0) + if disk.blkid(self.dev_path).get('TYPE') == 'crypto_LUKS': + terminal.info("Raw device {} might already be in use for a dmcrypt OSD, skipping.".format(self.dev_path)) + raise SystemExit(0) + super()._is_valid_device() + return self._device + +class ValidBatchDevice(ValidDevice): + def __call__(self, dev_path): + super().get_device(dev_path) + return self._format_device(self._is_valid_device()) + + def _is_valid_device(self, raise_sys_exit=False): + super()._is_valid_device() + if self._device.is_partition: raise argparse.ArgumentError( None, '{} is a partition, please pass ' - 'LVs or raw block devices'.format(dev_path)) - return self._format_device(dev) + 'LVs or raw block devices'.format(self.dev_path)) + return self._device + + +class ValidBatchDataDevice(ValidBatchDevice, ValidDataDevice): + def __call__(self, dev_path): + super().get_device(dev_path) + return self._format_device(self._is_valid_device()) + + def _is_valid_device(self): + # if device is already used by ceph, + # leave the validation to Batch.get_deployment_layout() + # This way the idempotency isn't broken (especially when using --osds-per-device) + for lv in self._device.lvs: + if lv.tags.get('ceph.type') in ['db', 'wal', 'journal']: + return self._device + if self._device.used_by_ceph: + return self._device + super()._is_valid_device(raise_sys_exit=False) + return self._device class OSDPath(object): diff --git a/ceph/src/ceph-volume/ceph_volume/util/device.py b/ceph/src/ceph-volume/ceph_volume/util/device.py index c30a094b6..edd6e804f 100644 --- a/ceph/src/ceph-volume/ceph_volume/util/device.py +++ b/ceph/src/ceph-volume/ceph_volume/util/device.py @@ -3,7 +3,7 @@ import logging import os from functools import total_ordering -from ceph_volume import sys_info +from ceph_volume import sys_info, process from ceph_volume.api import lvm from ceph_volume.util import disk, system from ceph_volume.util.lsmdisk import LSMDisk @@ -66,6 +66,7 @@ class Device(object): {attr:<25} {value}""" report_fields = [ + 'ceph_device', 'rejected_reasons', 'available', 'path', @@ -104,6 +105,7 @@ class Device(object): self._is_lvm_member = None self._parse() self.lsm_data = self.fetch_lsm(with_lsm) + self.ceph_device = None self.available_lvm, self.rejected_reasons_lvm = self._check_lvm_reject_reasons() self.available_raw, self.rejected_reasons_raw = self._check_raw_reject_reasons() @@ -173,6 +175,7 @@ class Device(object): self.abspath = lv.lv_path self.vg_name = lv.vg_name self.lv_name = lv.name + self.ceph_device = lvm.is_ceph_device(lv) else: dev = disk.lsblk(self.path) self.blkid_api = disk.blkid(self.path) @@ -181,6 +184,11 @@ class Device(object): # always check is this is an lvm member if device_type in ['part', 'disk']: self._set_lvm_membership() + out, err, rc = process.call([ + 'ceph-bluestore-tool', 'show-label', + '--dev', self.path], verbose_on_failure=False) + if rc: + self.ceph_device = True self.ceph_disk = CephDiskDevice(self) @@ -301,6 +309,10 @@ class Device(object): def exists(self): return os.path.exists(self.abspath) + @property + def has_fs(self): + return 'TYPE' in self.blkid_api + @property def has_gpt_headers(self): return self.blkid_api.get("PTTYPE") == "gpt" diff --git a/ceph/src/ceph-volume/tox.ini b/ceph/src/ceph-volume/tox.ini index 92213f769..c58951a9b 100644 --- a/ceph/src/ceph-volume/tox.ini +++ b/ceph/src/ceph-volume/tox.ini @@ -5,9 +5,10 @@ skip_missing_interpreters = true [testenv] deps= pytest + pytest-xdist mock install_command=./tox_install_command.sh {opts} {packages} -commands=py.test -v {posargs:ceph_volume/tests} --ignore=ceph_volume/tests/functional +commands=py.test --numprocesses=auto -vv {posargs:ceph_volume/tests} --ignore=ceph_volume/tests/functional [testenv:py3-flake8] deps=flake8 diff --git a/ceph/src/ceph.in b/ceph/src/ceph.in index d5023e608..1e2bf6e56 100755 --- a/ceph/src/ceph.in +++ b/ceph/src/ceph.in @@ -336,7 +336,8 @@ def parse_cmdargs(args=None, target='') -> Tuple[argparse.ArgumentParser, help="make less verbose") parser.add_argument('-f', '--format', choices=['json', 'json-pretty', - 'xml', 'xml-pretty', 'plain', 'yaml'], dest='output_format') + 'xml', 'xml-pretty', 'plain', 'yaml'], + help="Note: yaml is only valid for orch commands", dest='output_format') parser.add_argument('--connect-timeout', dest='cluster_timeout', type=int, diff --git a/ceph/src/cephadm/box/Dockerfile b/ceph/src/cephadm/box/Dockerfile index d3bd9c28a..e927bcb70 100644 --- a/ceph/src/cephadm/box/Dockerfile +++ b/ceph/src/cephadm/box/Dockerfile @@ -5,7 +5,7 @@ ENV CEPHADM_PATH=/usr/local/sbin/cephadm # Centos met EOL and the content of the CentOS 8 repos has been moved to vault.centos.org RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-* -RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-Linux-* +RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=https://vault.centos.org|g' /etc/yum.repos.d/CentOS-Linux-* RUN dnf -y install chrony firewalld lvm2 \ openssh-server openssh-clients python3 \ diff --git a/ceph/src/cephadm/box/box.py b/ceph/src/cephadm/box/box.py index da41e2a76..000e9adb8 100755 --- a/ceph/src/cephadm/box/box.py +++ b/ceph/src/cephadm/box/box.py @@ -6,10 +6,16 @@ import sys import host import osd -from util import (Config, Target, ensure_inside_container, - ensure_outside_container, get_boxes_container_info, - get_host_ips, inside_container, run_cephadm_shell_command, - run_dc_shell_command, run_shell_command) +from util import ( + Config, + Target, + ensure_inside_container, + ensure_outside_container, + get_boxes_container_info, + run_cephadm_shell_command, + run_dc_shell_command, + run_shell_command, +) CEPH_IMAGE = 'quay.ceph.io/ceph-ci/ceph:master' BOX_IMAGE = 'cephadm-box:latest' @@ -19,14 +25,17 @@ BOX_IMAGE = 'cephadm-box:latest' # image yourself with `box cluster setup` CEPH_IMAGE_TAR = 'docker/ceph/image/quay.ceph.image.tar' + def remove_ceph_image_tar(): if os.path.exists(CEPH_IMAGE_TAR): os.remove(CEPH_IMAGE_TAR) + def cleanup_box() -> None: osd.cleanup() remove_ceph_image_tar() + def image_exists(image_name: str): # extract_tag assert image_name.find(':') @@ -42,6 +51,7 @@ def image_exists(image_name: str): return True return False + def get_ceph_image(): print('Getting ceph image') run_shell_command(f'docker pull {CEPH_IMAGE}') @@ -55,24 +65,29 @@ def get_ceph_image(): run_shell_command(f'docker save {CEPH_IMAGE} -o {CEPH_IMAGE_TAR}') print('Ceph image added') + def get_box_image(): print('Getting box image') run_shell_command('docker build -t cephadm-box -f Dockerfile .') print('Box image added') - + class Cluster(Target): _help = 'Manage docker cephadm boxes' actions = ['bootstrap', 'start', 'down', 'list', 'sh', 'setup', 'cleanup'] def set_args(self): - self.parser.add_argument('action', choices=Cluster.actions, help='Action to perform on the box') - self.parser.add_argument('--osds', type=int, default=1, help='Number of osds') - self.parser.add_argument('--hosts', type=int, default=1, help='Number of hosts') - self.parser.add_argument('--skip_deploy_osds', action='store_true', help='skip deploy osd') - self.parser.add_argument('--skip_create_loop', action='store_true', help='skip create loopback device' ) - self.parser.add_argument('--skip_monitoring_stack', action='store_true', help='skip monitoring stack') - self.parser.add_argument('--skip_dashboard', action='store_true', help='skip dashboard') + self.parser.add_argument( + 'action', choices=Cluster.actions, help='Action to perform on the box' + ) + self.parser.add_argument('--osds', type=int, default=3, help='Number of osds') + + self.parser.add_argument('--hosts', type=int, default=2, help='Number of hosts') + self.parser.add_argument('--skip-deploy-osds', action='store_true', help='skip deploy osd') + self.parser.add_argument('--skip-create-loop', action='store_true', help='skip create loopback device') + self.parser.add_argument('--skip-monitoring-stack', action='store_true', help='skip monitoring stack') + self.parser.add_argument('--skip-dashboard', action='store_true', help='skip dashboard') + self.parser.add_argument('--expanded', action='store_true', help='deploy 3 hosts and 3 osds') @ensure_outside_container def setup(self): @@ -88,7 +103,9 @@ class Cluster(Target): print('Running bootstrap on seed') cephadm_path = os.environ.get('CEPHADM_PATH') os.symlink('/cephadm/cephadm', cephadm_path) - run_shell_command('systemctl restart docker') # restart to ensure docker is using daemon.json + run_shell_command( + 'systemctl restart docker' + ) # restart to ensure docker is using daemon.json st = os.stat(cephadm_path) os.chmod(cephadm_path, st.st_mode | stat.S_IEXEC) @@ -98,7 +115,9 @@ class Cluster(Target): # instead of master's tag run_shell_command('export CEPH_SOURCE_FOLDER=/ceph') run_shell_command('export CEPHADM_IMAGE=quay.ceph.io/ceph-ci/ceph:master') - run_shell_command('echo "export CEPHADM_IMAGE=quay.ceph.io/ceph-ci/ceph:master" >> ~/.bashrc') + run_shell_command( + 'echo "export CEPHADM_IMAGE=quay.ceph.io/ceph-ci/ceph:master" >> ~/.bashrc' + ) extra_args = [] @@ -109,13 +128,14 @@ class Cluster(Target): extra_args.append('2>&0') extra_args = ' '.join(extra_args) - skip_monitoring_stack = '--skip_monitoring_stack' if Config.get('skip_monitoring_stack') else '' - skip_dashboard = '--skip_dashboard' if Config.get('skip_dashboard') else '' + skip_monitoring_stack = ( + '--skip-monitoring-stack' if Config.get('skip-monitoring-stack') else '' + ) + skip_dashboard = '--skip-dashboard' if Config.get('skip-dashboard') else '' fsid = Config.get('fsid') config_folder = Config.get('config_folder') config = Config.get('config') - mon_config = Config.get('mon_config') keyring = Config.get('keyring') if not os.path.exists(config_folder): os.mkdir(config_folder) @@ -142,26 +162,16 @@ class Cluster(Target): run_shell_command(cephadm_bootstrap_command) print('Cephadm bootstrap complete') - run_shell_command('sudo vgchange --refresh') run_shell_command('cephadm ls') run_shell_command('ln -s /ceph/src/cephadm/box/box.py /usr/bin/box') - hostname = run_shell_command('hostname') # NOTE: sometimes cephadm in the box takes a while to update the containers # running in the cluster and it cannot deploy the osds. In this case # run: box -v osd deploy --vg vg1 to deploy osds again. - if not Config.get('skip_deploy_osds'): - print('Deploying osds...') - osds = Config.get('osds') - for o in range(osds): - osd.deploy_osd(f'vg1/lv{o}', hostname) - print('Osds deployed') run_cephadm_shell_command('ceph -s') print('Bootstrap completed!') - - @ensure_outside_container def start(self): osds = Config.get('osds') @@ -192,27 +202,40 @@ class Cluster(Target): run_shell_command('sudo iptables -P FORWARD ACCEPT') print('Seting up host ssh servers') - ips = get_host_ips() - print(ips) for h in range(hosts): - host._setup_ssh(h+1) + host._setup_ssh(h + 1) verbose = '-v' if Config.get('verbose') else '' - skip_deploy = '--skip_deploy_osds' if Config.get('skip_deploy_osds') else '' - skip_monitoring_stack = '--skip_monitoring_stack' if Config.get('skip_monitoring_stack') else '' - skip_dashboard = '--skip_dashboard' if Config.get('skip_dashboard') else '' + skip_deploy = '--skip-deploy-osds' if Config.get('skip-deploy-osds') else '' + skip_monitoring_stack = ( + '--skip-monitoring-stack' if Config.get('skip-monitoring-stack') else '' + ) + skip_dashboard = '--skip-dashboard' if Config.get('skip-dashboard') else '' box_bootstrap_command = ( f'/cephadm/box/box.py {verbose} cluster bootstrap ' - '--osds {osds} ' - '--hosts {hosts} ' + f'--osds {osds} ' + f'--hosts {hosts} ' f'{skip_deploy} ' f'{skip_dashboard} ' f'{skip_monitoring_stack} ' ) - run_dc_shell_command(f'/cephadm/box/box.py {verbose} cluster bootstrap --osds {osds} --hosts {hosts} {skip_deploy}', 1, 'seed') + run_dc_shell_command(box_bootstrap_command, 1, 'seed') + info = get_boxes_container_info() + ips = info['ips'] + hostnames = info['hostnames'] + print(ips) host._copy_cluster_ssh_key(ips) + expanded = Config.get('expanded') + if expanded: + host._add_hosts(ips, hostnames) + + if expanded and not Config.get('skip-deploy-osds'): + print('Deploying osds... This could take up to minutes') + osd.deploy_osds_in_vg('vg1') + print('Osds deployed') + print('Bootstrap finished successfully') @ensure_outside_container @@ -223,9 +246,12 @@ class Cluster(Target): @ensure_outside_container def list(self): - info = get_boxes_container_info() - for container in info: - print('\t'.join(container)) + info = get_boxes_container_info(with_seed=True) + for i in range(info['size']): + ip = info['ips'][i] + name = info['container_names'][i] + hostname = info['hostnames'][i] + print(f'{name} \t{ip} \t{hostname}') @ensure_outside_container def sh(self): @@ -235,17 +261,18 @@ class Cluster(Target): run_shell_command('docker-compose exec seed bash') - - targets = { 'cluster': Cluster, 'osd': osd.Osd, 'host': host.Host, } + def main(): parser = argparse.ArgumentParser() - parser.add_argument('-v', action='store_true', dest='verbose', help='be more verbose') + parser.add_argument( + '-v', action='store_true', dest='verbose', help='be more verbose' + ) subparsers = parser.add_subparsers() target_instances = {} @@ -257,7 +284,7 @@ def main(): instance = target_instances[arg] if hasattr(instance, 'main'): instance.argv = sys.argv[count:] - instance.set_args() + instance.set_args() args = parser.parse_args() Config.add_args(vars(args)) instance.main() @@ -265,5 +292,6 @@ def main(): parser.print_help() + if __name__ == '__main__': main() diff --git a/ceph/src/cephadm/box/host.py b/ceph/src/cephadm/box/host.py index df56fb53d..d7907812d 100644 --- a/ceph/src/cephadm/box/host.py +++ b/ceph/src/cephadm/box/host.py @@ -1,9 +1,15 @@ -import argparse import os -from typing import List +from typing import List, Union -from util import (Config, Target, inside_container, run_dc_shell_command, - run_shell_command) +from util import ( + Config, + Target, + get_boxes_container_info, + inside_container, + run_cephadm_shell_command, + run_dc_shell_command, + run_shell_command, +) def _setup_ssh(container_index): @@ -18,43 +24,89 @@ def _setup_ssh(container_index): f.flush() run_shell_command('/usr/sbin/sshd') else: - print('Redirecting to _setup_ssh to container') + print('Redirecting to _setup_ssh to container') verbose = '-v' if Config.get('verbose') else '' - run_dc_shell_command(f'/cephadm/box/box.py {verbose} host setup_ssh {container_index}', container_index, 'hosts') - + run_dc_shell_command( + f'/cephadm/box/box.py {verbose} host setup_ssh {container_index}', + container_index, + 'hosts', + ) -def _copy_cluster_ssh_key(ips: List[str]): + +def _add_hosts(ips: Union[List[str], str], hostnames: Union[List[str], str]): + if inside_container(): + assert len(ips) == len(hostnames) + for i in range(len(ips)): + run_cephadm_shell_command(f'ceph orch host add {hostnames[i]} {ips[i]}') + else: + print('Redirecting to _add_hosts to container') + verbose = '-v' if Config.get('verbose') else '' + print(ips) + ips = ' '.join(ips) + ips = f'{ips}' + hostnames = ' '.join(hostnames) + hostnames = f'{hostnames}' + run_dc_shell_command( + f'/cephadm/box/box.py {verbose} host add_hosts 1 --ips {ips} --hostnames {hostnames}', + 1, + 'seed', + ) + + +def _copy_cluster_ssh_key(ips: Union[List[str], str]): if inside_container(): local_ip = run_shell_command('hostname -i') for ip in ips: if ip != local_ip: - run_shell_command(('sshpass -p "root" ssh-copy-id -f ' - f'-o StrictHostKeyChecking=no -i /etc/ceph/ceph.pub "root@{ip}"')) + run_shell_command( + ( + 'sshpass -p "root" ssh-copy-id -f ' + f'-o StrictHostKeyChecking=no -i /etc/ceph/ceph.pub "root@{ip}"' + ) + ) else: - print('Redirecting to _copy_cluster_ssh to container') + print('Redirecting to _copy_cluster_ssh to container') verbose = '-v' if Config.get('verbose') else '' print(ips) ips = ' '.join(ips) - ips = f"{ips}" + ips = f'{ips}' # assume we only have one seed - run_dc_shell_command(f'/cephadm/box/box.py {verbose} host copy_cluster_ssh_key 1 --ips {ips}', - 1, 'seed') + run_dc_shell_command( + f'/cephadm/box/box.py {verbose} host copy_cluster_ssh_key 1 --ips {ips}', + 1, + 'seed', + ) + + class Host(Target): _help = 'Run seed/host related commands' - actions = ['setup_ssh', 'copy_cluster_ssh_key'] + actions = ['setup_ssh', 'copy_cluster_ssh_key', 'add_hosts'] def set_args(self): self.parser.add_argument('action', choices=Host.actions) - self.parser.add_argument('host_container_index', type=str, help='box_host_{index}') + self.parser.add_argument( + 'host_container_index', type=str, help='box_host_{index}' + ) self.parser.add_argument('--ips', nargs='*', help='List of host ips') + self.parser.add_argument( + '--hostnames', nargs='*', help='List of hostnames ips(relative to ip list)' + ) def setup_ssh(self): _setup_ssh(Config.get('host_container_index')) + def add_hosts(self): + ips = Config.get('ips') + if not ips: + ips = get_boxes_container_info()['ips'] + hostnames = Config.get('hostnames') + if not hostnames: + hostnames = get_boxes_container_info()['hostnames'] + _add_hosts(ips, hostnames) def copy_cluster_ssh_key(self): ips = Config.get('ips') if not ips: - ips = get_host_ips() + ips = get_boxes_container_info()['ips'] _copy_cluster_ssh_key(ips) diff --git a/ceph/src/cephadm/box/osd.py b/ceph/src/cephadm/box/osd.py index eaf520e77..72693ac85 100644 --- a/ceph/src/cephadm/box/osd.py +++ b/ceph/src/cephadm/box/osd.py @@ -1,11 +1,18 @@ -import argparse import json import os from typing import Dict -from util import (Config, Target, ensure_inside_container, - ensure_outside_container, run_cephadm_shell_command, - run_shell_command) +from util import ( + Config, + Target, + ensure_inside_container, + ensure_outside_container, + get_orch_hosts, + inside_container, + run_cephadm_shell_command, + run_dc_shell_command, + run_shell_command, +) def remove_loop_img() -> None: @@ -13,24 +20,24 @@ def remove_loop_img() -> None: if os.path.exists(loop_image): os.remove(loop_image) + @ensure_outside_container def create_loopback_devices(osds: int) -> None: assert osds size = (5 * osds) + 1 print(f'Using {size}GB of data to store osds') avail_loop = run_shell_command('sudo losetup -f') - base_name = os.path.basename(avail_loop) # create loop if we cannot find it if not os.path.exists(avail_loop): - num_loops = int(run_shell_command('lsmod | grep loop | awk \'{print $3}\'')) + num_loops = int(run_shell_command("lsmod | grep loop | awk '{print $3}'")) num_loops += 1 run_shell_command(f'mknod {avail_loop} b 7 {num_loops}') if os.path.ismount(avail_loop): os.umount(avail_loop) - loop_devices = json.loads(run_shell_command(f'losetup -l -J', expect_error=True)) + loop_devices = json.loads(run_shell_command('losetup -l -J', expect_error=True)) for dev in loop_devices['loopdevices']: if dev['name'] == avail_loop: run_shell_command(f'sudo losetup -d {avail_loop}') @@ -55,6 +62,7 @@ def create_loopback_devices(osds: int) -> None: run_shell_command('sudo vgchange --refresh') run_shell_command(f'sudo lvcreate -l {p}%VG --name lv{i} vg1') + def get_lvm_osd_data(data: str) -> Dict[str, str]: osd_lvm_info = run_cephadm_shell_command(f'ceph-volume lvm list {data}') osd_data = {} @@ -70,9 +78,12 @@ def get_lvm_osd_data(data: str) -> Dict[str, str]: osd_data[key] = line[-1] return osd_data + @ensure_inside_container -def deploy_osd(data: str, hostname: str): - run_cephadm_shell_command(f'ceph orch daemon add osd "{hostname}:{data}"') +def deploy_osd(data: str, hostname: str) -> bool: + out = run_cephadm_shell_command(f'ceph orch daemon add osd "{hostname}:{data}"') + return 'Created osd(s)' in out + def cleanup() -> None: vg = 'vg1' @@ -89,14 +100,43 @@ def cleanup() -> None: remove_loop_img() + +def deploy_osds_in_vg(vg: str): + """ + rotate host will deploy each osd in a different host + + deploying osds will not succeed with starting services so this + makes another process to run on the background + """ + if inside_container(): + lvs = json.loads(run_shell_command('lvs --reportformat json')) + # distribute osds per host + hosts = get_orch_hosts() + host_index = 0 + for lv in lvs['report'][0]['lv']: + if lv['vg_name'] == vg: + deployed = False + while not deployed: + deployed = deploy_osd( + f'{vg}/{lv["lv_name"]}', hosts[host_index]['hostname'] + ) + host_index = (host_index + 1) % len(hosts) + else: + verbose = '-v' if Config.get('verbose') else '' + print('Redirecting deploy osd in vg to inside container') + run_dc_shell_command( + f'/cephadm/box/box.py {verbose} osd deploy --vg {vg}', 1, 'seed' + ) + + class Osd(Target): - _help = ''' + _help = """ Deploy osds and create needed block devices with loopback devices: Actions: - deploy: Deploy an osd given a block device - create_loop: Create needed loopback devices and block devices in logical volumes for a number of osds. - ''' + """ actions = ['deploy', 'create_loop'] def set_args(self): @@ -104,9 +144,10 @@ class Osd(Target): self.parser.add_argument('--data', type=str, help='path to a block device') self.parser.add_argument('--hostname', type=str, help='host to deploy osd') self.parser.add_argument('--osds', type=int, default=0, help='number of osds') - self.parser.add_argument('--vg', type=str, help='Deploy with all lv from virtual group') + self.parser.add_argument( + '--vg', type=str, help='Deploy with all lv from virtual group' + ) - @ensure_inside_container def deploy(self): data = Config.get('data') hostname = Config.get('hostname') @@ -115,11 +156,7 @@ class Osd(Target): # assume this host hostname = run_shell_command('hostname') if vg: - # deploy with vg - lvs = json.loads(run_shell_command('lvs --reportformat json')) - for lv in lvs['report'][0]['lv']: - if lv['vg_name'] == vg: - deploy_osd(f'{vg}/{lv["lv_name"]}', hostname) + deploy_osds_in_vg(vg) else: deploy_osd(data, hostname) @@ -128,4 +165,3 @@ class Osd(Target): osds = Config.get('osds') create_loopback_devices(osds) print('Successfully added logical volumes in loopback devices') - diff --git a/ceph/src/cephadm/box/util.py b/ceph/src/cephadm/box/util.py index 01ca3dc70..6b939b6be 100644 --- a/ceph/src/cephadm/box/util.py +++ b/ceph/src/cephadm/box/util.py @@ -1,8 +1,8 @@ -import argparse +import json import os import subprocess import sys -from typing import Dict, List +from typing import Any, Callable, Dict class Config: @@ -13,6 +13,7 @@ class Config: 'keyring': '/etc/ceph/ceph.keyring', 'loop_img': 'loop-images/loop.img', } + @staticmethod def set(key, value): Config.args[key] = value @@ -24,14 +25,16 @@ class Config: return None @staticmethod - def add_args(args: Dict[str, str]) -> argparse.ArgumentParser: + def add_args(args: Dict[str, str]) -> None: Config.args.update(args) + class Target: def __init__(self, argv, subparsers): self.argv = argv - self.parser = subparsers.add_parser(self.__class__.__name__.lower(), - help=self.__class__._help) + self.parser = subparsers.add_parser( + self.__class__.__name__.lower(), help=self.__class__._help + ) def set_args(self): """ @@ -50,33 +53,39 @@ class Target: function = getattr(self, args.action) function() -def ensure_outside_container(func) -> bool: + +def ensure_outside_container(func) -> Callable: def wrapper(*args, **kwargs): if not inside_container(): return func(*args, **kwargs) else: raise RuntimeError('This command should be ran outside a container') + return wrapper - + + def ensure_inside_container(func) -> bool: def wrapper(*args, **kwargs): if inside_container(): return func(*args, **kwargs) else: raise RuntimeError('This command should be ran inside a container') + return wrapper def run_shell_command(command: str, expect_error=False) -> str: if Config.get('verbose'): print(f'Running command: {command}') - process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process = subprocess.Popen( + command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) out = '' # let's read when output comes so it is in real time while True: # TODO: improve performance of this part, I think this part is a problem - pout = process.stdout.read(1).decode('latin1') + pout = process.stdout.read(1).decode('latin1') if pout == '' and process.poll() is not None: break if pout: @@ -87,7 +96,9 @@ def run_shell_command(command: str, expect_error=False) -> str: process.wait() # no last break line - err = process.stderr.read().decode().rstrip() # remove trailing whitespaces and new lines + err = ( + process.stderr.read().decode().rstrip() + ) # remove trailing whitespaces and new lines out = out.strip() if process.returncode != 0 and not expect_error: @@ -95,42 +106,57 @@ def run_shell_command(command: str, expect_error=False) -> str: sys.exit(1) return out + @ensure_inside_container def run_cephadm_shell_command(command: str, expect_error=False) -> str: config = Config.get('config') keyring = Config.get('keyring') with_cephadm_image = 'CEPHADM_IMAGE=quay.ceph.io/ceph-ci/ceph:master' - out = run_shell_command(f'{with_cephadm_image} cephadm --verbose shell --config {config} --keyring {keyring} -- {command}', expect_error) + out = run_shell_command( + f'{with_cephadm_image} cephadm --verbose shell --config {config} --keyring {keyring} -- {command}', + expect_error, + ) return out -def run_dc_shell_command(command: str, index: int, box_type: str, expect_error=False) -> str: - out = run_shell_command(f'docker-compose exec --index={index} {box_type} {command}', expect_error) + +def run_dc_shell_command( + command: str, index: int, box_type: str, expect_error=False +) -> str: + out = run_shell_command( + f'docker-compose exec --index={index} {box_type} {command}', expect_error + ) return out + def inside_container() -> bool: return os.path.exists('/.dockerenv') -@ensure_outside_container -def get_host_ips() -> List[List[str]]: - containers_info = get_boxes_container_info() - if Config.get('verbose'): - print(containers_info) - ips = [] - for container in containers_info: - if container[1][:len('box_hosts')] == 'box_hosts': - ips.append(container[0]) - return ips - -@ensure_outside_container -def get_boxes_container_info() -> List[List[str]]: - ips_query = "docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} %tab% {{.Name}} %tab% {{.Config.Hostname}}' $(docker ps -aq) | sed 's#%tab%#\t#g' | sed 's#/##g' | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n" - out = run_shell_command(ips_query) - info = [] - for line in out.split('\n'): - container = line.split() - if container[1].strip()[:4] == 'box_': - info.append(container) - return info - +@ensure_outside_container +def get_boxes_container_info(with_seed: bool = False) -> Dict[str, Any]: + # NOTE: this could be cached + IP = 0 + CONTAINER_NAME = 1 + HOSTNAME = 2 + ips_query = "docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} %tab% {{.Name}} %tab% {{.Config.Hostname}}' $(docker ps -aq) | sed 's#%tab%#\t#g' | sed 's#/##g' | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n" + out = run_shell_command(ips_query) + # FIXME: if things get more complex a class representing a container info might be useful, + # for now representing data this way is faster. + info = {'size': 0, 'ips': [], 'container_names': [], 'hostnames': []} + for line in out.split('\n'): + container = line.split() + # Most commands use hosts only + name_filter = 'box_' if with_seed else 'box_hosts' + if container[1].strip()[: len(name_filter)] == name_filter: + info['size'] += 1 + info['ips'].append(container[IP]) + info['container_names'].append(container[CONTAINER_NAME]) + info['hostnames'].append(container[HOSTNAME]) + return info + + +def get_orch_hosts(): + orch_host_ls_out = run_cephadm_shell_command('ceph orch host ls --format json') + hosts = json.loads(orch_host_ls_out) + return hosts diff --git a/ceph/src/cephadm/cephadm b/ceph/src/cephadm/cephadm index ca64ae472..2353e7b1e 100755 --- a/ceph/src/cephadm/cephadm +++ b/ceph/src/cephadm/cephadm @@ -26,7 +26,6 @@ import errno import struct import ssl from enum import Enum - from typing import Dict, List, Tuple, Optional, Union, Any, NoReturn, Callable, IO, Sequence, TypeVar, cast, Set, Iterable import re @@ -49,6 +48,8 @@ DEFAULT_IMAGE = 'quay.io/ceph/ceph:v17' DEFAULT_IMAGE_IS_MASTER = False DEFAULT_IMAGE_RELEASE = 'quincy' DEFAULT_PROMETHEUS_IMAGE = 'quay.io/prometheus/prometheus:v2.33.4' +DEFAULT_LOKI_IMAGE = 'docker.io/grafana/loki:2.4.0' +DEFAULT_PROMTAIL_IMAGE = 'docker.io/grafana/promtail:2.4.0' DEFAULT_NODE_EXPORTER_IMAGE = 'quay.io/prometheus/node-exporter:v1.3.1' DEFAULT_ALERT_MANAGER_IMAGE = 'quay.io/prometheus/alertmanager:v0.23.0' DEFAULT_GRAFANA_IMAGE = 'quay.io/ceph/ceph-grafana:8.3.5' @@ -63,8 +64,15 @@ DATA_DIR = '/var/lib/ceph' LOG_DIR = '/var/log/ceph' LOCK_DIR = '/run/cephadm' LOGROTATE_DIR = '/etc/logrotate.d' -SYSCTL_DIR = '/usr/lib/sysctl.d' +SYSCTL_DIR = '/etc/sysctl.d' UNIT_DIR = '/etc/systemd/system' +CEPH_CONF_DIR = 'config' +CEPH_CONF = 'ceph.conf' +CEPH_PUBKEY = 'ceph.pub' +CEPH_KEYRING = 'ceph.client.admin.keyring' +CEPH_DEFAULT_CONF = f'/etc/ceph/{CEPH_CONF}' +CEPH_DEFAULT_KEYRING = f'/etc/ceph/{CEPH_KEYRING}' +CEPH_DEFAULT_PUBKEY = f'/etc/ceph/{CEPH_PUBKEY}' LOG_DIR_MODE = 0o770 DATA_DIR_MODE = 0o700 CONTAINER_INIT = True @@ -73,8 +81,6 @@ CGROUPS_SPLIT_PODMAN_VERSION = (2, 1, 0) CUSTOM_PS1 = r'[ceph: \u@\h \W]\$ ' DEFAULT_TIMEOUT = None # in seconds DEFAULT_RETRY = 15 -SHELL_DEFAULT_CONF = '/etc/ceph/ceph.conf' -SHELL_DEFAULT_KEYRING = '/etc/ceph/ceph.client.admin.keyring' DATEFMT = '%Y-%m-%dT%H:%M:%S.%fZ' logger: logging.Logger = None # type: ignore @@ -104,6 +110,42 @@ cached_stdin = None ################################## +class EndPoint: + """EndPoint representing an ip:port format""" + + def __init__(self, ip: str, port: int) -> None: + self.ip = ip + self.port = port + + def __str__(self) -> str: + return f'{self.ip}:{self.port}' + + def __repr__(self) -> str: + return f'{self.ip}:{self.port}' + + +class ContainerInfo: + def __init__(self, container_id: str, + image_name: str, + image_id: str, + start: str, + version: str) -> None: + self.container_id = container_id + self.image_name = image_name + self.image_id = image_id + self.start = start + self.version = version + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, ContainerInfo): + return NotImplemented + return (self.container_id == other.container_id + and self.image_name == other.image_name + and self.image_id == other.image_id + and self.start == other.start + and self.version == other.version) + + class BaseConfig: def __init__(self) -> None: @@ -207,7 +249,9 @@ class Docker(ContainerEngine): CONTAINER_PREFERENCE = (Podman, Docker) # prefer podman to docker -# Log and console output config +# During normal cephadm operations (cephadm ls, gather-facts, etc ) we use: +# stdout: for JSON output only +# stderr: for error, debug, info, etc logging_config = { 'version': 1, 'disable_existing_loggers': True, @@ -237,6 +281,56 @@ logging_config = { } +class ExcludeErrorsFilter(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + """Only lets through log messages with log level below WARNING .""" + return record.levelno < logging.WARNING + + +# When cephadm is used as standard binary (bootstrap, rm-cluster, etc) we use: +# stdout: for debug and info +# stderr: for errors and warnings +interactive_logging_config = { + 'version': 1, + 'filters': { + 'exclude_errors': { + '()': ExcludeErrorsFilter + } + }, + 'disable_existing_loggers': True, + 'formatters': { + 'cephadm': { + 'format': '%(asctime)s %(thread)x %(levelname)s %(message)s' + }, + }, + 'handlers': { + 'console_stdout': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'filters': ['exclude_errors'], + 'stream': sys.stdout + }, + 'console_stderr': { + 'level': 'WARNING', + 'class': 'logging.StreamHandler', + 'stream': sys.stderr + }, + 'log_file': { + 'level': 'DEBUG', + 'class': 'logging.handlers.WatchedFileHandler', + 'formatter': 'cephadm', + 'filename': '%s/cephadm.log' % LOG_DIR, + } + }, + 'loggers': { + '': { + 'level': 'DEBUG', + 'handlers': ['console_stdout', 'console_stderr', 'log_file'], + } + } +} + + class termcolor: yellow = '\033[93m' red = '\033[31m' @@ -250,11 +344,15 @@ class Error(Exception): class TimeoutExpired(Error): pass + +class UnauthorizedRegistryError(Error): + pass + ################################## class Ceph(object): - daemons = ('mon', 'mgr', 'mds', 'osd', 'rgw', 'rbd-mirror', + daemons = ('mon', 'mgr', 'osd', 'mds', 'rgw', 'rbd-mirror', 'crash', 'cephfs-mirror') ################################## @@ -426,6 +524,8 @@ class Monitoring(object): 'node-exporter': [9100], 'grafana': [3000], 'alertmanager': [9093, 9094], + 'loki': [3100], + 'promtail': [9080] } components = { @@ -441,6 +541,28 @@ class Monitoring(object): 'prometheus.yml', ], }, + 'loki': { + 'image': DEFAULT_LOKI_IMAGE, + 'cpus': '1', + 'memory': '1GB', + 'args': [ + '--config.file=/etc/loki/loki.yml', + ], + 'config-json-files': [ + 'loki.yml' + ], + }, + 'promtail': { + 'image': DEFAULT_PROMTAIL_IMAGE, + 'cpus': '1', + 'memory': '1GB', + 'args': [ + '--config.file=/etc/promtail/promtail.yml', + ], + 'config-json-files': [ + 'promtail.yml', + ], + }, 'node-exporter': { 'image': DEFAULT_NODE_EXPORTER_IMAGE, 'cpus': '1', @@ -481,9 +603,9 @@ class Monitoring(object): def get_version(ctx, container_id, daemon_type): # type: (CephadmContext, str, str) -> str """ - :param: daemon_type Either "prometheus", "alertmanager" or "node-exporter" + :param: daemon_type Either "prometheus", "alertmanager", "loki", "promtail" or "node-exporter" """ - assert daemon_type in ('prometheus', 'alertmanager', 'node-exporter') + assert daemon_type in ('prometheus', 'alertmanager', 'node-exporter', 'loki', 'promtail') cmd = daemon_type.replace('-', '_') code = -1 err = '' @@ -580,7 +702,7 @@ class NFSGanesha(object): def get_container_envs(): # type: () -> List[str] envs = [ - 'CEPH_CONF=%s' % ('/etc/ceph/ceph.conf') + 'CEPH_CONF=%s' % (CEPH_DEFAULT_CONF) ] return envs @@ -1204,16 +1326,17 @@ def port_in_use(ctx, port_num): )) -def check_ip_port(ctx, ip, port): - # type: (CephadmContext, str, int) -> None +def check_ip_port(ctx, ep): + # type: (CephadmContext, EndPoint) -> None if not ctx.skip_ping_check: - logger.info('Verifying IP %s port %d ...' % (ip, port)) - if is_ipv6(ip): + logger.info(f'Verifying IP {ep.ip} port {ep.port} ...') + if is_ipv6(ep.ip): s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - ip = unwrap_ipv6(ip) + ip = unwrap_ipv6(ep.ip) else: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - attempt_bind(ctx, s, ip, port) + ip = ep.ip + attempt_bind(ctx, s, ip, ep.port) ################################## @@ -1852,7 +1975,7 @@ def infer_fsid(func: FuncT) -> FuncT: logger.info('Inferring fsid %s' % fsids[0]) ctx.fsid = fsids[0] else: - raise Error('Cannot infer an fsid, one must be specified: %s' % fsids) + raise Error('Cannot infer an fsid, one must be specified (using --fsid): %s' % fsids) return func(ctx) return cast(FuncT, _infer_fsid) @@ -1860,29 +1983,54 @@ def infer_fsid(func: FuncT) -> FuncT: def infer_config(func: FuncT) -> FuncT: """ - If we find a MON daemon, use the config from that container + Infer the clusater configuration using the followign priority order: + 1- if the user has provided custom conf file (-c option) use it + 2- otherwise if daemon --name has been provided use daemon conf + 3- otherwise find the mon daemon conf file and use it (if v1) + 4- otherwise if {ctx.data_dir}/{fsid}/{CEPH_CONF_DIR} dir exists use it + 5- finally: fallback to the default file /etc/ceph/ceph.conf """ @wraps(func) def _infer_config(ctx: CephadmContext) -> Any: + + def config_path(daemon_type: str, daemon_name: str) -> str: + data_dir = get_data_dir(ctx.fsid, ctx.data_dir, daemon_type, daemon_name) + return os.path.join(data_dir, 'config') + + def get_mon_daemon_name(fsid: str) -> Optional[str]: + daemon_list = list_daemons(ctx, detail=False) + for daemon in daemon_list: + if ( + daemon.get('name', '').startswith('mon.') + and daemon.get('fsid', '') == fsid + and daemon.get('style', '') == 'cephadm:v1' + and os.path.exists(config_path('mon', daemon['name'].split('.', 1)[1])) + ): + return daemon['name'] + return None + ctx.config = ctx.config if 'config' in ctx else None - if ctx.config: - logger.debug('Using specified config: %s' % ctx.config) + # check if user has provided conf by using -c option + if ctx.config and (ctx.config != CEPH_DEFAULT_CONF): + logger.debug(f'Using specified config: {ctx.config}') return func(ctx) + if 'fsid' in ctx and ctx.fsid: - name = ctx.name if 'name' in ctx else None - if not name: - daemon_list = list_daemons(ctx, detail=False) - for daemon in daemon_list: - if daemon.get('name', '').startswith('mon.') and daemon.get('fsid', '') == ctx.fsid: - name = daemon['name'] - break - if name: - ctx.config = f'/var/lib/ceph/{ctx.fsid}/{name}/config' + name = ctx.name if ('name' in ctx and ctx.name) else get_mon_daemon_name(ctx.fsid) + if name is not None: + # daemon name has been specified (or inffered from mon), let's use its conf + ctx.config = config_path(name.split('.', 1)[0], name.split('.', 1)[1]) + else: + # no daemon, in case the cluster has a config dir then use it + ceph_conf = f'{ctx.data_dir}/{ctx.fsid}/{CEPH_CONF_DIR}/{CEPH_CONF}' + if os.path.exists(ceph_conf): + ctx.config = ceph_conf + if ctx.config: - logger.info('Inferring config %s' % ctx.config) - elif os.path.exists(SHELL_DEFAULT_CONF): - logger.debug('Using default config: %s' % SHELL_DEFAULT_CONF) - ctx.config = SHELL_DEFAULT_CONF + logger.info(f'Inferring config {ctx.config}') + elif os.path.exists(CEPH_DEFAULT_CONF): + logger.debug(f'Using default config {CEPH_DEFAULT_CONF}') + ctx.config = CEPH_DEFAULT_CONF return func(ctx) return cast(FuncT, _infer_config) @@ -1908,7 +2056,7 @@ def infer_image(func: FuncT) -> FuncT: if not ctx.image: ctx.image = os.environ.get('CEPHADM_IMAGE') if not ctx.image: - ctx.image = get_last_local_ceph_image(ctx, ctx.container_engine.path) + ctx.image = infer_local_ceph_image(ctx, ctx.container_engine.path) if not ctx.image: ctx.image = _get_default_image(ctx) return func(ctx) @@ -1940,24 +2088,70 @@ def default_image(func: FuncT) -> FuncT: return cast(FuncT, _default_image) -def get_last_local_ceph_image(ctx: CephadmContext, container_path: str) -> Optional[str]: +def get_container_info(ctx: CephadmContext, daemon_filter: str, by_name: bool) -> Optional[ContainerInfo]: + """ + :param ctx: Cephadm context + :param daemon_filter: daemon name or type + :param by_name: must be set to True if daemon name is provided + :return: Container information or None """ + def daemon_name_or_type(daemon: Dict[str, str]) -> str: + return daemon['name'] if by_name else daemon['name'].split('.', 1)[0] + + if by_name and '.' not in daemon_filter: + logger.warning(f'Trying to get container info using invalid daemon name {daemon_filter}') + return None + daemons = list_daemons(ctx, detail=False) + matching_daemons = [d for d in daemons if daemon_name_or_type(d) == daemon_filter and d['fsid'] == ctx.fsid] + if matching_daemons: + d_type, d_id = matching_daemons[0]['name'].split('.', 1) + out, _, code = get_container_stats(ctx, ctx.container_engine.path, ctx.fsid, d_type, d_id) + if not code: + (container_id, image_name, image_id, start, version) = out.strip().split(',') + return ContainerInfo(container_id, image_name, image_id, start, version) + return None + + +def infer_local_ceph_image(ctx: CephadmContext, container_path: str) -> Optional[str]: + """ + Infer the local ceph image based on the following priority criteria: + 1- the image specified by --image arg (if provided). + 2- the same image as the daemon container specified by --name arg (if provided). + 3- image used by any ceph container running on the host. In this case we use daemon types. + 4- if no container is found then we use the most ceph recent image on the host. + + Note: any selected container must have the same fsid inferred previously. + :return: The most recent local ceph image (already pulled) """ + # '|' special character is used to separate the output fields into: + # - Repository@digest + # - Image Id + # - Image Tag + # - Image creation date out, _, _ = call_throws(ctx, [container_path, 'images', '--filter', 'label=ceph=True', '--filter', 'dangling=false', - '--format', '{{.Repository}}@{{.Digest}}']) - return _filter_last_local_ceph_image(out) - + '--format', '{{.Repository}}@{{.Digest}}|{{.ID}}|{{.Tag}}|{{.CreatedAt}}']) + + container_info = None + daemon_name = ctx.name if ('name' in ctx and ctx.name and '.' in ctx.name) else None + daemons_ls = [daemon_name] if daemon_name is not None else Ceph.daemons # daemon types: 'mon', 'mgr', etc + for daemon in daemons_ls: + container_info = get_container_info(ctx, daemon, daemon_name is not None) + if container_info is not None: + logger.debug(f"Using container info for daemon '{daemon}'") + break -def _filter_last_local_ceph_image(out): - # type: (str) -> Optional[str] for image in out.splitlines(): - if image and not image.endswith('@'): - logger.info('Using recent ceph image %s' % image) - return image + if image and not image.isspace(): + (digest, image_id, tag, created_date) = image.lstrip().split('|') + if container_info is not None and image_id not in container_info.image_id: + continue + if digest and not digest.endswith('@'): + logger.info(f"Using ceph image with id '{image_id}' and tag '{tag}' created on {created_date}\n{digest}") + return digest return None @@ -2098,6 +2292,13 @@ def move_files(ctx, src, dst, uid=None, gid=None): os.chown(dst_file, uid, gid) +def recursive_chown(path: str, uid: int, gid: int) -> None: + for dirpath, dirnames, filenames in os.walk(path): + os.chown(dirpath, uid, gid) + for filename in filenames: + os.chown(os.path.join(dirpath, filename), uid, gid) + + # copied from distutils def find_executable(executable: str, path: Optional[str] = None) -> Optional[str]: """Tries to find 'executable' in the directories listed in 'path'. @@ -2333,7 +2534,7 @@ def get_daemon_args(ctx, fsid, daemon_type, daemon_id): metadata = Monitoring.components[daemon_type] r += metadata.get('args', list()) # set ip and port to bind to for nodeexporter,alertmanager,prometheus - if daemon_type != 'grafana': + if daemon_type not in ['grafana', 'loki', 'promtail']: ip = '' port = Monitoring.port_map[daemon_type][0] if 'meta_json' in ctx and ctx.meta_json: @@ -2343,6 +2544,10 @@ def get_daemon_args(ctx, fsid, daemon_type, daemon_id): if 'ports' in meta and meta['ports']: port = meta['ports'][0] r += [f'--web.listen-address={ip}:{port}'] + if daemon_type == 'prometheus': + scheme = 'http' + host = get_fqdn() + r += [f'--web.external-url={scheme}://{host}:{port}'] if daemon_type == 'alertmanager': config = get_parm(ctx.config_json) peers = config.get('peers', list()) # type: ignore @@ -2350,6 +2555,14 @@ def get_daemon_args(ctx, fsid, daemon_type, daemon_id): r += ['--cluster.peer={}'.format(peer)] # some alertmanager, by default, look elsewhere for a config r += ['--config.file=/etc/alertmanager/alertmanager.yml'] + if daemon_type == 'loki': + r += ['--config.file=/etc/loki/loki.yml'] + if daemon_type == 'promtail': + r += ['--config.file=/etc/promtail/promtail.yml'] + if daemon_type == 'node-exporter': + r += ['--path.procfs=/host/proc', + '--path.sysfs=/host/sys', + '--path.rootfs=/rootfs'] elif daemon_type == NFSGanesha.daemon_type: nfs_ganesha = NFSGanesha.init(ctx, fsid, daemon_id) r += nfs_ganesha.get_daemon_args() @@ -2403,6 +2616,8 @@ def create_daemon_dirs(ctx, fsid, daemon_type, daemon_id, uid, gid, makedirs(os.path.join(data_dir_root, config_dir), uid, gid, 0o755) makedirs(os.path.join(data_dir_root, config_dir, 'alerting'), uid, gid, 0o755) makedirs(os.path.join(data_dir_root, 'data'), uid, gid, 0o755) + recursive_chown(os.path.join(data_dir_root, 'etc'), uid, gid) + recursive_chown(os.path.join(data_dir_root, 'data'), uid, gid) elif daemon_type == 'grafana': data_dir_root = get_data_dir(fsid, ctx.data_dir, daemon_type, daemon_id) @@ -2418,6 +2633,18 @@ def create_daemon_dirs(ctx, fsid, daemon_type, daemon_id, uid, gid, config_dir = 'etc/alertmanager' makedirs(os.path.join(data_dir_root, config_dir), uid, gid, 0o755) makedirs(os.path.join(data_dir_root, config_dir, 'data'), uid, gid, 0o755) + elif daemon_type == 'promtail': + data_dir_root = get_data_dir(fsid, ctx.data_dir, + daemon_type, daemon_id) + config_dir = 'etc/promtail' + makedirs(os.path.join(data_dir_root, config_dir), uid, gid, 0o755) + makedirs(os.path.join(data_dir_root, 'data'), uid, gid, 0o755) + elif daemon_type == 'loki': + data_dir_root = get_data_dir(fsid, ctx.data_dir, + daemon_type, daemon_id) + config_dir = 'etc/loki' + makedirs(os.path.join(data_dir_root, config_dir), uid, gid, 0o755) + makedirs(os.path.join(data_dir_root, 'data'), uid, gid, 0o755) # populate the config directory for the component from the config-json if 'files' in config_json: @@ -2603,9 +2830,17 @@ def get_container_mounts(ctx, fsid, daemon_type, daemon_id, if daemon_type in Monitoring.components and daemon_id: data_dir = get_data_dir(fsid, ctx.data_dir, daemon_type, daemon_id) + log_dir = get_log_dir(fsid, ctx.log_dir) if daemon_type == 'prometheus': mounts[os.path.join(data_dir, 'etc/prometheus')] = '/etc/prometheus:Z' mounts[os.path.join(data_dir, 'data')] = '/prometheus:Z' + elif daemon_type == 'loki': + mounts[os.path.join(data_dir, 'etc/loki')] = '/etc/loki:Z' + mounts[os.path.join(data_dir, 'data')] = '/loki:Z' + elif daemon_type == 'promtail': + mounts[os.path.join(data_dir, 'etc/promtail')] = '/etc/promtail:Z' + mounts[log_dir] = '/var/log/ceph:z' + mounts[os.path.join(data_dir, 'data')] = '/promtail:Z' elif daemon_type == 'node-exporter': mounts['/proc'] = '/host/proc:ro' mounts['/sys'] = '/host/sys:ro' @@ -2744,6 +2979,11 @@ def get_container(ctx: CephadmContext, # by ubuntu 18.04 kernel!) ] container_args.extend(monitoring_args) + if daemon_type == 'node-exporter': + # in order to support setting '--path.procfs=/host/proc','--path.sysfs=/host/sys', + # '--path.rootfs=/rootfs' for node-exporter we need to disable selinux separation + # between the node-exporter container and the host to avoid selinux denials + container_args.extend(['--security-opt', 'label=disable']) elif daemon_type == 'crash': ceph_args = ['-n', name] elif daemon_type in Ceph.daemons: @@ -3048,6 +3288,16 @@ def deploy_daemon_units( bind_mounts=get_container_binds(ctx, fsid, daemon_type, daemon_id), cname='ceph-%s-%s.%s-activate' % (fsid, daemon_type, daemon_id), ) + if 'cluster' in ctx and ctx.cluster: + # ctx.cluster is only set during adoption of a daemon from a cluster + # with a custom name (not "ceph"). The initial activate command the first + # time we start the new cephadm based systemd unit for this osd must account + # for this by mounting to the correct data dir in the container. Otherwise + # necessary files from the old data dir of the daemon won't be copied over + # to the new data dir on the host. After the first start (e.g. on any redeploys) + # this is no longer necessary as we will have these files in the data dir on the host + if data_dir in prestart.volume_mounts: + prestart.volume_mounts[data_dir] = f'/var/lib/ceph/osd/{ctx.cluster}-{daemon_id}' _write_container_cmd_to_bash(ctx, f, prestart, 'LVM OSDs use ceph-volume lvm activate') elif daemon_type == CephIscsi.daemon_type: f.write(' '.join(CephIscsi.configfs_mount_umount(data_dir, mount=True)) + '\n') @@ -3104,8 +3354,12 @@ def deploy_daemon_units( # post-stop command(s) with open(data_dir + '/unit.stop.new', 'w') as f: - f.write('! ' + ' '.join(c.stop_cmd()) + '\n') - f.write('! ' + ' '.join(c.stop_cmd(old_cname=True)) + '\n') + # following generated script basically checks if the container exists + # before stopping it. Exit code will be success either if it doesn't + # exist or if it exists and is stopped successfully. + container_exists = f'{ctx.container_engine.path} inspect %s &>/dev/null' + f.write(f'! {container_exists % c.old_cname} || {" ".join(c.stop_cmd(old_cname=True))} \n') + f.write(f'! {container_exists % c.cname} || {" ".join(c.stop_cmd())} \n') os.fchmod(f.fileno(), 0o600) os.rename(data_dir + '/unit.stop.new', @@ -3251,9 +3505,10 @@ class Firewalld(object): def update_firewalld(ctx, daemon_type): # type: (CephadmContext, str) -> None - firewall = Firewalld(ctx) - firewall.enable_service_for(daemon_type) - firewall.apply_rules() + if not ('skip_firewalld' in ctx and ctx.skip_firewalld): + firewall = Firewalld(ctx) + firewall.enable_service_for(daemon_type) + firewall.apply_rules() def install_sysctl(ctx: CephadmContext, fsid: str, daemon_type: str) -> None: @@ -3287,6 +3542,47 @@ def install_sysctl(ctx: CephadmContext, fsid: str, daemon_type: str) -> None: call_throws(ctx, ['sysctl', '--system']) +def migrate_sysctl_dir(ctx: CephadmContext, fsid: str) -> None: + """ + Cephadm once used '/usr/lib/sysctl.d' for storing sysctl configuration. + This moves it to '/etc/sysctl.d'. + """ + deprecated_location: str = '/usr/lib/sysctl.d' + deprecated_confs: List[str] = glob(f'{deprecated_location}/90-ceph-{fsid}-*.conf') + if not deprecated_confs: + return + + file_count: int = len(deprecated_confs) + logger.info(f'Found sysctl {file_count} files in deprecated location {deprecated_location}. Starting Migration.') + for conf in deprecated_confs: + try: + shutil.move(conf, ctx.sysctl_dir) + file_count -= 1 + except shutil.Error as err: + if str(err).endswith('already exists'): + logger.warning(f'Destination file already exists. Deleting {conf}.') + try: + os.unlink(conf) + file_count -= 1 + except OSError as del_err: + logger.warning(f'Could not remove {conf}: {del_err}.') + else: + logger.warning(f'Could not move {conf} from {deprecated_location} to {ctx.sysctl_dir}: {err}') + + # Log successful migration + if file_count == 0: + logger.info(f'Successfully migrated sysctl config to {ctx.sysctl_dir}.') + return + + # Log partially successful / unsuccessful migration + files_processed: int = len(deprecated_confs) + if file_count < files_processed: + status: str = f'partially successful (failed {file_count}/{files_processed})' + elif file_count == files_processed: + status = 'unsuccessful' + logger.warning(f'Migration of sysctl configuration {status}. You may want to perform a migration manually.') + + def install_base_units(ctx, fsid): # type: (CephadmContext, str) -> None """ @@ -3389,7 +3685,7 @@ LimitNOFILE=1048576 LimitNPROC=1048576 EnvironmentFile=-/etc/environment ExecStart=/bin/bash {data_dir}/{fsid}/%i/unit.run -ExecStop=-/bin/bash -c '{container_path} stop ceph-{fsid}-%i ; bash {data_dir}/{fsid}/%i/unit.stop' +ExecStop=-/bin/bash -c 'bash {data_dir}/{fsid}/%i/unit.stop' ExecStopPost=-/bin/bash {data_dir}/{fsid}/%i/unit.poststop KillMode=none Restart=on-failure @@ -3401,8 +3697,7 @@ StartLimitBurst=5 {extra_args} [Install] WantedBy=ceph-{fsid}.target -""".format(container_path=ctx.container_engine.path, - fsid=fsid, +""".format(fsid=fsid, data_dir=ctx.data_dir, extra_args=extra_args, # if docker, we depend on docker.service @@ -4169,11 +4464,16 @@ def command_version(ctx): ################################## -@infer_image +@default_image def command_pull(ctx): # type: (CephadmContext) -> int - _pull_image(ctx, ctx.image, ctx.insecure) + try: + _pull_image(ctx, ctx.image, ctx.insecure) + except UnauthorizedRegistryError: + err_str = 'Failed to pull container image. Check that host(s) are logged into the registry' + logger.debug(f'Pulling image for `command_pull` failed: {err_str}') + raise Error(err_str) return command_inspect_image(ctx) @@ -4201,6 +4501,9 @@ def _pull_image(ctx, image, insecure=False): if not ret: return + if 'unauthorized' in err: + raise UnauthorizedRegistryError() + if not any(pattern in err for pattern in ignorelist): raise Error('Failed command: %s' % cmd_str) @@ -4285,6 +4588,7 @@ def check_subnet(subnets: str) -> Tuple[int, List[int], str]: subnet_list = subnets.split(',') for subnet in subnet_list: # ensure the format of the string is as expected address/netmask + subnet = subnet.strip() if not re.search(r'\/\d+$', subnet): rc = 1 errors.append(f'{subnet} is not in CIDR format (address/netmask)') @@ -4331,93 +4635,190 @@ def is_ipv6(address): return False -def prepare_mon_addresses( - ctx: CephadmContext -) -> Tuple[str, bool, Optional[str]]: +def ip_in_subnets(ip_addr: str, subnets: str) -> bool: + """Determine if the ip_addr belongs to any of the subnets list.""" + subnet_list = [x.strip() for x in subnets.split(',')] + for subnet in subnet_list: + ip_address = unwrap_ipv6(ip_addr) if is_ipv6(ip_addr) else ip_addr + if ipaddress.ip_address(ip_address) in ipaddress.ip_network(subnet): + return True + return False + + +def parse_mon_addrv(addrv_arg: str) -> List[EndPoint]: + """Parse mon-addrv param into a list of mon end points.""" r = re.compile(r':(\d+)$') - base_ip = '' + addrv_args = [] + addr_arg = addrv_arg + if addr_arg[0] != '[' or addr_arg[-1] != ']': + raise Error(f'--mon-addrv value {addr_arg} must use square backets') + + for addr in addr_arg[1: -1].split(','): + hasport = r.findall(addr) + if not hasport: + raise Error(f'--mon-addrv value {addr_arg} must include port number') + port_str = hasport[0] + addr = re.sub(r'^v\d+:', '', addr) # strip off v1: or v2: prefix + base_ip = addr[0:-(len(port_str)) - 1] + addrv_args.append(EndPoint(base_ip, int(port_str))) + + return addrv_args + + +def parse_mon_ip(mon_ip: str) -> List[EndPoint]: + """Parse mon-ip param into a list of mon end points.""" + r = re.compile(r':(\d+)$') + addrv_args = [] + hasport = r.findall(mon_ip) + if hasport: + port_str = hasport[0] + base_ip = mon_ip[0:-(len(port_str)) - 1] + addrv_args.append(EndPoint(base_ip, int(port_str))) + else: + # No port provided: use fixed ports for ceph monitor + addrv_args.append(EndPoint(mon_ip, 3300)) + addrv_args.append(EndPoint(mon_ip, 6789)) + + return addrv_args + + +def build_addrv_params(addrv: List[EndPoint]) -> str: + """Convert mon end-points (ip:port) into the format: [v[1|2]:ip:port1]""" + if len(addrv) > 2: + raise Error('Detected a local mon-addrv list with more than 2 entries.') + port_to_ver: Dict[int, str] = {6789: 'v1', 3300: 'v2'} + addr_arg_list: List[str] = [] + for ep in addrv: + if ep.port in port_to_ver: + ver = port_to_ver[ep.port] + else: + ver = 'v2' # default mon protocol version if port is not provided + logger.warning(f'Using msgr2 protocol for unrecognized port {ep}') + addr_arg_list.append(f'{ver}:{ep.ip}:{ep.port}') + + addr_arg = '[{0}]'.format(','.join(addr_arg_list)) + return addr_arg + + +def get_public_net_from_cfg(ctx: CephadmContext) -> Optional[str]: + """Get mon public network from configuration file.""" + cp = read_config(ctx.config) + if not cp.has_option('global', 'public_network'): + return None + + # Ensure all public CIDR networks are valid + public_network = cp.get('global', 'public_network') + rc, _, err_msg = check_subnet(public_network) + if rc: + raise Error(f'Invalid public_network {public_network} parameter: {err_msg}') + + # Ensure all public CIDR networks are configured locally + configured_subnets = set([x.strip() for x in public_network.split(',')]) + local_subnets = set([x[0] for x in list_networks(ctx).items()]) + valid_public_net = False + for net in configured_subnets: + if net in local_subnets: + valid_public_net = True + else: + logger.warning(f'The public CIDR network {net} (from -c conf file) is not configured locally.') + if not valid_public_net: + raise Error(f'None of the public CIDR network(s) {configured_subnets} (from -c conf file) is configured locally.') + + # Ensure public_network is compatible with the provided mon-ip (or mon-addrv) + if ctx.mon_ip: + if not ip_in_subnets(ctx.mon_ip, public_network): + raise Error(f'The provided --mon-ip {ctx.mon_ip} does not belong to any public_network(s) {public_network}') + elif ctx.mon_addrv: + addrv_args = parse_mon_addrv(ctx.mon_addrv) + for addrv in addrv_args: + if not ip_in_subnets(addrv.ip, public_network): + raise Error(f'The provided --mon-addrv {addrv.ip} ip does not belong to any public_network(s) {public_network}') + + logger.debug(f'Using mon public network from configuration file {public_network}') + return public_network + + +def infer_mon_network(ctx: CephadmContext, mon_eps: List[EndPoint]) -> Optional[str]: + """Infer mon public network from local network.""" + # Make sure IP is configured locally, and then figure out the CIDR network + mon_networks = [] + for net, ifaces in list_networks(ctx).items(): + # build local_ips list for the specified network + local_ips: List[str] = [] + for _, ls in ifaces.items(): + local_ips.extend([ipaddress.ip_address(ip) for ip in ls]) + + # check if any of mon ips belong to this net + for mon_ep in mon_eps: + try: + if ipaddress.ip_address(unwrap_ipv6(mon_ep.ip)) in local_ips: + mon_networks.append(net) + logger.info(f'Mon IP `{mon_ep.ip}` is in CIDR network `{net}`') + except ValueError as e: + logger.warning(f'Cannot infer CIDR network for mon IP `{mon_ep.ip}` : {e}') + + if not mon_networks: + raise Error('Cannot infer CIDR network. Pass --skip-mon-network to configure it later') + else: + logger.debug(f'Inferred mon public CIDR from local network configuration {mon_networks}') + + mon_networks = list(set(mon_networks)) # remove duplicates + return ','.join(mon_networks) + + +def prepare_mon_addresses(ctx: CephadmContext) -> Tuple[str, bool, Optional[str]]: + """Get mon public network configuration.""" ipv6 = False + addrv_args: List[EndPoint] = [] + mon_addrv: str = '' # i.e: [v2:192.168.100.1:3300,v1:192.168.100.1:6789] if ctx.mon_ip: ipv6 = is_ipv6(ctx.mon_ip) if ipv6: ctx.mon_ip = wrap_ipv6(ctx.mon_ip) - hasport = r.findall(ctx.mon_ip) - if hasport: - port_str = hasport[0] - port = int(port_str) - if port == 6789: - addr_arg = '[v1:%s]' % ctx.mon_ip - elif port == 3300: - addr_arg = '[v2:%s]' % ctx.mon_ip - else: - logger.warning('Using msgr2 protocol for unrecognized port %d' % - port) - addr_arg = '[v2:%s]' % ctx.mon_ip - base_ip = ctx.mon_ip[0:-(len(port_str)) - 1] - check_ip_port(ctx, base_ip, port) - else: - base_ip = ctx.mon_ip - addr_arg = '[v2:%s:3300,v1:%s:6789]' % (ctx.mon_ip, ctx.mon_ip) - check_ip_port(ctx, ctx.mon_ip, 3300) - check_ip_port(ctx, ctx.mon_ip, 6789) + addrv_args = parse_mon_ip(ctx.mon_ip) + mon_addrv = build_addrv_params(addrv_args) elif ctx.mon_addrv: - addr_arg = ctx.mon_addrv - if addr_arg[0] != '[' or addr_arg[-1] != ']': - raise Error('--mon-addrv value %s must use square backets' % - addr_arg) - ipv6 = addr_arg.count('[') > 1 - for addr in addr_arg[1: -1].split(','): - hasport = r.findall(addr) - if not hasport: - raise Error('--mon-addrv value %s must include port number' % - addr_arg) - port_str = hasport[0] - port = int(port_str) - # strip off v1: or v2: prefix - addr = re.sub(r'^v\d+:', '', addr) - base_ip = addr[0:-(len(port_str)) - 1] - check_ip_port(ctx, base_ip, port) + ipv6 = ctx.mon_addrv.count('[') > 1 + addrv_args = parse_mon_addrv(ctx.mon_addrv) + mon_addrv = ctx.mon_addrv else: raise Error('must specify --mon-ip or --mon-addrv') - logger.debug('Base mon IP is %s, final addrv is %s' % (base_ip, addr_arg)) + if addrv_args: + for end_point in addrv_args: + check_ip_port(ctx, end_point) + + logger.debug(f'Base mon IP(s) is {addrv_args}, mon addrv is {mon_addrv}') mon_network = None if not ctx.skip_mon_network: - # make sure IP is configured locally, and then figure out the - # CIDR network - errmsg = f'Cannot infer CIDR network for mon IP `{base_ip}`' - for net, ifaces in list_networks(ctx).items(): - ips: List[str] = [] - for iface, ls in ifaces.items(): - ips.extend(ls) - try: - if ipaddress.ip_address(unwrap_ipv6(base_ip)) in \ - [ipaddress.ip_address(ip) for ip in ips]: - mon_network = net - logger.info(f'Mon IP `{base_ip}` is in CIDR network `{mon_network}`') - break - except ValueError as e: - logger.warning(f'{errmsg}: {e}') - if not mon_network: - raise Error(f'{errmsg}: pass --skip-mon-network to configure it later') + mon_network = get_public_net_from_cfg(ctx) or infer_mon_network(ctx, addrv_args) - return (addr_arg, ipv6, mon_network) + return (mon_addrv, ipv6, mon_network) def prepare_cluster_network(ctx: CephadmContext) -> Tuple[str, bool]: - cluster_network = '' - ipv6_cluster_network = False # the cluster network may not exist on this node, so all we can do is # validate that the address given is valid ipv4 or ipv6 subnet - if ctx.cluster_network: - rc, versions, err_msg = check_subnet(ctx.cluster_network) + ipv6_cluster_network = False + cp = read_config(ctx.config) + cluster_network = ctx.cluster_network + if cluster_network is None and cp.has_option('global', 'cluster_network'): + cluster_network = cp.get('global', 'cluster_network') + + if cluster_network: + cluser_nets = set([x.strip() for x in cluster_network.split(',')]) + local_subnets = set([x[0] for x in list_networks(ctx).items()]) + for net in cluser_nets: + if net not in local_subnets: + logger.warning(f'The cluster CIDR network {net} is not configured locally.') + + rc, versions, err_msg = check_subnet(cluster_network) if rc: raise Error(f'Invalid --cluster-network parameter: {err_msg}') - cluster_network = ctx.cluster_network ipv6_cluster_network = True if 6 in versions else False else: - logger.info('- internal network (--cluster-network) has not ' + logger.info('Internal network (--cluster-network) has not ' 'been provided, OSD replication will default to ' 'the public_network') @@ -4637,44 +5038,16 @@ def prepare_ssh( } cli(['cephadm', 'set-priv-key', '-i', '/tmp/cephadm-ssh-key'], extra_mounts=mounts) cli(['cephadm', 'set-pub-key', '-i', '/tmp/cephadm-ssh-key.pub'], extra_mounts=mounts) + ssh_pub = cli(['cephadm', 'get-pub-key']) else: logger.info('Generating ssh key...') cli(['cephadm', 'generate-key']) ssh_pub = cli(['cephadm', 'get-pub-key']) - with open(ctx.output_pub_ssh_key, 'w') as f: f.write(ssh_pub) logger.info('Wrote public SSH key to %s' % ctx.output_pub_ssh_key) - logger.info('Adding key to %s@localhost authorized_keys...' % ctx.ssh_user) - try: - s_pwd = pwd.getpwnam(ctx.ssh_user) - except KeyError: - raise Error('Cannot find uid/gid for ssh-user: %s' % (ctx.ssh_user)) - ssh_uid = s_pwd.pw_uid - ssh_gid = s_pwd.pw_gid - ssh_dir = os.path.join(s_pwd.pw_dir, '.ssh') - - if not os.path.exists(ssh_dir): - makedirs(ssh_dir, ssh_uid, ssh_gid, 0o700) - - auth_keys_file = '%s/authorized_keys' % ssh_dir - add_newline = False - - if os.path.exists(auth_keys_file): - with open(auth_keys_file, 'r') as f: - f.seek(0, os.SEEK_END) - if f.tell() > 0: - f.seek(f.tell() - 1, os.SEEK_SET) # go to last char - if f.read() != '\n': - add_newline = True - - with open(auth_keys_file, 'a') as f: - os.fchown(f.fileno(), ssh_uid, ssh_gid) # just in case we created it - os.fchmod(f.fileno(), 0o600) # just in case we created it - if add_newline: - f.write('\n') - f.write(ssh_pub.strip() + '\n') + authorize_ssh_key(ssh_pub, ctx.ssh_user) host = get_hostname() logger.info('Adding host %s...' % host) @@ -4682,6 +5055,9 @@ def prepare_ssh( args = ['orch', 'host', 'add', host] if ctx.mon_ip: args.append(unwrap_ipv6(ctx.mon_ip)) + elif ctx.mon_addrv: + addrv_args = parse_mon_addrv(ctx.mon_addrv) + args.append(unwrap_ipv6(addrv_args[0].ip)) cli(args) except RuntimeError as e: raise Error('Failed to add host <%s>: %s' % (host, e)) @@ -4755,9 +5131,10 @@ def prepare_dashboard( port = int(out) # Open dashboard port - fw = Firewalld(ctx) - fw.open_ports([port]) - fw.apply_rules() + if not ('skip_firewalld' in ctx and ctx.skip_firewalld): + fw = Firewalld(ctx) + fw.open_ports([port]) + fw.apply_rules() logger.info('Ceph Dashboard is now available at:\n\n' '\t URL: https://%s:%s/\n' @@ -4863,6 +5240,9 @@ def finish_bootstrap_config( 'restart', get_unit_name(fsid, 'mon', mon_id) ]) + elif 'image' in ctx and ctx.image: + # we still want to assimilate the given container image if provided + cli(['config', 'set', 'global', 'container_image', f'{ctx.image}']) if mon_network: logger.info(f'Setting mon public_network to {mon_network}') @@ -4887,7 +5267,7 @@ def _parse_yaml_docs(f: Iterable[str]) -> List[List[str]]: docs = [] current_doc = [] # type: List[str] for line in f: - if '---' in line: + if re.search(r'^---\s+', line): if current_doc: docs.append(current_doc) current_doc = [] @@ -4923,7 +5303,7 @@ def parse_yaml_objs(f: Iterable[str]) -> List[Dict[str, str]]: def _distribute_ssh_keys(ctx: CephadmContext, host_spec: Dict[str, str], bootstrap_hostname: str) -> int: # copy ssh key to hosts in host spec (used for apply spec) - ssh_key = '/etc/ceph/ceph.pub' + ssh_key = CEPH_DEFAULT_PUBKEY if ctx.ssh_public_key: ssh_key = ctx.ssh_public_key.name @@ -4941,17 +5321,45 @@ def _distribute_ssh_keys(ctx: CephadmContext, host_spec: Dict[str, str], bootstr return 0 +def save_cluster_config(ctx: CephadmContext, uid: int, gid: int, fsid: str) -> None: + """Save cluster configuration to the per fsid directory """ + def copy_file(src: str, dst: str) -> None: + if src: + shutil.copyfile(src, dst) + + conf_dir = f'{ctx.data_dir}/{fsid}/{CEPH_CONF_DIR}' + makedirs(conf_dir, uid, gid, DATA_DIR_MODE) + if os.path.exists(conf_dir): + logger.info(f'Saving cluster configuration to {conf_dir} directory') + copy_file(ctx.output_config, os.path.join(conf_dir, CEPH_CONF)) + copy_file(ctx.output_keyring, os.path.join(conf_dir, CEPH_KEYRING)) + # ctx.output_pub_ssh_key may not exist if user has provided custom ssh keys + if (os.path.exists(ctx.output_pub_ssh_key)): + copy_file(ctx.output_pub_ssh_key, os.path.join(conf_dir, CEPH_PUBKEY)) + else: + logger.warning(f'Cannot create cluster configuration directory {conf_dir}') + + @default_image def command_bootstrap(ctx): # type: (CephadmContext) -> int if not ctx.output_config: - ctx.output_config = os.path.join(ctx.output_dir, 'ceph.conf') + ctx.output_config = os.path.join(ctx.output_dir, CEPH_CONF) if not ctx.output_keyring: - ctx.output_keyring = os.path.join(ctx.output_dir, - 'ceph.client.admin.keyring') + ctx.output_keyring = os.path.join(ctx.output_dir, CEPH_KEYRING) if not ctx.output_pub_ssh_key: - ctx.output_pub_ssh_key = os.path.join(ctx.output_dir, 'ceph.pub') + ctx.output_pub_ssh_key = os.path.join(ctx.output_dir, CEPH_PUBKEY) + + if bool(ctx.ssh_private_key) is not bool(ctx.ssh_public_key): + raise Error('--ssh-private-key and --ssh-public-key must be provided together or not at all.') + + if ctx.fsid: + data_dir_base = os.path.join(ctx.data_dir, ctx.fsid) + if os.path.exists(data_dir_base): + raise Error(f"A cluster with the same fsid '{ctx.fsid}' already exists.") + else: + logger.warning('Specifying an fsid for your cluster offers no advantages and may increase the likelihood of fsid conflicts.') # verify output files for f in [ctx.output_config, ctx.output_keyring, @@ -4972,6 +5380,9 @@ def command_bootstrap(ctx): (user_conf, _) = get_config_and_keyring(ctx) + if ctx.ssh_user != 'root': + check_ssh_connectivity(ctx) + if not ctx.skip_prepare_host: command_prepare_host(ctx) else: @@ -4998,7 +5409,12 @@ def command_bootstrap(ctx): config = prepare_bootstrap_config(ctx, fsid, addr_arg, ctx.image) if not ctx.skip_pull: - _pull_image(ctx, ctx.image) + try: + _pull_image(ctx, ctx.image) + except UnauthorizedRegistryError: + err_str = 'Failed to pull container image. Check that correct registry credentials are provided in bootstrap by --registry-url, --registry-username, --registry-password, or supply --registry-json with credentials' + logger.debug(f'Pulling image for bootstrap on {hostname} failed: {err_str}') + raise Error(err_str) image_ver = CephContainer(ctx, ctx.image, 'ceph', ['--version']).run().strip() logger.info(f'Ceph version: {image_ver}') @@ -5121,7 +5537,7 @@ def command_bootstrap(ctx): if not ctx.skip_dashboard: prepare_dashboard(ctx, uid, gid, cli, wait_for_mgr_restart) - if ctx.output_config == '/etc/ceph/ceph.conf' and not ctx.skip_admin_label: + if ctx.output_config == CEPH_DEFAULT_CONF and not ctx.skip_admin_label and not ctx.no_minimize_config: logger.info('Enabling client.admin keyring and conf on hosts with "admin" label') try: cli(['orch', 'client-keyring', 'set', 'client.admin', 'label:_admin']) @@ -5148,6 +5564,8 @@ def command_bootstrap(ctx): except Exception: logger.info('\nApplying %s to cluster failed!\n' % ctx.apply_spec) + save_cluster_config(ctx, uid, gid, fsid) + # enable autotune for osd_memory_target logger.info('Enabling autotune for osd_memory_target') cli(['config', 'set', 'osd', 'osd_memory_target_autotune', 'true']) @@ -5155,12 +5573,15 @@ def command_bootstrap(ctx): # Notify the Dashboard to show the 'Expand cluster' page on first log in. cli(['config-key', 'set', 'mgr/dashboard/cluster/status', 'INSTALLED']) - logger.info('You can access the Ceph CLI with:\n\n' + logger.info('You can access the Ceph CLI as following in case of multi-cluster or non-default config:\n\n' '\tsudo %s shell --fsid %s -c %s -k %s\n' % ( sys.argv[0], fsid, ctx.output_config, ctx.output_keyring)) + + logger.info('Or, if you are only running a single cluster on this host:\n\n\tsudo %s shell \n' % (sys.argv[0])) + logger.info('Please consider enabling telemetry to help improve Ceph:\n\n' '\tceph telemetry on\n\n' 'For more information see:\n\n' @@ -5224,6 +5645,10 @@ def extract_uid_gid_monitoring(ctx, daemon_type): uid, gid = 65534, 65534 elif daemon_type == 'grafana': uid, gid = extract_uid_gid(ctx, file_path='/var/lib/grafana') + elif daemon_type == 'loki': + uid, gid = extract_uid_gid(ctx, file_path='/etc/loki') + elif daemon_type == 'promtail': + uid, gid = extract_uid_gid(ctx, file_path='/etc/promtail') elif daemon_type == 'alertmanager': uid, gid = extract_uid_gid(ctx, file_path=['/etc/alertmanager', '/etc/prometheus']) else: @@ -5268,6 +5693,9 @@ def command_deploy(ctx): else: logger.info('%s daemon %s ...' % ('Deploy', ctx.name)) + # Migrate sysctl conf files from /usr/lib to /etc + migrate_sysctl_dir(ctx, ctx.fsid) + # Get and check ports explicitly required to be opened daemon_ports = [] # type: List[int] @@ -5421,11 +5849,16 @@ def command_shell(ctx): if daemon_id and not ctx.fsid: raise Error('must pass --fsid to specify cluster') - # use /etc/ceph files by default, if present. we do this instead of + # in case a dedicated keyring for the specified fsid is found we us it. + # Otherwise, use /etc/ceph files by default, if present. We do this instead of # making these defaults in the arg parser because we don't want an error # if they don't exist. - if not ctx.keyring and os.path.exists(SHELL_DEFAULT_KEYRING): - ctx.keyring = SHELL_DEFAULT_KEYRING + if not ctx.keyring: + keyring_file = f'{ctx.data_dir}/{ctx.fsid}/{CEPH_CONF_DIR}/{CEPH_KEYRING}' + if os.path.exists(keyring_file): + ctx.keyring = keyring_file + elif os.path.exists(CEPH_DEFAULT_KEYRING): + ctx.keyring = CEPH_DEFAULT_KEYRING container_args: List[str] = ['-i'] mounts = get_container_mounts(ctx, ctx.fsid, daemon_type, daemon_id, @@ -5568,19 +6001,19 @@ def command_ceph_volume(ctx): @infer_fsid def command_unit(ctx): - # type: (CephadmContext) -> None + # type: (CephadmContext) -> int if not ctx.fsid: raise Error('must pass --fsid to specify cluster') unit_name = get_unit_name_by_daemon_name(ctx, ctx.fsid, ctx.name) - call_throws(ctx, [ - 'systemctl', - ctx.command, - unit_name], + _, _, code = call( + ctx, + ['systemctl', ctx.command, unit_name], verbosity=CallVerbosity.VERBOSE, desc='' ) + return code ################################## @@ -5630,12 +6063,14 @@ def _list_ipv4_networks(ctx: CephadmContext) -> Dict[str, Dict[str, Set[str]]]: def _parse_ipv4_route(out: str) -> Dict[str, Dict[str, Set[str]]]: r = {} # type: Dict[str, Dict[str, Set[str]]] - p = re.compile(r'^(\S+) dev (\S+) (.*)scope link (.*)src (\S+)') + p = re.compile(r'^(\S+) (?:via \S+)? ?dev (\S+) (.*)scope link (.*)src (\S+)') for line in out.splitlines(): m = p.findall(line) if not m: continue net = m[0][0] + if '/' not in net: # aggregate /32 mask for single host sub-networks + net += '/32' iface = m[0][1] ip = m[0][4] if net not in r: @@ -5665,9 +6100,11 @@ def _parse_ipv6_route(routes: str, ips: str) -> Dict[str, Dict[str, Set[str]]]: if not m or m[0][0].lower() == 'default': continue net = m[0][0] - if '/' not in net: # only consider networks with a mask - continue + if '/' not in net: # aggregate /128 mask for single host sub-networks + net += '/128' iface = m[0][1] + if iface == 'lo': # skip loopback devices + continue if net not in r: r[net] = {} if iface not in r[net]: @@ -5749,8 +6186,9 @@ def list_daemons(ctx, detail=True, legacy_dir=None): # keep track of image digests seen_digests = {} # type: Dict[str, List[str]] - # keep track of memory usage we've seen + # keep track of memory and cpu usage we've seen seen_memusage = {} # type: Dict[str, int] + seen_cpuperc = {} # type: Dict[str, str] out, err, code = call( ctx, [container_path, 'stats', '--format', '{{.ID}},{{.MemUsage}}', '--no-stream'], @@ -5758,6 +6196,13 @@ def list_daemons(ctx, detail=True, legacy_dir=None): ) seen_memusage_cid_len, seen_memusage = _parse_mem_usage(code, out) + out, err, code = call( + ctx, + [container_path, 'stats', '--format', '{{.ID}},{{.CPUPerc}}', '--no-stream'], + verbosity=CallVerbosity.DEBUG + ) + seen_cpuperc_cid_len, seen_cpuperc = _parse_cpu_perc(code, out) + # /var/lib/ceph if os.path.exists(data_dir): for i in os.listdir(data_dir): @@ -5869,7 +6314,9 @@ def list_daemons(ctx, detail=True, legacy_dir=None): seen_versions[image_id] = version elif daemon_type in ['prometheus', 'alertmanager', - 'node-exporter']: + 'node-exporter', + 'loki', + 'promtail']: version = Monitoring.get_version(ctx, container_id, daemon_type) seen_versions[image_id] = version elif daemon_type == 'haproxy': @@ -5925,6 +6372,7 @@ def list_daemons(ctx, detail=True, legacy_dir=None): val['container_image_digests'] = image_digests if container_id: val['memory_usage'] = seen_memusage.get(container_id[0:seen_memusage_cid_len]) + val['cpu_percentage'] = seen_cpuperc.get(container_id[0:seen_cpuperc_cid_len]) val['version'] = version val['started'] = start_stamp val['created'] = get_file_timestamp( @@ -5934,7 +6382,6 @@ def list_daemons(ctx, detail=True, legacy_dir=None): os.path.join(data_dir, fsid, j, 'unit.image')) val['configured'] = get_file_timestamp( os.path.join(data_dir, fsid, j, 'unit.configured')) - ls.append(val) return ls @@ -5958,6 +6405,22 @@ def _parse_mem_usage(code: int, out: str) -> Tuple[int, Dict[str, int]]: return seen_memusage_cid_len, seen_memusage +def _parse_cpu_perc(code: int, out: str) -> Tuple[int, Dict[str, str]]: + seen_cpuperc = {} + seen_cpuperc_cid_len = 0 + if not code: + for line in out.splitlines(): + (cid, cpuperc) = line.split(',') + try: + seen_cpuperc[cid] = cpuperc + if not seen_cpuperc_cid_len: + seen_cpuperc_cid_len = len(cid) + except ValueError: + logger.info('unable to parse cpu percentage line\n>{}'.format(line)) + pass + return seen_cpuperc_cid_len, seen_cpuperc + + def get_daemon_description(ctx, fsid, name, detail=False, legacy_dir=None): # type: (CephadmContext, str, str, bool, Optional[str]) -> Dict[str, str] @@ -5992,7 +6455,12 @@ def command_adopt(ctx): # type: (CephadmContext) -> None if not ctx.skip_pull: - _pull_image(ctx, ctx.image) + try: + _pull_image(ctx, ctx.image) + except UnauthorizedRegistryError: + err_str = 'Failed to pull container image. Host may not be logged into container registry. Try `cephadm registry-login --registry-url --registry-username --registry-password ` or supply login info via a json file with `cephadm registry-login --registry-json `' + logger.debug(f'Pulling image for `command_adopt` failed: {err_str}') + raise Error(err_str) (daemon_type, daemon_id) = ctx.name.split('.', 1) @@ -6407,6 +6875,18 @@ def command_rm_daemon(ctx): else: call_throws(ctx, ['rm', '-rf', data_dir]) + if 'tcp_ports' in ctx and ctx.tcp_ports is not None: + ports: List[int] = [int(p) for p in ctx.tcp_ports.split()] + try: + fw = Firewalld(ctx) + fw.close_ports(ports) + fw.apply_rules() + except RuntimeError as e: + # in case we cannot close the ports we will remove + # the daemon but keep them open. + logger.warning(f' Error when trying to close ports: {e}') + + ################################## @@ -6462,6 +6942,10 @@ def command_zap_osds(ctx: CephadmContext) -> None: ################################## +def get_ceph_cluster_count(ctx: CephadmContext) -> int: + return len([c for c in os.listdir(ctx.data_dir) if is_fsid(c)]) + + def command_rm_cluster(ctx): # type: (CephadmContext) -> None if not ctx.force: @@ -6471,13 +6955,7 @@ def command_rm_cluster(ctx): lock = FileLock(ctx, ctx.fsid) lock.acquire() - # stop + disable individual daemon units - for d in list_daemons(ctx, detail=False): - if d['fsid'] != ctx.fsid: - continue - if d['style'] != 'cephadm:v1': - continue - unit_name = get_unit_name(ctx.fsid, d['name']) + def disable_systemd_service(unit_name: str) -> None: call(ctx, ['systemctl', 'stop', unit_name], verbosity=CallVerbosity.DEBUG) call(ctx, ['systemctl', 'reset-failed', unit_name], @@ -6485,14 +6963,17 @@ def command_rm_cluster(ctx): call(ctx, ['systemctl', 'disable', unit_name], verbosity=CallVerbosity.DEBUG) + # stop + disable individual daemon units + for d in list_daemons(ctx, detail=False): + if d['fsid'] != ctx.fsid: + continue + if d['style'] != 'cephadm:v1': + continue + disable_systemd_service(get_unit_name(ctx.fsid, d['name'])) + # cluster units for unit_name in ['ceph-%s.target' % ctx.fsid]: - call(ctx, ['systemctl', 'stop', unit_name], - verbosity=CallVerbosity.DEBUG) - call(ctx, ['systemctl', 'reset-failed', unit_name], - verbosity=CallVerbosity.DEBUG) - call(ctx, ['systemctl', 'disable', unit_name], - verbosity=CallVerbosity.DEBUG) + disable_systemd_service(unit_name) slice_name = 'system-ceph\\x2d{}.slice'.format(ctx.fsid.replace('-', '\\x2d')) call(ctx, ['systemctl', 'stop', slice_name], @@ -6521,29 +7002,47 @@ def command_rm_cluster(ctx): # rm logrotate config call_throws(ctx, ['rm', '-f', ctx.logrotate_dir + '/ceph-%s' % ctx.fsid]) - # rm cephadm logrotate config if last cluster on host - if not os.listdir(ctx.data_dir): + # if last cluster on host remove shared files + if get_ceph_cluster_count(ctx) == 0: + disable_systemd_service('ceph.target') + + # rm shared ceph target files + call_throws(ctx, ['rm', '-f', ctx.unit_dir + '/multi-user.target.wants/ceph.target']) + call_throws(ctx, ['rm', '-f', ctx.unit_dir + '/ceph.target']) + + # rm cephadm logrotate config call_throws(ctx, ['rm', '-f', ctx.logrotate_dir + '/cephadm']) + if not ctx.keep_logs: + # remove all cephadm logs + for fname in glob(f'{ctx.log_dir}/cephadm.log*'): + os.remove(fname) + # rm sysctl settings - sysctl_dir = Path(ctx.sysctl_dir) - for p in sysctl_dir.glob(f'90-ceph-{ctx.fsid}-*.conf'): - p.unlink() + sysctl_dirs: List[Path] = [Path(ctx.sysctl_dir), Path('/usr/lib/sysctl.d')] - # clean up config, keyring, and pub key files - files = ['/etc/ceph/ceph.conf', '/etc/ceph/ceph.pub', '/etc/ceph/ceph.client.admin.keyring'] + for sysctl_dir in sysctl_dirs: + for p in sysctl_dir.glob(f'90-ceph-{ctx.fsid}-*.conf'): + p.unlink() + # cleanup remaining ceph directories + ceph_dirs = [f'/run/ceph/{ctx.fsid}', f'/tmp/var/lib/ceph/{ctx.fsid}', f'/var/run/ceph/{ctx.fsid}'] + for dd in ceph_dirs: + shutil.rmtree(dd, ignore_errors=True) + + # clean up config, keyring, and pub key files + files = [CEPH_DEFAULT_CONF, CEPH_DEFAULT_PUBKEY, CEPH_DEFAULT_KEYRING] if os.path.exists(files[0]): valid_fsid = False with open(files[0]) as f: if ctx.fsid in f.read(): valid_fsid = True if valid_fsid: + # rm configuration files on /etc/ceph for n in range(0, len(files)): if os.path.exists(files[n]): os.remove(files[n]) - ################################## @@ -6601,6 +7100,142 @@ def command_check_host(ctx: CephadmContext) -> None: ################################## +def get_ssh_vars(ssh_user: str) -> Tuple[int, int, str]: + try: + s_pwd = pwd.getpwnam(ssh_user) + except KeyError: + raise Error('Cannot find uid/gid for ssh-user: %s' % (ssh_user)) + + ssh_uid = s_pwd.pw_uid + ssh_gid = s_pwd.pw_gid + ssh_dir = os.path.join(s_pwd.pw_dir, '.ssh') + return ssh_uid, ssh_gid, ssh_dir + + +def authorize_ssh_key(ssh_pub_key: str, ssh_user: str) -> bool: + """Authorize the public key for the provided ssh user""" + + def key_in_file(path: str, key: str) -> bool: + if not os.path.exists(path): + return False + with open(path) as f: + lines = f.readlines() + for line in lines: + if line.strip() == key.strip(): + return True + return False + + logger.info(f'Adding key to {ssh_user}@localhost authorized_keys...') + if ssh_pub_key is None or ssh_pub_key.isspace(): + raise Error('Trying to authorize an empty ssh key') + + ssh_pub_key = ssh_pub_key.strip() + ssh_uid, ssh_gid, ssh_dir = get_ssh_vars(ssh_user) + if not os.path.exists(ssh_dir): + makedirs(ssh_dir, ssh_uid, ssh_gid, 0o700) + + auth_keys_file = '%s/authorized_keys' % ssh_dir + if key_in_file(auth_keys_file, ssh_pub_key): + logger.info(f'key already in {ssh_user}@localhost authorized_keys...') + return False + + add_newline = False + if os.path.exists(auth_keys_file): + with open(auth_keys_file, 'r') as f: + f.seek(0, os.SEEK_END) + if f.tell() > 0: + f.seek(f.tell() - 1, os.SEEK_SET) # go to last char + if f.read() != '\n': + add_newline = True + + with open(auth_keys_file, 'a') as f: + os.fchown(f.fileno(), ssh_uid, ssh_gid) # just in case we created it + os.fchmod(f.fileno(), 0o600) # just in case we created it + if add_newline: + f.write('\n') + f.write(ssh_pub_key + '\n') + + return True + + +def revoke_ssh_key(key: str, ssh_user: str) -> None: + """Revoke the public key authorization for the ssh user""" + ssh_uid, ssh_gid, ssh_dir = get_ssh_vars(ssh_user) + auth_keys_file = '%s/authorized_keys' % ssh_dir + deleted = False + if os.path.exists(auth_keys_file): + with open(auth_keys_file, 'r') as f: + lines = f.readlines() + _, filename = tempfile.mkstemp() + with open(filename, 'w') as f: + os.fchown(f.fileno(), ssh_uid, ssh_gid) + os.fchmod(f.fileno(), 0o600) # secure access to the keys file + for line in lines: + if line.strip() == key.strip(): + deleted = True + else: + f.write(line) + + if deleted: + shutil.move(filename, auth_keys_file) + else: + logger.warning('Cannot find the ssh key to be deleted') + + +def check_ssh_connectivity(ctx: CephadmContext) -> None: + + def cmd_is_available(cmd: str) -> bool: + if shutil.which(cmd) is None: + logger.warning(f'Command not found: {cmd}') + return False + return True + + if not cmd_is_available('ssh') or not cmd_is_available('ssh-keygen'): + logger.warning('Cannot check ssh connectivity. Skipping...') + return + + logger.info('Verifying ssh connectivity ...') + if ctx.ssh_private_key and ctx.ssh_public_key: + # let's use the keys provided by the user + ssh_priv_key_path = pathify(ctx.ssh_private_key.name) + ssh_pub_key_path = pathify(ctx.ssh_public_key.name) + else: + # no custom keys, let's generate some random keys just for this check + ssh_priv_key_path = f'/tmp/ssh_key_{uuid.uuid1()}' + ssh_pub_key_path = f'{ssh_priv_key_path}.pub' + ssh_key_gen_cmd = ['ssh-keygen', '-q', '-t', 'rsa', '-N', '', '-C', '', '-f', ssh_priv_key_path] + _, _, code = call(ctx, ssh_key_gen_cmd) + if code != 0: + logger.warning('Cannot generate keys to check ssh connectivity.') + return + + with open(ssh_pub_key_path, 'r') as f: + key = f.read().strip() + new_key = authorize_ssh_key(key, ctx.ssh_user) + ssh_cfg_file_arg = ['-F', pathify(ctx.ssh_config.name)] if ctx.ssh_config else [] + _, _, code = call(ctx, ['ssh', '-o StrictHostKeyChecking=no', + *ssh_cfg_file_arg, '-i', ssh_priv_key_path, + '-o PasswordAuthentication=no', + f'{ctx.ssh_user}@{get_hostname()}', + 'sudo echo']) + + # we only remove the key if it's a new one. In case the user has provided + # some already existing key then we don't alter authorized_keys file + if new_key: + revoke_ssh_key(key, ctx.ssh_user) + + pub_key_msg = '- The public key file configured by --ssh-public-key is valid\n' if ctx.ssh_public_key else '' + prv_key_msg = '- The private key file configured by --ssh-private-key is valid\n' if ctx.ssh_private_key else '' + ssh_cfg_msg = '- The ssh configuration file configured by --ssh-config is valid\n' if ctx.ssh_config else '' + err_msg = f""" +** Please verify your user's ssh configuration and make sure: +- User {ctx.ssh_user} must have passwordless sudo access +{pub_key_msg}{prv_key_msg}{ssh_cfg_msg} +""" + if code != 0: + raise Error(err_msg) + + def command_prepare_host(ctx: CephadmContext) -> None: logger.info('Verifying podman|docker is present...') pkg = None @@ -7518,6 +8153,16 @@ class HostFacts(): continue for iface in os.listdir(nic_path): + if os.path.exists(os.path.join(nic_path, iface, 'bridge')): + nic_type = 'bridge' + elif os.path.exists(os.path.join(nic_path, iface, 'bonding')): + nic_type = 'bonding' + else: + nic_type = hw_lookup.get(read_file([os.path.join(nic_path, iface, 'type')]), 'Unknown') + + if nic_type == 'loopback': # skip loopback devices + continue + lower_devs_list = [os.path.basename(link.replace('lower_', '')) for link in glob(os.path.join(nic_path, iface, 'lower_*'))] upper_devs_list = [os.path.basename(link.replace('upper_', '')) for link in glob(os.path.join(nic_path, iface, 'upper_*'))] @@ -7536,13 +8181,6 @@ class HostFacts(): # Either way, we show a -1 when speed isn't available speed = -1 - if os.path.exists(os.path.join(nic_path, iface, 'bridge')): - nic_type = 'bridge' - elif os.path.exists(os.path.join(nic_path, iface, 'bonding')): - nic_type = 'bonding' - else: - nic_type = hw_lookup.get(read_file([os.path.join(nic_path, iface, 'type')]), 'Unknown') - dev_link = os.path.join(nic_path, iface, 'device') if os.path.exists(dev_link): iftype = 'physical' @@ -7955,7 +8593,7 @@ def _get_parser(): parser_version.set_defaults(func=command_version) parser_pull = subparsers.add_parser( - 'pull', help='pull latest image version') + 'pull', help='pull the default container image') parser_pull.set_defaults(func=command_pull) parser_pull.add_argument( '--insecure', @@ -8012,7 +8650,7 @@ def _get_parser(): parser_adopt.add_argument( '--skip-pull', action='store_true', - help='do not pull the latest image before adopting') + help='do not pull the default image before adopting') parser_adopt.add_argument( '--force-start', action='store_true', @@ -8031,6 +8669,9 @@ def _get_parser(): required=True, action=CustomValidation, help='daemon name (type.id)') + parser_rm_daemon.add_argument( + '--tcp-ports', + help='List of tcp ports to close in the host firewall') parser_rm_daemon.add_argument( '--fsid', required=True, @@ -8207,10 +8848,11 @@ def _get_parser(): '--mon-id', required=False, help='mon id (default: local hostname)') - parser_bootstrap.add_argument( + group = parser_bootstrap.add_mutually_exclusive_group() + group.add_argument( '--mon-addrv', help='mon IPs (e.g., [v2:localipaddr:3300,v1:localipaddr:6789])') - parser_bootstrap.add_argument( + group.add_argument( '--mon-ip', help='mon IP') parser_bootstrap.add_argument( @@ -8301,7 +8943,7 @@ def _get_parser(): parser_bootstrap.add_argument( '--skip-pull', action='store_true', - help='do not pull the latest image before bootstrapping') + help='do not pull the default image before bootstrapping') parser_bootstrap.add_argument( '--skip-firewalld', action='store_true', @@ -8566,7 +9208,12 @@ def cephadm_init_logging(ctx: CephadmContext, args: List[str]) -> None: global logger if not os.path.exists(LOG_DIR): os.makedirs(LOG_DIR) - dictConfig(logging_config) + operations = ['bootstrap', 'rm-cluster'] + if any(op in args for op in operations): + dictConfig(interactive_logging_config) + else: + dictConfig(logging_config) + logger = logging.getLogger() if not os.path.exists(ctx.logrotate_dir + '/cephadm'): diff --git a/ceph/src/cephadm/tests/fixtures.py b/ceph/src/cephadm/tests/fixtures.py index f789ba80e..1f5f17121 100644 --- a/ceph/src/cephadm/tests/fixtures.py +++ b/ceph/src/cephadm/tests/fixtures.py @@ -31,6 +31,14 @@ def _daemon_path(): return os.getcwd() +def mock_bad_firewalld(): + def raise_bad_firewalld(): + raise Exception('Called bad firewalld') + f = mock.Mock(cd.Firewalld) + f.enable_service_for = lambda _ : raise_bad_firewalld() + f.apply_rules = lambda : raise_bad_firewalld() + f.open_ports = lambda _ : raise_bad_firewalld() + def _mock_scrape_host(obj, interval): try: raise ValueError("wah") @@ -90,6 +98,7 @@ def with_cephadm_ctx( mock.patch('cephadm.call_timeout', return_value=0), \ mock.patch('cephadm.find_executable', return_value='foo'), \ mock.patch('cephadm.is_available', return_value=True), \ + mock.patch('cephadm.get_container_info', return_value=None), \ mock.patch('cephadm.json_loads_retry', return_value={'epoch' : 1}), \ mock.patch('socket.gethostname', return_value=hostname): ctx: cd.CephadmContext = cd.cephadm_init_ctx(cmd) diff --git a/ceph/src/cephadm/tests/test_cephadm.py b/ceph/src/cephadm/tests/test_cephadm.py index 2f3f01582..63706198f 100644 --- a/ceph/src/cephadm/tests/test_cephadm.py +++ b/ceph/src/cephadm/tests/test_cephadm.py @@ -6,20 +6,15 @@ import mock import os import pytest import socket -import sys -import time -import threading import unittest - from textwrap import dedent -from typing import List, Optional - from .fixtures import ( cephadm_fs, mock_docker, mock_podman, with_cephadm_ctx, + mock_bad_firewalld, ) with mock.patch('builtins.open', create=True): @@ -106,7 +101,7 @@ class TestCephAdm(object): ('::', socket.AF_INET6), ): try: - cd.check_ip_port(ctx, address, 9100) + cd.check_ip_port(ctx, cd.EndPoint(address, 9100)) except: assert False else: @@ -137,7 +132,7 @@ class TestCephAdm(object): mock_socket_obj.bind.side_effect = side_effect _socket.return_value = mock_socket_obj try: - cd.check_ip_port(ctx, address, 9100) + cd.check_ip_port(ctx, cd.EndPoint(address, 9100)) except Exception as e: assert isinstance(e, expected_exception) else: @@ -214,10 +209,43 @@ class TestCephAdm(object): for address, expected in tests: wrap_test(address, expected) + @mock.patch('cephadm.Firewalld', mock_bad_firewalld) + @mock.patch('cephadm.logger') + def test_skip_firewalld(self, logger, cephadm_fs): + """ + test --skip-firewalld actually skips changing firewall + """ + + ctx = cd.CephadmContext() + with pytest.raises(Exception): + cd.update_firewalld(ctx, 'mon') + + ctx.skip_firewalld = True + cd.update_firewalld(ctx, 'mon') + + ctx.skip_firewalld = False + with pytest.raises(Exception): + cd.update_firewalld(ctx, 'mon') + + ctx = cd.CephadmContext() + ctx.ssl_dashboard_port = 8888 + ctx.dashboard_key = None + ctx.dashboard_password_noupdate = True + ctx.initial_dashboard_password = 'password' + ctx.initial_dashboard_user = 'User' + with pytest.raises(Exception): + cd.prepare_dashboard(ctx, 0, 0, lambda _, extra_mounts=None, ___=None : '5', lambda : None) + + ctx.skip_firewalld = True + cd.prepare_dashboard(ctx, 0, 0, lambda _, extra_mounts=None, ___=None : '5', lambda : None) + + ctx.skip_firewalld = False + with pytest.raises(Exception): + cd.prepare_dashboard(ctx, 0, 0, lambda _, extra_mounts=None, ___=None : '5', lambda : None) + @mock.patch('cephadm.call_throws') @mock.patch('cephadm.get_parm') def test_registry_login(self, get_parm, call_throws): - # test normal valid login with url, username and password specified call_throws.return_value = '', '', 0 ctx: cd.CephadmContext = cd.cephadm_init_ctx( @@ -320,14 +348,203 @@ class TestCephAdm(object): result = cd.dict_get_join({'a': 1}, 'a') assert result == 1 - def test_last_local_images(self): - out = ''' -docker.io/ceph/daemon-base@ -docker.io/ceph/ceph:v15.2.5 -docker.io/ceph/daemon-base:octopus - ''' - image = cd._filter_last_local_ceph_image(out) - assert image == 'docker.io/ceph/ceph:v15.2.5' + @mock.patch('os.listdir', return_value=[]) + @mock.patch('cephadm.logger') + def test_infer_local_ceph_image(self, _logger, _listdir): + ctx = cd.CephadmContext() + ctx.fsid = '00000000-0000-0000-0000-0000deadbeez' + ctx.container_engine = mock_podman() + + # make sure the right image is selected when container is found + cinfo = cd.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972', + 'registry.hub.docker.com/rkachach/ceph:custom-v0.5', + '514e6a882f6e74806a5856468489eeff8d7106095557578da96935e4d0ba4d9d', + '2022-04-19 13:45:20.97146228 +0000 UTC', + '') + out = '''quay.ceph.io/ceph-ci/ceph@sha256:87f200536bb887b36b959e887d5984dd7a3f008a23aa1f283ab55d48b22c6185|dad864ee21e9|master|2022-03-23 16:29:19 +0000 UTC + quay.ceph.io/ceph-ci/ceph@sha256:b50b130fcda2a19f8507ddde3435bb4722266956e1858ac395c838bc1dcf1c0e|514e6a882f6e|pacific|2022-03-23 15:58:34 +0000 UTC + docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508|666bbfa87e8d|v15.2.5|2020-09-16 14:15:15 +0000 UTC''' + with mock.patch('cephadm.call_throws', return_value=(out, '', '')): + with mock.patch('cephadm.get_container_info', return_value=cinfo): + image = cd.infer_local_ceph_image(ctx, ctx.container_engine) + assert image == 'quay.ceph.io/ceph-ci/ceph@sha256:b50b130fcda2a19f8507ddde3435bb4722266956e1858ac395c838bc1dcf1c0e' + + # make sure first valid image is used when no container_info is found + out = '''quay.ceph.io/ceph-ci/ceph@sha256:87f200536bb887b36b959e887d5984dd7a3f008a23aa1f283ab55d48b22c6185|dad864ee21e9|master|2022-03-23 16:29:19 +0000 UTC + quay.ceph.io/ceph-ci/ceph@sha256:b50b130fcda2a19f8507ddde3435bb4722266956e1858ac395c838bc1dcf1c0e|514e6a882f6e|pacific|2022-03-23 15:58:34 +0000 UTC + docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508|666bbfa87e8d|v15.2.5|2020-09-16 14:15:15 +0000 UTC''' + with mock.patch('cephadm.call_throws', return_value=(out, '', '')): + with mock.patch('cephadm.get_container_info', return_value=None): + image = cd.infer_local_ceph_image(ctx, ctx.container_engine) + assert image == 'quay.ceph.io/ceph-ci/ceph@sha256:87f200536bb887b36b959e887d5984dd7a3f008a23aa1f283ab55d48b22c6185' + + # make sure images without digest are discarded (no container_info is found) + out = '''quay.ceph.io/ceph-ci/ceph@||| + docker.io/ceph/ceph@||| + docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508|666bbfa87e8d|v15.2.5|2020-09-16 14:15:15 +0000 UTC''' + with mock.patch('cephadm.call_throws', return_value=(out, '', '')): + with mock.patch('cephadm.get_container_info', return_value=None): + image = cd.infer_local_ceph_image(ctx, ctx.container_engine) + assert image == 'docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508' + + + + @pytest.mark.parametrize('daemon_filter, by_name, daemon_list, container_stats, output', + [ + # get container info by type ('mon') + ( + 'mon', + False, + [ + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + {'name': 'mgr.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + ], + ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,", + "", + 0), + cd.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972', + 'registry.hub.docker.com/rkachach/ceph:custom-v0.5', + '666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4', + '2022-04-19 13:45:20.97146228 +0000 UTC', + '') + ), + # get container info by name ('mon.ceph-node-0') + ( + 'mon.ceph-node-0', + True, + [ + {'name': 'mgr.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + ], + ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,", + "", + 0), + cd.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972', + 'registry.hub.docker.com/rkachach/ceph:custom-v0.5', + '666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4', + '2022-04-19 13:45:20.97146228 +0000 UTC', + '') + ), + # get container info by name (same daemon but two different fsids) + ( + 'mon.ceph-node-0', + True, + [ + {'name': 'mon.ceph-node-0', 'fsid': '10000000-0000-0000-0000-0000deadbeef'}, + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + ], + ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,", + "", + 0), + cd.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972', + 'registry.hub.docker.com/rkachach/ceph:custom-v0.5', + '666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4', + '2022-04-19 13:45:20.97146228 +0000 UTC', + '') + ), + # get container info by type (bad container stats: 127 code) + ( + 'mon', + False, + [ + {'name': 'mon.ceph-node-0', 'fsid': '00000000-FFFF-0000-0000-0000deadbeef'}, + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + ], + ("", + "", + 127), + None + ), + # get container info by name (bad container stats: 127 code) + ( + 'mon.ceph-node-0', + True, + [ + {'name': 'mgr.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + ], + ("", + "", + 127), + None + ), + # get container info by invalid name (doens't contain '.') + ( + 'mon-ceph-node-0', + True, + [ + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + ], + ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,", + "", + 0), + None + ), + # get container info by invalid name (empty) + ( + '', + True, + [ + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + ], + ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,", + "", + 0), + None + ), + # get container info by invalid type (empty) + ( + '', + False, + [ + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}, + ], + ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,", + "", + 0), + None + ), + # get container info by name: no match (invalid fsid) + ( + 'mon', + False, + [ + {'name': 'mon.ceph-node-0', 'fsid': '00000000-1111-0000-0000-0000deadbeef'}, + {'name': 'mon.ceph-node-0', 'fsid': '00000000-2222-0000-0000-0000deadbeef'}, + ], + ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,", + "", + 0), + None + ), + # get container info by name: no match + ( + 'mon.ceph-node-0', + True, + [], + None, + None + ), + # get container info by type: no match + ( + 'mgr', + False, + [], + None, + None + ), + ]) + def test_get_container_info(self, daemon_filter, by_name, daemon_list, container_stats, output): + cd.logger = mock.Mock() + ctx = cd.CephadmContext() + ctx.fsid = '00000000-0000-0000-0000-0000deadbeef' + ctx.container_engine = mock_podman() + with mock.patch('cephadm.list_daemons', return_value=daemon_list): + with mock.patch('cephadm.get_container_stats', return_value=container_stats): + assert cd.get_container_info(ctx, daemon_filter, by_name) == output def test_should_log_to_journald(self): ctx = cd.CephadmContext() @@ -453,6 +670,77 @@ docker.io/ceph/daemon-base:octopus infer_fsid(ctx) assert ctx.fsid == result + @pytest.mark.parametrize('fsid, other_conf_files, config, name, list_daemons, result, ', + [ + # per cluster conf has more precedence than default conf + ( + '00000000-0000-0000-0000-0000deadbeef', + [cd.CEPH_DEFAULT_CONF], + None, + None, + [], + '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf', + ), + # mon daemon conf has more precedence than cluster conf and default conf + ( + '00000000-0000-0000-0000-0000deadbeef', + ['/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf', + cd.CEPH_DEFAULT_CONF], + None, + None, + [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}], + '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config', + ), + # daemon conf (--name option) has more precedence than cluster, default and mon conf + ( + '00000000-0000-0000-0000-0000deadbeef', + ['/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf', + '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config', + cd.CEPH_DEFAULT_CONF], + None, + 'osd.0', + [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}, + {'name': 'osd.0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}], + '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/osd.0/config', + ), + # user provided conf ('/foo/ceph.conf') more precedence than any other conf + ( + '00000000-0000-0000-0000-0000deadbeef', + ['/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf', + cd.CEPH_DEFAULT_CONF, + '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config'], + '/foo/ceph.conf', + None, + [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}], + '/foo/ceph.conf', + ), + ]) + @mock.patch('cephadm.call') + @mock.patch('cephadm.logger') + def test_infer_config_precedence(self, logger, _call, other_conf_files, fsid, config, name, list_daemons, result, cephadm_fs): + # build the context + ctx = cd.CephadmContext() + ctx.fsid = fsid + ctx.config = config + ctx.name = name + + # mock the decorator + mock_fn = mock.Mock() + mock_fn.return_value = 0 + infer_config = cd.infer_config(mock_fn) + + # mock the config file + cephadm_fs.create_file(result) + + # mock other potential config files + for f in other_conf_files: + cephadm_fs.create_file(f) + + # test + with mock.patch('cephadm.list_daemons', return_value=list_daemons): + infer_config(ctx) + assert ctx.config == result + @pytest.mark.parametrize('fsid, config, name, list_daemons, result, ', [ ( @@ -467,34 +755,48 @@ docker.io/ceph/daemon-base:octopus None, None, [], - cd.SHELL_DEFAULT_CONF, + cd.CEPH_DEFAULT_CONF, + ), + ( + '00000000-0000-0000-0000-0000deadbeef', + None, + None, + [], + '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf', ), ( '00000000-0000-0000-0000-0000deadbeef', None, None, - [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}], + [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}], '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config', ), ( '00000000-0000-0000-0000-0000deadbeef', None, None, - [{'name': 'mon.a', 'fsid': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'}], - cd.SHELL_DEFAULT_CONF, + [{'name': 'mon.a', 'fsid': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'style': 'cephadm:v1'}], + cd.CEPH_DEFAULT_CONF, + ), + ( + '00000000-0000-0000-0000-0000deadbeef', + None, + None, + [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'legacy'}], + cd.CEPH_DEFAULT_CONF, ), ( '00000000-0000-0000-0000-0000deadbeef', None, None, [{'name': 'osd.0'}], - cd.SHELL_DEFAULT_CONF, + cd.CEPH_DEFAULT_CONF, ), ( '00000000-0000-0000-0000-0000deadbeef', '/foo/bar.conf', 'mon.a', - [{'name': 'mon.a'}], + [{'name': 'mon.a', 'style': 'cephadm:v1'}], '/foo/bar.conf', ), ( @@ -516,11 +818,12 @@ docker.io/ceph/daemon-base:octopus None, None, [], - cd.SHELL_DEFAULT_CONF, + cd.CEPH_DEFAULT_CONF, ), ]) @mock.patch('cephadm.call') - def test_infer_config(self, _call, fsid, config, name, list_daemons, result, cephadm_fs): + @mock.patch('cephadm.logger') + def test_infer_config(self, logger, _call, fsid, config, name, list_daemons, result, cephadm_fs): # build the context ctx = cd.CephadmContext() ctx.fsid = fsid @@ -532,8 +835,8 @@ docker.io/ceph/daemon-base:octopus mock_fn.return_value = 0 infer_config = cd.infer_config(mock_fn) - # mock the shell config - cephadm_fs.create_file(cd.SHELL_DEFAULT_CONF) + # mock the config file + cephadm_fs.create_file(result) # test with mock.patch('cephadm.list_daemons', return_value=list_daemons): @@ -550,6 +853,36 @@ ff792c06d8544b983.scope not found.: OCI runtime error""" with pytest.raises(cd.Error, match='OCI'): cd.extract_uid_gid(ctx) + @pytest.mark.parametrize('test_input, expected', [ + ([cd.make_fsid(), cd.make_fsid(), cd.make_fsid()], 3), + ([cd.make_fsid(), 'invalid-fsid', cd.make_fsid(), '0b87e50c-8e77-11ec-b890-'], 2), + (['f6860ec2-8e76-11ec-', '0b87e50c-8e77-11ec-b890-', ''], 0), + ([], 0), + ]) + def test_get_ceph_cluster_count(self, test_input, expected): + ctx = cd.CephadmContext() + with mock.patch('os.listdir', return_value=test_input): + assert cd.get_ceph_cluster_count(ctx) == expected + + def test_set_image_minimize_config(self): + def throw_cmd(cmd): + raise cd.Error(' '.join(cmd)) + ctx = cd.CephadmContext() + ctx.image = 'test_image' + ctx.no_minimize_config = True + fake_cli = lambda cmd, __=None, ___=None: throw_cmd(cmd) + with pytest.raises(cd.Error, match='config set global container_image test_image'): + cd.finish_bootstrap_config( + ctx=ctx, + fsid=cd.make_fsid(), + config='', + mon_id='a', mon_dir='mon_dir', + mon_network=None, ipv6=False, + cli=fake_cli, + cluster_network=None, + ipv6_cluster_network=False + ) + class TestCustomContainer(unittest.TestCase): cc: cd.CustomContainer @@ -680,7 +1013,7 @@ class TestMaintenance: @mock.patch('cephadm.call') @mock.patch('cephadm.systemd_target_state') def test_enter_failure_2(self, _target_state, _call, _listdir): - _call.side_effect = [('', '', 0), ('', '', 999)] + _call.side_effect = [('', '', 0), ('', '', 999), ('', '', 0), ('', '', 999)] _target_state.return_value = True ctx: cd.CephadmContext = cd.cephadm_init_ctx( ['host-maintenance', 'enter', '--fsid', TestMaintenance.fsid]) @@ -707,7 +1040,7 @@ class TestMaintenance: @mock.patch('cephadm.systemd_target_state') @mock.patch('cephadm.target_exists') def test_exit_failure_2(self, _target_exists, _target_state, _call, _listdir): - _call.side_effect = [('', '', 0), ('', '', 999)] + _call.side_effect = [('', '', 0), ('', '', 999), ('', '', 0), ('', '', 999)] _target_state.return_value = False _target_exists.return_value = True ctx: cd.CephadmContext = cd.cephadm_init_ctx( @@ -746,6 +1079,14 @@ class TestMonitoring(object): version = cd.Monitoring.get_version(ctx, 'container_id', daemon_type) assert version == '0.16.1' + def test_prometheus_external_url(self): + ctx = cd.CephadmContext() + daemon_type = 'prometheus' + daemon_id = 'home' + fsid = 'aaf5a720-13fe-4a3b-82b9-2d99b7fd9704' + args = cd.get_daemon_args(ctx, fsid, daemon_type, daemon_id) + assert any([x.startswith('--web.external-url=http://') for x in args]) + @mock.patch('cephadm.call') def test_get_version_node_exporter(self, _call): ctx = cd.CephadmContext() @@ -802,6 +1143,22 @@ class TestMonitoring(object): with open(file) as f: assert f.read() == content + # assert uid/gid after redeploy + new_uid = uid+1 + new_gid = gid+1 + cd.create_daemon_dirs(ctx, + fsid, + daemon_type, + daemon_id, + new_uid, + new_gid, + config=None, + keyring=None) + for file,content in expected.items(): + file = os.path.join(prefix, file) + assert os.stat(file).st_uid == new_uid + assert os.stat(file).st_gid == new_gid + class TestBootstrap(object): @@ -1100,11 +1457,11 @@ class TestShell(object): assert retval == 0 assert ctx.config == None - cephadm_fs.create_file(cd.SHELL_DEFAULT_CONF) + cephadm_fs.create_file(cd.CEPH_DEFAULT_CONF) with with_cephadm_ctx(cmd) as ctx: retval = cd.command_shell(ctx) assert retval == 0 - assert ctx.config == cd.SHELL_DEFAULT_CONF + assert ctx.config == cd.CEPH_DEFAULT_CONF cmd = ['shell', '--config', 'foo'] with with_cephadm_ctx(cmd) as ctx: @@ -1119,11 +1476,11 @@ class TestShell(object): assert retval == 0 assert ctx.keyring == None - cephadm_fs.create_file(cd.SHELL_DEFAULT_KEYRING) + cephadm_fs.create_file(cd.CEPH_DEFAULT_KEYRING) with with_cephadm_ctx(cmd) as ctx: retval = cd.command_shell(ctx) assert retval == 0 - assert ctx.keyring == cd.SHELL_DEFAULT_KEYRING + assert ctx.keyring == cd.CEPH_DEFAULT_KEYRING cmd = ['shell', '--keyring', 'foo'] with with_cephadm_ctx(cmd) as ctx: @@ -1634,11 +1991,35 @@ class TestPull: cd.command_pull(ctx) assert err in str(e.value) + @mock.patch('cephadm.logger') + @mock.patch('cephadm.get_image_info_from_inspect', return_value={}) + @mock.patch('cephadm.infer_local_ceph_image', return_value='last_local_ceph_image') + def test_image(self, infer_local_ceph_image, get_image_info_from_inspect, logger): + cmd = ['pull'] + with with_cephadm_ctx(cmd) as ctx: + retval = cd.command_pull(ctx) + assert retval == 0 + assert ctx.image == cd.DEFAULT_IMAGE + + with mock.patch.dict(os.environ, {"CEPHADM_IMAGE": 'cephadm_image_environ'}): + cmd = ['pull'] + with with_cephadm_ctx(cmd) as ctx: + retval = cd.command_pull(ctx) + assert retval == 0 + assert ctx.image == 'cephadm_image_environ' + + cmd = ['--image', 'cephadm_image_param', 'pull'] + with with_cephadm_ctx(cmd) as ctx: + retval = cd.command_pull(ctx) + assert retval == 0 + assert ctx.image == 'cephadm_image_param' + class TestApplySpec: def test_parse_yaml(self, cephadm_fs): - yaml = '''service_type: host + yaml = '''--- +service_type: host hostname: vm-00 addr: 192.168.122.44 labels: @@ -1650,16 +2031,46 @@ hostname: vm-01 addr: 192.168.122.247 labels: - grafana ---- +--- service_type: host hostname: vm-02 -addr: 192.168.122.165''' +addr: 192.168.122.165 +--- +--- +service_type: rgw +service_id: myrgw +spec: + rgw_frontend_ssl_certificate: | + -----BEGIN PRIVATE KEY----- + V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFt + ZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15 + IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu + YSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3Mg + ZXQgYWNjdXNhbSBldCBqdXN0byBkdW8= + -----END PRIVATE KEY----- + -----BEGIN CERTIFICATE----- + V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFt + ZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15 + IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu + YSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3Mg + ZXQgYWNjdXNhbSBldCBqdXN0byBkdW8= + -----END CERTIFICATE----- + ssl: true +--- +''' cephadm_fs.create_file('spec.yml', contents=yaml) - retdic = [{'service_type': 'host', 'hostname': 'vm-00', 'addr': '192.168.122.44', 'labels': '- example1- example2'}, {'service_type': 'host', 'hostname': 'vm-01', 'addr': '192.168.122.247', 'labels': '- grafana'}, - {'service_type': 'host', 'hostname': 'vm-02', 'addr': '192.168.122.165'}] + {'service_type': 'host', 'hostname': 'vm-02', 'addr': '192.168.122.165'}, + {'service_id': 'myrgw', + 'service_type': 'rgw', + 'spec': + 'rgw_frontend_ssl_certificate: |-----BEGIN PRIVATE ' + 'KEY-----V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3MgZXQgYWNjdXNhbSBldCBqdXN0byBkdW8=-----END ' + 'PRIVATE KEY----------BEGIN ' + 'CERTIFICATE-----V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3MgZXQgYWNjdXNhbSBldCBqdXN0byBkdW8=-----END ' + 'CERTIFICATE-----ssl: true'}] with open('spec.yml') as f: dic = cd.parse_yaml_objs(f) @@ -1838,3 +2249,78 @@ class TestSNMPGateway: with pytest.raises(Exception) as e: c = cd.get_container(ctx, fsid, 'snmp-gateway', 'daemon_id') assert str(e.value) == 'not a valid snmp version: V1' + +class TestNetworkValidation: + + def test_ipv4_subnet(self): + rc, v, msg = cd.check_subnet('192.168.1.0/24') + assert rc == 0 and v[0] == 4 + + def test_ipv4_subnet_list(self): + rc, v, msg = cd.check_subnet('192.168.1.0/24,10.90.90.0/24') + assert rc == 0 and not msg + + def test_ipv4_subnet_list_with_spaces(self): + rc, v, msg = cd.check_subnet('192.168.1.0/24, 10.90.90.0/24 ') + assert rc == 0 and not msg + + def test_ipv4_subnet_badlist(self): + rc, v, msg = cd.check_subnet('192.168.1.0/24,192.168.1.1') + assert rc == 1 and msg + + def test_ipv4_subnet_mixed(self): + rc, v, msg = cd.check_subnet('192.168.100.0/24,fe80::/64') + assert rc == 0 and v == [4,6] + + def test_ipv6_subnet(self): + rc, v, msg = cd.check_subnet('fe80::/64') + assert rc == 0 and v[0] == 6 + + def test_subnet_mask_missing(self): + rc, v, msg = cd.check_subnet('192.168.1.58') + assert rc == 1 and msg + + def test_subnet_mask_junk(self): + rc, v, msg = cd.check_subnet('wah') + assert rc == 1 and msg + + def test_ip_in_subnet(self): + # valid ip and only one valid subnet + rc = cd.ip_in_subnets('192.168.100.1', '192.168.100.0/24') + assert rc is True + + # valid ip and valid subnets list without spaces + rc = cd.ip_in_subnets('192.168.100.1', '192.168.100.0/24,10.90.90.0/24') + assert rc is True + + # valid ip and valid subnets list with spaces + rc = cd.ip_in_subnets('10.90.90.2', '192.168.1.0/24, 192.168.100.0/24, 10.90.90.0/24') + assert rc is True + + # valid ip that doesn't belong to any subnet + rc = cd.ip_in_subnets('192.168.100.2', '192.168.50.0/24, 10.90.90.0/24') + assert rc is False + + # valid ip that doesn't belong to the subnet (only 14 hosts) + rc = cd.ip_in_subnets('192.168.100.20', '192.168.100.0/28') + assert rc is False + + # valid ip and valid IPV6 network + rc = cd.ip_in_subnets('fe80::5054:ff:fef4:873a', 'fe80::/64') + assert rc is True + + # valid wrapped ip and valid IPV6 network + rc = cd.ip_in_subnets('[fe80::5054:ff:fef4:873a]', 'fe80::/64') + assert rc is True + + # valid ip and that doesn't belong to IPV6 network + rc = cd.ip_in_subnets('fe80::5054:ff:fef4:873a', '2001:db8:85a3::/64') + assert rc is False + + # invalid IPv4 and valid subnets list + with pytest.raises(Exception): + rc = cd.ip_in_sublets('10.90.200.', '192.168.1.0/24, 192.168.100.0/24, 10.90.90.0/24') + + # invalid IPv6 and valid subnets list + with pytest.raises(Exception): + rc = cd.ip_in_sublets('fe80:2030:31:24', 'fe80::/64') \ No newline at end of file diff --git a/ceph/src/cephadm/tests/test_networks.py b/ceph/src/cephadm/tests/test_networks.py index ef0f6778a..6fa47ea27 100644 --- a/ceph/src/cephadm/tests/test_networks.py +++ b/ceph/src/cephadm/tests/test_networks.py @@ -24,6 +24,8 @@ class TestCommandListNetworks: 139.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50 140.1.0.0/17 via 10.4.0.1 dev tun0 proto static metric 50 141.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50 + 172.16.100.34 via 172.16.100.34 dev eth1 proto kernel scope link src 172.16.100.34 + 192.168.122.1 dev ens3 proto dhcp scope link src 192.168.122.236 metric 100 169.254.0.0/16 dev docker0 scope link metric 1000 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 192.168.39.0/24 dev virbr1 proto kernel scope link src 192.168.39.1 linkdown @@ -33,7 +35,9 @@ class TestCommandListNetworks: 195.135.221.12 via 192.168.178.1 dev enxd89ef3f34260 proto static metric 100 """), { - '10.4.0.1': {'tun0': {'10.4.0.2'}}, + '172.16.100.34/32': {'eth1': {'172.16.100.34'}}, + '192.168.122.1/32': {'ens3': {'192.168.122.236'}}, + '10.4.0.1/32': {'tun0': {'10.4.0.2'}}, '172.17.0.0/16': {'docker0': {'172.17.0.1'}}, '192.168.39.0/24': {'virbr1': {'192.168.39.1'}}, '192.168.122.0/24': {'virbr0': {'192.168.122.1'}}, @@ -61,7 +65,7 @@ class TestCommandListNetworks: { '10.3.64.0/24': {'eno1': {'10.3.64.23', '10.3.64.27'}}, '10.88.0.0/16': {'cni-podman0': {'10.88.0.1'}}, - '172.21.3.1': {'tun0': {'172.21.3.2'}}, + '172.21.3.1/32': {'tun0': {'172.21.3.2'}}, '192.168.122.0/24': {'virbr0': {'192.168.122.1'}} } ), @@ -175,11 +179,14 @@ class TestCommandListNetworks: valid_lft forever preferred_lft forever """), { - '2001:1458:301:eb::/64': { + '2001:1458:301:eb::100:1a/128': { 'ens20f0': { '2001:1458:301:eb::100:1a' }, }, + '2001:1458:301:eb::/64': { + 'ens20f0': set(), + }, 'fe80::/64': { 'ens20f0': {'fe80::2e60:cff:fef8:da41'}, }, @@ -224,5 +231,5 @@ class TestCommandListNetworks: with with_cephadm_ctx([]) as ctx: cd.command_list_networks(ctx) assert json.loads(capsys.readouterr().out) == { - '10.4.0.1': {'tun0': ['10.4.0.2']} + '10.4.0.1/32': {'tun0': ['10.4.0.2']} } \ No newline at end of file diff --git a/ceph/src/cephadm/tox.ini b/ceph/src/cephadm/tox.ini index cf76cfa31..4f8d3d7af 100644 --- a/ceph/src/cephadm/tox.ini +++ b/ceph/src/cephadm/tox.ini @@ -62,4 +62,6 @@ deps = flake8-quotes commands = flake8 --config=tox.ini {posargs:cephadm} - bash -c "test $(grep 'docker.io' cephadm | wc -l) == 11" + bash -c "test $(grep 'docker.io' cephadm | wc -l) == 13" +# Downstream distributions may choose to alter this "docker.io" number, +# to make sure no new references to docker.io are creeping in unnoticed. diff --git a/ceph/src/client/Client.cc b/ceph/src/client/Client.cc index 58b194fe7..33253ed10 100644 --- a/ceph/src/client/Client.cc +++ b/ceph/src/client/Client.cc @@ -354,6 +354,9 @@ Client::Client(Messenger *m, MonClient *mc, Objecter *objecter_) fuse_default_permissions = cct->_conf.get_val( "fuse_default_permissions"); + _collect_and_send_global_metrics = cct->_conf.get_val( + "client_collect_and_send_global_metrics"); + if (cct->_conf->client_acl_type == "posix_acl") acl_type = POSIX_ACL; @@ -552,7 +555,6 @@ void Client::_pre_init() objecter_finisher.start(); filer.reset(new Filer(objecter, &objecter_finisher)); - objecter->enable_blocklist_events(); objectcacher->start(); } @@ -2227,6 +2229,7 @@ void Client::handle_client_session(const MConstRef& m) break; } session->mds_features = std::move(m->supported_features); + session->mds_metric_flags = std::move(m->metric_spec.metric_flags); renew_caps(session.get()); session->state = MetaSession::STATE_OPEN; @@ -2444,6 +2447,36 @@ void Client::handle_client_request_forward(const MConstRefnum_fwd); + max_fwd = 1 << (max_fwd * CHAR_BIT) - 1; + auto num_fwd = fwd->get_num_fwd(); + if (num_fwd <= request->num_fwd || num_fwd >= max_fwd) { + if (request->num_fwd >= max_fwd || num_fwd >= max_fwd) { + request->abort(-EMULTIHOP); + request->caller_cond->notify_all(); + ldout(cct, 1) << __func__ << " tid " << tid << " seq overflow" + << ", abort it" << dendl; + } else { + ldout(cct, 10) << __func__ << " tid " << tid + << " old fwd seq " << fwd->get_num_fwd() + << " <= req fwd " << request->num_fwd + << ", ignore it" << dendl; + } + return; + } + // reset retry counter request->retry_attempt = 0; @@ -2457,7 +2490,7 @@ void Client::handle_client_request_forward(const MConstRefmds = -1; request->item.remove_myself(); - request->num_fwd = fwd->get_num_fwd(); + request->num_fwd = num_fwd; request->resend_mds = fwd->get_dest_mds(); request->caller_cond->notify_all(); } @@ -2620,36 +2653,15 @@ void Client::_handle_full_flag(int64_t pool) void Client::handle_osd_map(const MConstRef& m) { - std::set new_blocklists; - std::scoped_lock cl(client_lock); - objecter->consume_blocklist_events(&new_blocklists); const auto myaddrs = messenger->get_myaddrs(); - bool new_blocklist = false; - bool prenautilus = objecter->with_osdmap( + bool new_blocklist = objecter->with_osdmap( [&](const OSDMap& o) { - return o.require_osd_release < ceph_release_t::nautilus; + return o.is_blocklisted(myaddrs); }); - if (!blocklisted) { - for (auto a : myaddrs.v) { - // blocklist entries are always TYPE_ANY for nautilus+ - a.set_type(entity_addr_t::TYPE_ANY); - if (new_blocklists.count(a)) { - new_blocklist = true; - break; - } - if (prenautilus) { - // ...except pre-nautilus, they were TYPE_LEGACY - a.set_type(entity_addr_t::TYPE_LEGACY); - if (new_blocklists.count(a)) { - new_blocklist = true; - break; - } - } - } - } - if (new_blocklist) { + + if (new_blocklist && !blocklisted) { auto epoch = objecter->with_osdmap([](const OSDMap &o){ return o.get_epoch(); }); @@ -6663,57 +6675,81 @@ void Client::collect_and_send_global_metrics() { std::vector message; // read latency - metric = ClientMetricMessage(ReadLatencyPayload(logger->tget(l_c_read))); - message.push_back(metric); + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_READ_LATENCY)) { + metric = ClientMetricMessage(ReadLatencyPayload(logger->tget(l_c_read))); + message.push_back(metric); + } // write latency - metric = ClientMetricMessage(WriteLatencyPayload(logger->tget(l_c_wrlat))); - message.push_back(metric); + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_WRITE_LATENCY)) { + metric = ClientMetricMessage(WriteLatencyPayload(logger->tget(l_c_wrlat))); + message.push_back(metric); + } // metadata latency - metric = ClientMetricMessage(MetadataLatencyPayload(logger->tget(l_c_lat))); - message.push_back(metric); + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_METADATA_LATENCY)) { + metric = ClientMetricMessage(MetadataLatencyPayload(logger->tget(l_c_lat))); + message.push_back(metric); + } // cap hit ratio -- nr_caps is unused right now - auto [cap_hits, cap_misses] = get_cap_hit_rates(); - metric = ClientMetricMessage(CapInfoPayload(cap_hits, cap_misses, 0)); - message.push_back(metric); + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_CAP_INFO)) { + auto [cap_hits, cap_misses] = get_cap_hit_rates(); + metric = ClientMetricMessage(CapInfoPayload(cap_hits, cap_misses, 0)); + message.push_back(metric); + } // dentry lease hit ratio - auto [dlease_hits, dlease_misses, nr] = get_dlease_hit_rates(); - metric = ClientMetricMessage(DentryLeasePayload(dlease_hits, dlease_misses, nr)); - message.push_back(metric); + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_DENTRY_LEASE)) { + auto [dlease_hits, dlease_misses, nr] = get_dlease_hit_rates(); + metric = ClientMetricMessage(DentryLeasePayload(dlease_hits, dlease_misses, nr)); + message.push_back(metric); + } // opened files - { + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_OPENED_FILES)) { auto [opened_files, total_inodes] = get_opened_files_rates(); metric = ClientMetricMessage(OpenedFilesPayload(opened_files, total_inodes)); + message.push_back(metric); } - message.push_back(metric); // pinned i_caps - { + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_PINNED_ICAPS)) { auto [pinned_icaps, total_inodes] = get_pinned_icaps_rates(); metric = ClientMetricMessage(PinnedIcapsPayload(pinned_icaps, total_inodes)); + message.push_back(metric); } - message.push_back(metric); // opened inodes - { + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_OPENED_INODES)) { auto [opened_inodes, total_inodes] = get_opened_inodes_rates(); metric = ClientMetricMessage(OpenedInodesPayload(opened_inodes, total_inodes)); + message.push_back(metric); } - message.push_back(metric); // read io sizes - metric = ClientMetricMessage(ReadIoSizesPayload(total_read_ops, - total_read_size)); - message.push_back(metric); + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_READ_IO_SIZES)) { + metric = ClientMetricMessage(ReadIoSizesPayload(total_read_ops, + total_read_size)); + message.push_back(metric); + } // write io sizes - metric = ClientMetricMessage(WriteIoSizesPayload(total_write_ops, - total_write_size)); - message.push_back(metric); + if (_collect_and_send_global_metrics || + session->mds_metric_flags.test(CLIENT_METRIC_TYPE_WRITE_IO_SIZES)) { + metric = ClientMetricMessage(WriteIoSizesPayload(total_write_ops, + total_write_size)); + message.push_back(metric); + } session->con->send_message2(make_message(std::move(message))); } @@ -15712,6 +15748,10 @@ void Client::handle_conf_change(const ConfigProxy& conf, if (changed.count("client_oc_max_dirty_age")) { objectcacher->set_max_dirty_age(cct->_conf->client_oc_max_dirty_age); } + if (changed.count("client_collect_and_send_global_metrics")) { + _collect_and_send_global_metrics = cct->_conf.get_val( + "client_collect_and_send_global_metrics"); + } } void intrusive_ptr_add_ref(Inode *in) diff --git a/ceph/src/client/Client.h b/ceph/src/client/Client.h index c3cfe09eb..84fc15d3f 100644 --- a/ceph/src/client/Client.h +++ b/ceph/src/client/Client.h @@ -888,6 +888,7 @@ public: std::unique_ptr mdsmap; bool fuse_default_permissions; + bool _collect_and_send_global_metrics; protected: /* Flags for check_caps() */ diff --git a/ceph/src/client/MetaSession.h b/ceph/src/client/MetaSession.h index 4eae49b9d..ad74ae58a 100644 --- a/ceph/src/client/MetaSession.h +++ b/ceph/src/client/MetaSession.h @@ -25,6 +25,7 @@ struct MetaSession { uint64_t cap_renew_seq = 0; entity_addrvec_t addrs; feature_bitset_t mds_features; + feature_bitset_t mds_metric_flags; enum { STATE_NEW, // Unused diff --git a/ceph/src/client/fuse_ll.cc b/ceph/src/client/fuse_ll.cc index c2de8940f..ee8175272 100644 --- a/ceph/src/client/fuse_ll.cc +++ b/ceph/src/client/fuse_ll.cc @@ -24,6 +24,13 @@ #include #include +#if defined(__linux__) +#include +#include +#include +#include +#endif + // ceph #include "common/errno.h" #include "common/safe_io.h" @@ -53,6 +60,14 @@ #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) +#if defined(__linux__) +#ifndef FUSE_SUPER_MAGIC +#define FUSE_SUPER_MAGIC 0x65735546 +#endif + +#define _CEPH_CLIENT_ID "ceph.client_id" +#endif + using namespace std; static const ceph::unordered_map cephfs_errno_to_system_errno = { @@ -163,6 +178,84 @@ public: struct fuse_args args; }; +#if defined(__linux__) +static int already_fuse_mounted(const char *path, bool &already_mounted) +{ + struct statx path_statx; + struct statx parent_statx; + char path_copy[PATH_MAX] = {0}; + char *parent_path = NULL; + int err = 0; + + already_mounted = false; + + strncpy(path_copy, path, sizeof(path_copy)-1); + parent_path = dirname(path_copy); + + // get stat information for original path + if (-1 == statx(AT_FDCWD, path, AT_STATX_DONT_SYNC, STATX_INO, &path_statx)) { + err = errno; + derr << "fuse_ll: already_fuse_mounted: statx(" << path << ") failed with error " + << cpp_strerror(err) << dendl; + return err; + } + + // if path isn't directory, then it can't be a mountpoint. + if (!(path_statx.stx_mode & S_IFDIR)) { + err = EINVAL; + derr << "fuse_ll: already_fuse_mounted: " + << path << " is not a directory" << dendl; + return err; + } + + // get stat information for parent path + if (-1 == statx(AT_FDCWD, parent_path, AT_STATX_DONT_SYNC, STATX_INO, &parent_statx)) { + err = errno; + derr << "fuse_ll: already_fuse_mounted: statx(" << parent_path << ") failed with error " + << cpp_strerror(err) << dendl; + return err; + } + + // if original path and parent have different device ids, + // then the path is a mount point + // or, if they refer to the same path, then it's probably + // the root directory '/' and therefore path is a mountpoint + if( path_statx.stx_dev_major != parent_statx.stx_dev_major || + path_statx.stx_dev_minor != parent_statx.stx_dev_minor || + ( path_statx.stx_dev_major == parent_statx.stx_dev_major && + path_statx.stx_dev_minor == parent_statx.stx_dev_minor && + path_statx.stx_ino == parent_statx.stx_ino + ) + ) { + struct statfs path_statfs; + if (-1 == statfs(path, &path_statfs)) { + err = errno; + derr << "fuse_ll: already_fuse_mounted: statfs(" << path << ") failed with error " + << cpp_strerror(err) << dendl; + return err; + } + + if(FUSE_SUPER_MAGIC == path_statfs.f_type) { + // if getxattr returns positive length means value exist for ceph.client_id + // then ceph fuse is already mounted on path + char client_id[128] = {0}; + if (getxattr(path, _CEPH_CLIENT_ID, &client_id, sizeof(client_id)) > 0) { + already_mounted = true; + derr << path << " already mounted by " << client_id << dendl; + } + } + } + + return err; +} +#else // non-linux platforms +static int already_fuse_mounted(const char *path, bool &already_mounted) +{ + already_mounted = false; + return 0; +} +#endif + static int getgroups(fuse_req_t req, gid_t **sgids) { #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) @@ -1349,6 +1442,20 @@ int CephFuse::Handle::init(int argc, const char *argv[]) int CephFuse::Handle::start() { + bool is_mounted = false; +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + int err = already_fuse_mounted(opts.mountpoint, is_mounted); +#else + int err = already_fuse_mounted(mountpoint, is_mounted); +#endif + if (err) { + return err; + } + + if (is_mounted) { + return EBUSY; + } + #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) se = fuse_session_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this); if (!se) { diff --git a/ceph/src/cls/rgw/cls_rgw.cc b/ceph/src/cls/rgw/cls_rgw.cc index 432b3aef7..d0046e87d 100644 --- a/ceph/src/cls/rgw/cls_rgw.cc +++ b/ceph/src/cls/rgw/cls_rgw.cc @@ -2205,6 +2205,8 @@ int rgw_dir_suggest_changes(cls_method_context_t hctx, return -EINVAL; } + // remove any pending entries whose tag timeout has expired. until expiry, + // these pending entries will prevent us from applying suggested changes real_time cur_time = real_clock::now(); auto iter = cur_disk.pending_map.begin(); while(iter != cur_disk.pending_map.end()) { @@ -2215,9 +2217,18 @@ int rgw_dir_suggest_changes(cls_method_context_t hctx, } } - CLS_LOG(20, "cur_disk.pending_map.empty()=%d op=%d cur_disk.exists=%d cur_change.pending_map.size()=%d cur_change.exists=%d", + CLS_LOG(20, "cur_disk.pending_map.empty()=%d op=%d cur_disk.exists=%d " + "cur_disk.index_ver=%d cur_change.exists=%d cur_change.index_ver=%d", cur_disk.pending_map.empty(), (int)op, cur_disk.exists, - (int)cur_change.pending_map.size(), cur_change.exists); + (int)cur_disk.index_ver, cur_change.exists, + (int)cur_change.index_ver); + + if (cur_change.index_ver < cur_disk.index_ver) { + // a pending on-disk entry was completed since this suggestion was made, + // don't apply it yet. if the index really is inconsistent, the next + // listing will get the latest version and resend the suggestion + continue; + } if (cur_disk.pending_map.empty()) { if (cur_disk.exists) { diff --git a/ceph/src/common/options/global.yaml.in b/ceph/src/common/options/global.yaml.in index 1e9fd067f..3bba58829 100644 --- a/ceph/src/common/options/global.yaml.in +++ b/ceph/src/common/options/global.yaml.in @@ -5418,6 +5418,17 @@ options: flags: - runtime with_legacy: true +- name: bluestore_zero_block_detection + type: bool + level: dev + desc: punch holes instead of writing zeros + long_desc: Intended for large-scale synthetic testing. Currently this is implemented + with punch hole semantics, affecting the logical extent map of the object. This does + not interact well with some RBD and CephFS features. + default: false + flags: + - runtime + with_legacy: true - name: kstore_max_ops type: uint level: advanced diff --git a/ceph/src/common/options/mds-client.yaml.in b/ceph/src/common/options/mds-client.yaml.in index 403e00975..1881130f8 100644 --- a/ceph/src/common/options/mds-client.yaml.in +++ b/ceph/src/common/options/mds-client.yaml.in @@ -546,3 +546,16 @@ options: min: 0 flags: - runtime +- name: client_collect_and_send_global_metrics + type: bool + level: advanced + desc: to enable and force collecting and sending the global metrics to MDS + long_desc: To be careful for this, when connecting to some old ceph clusters + it may crash the MDS daemons while upgrading. + default: false + tags: + - client + services: + - mds_client + flags: + - runtime diff --git a/ceph/src/common/options/mds.yaml.in b/ceph/src/common/options/mds.yaml.in index 697710484..53b6e0423 100644 --- a/ceph/src/common/options/mds.yaml.in +++ b/ceph/src/common/options/mds.yaml.in @@ -196,6 +196,14 @@ options: services: - mds with_legacy: true +- name: mds_heartbeat_reset_grace + type: uint + level: advanced + desc: the basic unit of tolerance in how many circles in a loop, which will + keep running by holding the mds_lock, it must trigger to reset heartbeat + default: 1000 + services: + - mds - name: mds_heartbeat_grace type: float level: advanced diff --git a/ceph/src/common/options/osd.yaml.in b/ceph/src/common/options/osd.yaml.in index 8360e4a92..d8c98b1f2 100644 --- a/ceph/src/common/options/osd.yaml.in +++ b/ceph/src/common/options/osd.yaml.in @@ -1278,3 +1278,9 @@ options: default: 1 flags: - runtime +- name: osd_rocksdb_iterator_bounds_enabled + desc: Whether omap iterator bounds are applied to rocksdb iterator ReadOptions + type: bool + level: dev + default: true + with_legacy: true diff --git a/ceph/src/common/options/rbd.yaml.in b/ceph/src/common/options/rbd.yaml.in index 143241c2b..770365db5 100644 --- a/ceph/src/common/options/rbd.yaml.in +++ b/ceph/src/common/options/rbd.yaml.in @@ -830,13 +830,6 @@ options: - disabled - rwl - ssd -- name: rbd_persistent_cache_log_periodic_stats - type: bool - level: advanced - desc: emit periodic perf stats to debug log - default: false - services: - - rbd - name: rbd_persistent_cache_size type: uint level: advanced diff --git a/ceph/src/common/options/rgw.yaml.in b/ceph/src/common/options/rgw.yaml.in index ac7f6ac1b..a561a3b12 100644 --- a/ceph/src/common/options/rgw.yaml.in +++ b/ceph/src/common/options/rgw.yaml.in @@ -1157,15 +1157,6 @@ options: - rgw min: 0.01 max: 100000 -- name: rgw_rados_pool_pg_num_min - type: uint - level: advanced - desc: pg_num_min value for RGW metadata (omap-heavy) pools - default: 8 - services: - - rgw - min: 1 - max: 1_K - name: rgw_rados_pool_recovery_priority type: uint level: advanced diff --git a/ceph/src/include/ceph_features.h b/ceph/src/include/ceph_features.h index ab05205c1..1287d3943 100644 --- a/ceph/src/include/ceph_features.h +++ b/ceph/src/include/ceph_features.h @@ -133,7 +133,7 @@ DEFINE_CEPH_FEATURE(32, 3, STRETCH_MODE) DEFINE_CEPH_FEATURE_RETIRED(33, 1, MON_SCRUB, JEWEL, LUMINOUS) DEFINE_CEPH_FEATURE(33, 3, SERVER_QUINCY) DEFINE_CEPH_FEATURE_RETIRED(34, 1, OSD_PACKED_RECOVERY, JEWEL, LUMINOUS) -// available +DEFINE_CEPH_FEATURE(34, 3, RANGE_BLOCKLIST) DEFINE_CEPH_FEATURE(35, 1, OSD_CACHEPOOL) // 3.14 DEFINE_CEPH_FEATURE(36, 1, CRUSH_V2) // 3.14 DEFINE_CEPH_FEATURE(37, 1, EXPORT_PEER) // 3.14 @@ -249,6 +249,7 @@ DEFINE_CEPH_FEATURE_DEPRECATED(63, 1, RESERVED_BROKEN, LUMINOUS) // client-facin CEPH_FEATUREMASK_SERVER_PACIFIC | \ CEPH_FEATURE_OSD_FIXED_COLLECTION_LIST | \ CEPH_FEATUREMASK_SERVER_QUINCY | \ + CEPH_FEATURE_RANGE_BLOCKLIST | \ 0ULL) #define CEPH_FEATURES_SUPPORTED_DEFAULT CEPH_FEATURES_ALL diff --git a/ceph/src/kv/KeyValueDB.h b/ceph/src/kv/KeyValueDB.h index 3dd0894bb..98bf0c07c 100644 --- a/ceph/src/kv/KeyValueDB.h +++ b/ceph/src/kv/KeyValueDB.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include "include/encoding.h" @@ -324,8 +325,14 @@ private: public: typedef uint32_t IteratorOpts; static const uint32_t ITERATOR_NOCACHE = 1; + + struct IteratorBounds { + std::optional lower_bound; + std::optional upper_bound; + }; + virtual WholeSpaceIterator get_wholespace_iterator(IteratorOpts opts = 0) = 0; - virtual Iterator get_iterator(const std::string &prefix, IteratorOpts opts = 0) { + virtual Iterator get_iterator(const std::string &prefix, IteratorOpts opts = 0, IteratorBounds bounds = IteratorBounds()) { return std::make_shared( prefix, get_wholespace_iterator(opts)); diff --git a/ceph/src/kv/RocksDBStore.cc b/ceph/src/kv/RocksDBStore.cc index 0379966c1..070f79a60 100644 --- a/ceph/src/kv/RocksDBStore.cc +++ b/ceph/src/kv/RocksDBStore.cc @@ -622,6 +622,18 @@ bool RocksDBStore::is_column_family(const std::string& prefix) { return cf_handles.count(prefix); } +std::string_view RocksDBStore::get_key_hash_view(const prefix_shards& shards, const char* key, const size_t keylen) { + uint32_t hash_l = std::min(shards.hash_l, keylen); + uint32_t hash_h = std::min(shards.hash_h, keylen); + return { key + hash_l, hash_h - hash_l }; +} + +rocksdb::ColumnFamilyHandle *RocksDBStore::get_key_cf(const prefix_shards& shards, const char* key, const size_t keylen) { + auto sv = get_key_hash_view(shards, key, keylen); + uint32_t hash = ceph_str_hash_rjenkins(sv.data(), sv.size()); + return shards.handles[hash % shards.handles.size()]; +} + rocksdb::ColumnFamilyHandle *RocksDBStore::get_cf_handle(const std::string& prefix, const std::string& key) { auto iter = cf_handles.find(prefix); if (iter == cf_handles.end()) { @@ -630,10 +642,7 @@ rocksdb::ColumnFamilyHandle *RocksDBStore::get_cf_handle(const std::string& pref if (iter->second.handles.size() == 1) { return iter->second.handles[0]; } else { - uint32_t hash_l = std::min(iter->second.hash_l, key.size()); - uint32_t hash_h = std::min(iter->second.hash_h, key.size()); - uint32_t hash = ceph_str_hash_rjenkins(&key[hash_l], hash_h - hash_l); - return iter->second.handles[hash % iter->second.handles.size()]; + return get_key_cf(iter->second, key.data(), key.size()); } } } @@ -646,14 +655,37 @@ rocksdb::ColumnFamilyHandle *RocksDBStore::get_cf_handle(const std::string& pref if (iter->second.handles.size() == 1) { return iter->second.handles[0]; } else { - uint32_t hash_l = std::min(iter->second.hash_l, keylen); - uint32_t hash_h = std::min(iter->second.hash_h, keylen); - uint32_t hash = ceph_str_hash_rjenkins(&key[hash_l], hash_h - hash_l); - return iter->second.handles[hash % iter->second.handles.size()]; + return get_key_cf(iter->second, key, keylen); } } } +/** + * If the specified IteratorBounds arg has both an upper and a lower bound defined, and they have equal placement hash + * strings, we can be sure that the entire iteration range exists in a single CF. In that case, we return the relevant + * CF handle. In all other cases, we return a nullptr to indicate that the specified bounds cannot necessarily be mapped + * to a single CF. + */ +rocksdb::ColumnFamilyHandle *RocksDBStore::get_cf_handle(const std::string& prefix, const IteratorBounds& bounds) { + if (!bounds.lower_bound || !bounds.upper_bound) { + return nullptr; + } + auto iter = cf_handles.find(prefix); + ceph_assert(iter != cf_handles.end()); + ceph_assert(iter->second.handles.size() != 1); + if (iter->second.hash_l != 0) { + return nullptr; + } + auto lower_bound_hash_str = get_key_hash_view(iter->second, bounds.lower_bound->data(), bounds.lower_bound->size()); + auto upper_bound_hash_str = get_key_hash_view(iter->second, bounds.upper_bound->data(), bounds.upper_bound->size()); + if (lower_bound_hash_str == upper_bound_hash_str) { + auto key = *bounds.lower_bound; + return get_key_cf(iter->second, key.data(), key.size()); + } else { + return nullptr; + } +} + /** * Definition of sharding: * space-separated list of: column_def [ '=' options ] @@ -1353,9 +1385,6 @@ int64_t RocksDBStore::estimate_prefix_size(const string& prefix, const string& key_prefix) { uint64_t size = 0; - uint8_t flags = - //rocksdb::DB::INCLUDE_MEMTABLES | // do not include memtables... - rocksdb::DB::INCLUDE_FILES; auto p_iter = cf_handles.find(prefix); if (p_iter != cf_handles.end()) { for (auto cf : p_iter->second.handles) { @@ -1363,14 +1392,14 @@ int64_t RocksDBStore::estimate_prefix_size(const string& prefix, string start = key_prefix + string(1, '\x00'); string limit = key_prefix + string("\xff\xff\xff\xff"); rocksdb::Range r(start, limit); - db->GetApproximateSizes(cf, &r, 1, &s, flags); + db->GetApproximateSizes(cf, &r, 1, &s); size += s; } } else { string start = combine_strings(prefix , key_prefix); string limit = combine_strings(prefix , key_prefix + "\xff\xff\xff\xff"); rocksdb::Range r(start, limit); - db->GetApproximateSizes(default_cf, &r, 1, &size, flags); + db->GetApproximateSizes(default_cf, &r, 1, &size); } return size; } @@ -2194,10 +2223,29 @@ class CFIteratorImpl : public KeyValueDB::IteratorImpl { protected: string prefix; rocksdb::Iterator *dbiter; + const KeyValueDB::IteratorBounds bounds; + const rocksdb::Slice iterate_lower_bound; + const rocksdb::Slice iterate_upper_bound; public: - explicit CFIteratorImpl(const std::string& p, - rocksdb::Iterator *iter) - : prefix(p), dbiter(iter) { } + explicit CFIteratorImpl(const RocksDBStore* db, + const std::string& p, + rocksdb::ColumnFamilyHandle* cf, + KeyValueDB::IteratorBounds bounds_) + : prefix(p), bounds(std::move(bounds_)), + iterate_lower_bound(make_slice(bounds.lower_bound)), + iterate_upper_bound(make_slice(bounds.upper_bound)) + { + auto options = rocksdb::ReadOptions(); + if (db->cct->_conf->osd_rocksdb_iterator_bounds_enabled) { + if (bounds.lower_bound) { + options.iterate_lower_bound = &iterate_lower_bound; + } + if (bounds.upper_bound) { + options.iterate_upper_bound = &iterate_upper_bound; + } + } + dbiter = db->db->NewIterator(options, cf); + } ~CFIteratorImpl() { delete dbiter; } @@ -2729,16 +2777,31 @@ private: const RocksDBStore* db; KeyLess keyless; string prefix; + const KeyValueDB::IteratorBounds bounds; + const rocksdb::Slice iterate_lower_bound; + const rocksdb::Slice iterate_upper_bound; std::vector iters; public: explicit ShardMergeIteratorImpl(const RocksDBStore* db, const std::string& prefix, - const std::vector& shards) - : db(db), keyless(db->comparator), prefix(prefix) + const std::vector& shards, + KeyValueDB::IteratorBounds bounds_) + : db(db), keyless(db->comparator), prefix(prefix), bounds(std::move(bounds_)), + iterate_lower_bound(make_slice(bounds.lower_bound)), + iterate_upper_bound(make_slice(bounds.upper_bound)) { iters.reserve(shards.size()); + auto options = rocksdb::ReadOptions(); + if (db->cct->_conf->osd_rocksdb_iterator_bounds_enabled) { + if (bounds.lower_bound) { + options.iterate_lower_bound = &iterate_lower_bound; + } + if (bounds.upper_bound) { + options.iterate_upper_bound = &iterate_upper_bound; + } + } for (auto& s : shards) { - iters.push_back(db->db->NewIterator(rocksdb::ReadOptions(), s)); + iters.push_back(db->db->NewIterator(options, s)); } } ~ShardMergeIteratorImpl() { @@ -2909,19 +2972,28 @@ public: } }; -KeyValueDB::Iterator RocksDBStore::get_iterator(const std::string& prefix, IteratorOpts opts) +KeyValueDB::Iterator RocksDBStore::get_iterator(const std::string& prefix, IteratorOpts opts, IteratorBounds bounds) { auto cf_it = cf_handles.find(prefix); if (cf_it != cf_handles.end()) { + rocksdb::ColumnFamilyHandle* cf = nullptr; if (cf_it->second.handles.size() == 1) { + cf = cf_it->second.handles[0]; + } else if (cct->_conf->osd_rocksdb_iterator_bounds_enabled) { + cf = get_cf_handle(prefix, bounds); + } + if (cf) { return std::make_shared( - prefix, - db->NewIterator(rocksdb::ReadOptions(), cf_it->second.handles[0])); + this, + prefix, + cf, + std::move(bounds)); } else { return std::make_shared( this, prefix, - cf_it->second.handles); + cf_it->second.handles, + std::move(bounds)); } } else { return KeyValueDB::get_iterator(prefix, opts); @@ -2936,11 +3008,8 @@ rocksdb::Iterator* RocksDBStore::new_shard_iterator(rocksdb::ColumnFamilyHandle* RocksDBStore::WholeSpaceIterator RocksDBStore::get_wholespace_iterator(IteratorOpts opts) { if (cf_handles.size() == 0) { - rocksdb::ReadOptions opt = rocksdb::ReadOptions(); - if (opts & ITERATOR_NOCACHE) - opt.fill_cache=false; return std::make_shared( - db->NewIterator(opt, default_cf)); + this, default_cf, opts); } else { return std::make_shared(this); } @@ -2948,8 +3017,7 @@ RocksDBStore::WholeSpaceIterator RocksDBStore::get_wholespace_iterator(IteratorO RocksDBStore::WholeSpaceIterator RocksDBStore::get_default_cf_iterator() { - return std::make_shared( - db->NewIterator(rocksdb::ReadOptions(), default_cf)); + return std::make_shared(this, default_cf, 0); } int RocksDBStore::prepare_for_reshard(const std::string& new_sharding, diff --git a/ceph/src/kv/RocksDBStore.h b/ceph/src/kv/RocksDBStore.h index c15ff7bf0..25a2045ff 100644 --- a/ceph/src/kv/RocksDBStore.h +++ b/ceph/src/kv/RocksDBStore.h @@ -64,6 +64,14 @@ namespace rocksdb{ extern rocksdb::Logger *create_rocksdb_ceph_logger(); +inline rocksdb::Slice make_slice(const std::optional& bound) { + if (bound) { + return {*bound}; + } else { + return {}; + } +} + /** * Uses RocksDB to implement the KeyValueDB interface */ @@ -83,6 +91,7 @@ class RocksDBStore : public KeyValueDB { uint64_t cache_size = 0; bool set_cache_flag = false; friend class ShardMergeIteratorImpl; + friend class CFIteratorImpl; friend class WholeMergeIteratorImpl; /* * See RocksDB's definition of a column family(CF) and how to use it. @@ -119,8 +128,11 @@ private: void add_column_family(const std::string& cf_name, uint32_t hash_l, uint32_t hash_h, size_t shard_idx, rocksdb::ColumnFamilyHandle *handle); bool is_column_family(const std::string& prefix); + std::string_view get_key_hash_view(const prefix_shards& shards, const char* key, const size_t keylen); + rocksdb::ColumnFamilyHandle *get_key_cf(const prefix_shards& shards, const char* key, const size_t keylen); rocksdb::ColumnFamilyHandle *get_cf_handle(const std::string& prefix, const std::string& key); rocksdb::ColumnFamilyHandle *get_cf_handle(const std::string& prefix, const char* key, size_t keylen); + rocksdb::ColumnFamilyHandle *get_cf_handle(const std::string& prefix, const IteratorBounds& bounds); int submit_common(rocksdb::WriteOptions& woptions, KeyValueDB::Transaction t); int install_cf_mergeop(const std::string &cf_name, rocksdb::ColumnFamilyOptions *cf_opt); @@ -342,9 +354,15 @@ public: protected: rocksdb::Iterator *dbiter; public: - explicit RocksDBWholeSpaceIteratorImpl(rocksdb::Iterator *iter) : - dbiter(iter) { } - //virtual ~RocksDBWholeSpaceIteratorImpl() { } + explicit RocksDBWholeSpaceIteratorImpl(const RocksDBStore* db, + rocksdb::ColumnFamilyHandle* cf, + const KeyValueDB::IteratorOpts opts) + { + rocksdb::ReadOptions options = rocksdb::ReadOptions(); + if (opts & ITERATOR_NOCACHE) + options.fill_cache=false; + dbiter = db->db->NewIterator(options, cf); + } ~RocksDBWholeSpaceIteratorImpl() override; int seek_to_first() override; @@ -366,7 +384,7 @@ public: size_t value_size() override; }; - Iterator get_iterator(const std::string& prefix, IteratorOpts opts = 0) override; + Iterator get_iterator(const std::string& prefix, IteratorOpts opts = 0, IteratorBounds = IteratorBounds()) override; private: /// this iterator spans single cf rocksdb::Iterator* new_shard_iterator(rocksdb::ColumnFamilyHandle* cf); diff --git a/ceph/src/kv/rocksdb_cache/BinnedLRUCache.cc b/ceph/src/kv/rocksdb_cache/BinnedLRUCache.cc index fce26c7b0..2e66c16ee 100644 --- a/ceph/src/kv/rocksdb_cache/BinnedLRUCache.cc +++ b/ceph/src/kv/rocksdb_cache/BinnedLRUCache.cc @@ -570,7 +570,7 @@ void BinnedLRUCache::DisownData() { #endif // !__SANITIZE_ADDRESS__ } -#if (ROCKSDB_MAJOR >= 6 && ROCKSDB_MINOR >= 22) +#if (ROCKSDB_MAJOR >= 7 || (ROCKSDB_MAJOR == 6 && ROCKSDB_MINOR >= 22)) DeleterFn BinnedLRUCache::GetDeleter(Handle* handle) const { return reinterpret_cast(handle)->deleter; diff --git a/ceph/src/kv/rocksdb_cache/BinnedLRUCache.h b/ceph/src/kv/rocksdb_cache/BinnedLRUCache.h index fcf49b7e8..d6d109d9d 100644 --- a/ceph/src/kv/rocksdb_cache/BinnedLRUCache.h +++ b/ceph/src/kv/rocksdb_cache/BinnedLRUCache.h @@ -327,7 +327,7 @@ class BinnedLRUCache : public ShardedCache { virtual size_t GetCharge(Handle* handle) const override; virtual uint32_t GetHash(Handle* handle) const override; virtual void DisownData() override; -#if (ROCKSDB_MAJOR >= 6 && ROCKSDB_MINOR >= 22) +#if (ROCKSDB_MAJOR >= 7 || (ROCKSDB_MAJOR == 6 && ROCKSDB_MINOR >= 22)) virtual DeleterFn GetDeleter(Handle* handle) const override; #endif // Retrieves number of elements in LRU, for unit test purpose only diff --git a/ceph/src/kv/rocksdb_cache/ShardedCache.cc b/ceph/src/kv/rocksdb_cache/ShardedCache.cc index 6cbd89ad6..7d160f9c7 100644 --- a/ceph/src/kv/rocksdb_cache/ShardedCache.cc +++ b/ceph/src/kv/rocksdb_cache/ShardedCache.cc @@ -109,7 +109,7 @@ size_t ShardedCache::GetPinnedUsage() const { return usage; } -#if (ROCKSDB_MAJOR >= 6 && ROCKSDB_MINOR >= 22) +#if (ROCKSDB_MAJOR >= 7 || (ROCKSDB_MAJOR == 6 && ROCKSDB_MINOR >= 22)) DeleterFn ShardedCache::GetDeleter(Handle* handle) const { uint32_t hash = GetHash(handle); diff --git a/ceph/src/kv/rocksdb_cache/ShardedCache.h b/ceph/src/kv/rocksdb_cache/ShardedCache.h index 0ed692233..ef4b10d8f 100644 --- a/ceph/src/kv/rocksdb_cache/ShardedCache.h +++ b/ceph/src/kv/rocksdb_cache/ShardedCache.h @@ -83,11 +83,11 @@ class ShardedCache : public rocksdb::Cache, public PriorityCache::PriCache { virtual size_t GetUsage(rocksdb::Cache::Handle* handle) const override; virtual size_t GetPinnedUsage() const override; virtual size_t GetCharge(Handle* handle) const = 0; -#if (ROCKSDB_MAJOR >= 6 && ROCKSDB_MINOR >= 22) +#if (ROCKSDB_MAJOR >= 7 || (ROCKSDB_MAJOR == 6 && ROCKSDB_MINOR >= 22)) virtual DeleterFn GetDeleter(Handle* handle) const override; #endif virtual void DisownData() override = 0; -#if (ROCKSDB_MAJOR >= 6 && ROCKSDB_MINOR >= 22) +#if (ROCKSDB_MAJOR >= 7 || (ROCKSDB_MAJOR == 6 && ROCKSDB_MINOR >= 22)) virtual void ApplyToAllEntries( const std::function& callback, diff --git a/ceph/src/librbd/cache/Types.h b/ceph/src/librbd/cache/Types.h index b791d5908..43dcd758f 100644 --- a/ceph/src/librbd/cache/Types.h +++ b/ceph/src/librbd/cache/Types.h @@ -20,7 +20,7 @@ enum ImageCacheType { typedef std::list Contexts; -const std::string IMAGE_CACHE_STATE = ".librbd/image_cache_state"; +const std::string PERSISTENT_CACHE_STATE = ".rbd_persistent_cache_state"; } // namespace cache } // namespace librbd diff --git a/ceph/src/librbd/cache/pwl/AbstractWriteLog.cc b/ceph/src/librbd/cache/pwl/AbstractWriteLog.cc index b11d947d3..49f4161ef 100644 --- a/ceph/src/librbd/cache/pwl/AbstractWriteLog.cc +++ b/ceph/src/librbd/cache/pwl/AbstractWriteLog.cc @@ -315,7 +315,8 @@ void AbstractWriteLog::log_perf() { template void AbstractWriteLog::periodic_stats() { std::lock_guard locker(m_lock); - ldout(m_image_ctx.cct, 1) << "STATS: m_log_entries=" << m_log_entries.size() + update_image_cache_state(); + ldout(m_image_ctx.cct, 5) << "STATS: m_log_entries=" << m_log_entries.size() << ", m_dirty_log_entries=" << m_dirty_log_entries.size() << ", m_free_log_entries=" << m_free_log_entries << ", m_bytes_allocated=" << m_bytes_allocated @@ -332,15 +333,12 @@ void AbstractWriteLog::periodic_stats() { template void AbstractWriteLog::arm_periodic_stats() { ceph_assert(ceph_mutex_is_locked(*m_timer_lock)); - if (m_periodic_stats_enabled) { - m_timer_ctx = new LambdaContext( - [this](int r) { - /* m_timer_lock is held */ - periodic_stats(); - arm_periodic_stats(); - }); - m_timer->add_event_after(LOG_STATS_INTERVAL_SECONDS, m_timer_ctx); - } + m_timer_ctx = new LambdaContext([this](int r) { + /* m_timer_lock is held */ + periodic_stats(); + arm_periodic_stats(); + }); + m_timer->add_event_after(LOG_STATS_INTERVAL_SECONDS, m_timer_ctx); } template @@ -560,25 +558,56 @@ void AbstractWriteLog::pwl_init(Context *on_finish, DeferredContexts &later) // Start the thread m_thread_pool.start(); - m_periodic_stats_enabled = m_cache_state->log_periodic_stats; /* Do these after we drop lock */ later.add(new LambdaContext([this](int r) { - if (m_periodic_stats_enabled) { /* Log stats for the first time */ periodic_stats(); /* Arm periodic stats logging for the first time */ std::lock_guard timer_locker(*m_timer_lock); arm_periodic_stats(); - } - })); + })); m_image_ctx.op_work_queue->queue(on_finish, 0); } +template +void AbstractWriteLog::update_image_cache_state() { + using klass = AbstractWriteLog; + Context *ctx = util::create_context_callback< + klass, &klass::handle_update_image_cache_state>(this); + update_image_cache_state(ctx); +} + template void AbstractWriteLog::update_image_cache_state(Context *on_finish) { + ldout(m_image_ctx.cct, 10) << dendl; + + ceph_assert(ceph_mutex_is_locked_by_me(m_lock)); + m_cache_state->allocated_bytes = m_bytes_allocated; + m_cache_state->cached_bytes = m_bytes_cached; + m_cache_state->dirty_bytes = m_bytes_dirty; + m_cache_state->free_bytes = m_bytes_allocated_cap - m_bytes_allocated; + m_cache_state->hits_full = m_perfcounter->get(l_librbd_pwl_rd_hit_req); + m_cache_state->hits_partial = m_perfcounter->get(l_librbd_pwl_rd_part_hit_req); + m_cache_state->misses = m_perfcounter->get(l_librbd_pwl_rd_req) - + m_cache_state->hits_full - m_cache_state->hits_partial; + m_cache_state->hit_bytes = m_perfcounter->get(l_librbd_pwl_rd_hit_bytes); + m_cache_state->miss_bytes = m_perfcounter->get(l_librbd_pwl_rd_bytes) - + m_cache_state->hit_bytes; m_cache_state->write_image_cache_state(on_finish); } +template +void AbstractWriteLog::handle_update_image_cache_state(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to update image cache state: " << cpp_strerror(r) + << dendl; + return; + } +} + template void AbstractWriteLog::init(Context *on_finish) { CephContext *cct = m_image_ctx.cct; @@ -593,6 +622,7 @@ void AbstractWriteLog::init(Context *on_finish) { Context *ctx = new LambdaContext( [this, on_finish](int r) { if (r >= 0) { + std::lock_guard locker(m_lock); update_image_cache_state(on_finish); } else { on_finish->complete(r); @@ -612,6 +642,9 @@ void AbstractWriteLog::shut_down(Context *on_finish) { Context *ctx = new LambdaContext( [this, on_finish](int r) { + if (m_perfcounter) { + perf_stop(); + } ldout(m_image_ctx.cct, 6) << "shutdown complete" << dendl; m_image_ctx.op_work_queue->queue(on_finish, r); }); @@ -619,26 +652,15 @@ void AbstractWriteLog::shut_down(Context *on_finish) { [this, ctx](int r) { ldout(m_image_ctx.cct, 6) << "image cache cleaned" << dendl; Context *next_ctx = override_ctx(r, ctx); - bool periodic_stats_enabled = m_periodic_stats_enabled; - m_periodic_stats_enabled = false; - - if (periodic_stats_enabled) { - /* Log stats one last time if they were enabled */ - periodic_stats(); - } - { - std::lock_guard locker(m_lock); - check_image_cache_state_clean(); - m_wake_up_enabled = false; - m_cache_state->clean = true; - m_log_entries.clear(); - - remove_pool_file(); + periodic_stats(); - if (m_perfcounter) { - perf_stop(); - } - } + std::lock_guard locker(m_lock); + check_image_cache_state_clean(); + m_wake_up_enabled = false; + m_log_entries.clear(); + m_cache_state->clean = true; + m_cache_state->empty = true; + remove_pool_file(); update_image_cache_state(next_ctx); }); ctx = new LambdaContext( @@ -659,9 +681,7 @@ void AbstractWriteLog::shut_down(Context *on_finish) { /* Flush all writes to OSDs (unless disabled) and wait for all in-progress flush writes to complete */ ldout(m_image_ctx.cct, 6) << "flushing" << dendl; - if (m_periodic_stats_enabled) { - periodic_stats(); - } + periodic_stats(); } flush_dirty_entries(next_ctx); }); @@ -1304,6 +1324,10 @@ void AbstractWriteLog::complete_op_log_entries(GenericLogOperations &&ops, std::lock_guard locker(m_lock); m_unpublished_reserves -= published_reserves; m_dirty_log_entries.splice(m_dirty_log_entries.end(), dirty_entries); + if (m_cache_state->clean && !this->m_dirty_log_entries.empty()) { + m_cache_state->clean = false; + update_image_cache_state(); + } } op->complete(result); m_perfcounter->tinc(l_librbd_pwl_log_op_dis_to_app_t, @@ -1394,7 +1418,7 @@ void AbstractWriteLog::dispatch_deferred_writes(void) if (allocated_req && front_req && allocated) { /* Push dispatch of the first allocated req to a wq */ m_work_queue.queue(new LambdaContext( - [this, allocated_req](int r) { + [allocated_req](int r) { allocated_req->dispatch(); }), 0); allocated_req = nullptr; @@ -1733,6 +1757,10 @@ void AbstractWriteLog::process_writeback_dirty_entries() { ldout(cct, 20) << "Nothing new to flush" << dendl; /* Do flush complete only when all flush ops are finished */ all_clean = !m_flush_ops_in_flight; + if (!m_cache_state->clean && all_clean) { + m_cache_state->clean = true; + update_image_cache_state(); + } break; } @@ -1873,7 +1901,7 @@ void AbstractWriteLog::new_sync_point(DeferredContexts &later) { /* This sync point will acquire no more sub-ops. Activation needs * to acquire m_lock, so defer to later*/ later.add(new LambdaContext( - [this, old_sync_point](int r) { + [old_sync_point](int r) { old_sync_point->prior_persisted_gather_activate(); })); } @@ -1930,7 +1958,7 @@ void AbstractWriteLog::flush_new_sync_point(C_FlushRequestT *flush_req, * now has its finisher. If the sub is already complete, activation will * complete the Gather. The finisher will acquire m_lock, so we'll activate * this when we release m_lock.*/ - later.add(new LambdaContext([this, to_append](int r) { + later.add(new LambdaContext([to_append](int r) { to_append->persist_gather_activate(); })); @@ -1982,6 +2010,10 @@ void AbstractWriteLog::flush_dirty_entries(Context *on_finish) { std::lock_guard locker(m_lock); flushing = (0 != m_flush_ops_in_flight); all_clean = m_dirty_log_entries.empty(); + if (!m_cache_state->clean && all_clean && !flushing) { + m_cache_state->clean = true; + update_image_cache_state(); + } stop_flushing = (m_shutting_down); } diff --git a/ceph/src/librbd/cache/pwl/AbstractWriteLog.h b/ceph/src/librbd/cache/pwl/AbstractWriteLog.h index e53c63206..4905edde6 100644 --- a/ceph/src/librbd/cache/pwl/AbstractWriteLog.h +++ b/ceph/src/librbd/cache/pwl/AbstractWriteLog.h @@ -212,8 +212,6 @@ private: /* Throttle writes concurrently allocating & replicating */ unsigned int m_free_lanes = pwl::MAX_CONCURRENT_WRITES; - /* Initialized from config, then set false during shutdown */ - std::atomic m_periodic_stats_enabled = {false}; SafeTimer *m_timer = nullptr; /* Used with m_timer_lock */ mutable ceph::mutex *m_timer_lock = nullptr; /* Used with and by m_timer */ Context *m_timer_ctx = nullptr; @@ -236,6 +234,7 @@ private: void pwl_init(Context *on_finish, pwl::DeferredContexts &later); void update_image_cache_state(Context *on_finish); + void handle_update_image_cache_state(int r); void check_image_cache_state_clean(); void flush_dirty_entries(Context *on_finish); @@ -399,7 +398,7 @@ protected: virtual uint64_t get_max_extent() { return 0; } - + void update_image_cache_state(void); }; } // namespace pwl diff --git a/ceph/src/librbd/cache/pwl/DiscardRequest.cc b/ceph/src/librbd/cache/pwl/DiscardRequest.cc index decefafb5..1b537f32d 100644 --- a/ceph/src/librbd/cache/pwl/DiscardRequest.cc +++ b/ceph/src/librbd/cache/pwl/DiscardRequest.cc @@ -68,7 +68,13 @@ void DiscardRequest::delete_image_cache_file() { if (m_cache_state->present && !m_cache_state->host.compare(ceph_get_short_hostname()) && fs::exists(m_cache_state->path)) { - fs::remove(m_cache_state->path); + std::error_code ec; + fs::remove(m_cache_state->path, ec); + if (ec) { + lderr(cct) << "failed to remove persistent cache file: " << ec.message() + << dendl; + // not fatal + } } remove_image_cache_state(); @@ -107,10 +113,6 @@ void DiscardRequest::remove_feature_bit() { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << dendl; - if (!(m_image_ctx.features &&RBD_FEATURE_DIRTY_CACHE)) { - finish(); - return; - } uint64_t new_features = m_image_ctx.features & ~RBD_FEATURE_DIRTY_CACHE; uint64_t features_mask = RBD_FEATURE_DIRTY_CACHE; ldout(cct, 10) << "old_features=" << m_image_ctx.features diff --git a/ceph/src/librbd/cache/pwl/ImageCacheState.cc b/ceph/src/librbd/cache/pwl/ImageCacheState.cc index 65de7b70f..fe6e1087d 100644 --- a/ceph/src/librbd/cache/pwl/ImageCacheState.cc +++ b/ceph/src/librbd/cache/pwl/ImageCacheState.cc @@ -7,7 +7,6 @@ #include "librbd/ImageCtx.h" #include "librbd/Operations.h" #include "common/config_proxy.h" -#include "common/ceph_json.h" #include "common/environment.h" #include "common/hostname.h" #include "librbd/plugin/Api.h" @@ -24,63 +23,69 @@ namespace pwl { using namespace std; -namespace { -bool get_json_format(const std::string& s, JSONFormattable *f) { - JSONParser p; - bool success = p.parse(s.c_str(), s.size()); - if (success) { - decode_json_obj(*f, &p); - } - return success; +template +void ImageCacheState::init_from_config() { + ldout(m_image_ctx->cct, 20) << dendl; + + present = false; + empty = true; + clean = true; + host = ""; + path = ""; + ConfigProxy &config = m_image_ctx->config; + mode = config.get_val("rbd_persistent_cache_mode"); + size = 0; } -} // namespace template -ImageCacheState::ImageCacheState(I *image_ctx, plugin::Api& plugin_api) : - m_image_ctx(image_ctx), m_plugin_api(plugin_api) { - ldout(image_ctx->cct, 20) << "Initialize RWL cache state with config data. " +bool ImageCacheState::init_from_metadata(json_spirit::mValue& json_root) { + ldout(m_image_ctx->cct, 20) << dendl; + + try { + auto& o = json_root.get_obj(); + present = o["present"].get_bool(); + empty = o["empty"].get_bool(); + clean = o["clean"].get_bool(); + host = o["host"].get_str(); + path = o["path"].get_str(); + mode = o["mode"].get_str(); + size = o["size"].get_uint64(); + } catch (std::runtime_error& e) { + lderr(m_image_ctx->cct) << "failed to parse cache state: " << e.what() << dendl; + return false; + } - ConfigProxy &config = image_ctx->config; - log_periodic_stats = config.get_val("rbd_persistent_cache_log_periodic_stats"); - cache_type = config.get_val("rbd_persistent_cache_mode"); -} - -template -ImageCacheState::ImageCacheState( - I *image_ctx, JSONFormattable &f, plugin::Api& plugin_api) : - m_image_ctx(image_ctx), m_plugin_api(plugin_api) { - ldout(image_ctx->cct, 20) << "Initialize RWL cache state with data from " - << "server side"<< dendl; - - present = (bool)f["present"]; - empty = (bool)f["empty"]; - clean = (bool)f["clean"]; - cache_type = f["cache_type"]; - host = f["pwl_host"]; - path = f["pwl_path"]; - uint64_t pwl_size; - std::istringstream iss(f["pwl_size"]); - iss >> pwl_size; - size = pwl_size; - - // Others from config - ConfigProxy &config = image_ctx->config; - log_periodic_stats = config.get_val("rbd_persistent_cache_log_periodic_stats"); + return true; } template void ImageCacheState::write_image_cache_state(Context *on_finish) { + stats_timestamp = ceph_clock_now(); std::shared_lock owner_lock{m_image_ctx->owner_lock}; - JSONFormattable f; - ::encode_json(IMAGE_CACHE_STATE.c_str(), *this, &f); - std::ostringstream oss; - f.flush(oss); - std::string image_state_json = oss.str(); + json_spirit::mObject o; + o["present"] = present; + o["empty"] = empty; + o["clean"] = clean; + o["host"] = host; + o["path"] = path; + o["mode"] = mode; + o["size"] = size; + o["stats_timestamp"] = stats_timestamp.sec(); + o["allocated_bytes"] = allocated_bytes; + o["cached_bytes"] = cached_bytes; + o["dirty_bytes"] = dirty_bytes; + o["free_bytes"] = free_bytes; + o["hits_full"] = hits_full; + o["hits_partial"] = hits_partial; + o["misses"] = misses; + o["hit_bytes"] = hit_bytes; + o["miss_bytes"] = miss_bytes; + std::string image_state_json = json_spirit::write(o); ldout(m_image_ctx->cct, 20) << __func__ << " Store state: " << image_state_json << dendl; - m_plugin_api.execute_image_metadata_set(m_image_ctx, IMAGE_CACHE_STATE, + m_plugin_api.execute_image_metadata_set(m_image_ctx, PERSISTENT_CACHE_STATE, image_state_json, on_finish); } @@ -89,18 +94,7 @@ void ImageCacheState::clear_image_cache_state(Context *on_finish) { std::shared_lock owner_lock{m_image_ctx->owner_lock}; ldout(m_image_ctx->cct, 20) << __func__ << " Remove state: " << dendl; m_plugin_api.execute_image_metadata_remove( - m_image_ctx, IMAGE_CACHE_STATE, on_finish); -} - -template -void ImageCacheState::dump(ceph::Formatter *f) const { - ::encode_json("present", present, f); - ::encode_json("empty", empty, f); - ::encode_json("clean", clean, f); - ::encode_json("cache_type", cache_type, f); - ::encode_json("pwl_host", host, f); - ::encode_json("pwl_path", path, f); - ::encode_json("pwl_size", size, f); + m_image_ctx, PERSISTENT_CACHE_STATE, on_finish); } template @@ -113,7 +107,7 @@ ImageCacheState* ImageCacheState::create_image_cache_state( bool dirty_cache = plugin_api.test_image_features(image_ctx, RBD_FEATURE_DIRTY_CACHE); if (dirty_cache) { cls_client::metadata_get(&image_ctx->md_ctx, image_ctx->header_oid, - IMAGE_CACHE_STATE, &cache_state_str); + PERSISTENT_CACHE_STATE, &cache_state_str); } ldout(image_ctx->cct, 20) << "image_cache_state: " << cache_state_str << dendl; @@ -133,22 +127,23 @@ ImageCacheState* ImageCacheState::create_image_cache_state( r = -EINVAL; }else if ((!dirty_cache || cache_state_str.empty()) && cache_desired) { cache_state = new ImageCacheState(image_ctx, plugin_api); + cache_state->init_from_config(); } else { ceph_assert(!cache_state_str.empty()); - JSONFormattable f; - bool success = get_json_format(cache_state_str, &f); - if (!success) { - lderr(image_ctx->cct) << "Failed to parse cache state: " - << cache_state_str << dendl; + json_spirit::mValue json_root; + if (!json_spirit::read(cache_state_str.c_str(), json_root)) { + lderr(image_ctx->cct) << "failed to parse cache state" << dendl; r = -EINVAL; return nullptr; } - - bool cache_exists = (bool)f["present"]; - if (!cache_exists) { - cache_state = new ImageCacheState(image_ctx, plugin_api); - } else { - cache_state = new ImageCacheState(image_ctx, f, plugin_api); + cache_state = new ImageCacheState(image_ctx, plugin_api); + if (!cache_state->init_from_metadata(json_root)) { + delete cache_state; + r = -EINVAL; + return nullptr; + } + if (!cache_state->present) { + cache_state->init_from_config(); } } return cache_state; @@ -160,14 +155,15 @@ ImageCacheState* ImageCacheState::get_image_cache_state( ImageCacheState* cache_state = nullptr; string cache_state_str; cls_client::metadata_get(&image_ctx->md_ctx, image_ctx->header_oid, - IMAGE_CACHE_STATE, &cache_state_str); + PERSISTENT_CACHE_STATE, &cache_state_str); if (!cache_state_str.empty()) { - JSONFormattable f; - bool success = get_json_format(cache_state_str, &f); - if (!success) { - cache_state = new ImageCacheState(image_ctx, plugin_api); + // ignore errors, best effort + cache_state = new ImageCacheState(image_ctx, plugin_api); + json_spirit::mValue json_root; + if (!json_spirit::read(cache_state_str.c_str(), json_root)) { + lderr(image_ctx->cct) << "failed to parse cache state" << dendl; } else { - cache_state = new ImageCacheState(image_ctx, f, plugin_api); + cache_state->init_from_metadata(json_root); } } return cache_state; diff --git a/ceph/src/librbd/cache/pwl/ImageCacheState.h b/ceph/src/librbd/cache/pwl/ImageCacheState.h index 7ea1412e2..c2fd4b778 100644 --- a/ceph/src/librbd/cache/pwl/ImageCacheState.h +++ b/ceph/src/librbd/cache/pwl/ImageCacheState.h @@ -4,11 +4,11 @@ #ifndef CEPH_LIBRBD_CACHE_RWL_IMAGE_CACHE_STATE_H #define CEPH_LIBRBD_CACHE_RWL_IMAGE_CACHE_STATE_H +#include "json_spirit/json_spirit.h" #include "librbd/ImageCtx.h" #include "librbd/cache/Types.h" #include -class JSONFormattable; namespace ceph { class Formatter; } @@ -31,33 +31,42 @@ public: bool clean = true; std::string host; std::string path; - std::string cache_type; + std::string mode; uint64_t size = 0; - bool log_periodic_stats; - - ImageCacheState(ImageCtxT* image_ctx, plugin::Api& plugin_api); - - ImageCacheState(ImageCtxT* image_ctx, JSONFormattable& f, - plugin::Api& plugin_api); + /* After reloading, the following data does not need to be read, + * but recalculated. */ + utime_t stats_timestamp; + uint64_t allocated_bytes = 0; + uint64_t cached_bytes = 0; + uint64_t dirty_bytes = 0; + uint64_t free_bytes = 0; + uint64_t hits_full = 0; + uint64_t hits_partial = 0; + uint64_t misses = 0; + uint64_t hit_bytes = 0; + uint64_t miss_bytes = 0; + + ImageCacheState(ImageCtxT* image_ctx, plugin::Api& plugin_api) + : m_image_ctx(image_ctx), m_plugin_api(plugin_api) {} ~ImageCacheState() {} - ImageCacheType get_image_cache_type() const { - if (cache_type == "rwl") { + ImageCacheType get_image_cache_mode() const { + if (mode == "rwl") { return IMAGE_CACHE_TYPE_RWL; - } else if (cache_type == "ssd") { + } else if (mode == "ssd") { return IMAGE_CACHE_TYPE_SSD; } return IMAGE_CACHE_TYPE_UNKNOWN; } + void init_from_config(); + bool init_from_metadata(json_spirit::mValue& json_root); void write_image_cache_state(Context *on_finish); void clear_image_cache_state(Context *on_finish); - void dump(ceph::Formatter *f) const; - static ImageCacheState* create_image_cache_state( ImageCtxT* image_ctx, plugin::Api& plugin_api, int &r); diff --git a/ceph/src/librbd/cache/pwl/InitRequest.cc b/ceph/src/librbd/cache/pwl/InitRequest.cc index 13fc539dc..65dac8b46 100644 --- a/ceph/src/librbd/cache/pwl/InitRequest.cc +++ b/ceph/src/librbd/cache/pwl/InitRequest.cc @@ -85,8 +85,8 @@ void InitRequest::get_image_cache_state() { return; } - auto cache_type = cache_state->get_image_cache_type(); - switch(cache_type) { + auto mode = cache_state->get_image_cache_mode(); + switch (mode) { #ifdef WITH_RBD_RWL case cache::IMAGE_CACHE_TYPE_RWL: m_image_cache = diff --git a/ceph/src/librbd/cache/pwl/LogEntry.cc b/ceph/src/librbd/cache/pwl/LogEntry.cc index c9af23688..8a050eb79 100644 --- a/ceph/src/librbd/cache/pwl/LogEntry.cc +++ b/ceph/src/librbd/cache/pwl/LogEntry.cc @@ -46,7 +46,7 @@ std::ostream &operator<<(std::ostream &os, bool GenericWriteLogEntry::can_writeback() const { return (this->completed && - (ram_entry.sequenced || + (ram_entry.is_sequenced() || (sync_point_entry && sync_point_entry->completed))); } @@ -71,7 +71,7 @@ std::ostream &operator<<(std::ostream &os, void WriteLogEntry::init(bool has_data, uint64_t current_sync_gen, uint64_t last_op_sequence_num, bool persist_on_flush) { - ram_entry.has_data = 1; + ram_entry.set_has_data(has_data); ram_entry.sync_gen_number = current_sync_gen; if (persist_on_flush) { /* Persist on flush. Sequence #0 is never used. */ @@ -79,10 +79,10 @@ void WriteLogEntry::init(bool has_data, } else { /* Persist on write */ ram_entry.write_sequence_number = last_op_sequence_num; - ram_entry.sequenced = 1; + ram_entry.set_sequenced(true); } - ram_entry.sync_point = 0; - ram_entry.discard = 0; + ram_entry.set_sync_point(false); + ram_entry.set_discard(false); } std::ostream& WriteLogEntry::format(std::ostream &os) const { @@ -115,7 +115,7 @@ void DiscardLogEntry::init(uint64_t current_sync_gen, bool persist_on_flush, } else { /* Persist on write */ ram_entry.write_sequence_number = last_op_sequence_num; - ram_entry.sequenced = 1; + ram_entry.set_sequenced(true); } } diff --git a/ceph/src/librbd/cache/pwl/LogEntry.h b/ceph/src/librbd/cache/pwl/LogEntry.h index 78eb4a6de..ecaca0b7b 100644 --- a/ceph/src/librbd/cache/pwl/LogEntry.h +++ b/ceph/src/librbd/cache/pwl/LogEntry.h @@ -93,7 +93,7 @@ public: std::shared_ptr next_sync_point_entry = nullptr; SyncPointLogEntry(uint64_t sync_gen_number) { ram_entry.sync_gen_number = sync_gen_number; - ram_entry.sync_point = 1; + ram_entry.set_sync_point(true); }; ~SyncPointLogEntry() override {}; SyncPointLogEntry(const SyncPointLogEntry&) = delete; @@ -185,14 +185,14 @@ public: uint64_t image_offset_bytes, uint64_t write_bytes, uint32_t data_length) : WriteLogEntry(sync_point_entry, image_offset_bytes, write_bytes) { - ram_entry.writesame = 1; + ram_entry.set_writesame(true); ram_entry.ws_datalen = data_length; is_writesame = true; }; WriteLogEntry(uint64_t image_offset_bytes, uint64_t write_bytes, uint32_t data_length) : WriteLogEntry(nullptr, image_offset_bytes, write_bytes) { - ram_entry.writesame = 1; + ram_entry.set_writesame(true); ram_entry.ws_datalen = data_length; is_writesame = true; }; @@ -241,11 +241,11 @@ public: uint32_t discard_granularity_bytes) : GenericWriteLogEntry(sync_point_entry, image_offset_bytes, write_bytes), m_discard_granularity_bytes(discard_granularity_bytes) { - ram_entry.discard = 1; + ram_entry.set_discard(true); }; DiscardLogEntry(uint64_t image_offset_bytes, uint64_t write_bytes) : GenericWriteLogEntry(nullptr, image_offset_bytes, write_bytes) { - ram_entry.discard = 1; + ram_entry.set_discard(true); }; DiscardLogEntry(const DiscardLogEntry&) = delete; DiscardLogEntry &operator=(const DiscardLogEntry&) = delete; diff --git a/ceph/src/librbd/cache/pwl/Request.cc b/ceph/src/librbd/cache/pwl/Request.cc index c5dea8c83..963331925 100644 --- a/ceph/src/librbd/cache/pwl/Request.cc +++ b/ceph/src/librbd/cache/pwl/Request.cc @@ -251,7 +251,7 @@ bool C_WriteRequest::append_write_request(std::shared_ptr sync_poi std::lock_guard locker(m_lock); auto write_req_sp = this; if (sync_point->earlier_sync_point) { - Context *schedule_append_ctx = new LambdaContext([this, write_req_sp](int r) { + Context *schedule_append_ctx = new LambdaContext([write_req_sp](int r) { write_req_sp->schedule_append(); }); sync_point->earlier_sync_point->on_sync_point_appending.push_back(schedule_append_ctx); diff --git a/ceph/src/librbd/cache/pwl/ShutdownRequest.cc b/ceph/src/librbd/cache/pwl/ShutdownRequest.cc index 4475712dd..e022328ba 100644 --- a/ceph/src/librbd/cache/pwl/ShutdownRequest.cc +++ b/ceph/src/librbd/cache/pwl/ShutdownRequest.cc @@ -132,7 +132,7 @@ void ShutdownRequest::send_remove_image_cache_state() { Context *ctx = create_context_callback( this); std::shared_lock owner_lock{m_image_ctx.owner_lock}; - m_plugin_api.execute_image_metadata_remove(&m_image_ctx, IMAGE_CACHE_STATE, ctx); + m_plugin_api.execute_image_metadata_remove(&m_image_ctx, PERSISTENT_CACHE_STATE, ctx); } template diff --git a/ceph/src/librbd/cache/pwl/Types.cc b/ceph/src/librbd/cache/pwl/Types.cc index 0ad162583..c29305eec 100644 --- a/ceph/src/librbd/cache/pwl/Types.cc +++ b/ceph/src/librbd/cache/pwl/Types.cc @@ -60,30 +60,30 @@ void WriteLogCacheEntry::dump(Formatter *f) const { f->dump_unsigned("image_offset_bytes", image_offset_bytes); f->dump_unsigned("write_bytes", write_bytes); f->dump_unsigned("write_data_pos", write_data_pos); - f->dump_unsigned("entry_valid", entry_valid); - f->dump_unsigned("sync_point", sync_point); - f->dump_unsigned("sequenced", sequenced); - f->dump_unsigned("has_data", has_data); - f->dump_unsigned("discard", discard); - f->dump_unsigned("writesame", writesame); + f->dump_bool("entry_valid", is_entry_valid()); + f->dump_bool("sync_point", is_sync_point()); + f->dump_bool("sequenced", is_sequenced()); + f->dump_bool("has_data", has_data()); + f->dump_bool("discard", is_discard()); + f->dump_bool("writesame", is_writesame()); f->dump_unsigned("ws_datalen", ws_datalen); f->dump_unsigned("entry_index", entry_index); } void WriteLogCacheEntry::generate_test_instances(std::list& ls) { - ls.push_back(new WriteLogCacheEntry); + ls.push_back(new WriteLogCacheEntry()); ls.push_back(new WriteLogCacheEntry); ls.back()->sync_gen_number = 1; ls.back()->write_sequence_number = 1; ls.back()->image_offset_bytes = 1; ls.back()->write_bytes = 1; ls.back()->write_data_pos = 1; - ls.back()->entry_valid = 1; - ls.back()->sync_point = 1; - ls.back()->sequenced = 1; - ls.back()->has_data = 1; - ls.back()->discard = 1; - ls.back()->writesame = 1; + ls.back()->set_entry_valid(true); + ls.back()->set_sync_point(true); + ls.back()->set_sequenced(true); + ls.back()->set_has_data(true); + ls.back()->set_discard(true); + ls.back()->set_writesame(true); ls.back()->ws_datalen = 1; ls.back()->entry_index = 1; } @@ -96,10 +96,11 @@ void WriteLogPoolRoot::dump(Formatter *f) const { f->dump_unsigned("block_size", block_size); f->dump_unsigned("num_log_entries", num_log_entries); f->dump_unsigned("first_free_entry", first_free_entry); - f->dump_unsigned("first_valid_entry", first_valid_entry); } + f->dump_unsigned("first_valid_entry", first_valid_entry); +} void WriteLogPoolRoot::generate_test_instances(std::list& ls) { - ls.push_back(new WriteLogPoolRoot); + ls.push_back(new WriteLogPoolRoot()); ls.push_back(new WriteLogPoolRoot); ls.back()->layout_version = 2; ls.back()->cur_sync_gen = 1; @@ -114,12 +115,12 @@ void WriteLogPoolRoot::generate_test_instances(std::list& ls) std::ostream& operator<<(std::ostream& os, const WriteLogCacheEntry &entry) { - os << "entry_valid=" << (bool)entry.entry_valid - << ", sync_point=" << (bool)entry.sync_point - << ", sequenced=" << (bool)entry.sequenced - << ", has_data=" << (bool)entry.has_data - << ", discard=" << (bool)entry.discard - << ", writesame=" << (bool)entry.writesame + os << "entry_valid=" << entry.is_entry_valid() + << ", sync_point=" << entry.is_sync_point() + << ", sequenced=" << entry.is_sequenced() + << ", has_data=" << entry.has_data() + << ", discard=" << entry.is_discard() + << ", writesame=" << entry.is_writesame() << ", sync_gen_number=" << entry.sync_gen_number << ", write_sequence_number=" << entry.write_sequence_number << ", image_offset_bytes=" << entry.image_offset_bytes diff --git a/ceph/src/librbd/cache/pwl/Types.h b/ceph/src/librbd/cache/pwl/Types.h index f7cd6cfac..0d8c93a24 100644 --- a/ceph/src/librbd/cache/pwl/Types.h +++ b/ceph/src/librbd/cache/pwl/Types.h @@ -150,6 +150,16 @@ enum { l_librbd_pwl_last, }; +enum { + WRITE_LOG_CACHE_ENTRY_VALID = 1U << 0, /* if 0, this entry is free */ + WRITE_LOG_CACHE_ENTRY_SYNC_POINT = 1U << 1, /* No data. No write sequence number. + Marks sync point for this sync gen number */ + WRITE_LOG_CACHE_ENTRY_SEQUENCED = 1U << 2, /* write sequence number is valid */ + WRITE_LOG_CACHE_ENTRY_HAS_DATA = 1U << 3, /* write_data field is valid (else ignore) */ + WRITE_LOG_CACHE_ENTRY_DISCARD = 1U << 4, /* has_data will be 0 if this is a discard */ + WRITE_LOG_CACHE_ENTRY_WRITESAME = 1U << 5, /* ws_datalen indicates length of data at write_bytes */ +}; + namespace librbd { namespace cache { namespace pwl { @@ -220,45 +230,83 @@ struct WriteLogCacheEntry { #ifdef WITH_RBD_SSD_CACHE uint64_t write_data_pos = 0; /* SSD data offset */ #endif - union { - uint8_t flags; - struct { - uint8_t entry_valid :1; /* if 0, this entry is free */ - uint8_t sync_point :1; /* No data. No write sequence number. Marks sync - point for this sync gen number */ - uint8_t sequenced :1; /* write sequence number is valid */ - uint8_t has_data :1; /* write_data field is valid (else ignore) */ - uint8_t discard :1; /* has_data will be 0 if this is a discard */ - uint8_t writesame :1; /* ws_datalen indicates length of data at write_bytes */ - }; - }; + uint8_t flags = 0; uint32_t ws_datalen = 0; /* Length of data buffer (writesame only) */ uint32_t entry_index = 0; /* For debug consistency check. Can be removed if * we need the space */ WriteLogCacheEntry(uint64_t image_offset_bytes=0, uint64_t write_bytes=0) - : image_offset_bytes(image_offset_bytes), write_bytes(write_bytes), - entry_valid(0), sync_point(0), sequenced(0), has_data(0), discard(0), writesame(0) { - } + : image_offset_bytes(image_offset_bytes), write_bytes(write_bytes) {} BlockExtent block_extent(); uint64_t get_offset_bytes(); uint64_t get_write_bytes(); - bool is_sync_point() { - return sync_point; + bool is_entry_valid() const { + return flags & WRITE_LOG_CACHE_ENTRY_VALID; + } + bool is_sync_point() const { + return flags & WRITE_LOG_CACHE_ENTRY_SYNC_POINT; } - bool is_discard() { - return discard; + bool is_sequenced() const { + return flags & WRITE_LOG_CACHE_ENTRY_SEQUENCED; } - bool is_writesame() { - return writesame; + bool has_data() const { + return flags & WRITE_LOG_CACHE_ENTRY_HAS_DATA; } - bool is_write() { + bool is_discard() const { + return flags & WRITE_LOG_CACHE_ENTRY_DISCARD; + } + bool is_writesame() const { + return flags & WRITE_LOG_CACHE_ENTRY_WRITESAME; + } + bool is_write() const { /* Log entry is a basic write */ return !is_sync_point() && !is_discard() && !is_writesame(); } - bool is_writer() { + bool is_writer() const { /* Log entry is any type that writes data */ return is_write() || is_discard() || is_writesame(); } + void set_entry_valid(bool flag) { + if (flag) { + flags |= WRITE_LOG_CACHE_ENTRY_VALID; + } else { + flags &= ~WRITE_LOG_CACHE_ENTRY_VALID; + } + } + void set_sync_point(bool flag) { + if (flag) { + flags |= WRITE_LOG_CACHE_ENTRY_SYNC_POINT; + } else { + flags &= ~WRITE_LOG_CACHE_ENTRY_SYNC_POINT; + } + } + void set_sequenced(bool flag) { + if (flag) { + flags |= WRITE_LOG_CACHE_ENTRY_SEQUENCED; + } else { + flags &= ~WRITE_LOG_CACHE_ENTRY_SEQUENCED; + } + } + void set_has_data(bool flag) { + if (flag) { + flags |= WRITE_LOG_CACHE_ENTRY_HAS_DATA; + } else { + flags &= ~WRITE_LOG_CACHE_ENTRY_HAS_DATA; + } + } + void set_discard(bool flag) { + if (flag) { + flags |= WRITE_LOG_CACHE_ENTRY_DISCARD; + } else { + flags &= ~WRITE_LOG_CACHE_ENTRY_DISCARD; + } + } + void set_writesame(bool flag) { + if (flag) { + flags |= WRITE_LOG_CACHE_ENTRY_WRITESAME; + } else { + flags &= ~WRITE_LOG_CACHE_ENTRY_WRITESAME; + } + } friend std::ostream& operator<<(std::ostream& os, const WriteLogCacheEntry &entry); #ifdef WITH_RBD_SSD_CACHE diff --git a/ceph/src/librbd/cache/pwl/rwl/WriteLog.cc b/ceph/src/librbd/cache/pwl/rwl/WriteLog.cc index c5de5fb4e..41bdafe5b 100644 --- a/ceph/src/librbd/cache/pwl/rwl/WriteLog.cc +++ b/ceph/src/librbd/cache/pwl/rwl/WriteLog.cc @@ -113,10 +113,14 @@ void WriteLog::alloc_op_log_entries(GenericLogOperations &ops) log_entry->log_entry_index = entry_index; log_entry->ram_entry.entry_index = entry_index; log_entry->cache_entry = &pmem_log_entries[entry_index]; - log_entry->ram_entry.entry_valid = 1; + log_entry->ram_entry.set_entry_valid(true); m_log_entries.push_back(log_entry); ldout(m_image_ctx.cct, 20) << "operation=[" << *operation << "]" << dendl; } + if (m_cache_state->empty && !m_log_entries.empty()) { + m_cache_state->empty = false; + this->update_image_cache_state(); + } } /* @@ -246,8 +250,6 @@ void WriteLog::remove_pool_file() { lderr(m_image_ctx.cct) << "failed to remove empty pool \"" << this->m_log_pool_name << "\": " << pmemobj_errormsg() << dendl; } else { - m_cache_state->clean = true; - m_cache_state->empty = true; m_cache_state->present = false; } } else { @@ -321,7 +323,7 @@ bool WriteLog::initialize_pool(Context *on_finish, pwl::DeferredContexts &lat } TX_FINALLY { } TX_END; } else { - m_cache_state->present = true; + ceph_assert(m_cache_state->present); /* Open existing pool */ if ((m_log_pool = pmemobj_open(this->m_log_pool_name.c_str(), @@ -552,6 +554,10 @@ bool WriteLog::retire_entries(const unsigned long int frees_per_tx) { ceph_assert(this->m_first_valid_entry == initial_first_valid_entry); this->m_first_valid_entry = first_valid_entry; this->m_free_log_entries += retiring_entries.size(); + if (!m_cache_state->empty && m_log_entries.empty()) { + m_cache_state->empty = true; + this->update_image_cache_state(); + } for (auto &entry: retiring_entries) { if (entry->write_bytes()) { ceph_assert(this->m_bytes_cached >= entry->write_bytes()); diff --git a/ceph/src/librbd/cache/pwl/ssd/LogOperation.cc b/ceph/src/librbd/cache/pwl/ssd/LogOperation.cc index f759d8a8e..c8080e37d 100644 --- a/ceph/src/librbd/cache/pwl/ssd/LogOperation.cc +++ b/ceph/src/librbd/cache/pwl/ssd/LogOperation.cc @@ -20,7 +20,7 @@ void DiscardLogOperation::init_op( log_entry->init(current_sync_gen, persist_on_flush, last_op_sequence_num); if (persist_on_flush) { this->on_write_append = new LambdaContext( - [this, write_persist, write_append] (int r) { + [write_persist, write_append] (int r) { write_append->complete(r); write_persist->complete(r); }); diff --git a/ceph/src/librbd/cache/pwl/ssd/Types.h b/ceph/src/librbd/cache/pwl/ssd/Types.h index fe82dee9e..52f67ae20 100644 --- a/ceph/src/librbd/cache/pwl/ssd/Types.h +++ b/ceph/src/librbd/cache/pwl/ssd/Types.h @@ -28,8 +28,15 @@ struct SuperBlock{ } static void generate_test_instances(std::list& ls) { + ls.push_back(new SuperBlock()); ls.push_back(new SuperBlock); - ls.push_back(new SuperBlock); + ls.back()->root.layout_version = 3; + ls.back()->root.cur_sync_gen = 1; + ls.back()->root.pool_size = 10737418240; + ls.back()->root.flushed_sync_gen = 1; + ls.back()->root.block_size = 4096; + ls.back()->root.num_log_entries = 0; + ls.back()->root.first_free_entry = 30601; ls.back()->root.first_valid_entry = 2; } }; diff --git a/ceph/src/librbd/cache/pwl/ssd/WriteLog.cc b/ceph/src/librbd/cache/pwl/ssd/WriteLog.cc index a3b183411..2c0dc258b 100644 --- a/ceph/src/librbd/cache/pwl/ssd/WriteLog.cc +++ b/ceph/src/librbd/cache/pwl/ssd/WriteLog.cc @@ -197,7 +197,7 @@ bool WriteLog::initialize_pool(Context *on_finish, return false; } } else { - m_cache_state->present = true; + ceph_assert(m_cache_state->present); r = create_and_open_bdev(); if (r < 0) { on_finish->complete(r); @@ -271,8 +271,6 @@ void WriteLog::remove_pool_file() { lderr(m_image_ctx.cct) << "failed to remove empty pool \"" << this->m_log_pool_name << "\": " << dendl; } else { - m_cache_state->clean = true; - m_cache_state->empty = true; m_cache_state->present = false; } } else { @@ -537,10 +535,14 @@ void WriteLog::alloc_op_log_entries(GenericLogOperations &ops) { for (auto &operation : ops) { auto &log_entry = operation->get_log_entry(); - log_entry->ram_entry.entry_valid = 1; + log_entry->ram_entry.set_entry_valid(true); m_log_entries.push_back(log_entry); ldout(m_image_ctx.cct, 20) << "operation=[" << *operation << "]" << dendl; } + if (m_cache_state->empty && !m_log_entries.empty()) { + m_cache_state->empty = false; + this->update_image_cache_state(); + } } template @@ -813,6 +815,10 @@ bool WriteLog::retire_entries(const unsigned long int frees_per_tx) { this->m_bytes_allocated -= allocated_bytes; ceph_assert(this->m_bytes_cached >= cached_bytes); this->m_bytes_cached -= cached_bytes; + if (!m_cache_state->empty && m_log_entries.empty()) { + m_cache_state->empty = true; + this->update_image_cache_state(); + } ldout(m_image_ctx.cct, 20) << "Finished root update: initial_first_valid_entry=" diff --git a/ceph/src/mds/CDir.cc b/ceph/src/mds/CDir.cc index bace9f6b8..b5012c340 100644 --- a/ceph/src/mds/CDir.cc +++ b/ceph/src/mds/CDir.cc @@ -2033,6 +2033,7 @@ void CDir::_omap_fetched(bufferlist& hdrbl, map& omap, } } + int count = 0; unsigned pos = omap.size() - 1; double rand_threshold = get_inode()->get_ephemeral_rand(); for (map::reverse_iterator p = omap.rbegin(); @@ -2042,6 +2043,9 @@ void CDir::_omap_fetched(bufferlist& hdrbl, map& omap, snapid_t last; dentry_key_t::decode_helper(p->first, dname, last); + if (!(++count % mdcache->mds->heartbeat_reset_grace(2))) + mdcache->mds->heartbeat_reset(); + CDentry *dn = NULL; try { dn = _load_dentry( @@ -2075,6 +2079,9 @@ void CDir::_omap_fetched(bufferlist& hdrbl, map& omap, if (wanted_items.count(mempool::mds_co::string(dname)) > 0 || !complete) { dout(10) << " touching wanted dn " << *dn << dendl; mdcache->touch_dentry(dn); + + if (!(++count % mdcache->mds->heartbeat_reset_grace(2))) + mdcache->mds->heartbeat_reset(); } } @@ -2090,9 +2097,13 @@ void CDir::_omap_fetched(bufferlist& hdrbl, map& omap, // open & force frags while (!undef_inodes.empty()) { CInode *in = undef_inodes.front(); + undef_inodes.pop_front(); in->state_clear(CInode::STATE_REJOINUNDEF); mdcache->opened_undef_inode(in); + + if (!(++count % mdcache->mds->heartbeat_reset_grace())) + mdcache->mds->heartbeat_reset(); } // dirty myself to remove stale snap dentries @@ -2324,6 +2335,7 @@ void CDir::_omap_commit_ops(int r, int op_prio, int64_t metapool, version_t vers _rm.clear(); }; + int count = 0; for (auto &key : stales) { unsigned size = key.length() + sizeof(__u32); if (write_size + size > max_write_size) @@ -2331,6 +2343,9 @@ void CDir::_omap_commit_ops(int r, int op_prio, int64_t metapool, version_t vers write_size += size; _rm.emplace(key); + + if (!(++count % mdcache->mds->heartbeat_reset_grace(2))) + mdcache->mds->heartbeat_reset(); } for (auto &key : to_remove) { @@ -2340,6 +2355,9 @@ void CDir::_omap_commit_ops(int r, int op_prio, int64_t metapool, version_t vers write_size += size; _rm.emplace(std::move(key)); + + if (!(++count % mdcache->mds->heartbeat_reset_grace(2))) + mdcache->mds->heartbeat_reset(); } bufferlist bl; @@ -2365,6 +2383,9 @@ void CDir::_omap_commit_ops(int r, int op_prio, int64_t metapool, version_t vers write_size += size; _set[std::move(item.key)].swap(bl); + + if (!(++count % mdcache->mds->heartbeat_reset_grace())) + mdcache->mds->heartbeat_reset(); } commit_one(true); @@ -2393,21 +2414,21 @@ void CDir::_omap_commit(int op_prio) // fnode.snap_purged_thru = realm->get_last_destroyed(); } - size_t count = 0; + size_t items_count = 0; if (state_test(CDir::STATE_FRAGMENTING) && is_new()) { - count = get_num_head_items() + get_num_snap_items(); + items_count = get_num_head_items() + get_num_snap_items(); } else { for (elist::iterator it = dirty_dentries.begin(); !it.end(); ++it) - ++count; + ++items_count; } vector to_remove; // reverve enough memories, which maybe larger than the actually needed - to_remove.reserve(count); + to_remove.reserve(items_count); vector to_set; // reverve enough memories, which maybe larger than the actually needed - to_set.reserve(count); + to_set.reserve(items_count); // for dir fragtrees bufferlist dfts(CEPH_PAGE_SIZE); @@ -2443,6 +2464,7 @@ void CDir::_omap_commit(int op_prio) } }; + int count = 0; if (state_test(CDir::STATE_FRAGMENTING) && is_new()) { ceph_assert(committed_version == 0); for (auto p = items.begin(); p != items.end(); ) { @@ -2451,12 +2473,18 @@ void CDir::_omap_commit(int op_prio) if (dn->get_linkage()->is_null()) continue; write_one(dn); + + if (!(++count % mdcache->mds->heartbeat_reset_grace())) + mdcache->mds->heartbeat_reset(); } } else { for (auto p = dirty_dentries.begin(); !p.end(); ) { CDentry *dn = *p; ++p; write_one(dn); + + if (!(++count % mdcache->mds->heartbeat_reset_grace())) + mdcache->mds->heartbeat_reset(); } } @@ -2608,6 +2636,8 @@ void CDir::_committed(int r, version_t v) if (committed_version == get_version()) mark_clean(); + int count = 0; + // dentries clean? for (auto p = dirty_dentries.begin(); !p.end(); ) { CDentry *dn = *p; @@ -2645,11 +2675,14 @@ void CDir::_committed(int r, version_t v) dout(15) << " dir " << committed_version << " < dn " << dn->get_version() << " still dirty " << *dn << dendl; ceph_assert(dn->is_dirty()); } + + if (!(++count % mdcache->mds->heartbeat_reset_grace())) + mdcache->mds->heartbeat_reset(); } // finishers? bool were_waiters = !waiting_for_commit.empty(); - + auto it = waiting_for_commit.begin(); while (it != waiting_for_commit.end()) { auto _it = it; @@ -2665,7 +2698,10 @@ void CDir::_committed(int r, version_t v) mdcache->mds->queue_waiters(t); waiting_for_commit.erase(it); it = _it; - } + + if (!(++count % mdcache->mds->heartbeat_reset_grace())) + mdcache->mds->heartbeat_reset(); + } // try drop dentries in this dirfrag if it's about to be purged if (!inode->is_base() && get_parent_dir()->inode->is_stray() && diff --git a/ceph/src/mds/MDCache.cc b/ceph/src/mds/MDCache.cc index ef6d3c4c8..8696c6d92 100644 --- a/ceph/src/mds/MDCache.cc +++ b/ceph/src/mds/MDCache.cc @@ -3785,13 +3785,13 @@ void MDCache::trim_unlinked_inodes() q.push_back(in); } - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } for (auto& in : q) { remove_inode_recursive(in); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } } @@ -5435,7 +5435,7 @@ bool MDCache::process_imported_caps() } else { open_ino(p.first, (int64_t)-1, fin, false); } - if (!(cap_imports_num_opening % 1000)) + if (!(cap_imports_num_opening % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } @@ -5619,7 +5619,7 @@ void MDCache::choose_lock_states_and_reconnect_caps() rejoin_pending_snaprealms.insert(in); } - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } } @@ -5788,7 +5788,7 @@ void MDCache::export_remaining_imported_caps() } } - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } @@ -6244,7 +6244,7 @@ void MDCache::reissue_all_caps() n += mds->locker->issue_caps(in); } - if ((count % 1000) + n >= 1000) + if ((count % mds->heartbeat_reset_grace()) + n >= mds->heartbeat_reset_grace()) mds->heartbeat_reset(); count += n; } @@ -6365,7 +6365,7 @@ void MDCache::identify_files_to_recover() rejoin_check_q.push_back(in); } - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } } @@ -6377,13 +6377,13 @@ void MDCache::start_files_to_recover() if (in->filelock.get_state() == LOCK_XLOCKSNAP) mds->locker->issue_caps(in); mds->locker->check_inode_max_size(in); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } rejoin_check_q.clear(); for (CInode *in : rejoin_recover_q) { mds->locker->file_recover(&in->filelock); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } if (!rejoin_recover_q.empty()) { @@ -9326,6 +9326,19 @@ void MDCache::handle_find_ino(const cref_t &m) if (in) { in->make_path(r->path); dout(10) << " have " << r->path << " " << *in << dendl; + + /* + * If the the CInode was just created by using openc in current + * auth MDS, but the client just sends a getattr request to another + * replica MDS. Then here it will make a path of '#INODE-NUMBER' + * only because the CInode hasn't been linked yet, and the replica + * MDS will keep retrying until the auth MDS flushes the mdlog and + * the C_MDS_openc_finish and link_primary_inode are called at most + * 5 seconds later. + */ + if (!in->get_parent_dn() && in->is_auth()) { + mds->mdlog->flush(); + } } mds->send_message_mds(r, mds_rank_t(m->get_source().num())); } @@ -10787,7 +10800,7 @@ void MDCache::decode_replica_inode(CInode *&in, bufferlist::const_iterator& p, C void MDCache::encode_replica_stray(CDentry *straydn, mds_rank_t who, bufferlist& bl) { ceph_assert(straydn->get_num_auth_pins()); - ENCODE_START(1, 1, bl); + ENCODE_START(2, 1, bl); uint64_t features = mds->mdsmap->get_up_features(); encode_replica_inode(get_myin(), who, bl, features); encode_replica_dir(straydn->get_dir()->inode->get_parent_dn()->get_dir(), who, bl); @@ -10795,15 +10808,18 @@ void MDCache::encode_replica_stray(CDentry *straydn, mds_rank_t who, bufferlist& encode_replica_inode(straydn->get_dir()->inode, who, bl, features); encode_replica_dir(straydn->get_dir(), who, bl); encode_replica_dentry(straydn, who, bl); + if (!straydn->get_projected_linkage()->is_null()) { + encode_replica_inode(straydn->get_projected_linkage()->get_inode(), who, bl, features); + } ENCODE_FINISH(bl); } -void MDCache::decode_replica_stray(CDentry *&straydn, const bufferlist &bl, mds_rank_t from) +void MDCache::decode_replica_stray(CDentry *&straydn, CInode **in, const bufferlist &bl, mds_rank_t from) { MDSContext::vec finished; auto p = bl.cbegin(); - DECODE_START(1, p); + DECODE_START(2, p); CInode *mdsin = nullptr; decode_replica_inode(mdsin, p, NULL, finished); CDir *mdsdir = nullptr; @@ -10816,6 +10832,9 @@ void MDCache::decode_replica_stray(CDentry *&straydn, const bufferlist &bl, mds_ decode_replica_dir(straydir, p, strayin, from, finished); decode_replica_dentry(straydn, p, straydir, finished); + if (struct_v >= 2 && in) { + decode_replica_inode(*in, p, straydn, finished); + } if (!finished.empty()) mds->queue_waiters(finished); DECODE_FINISH(p); @@ -11047,8 +11066,9 @@ void MDCache::handle_dentry_unlink(const cref_t &m) { // straydn CDentry *straydn = nullptr; + CInode *strayin = nullptr; if (m->straybl.length()) - decode_replica_stray(straydn, m->straybl, mds_rank_t(m->get_source().num())); + decode_replica_stray(straydn, &strayin, m->straybl, mds_rank_t(m->get_source().num())); CDir *dir = get_dirfrag(m->get_dirfrag()); if (!dir) { @@ -12304,7 +12324,7 @@ void MDCache::force_readonly() CInode *in = p.second; if (in->is_head()) mds->locker->eval(in, CEPH_CAP_LOCKS); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } diff --git a/ceph/src/mds/MDCache.h b/ceph/src/mds/MDCache.h index e08168b71..f5b4e2a06 100644 --- a/ceph/src/mds/MDCache.h +++ b/ceph/src/mds/MDCache.h @@ -873,7 +873,7 @@ class MDCache { void decode_replica_inode(CInode *&in, bufferlist::const_iterator& p, CDentry *dn, MDSContext::vec& finished); void encode_replica_stray(CDentry *straydn, mds_rank_t who, bufferlist& bl); - void decode_replica_stray(CDentry *&straydn, const bufferlist &bl, mds_rank_t from); + void decode_replica_stray(CDentry *&straydn, CInode **in, const bufferlist &bl, mds_rank_t from); // -- namespace -- void encode_remote_dentry_link(CDentry::linkage_t *dnl, bufferlist& bl); diff --git a/ceph/src/mds/MDSRank.cc b/ceph/src/mds/MDSRank.cc index 886a379dd..1199a6539 100644 --- a/ceph/src/mds/MDSRank.cc +++ b/ceph/src/mds/MDSRank.cc @@ -543,6 +543,7 @@ MDSRank::MDSRank( server = new Server(this, &metrics_handler); locker = new Locker(this, mdcache); + _heartbeat_reset_grace = g_conf().get_val("mds_heartbeat_reset_grace"); heartbeat_grace = g_conf().get_val("mds_heartbeat_grace"); op_tracker.set_complaint_and_threshold(cct->_conf->mds_op_complaint_time, cct->_conf->mds_op_log_threshold); @@ -1948,8 +1949,8 @@ void MDSRank::resolve_done() } void MDSRank::apply_blocklist(const std::set &addrs, epoch_t epoch) { - auto victims = server->apply_blocklist(addrs); - dout(4) << __func__ << ": killed " << victims << " blocklisted sessions (" + auto victims = server->apply_blocklist(); + dout(4) << __func__ << ": killed " << victims << ", blocklisted sessions (" << addrs.size() << " blocklist entries, " << sessionmap.get_sessions().size() << ")" << dendl; if (victims) { @@ -1970,9 +1971,10 @@ void MDSRank::reconnect_start() // into reconnect, so that we don't wait for them. objecter->enable_blocklist_events(); std::set blocklist; + std::set range; epoch_t epoch = 0; - objecter->with_osdmap([&blocklist, &epoch](const OSDMap& o) { - o.get_blocklist(&blocklist); + objecter->with_osdmap([&blocklist, &range, &epoch](const OSDMap& o) { + o.get_blocklist(&blocklist, &range); epoch = o.get_epoch(); }); @@ -3346,6 +3348,8 @@ void MDSRank::create_logger() "exi", PerfCountersBuilder::PRIO_INTERESTING); mds_plb.add_u64_counter(l_mds_imported_inodes, "imported_inodes", "Imported inodes", "imi", PerfCountersBuilder::PRIO_INTERESTING); + mds_plb.add_u64_counter(l_mds_slow_reply, "slow_reply", "Slow replies", "slr", + PerfCountersBuilder::PRIO_INTERESTING); // caps msg stats mds_plb.add_u64_counter(l_mdss_handle_client_caps, "handle_client_caps", @@ -3744,6 +3748,7 @@ const char** MDSRankDispatcher::get_tracked_conf_keys() const "mds_recall_warning_decay_rate", "mds_request_load_average_decay_rate", "mds_session_cache_liveness_decay_rate", + "mds_heartbeat_reset_grace", "mds_heartbeat_grace", "mds_session_cap_acquisition_decay_rate", "mds_max_caps_per_client", @@ -3762,6 +3767,9 @@ void MDSRankDispatcher::handle_conf_change(const ConfigProxy& conf, const std::s { // XXX with or without mds_lock! + if (changed.count("mds_heartbeat_reset_grace")) { + _heartbeat_reset_grace = conf.get_val("mds_heartbeat_reset_grace"); + } if (changed.count("mds_heartbeat_grace")) { heartbeat_grace = conf.get_val("mds_heartbeat_grace"); } diff --git a/ceph/src/mds/MDSRank.h b/ceph/src/mds/MDSRank.h index 784aa5741..c08970710 100644 --- a/ceph/src/mds/MDSRank.h +++ b/ceph/src/mds/MDSRank.h @@ -53,6 +53,7 @@ enum { l_mds_request, l_mds_reply, l_mds_reply_latency, + l_mds_slow_reply, l_mds_forward, l_mds_dir_fetch, l_mds_dir_commit, @@ -266,6 +267,9 @@ class MDSRank { * of code while holding the mds_lock */ void heartbeat_reset(); + int heartbeat_reset_grace(int count=1) { + return count * _heartbeat_reset_grace; + } /** * Report state DAMAGED to the mon, and then pass on to respawn(). Call @@ -575,6 +579,7 @@ class MDSRank { ceph::heartbeat_handle_d *hb = nullptr; // Heartbeat for threads using mds_lock double heartbeat_grace; + int _heartbeat_reset_grace; std::map peer_mdsmap_epoch; @@ -704,4 +709,3 @@ public: }; #endif // MDS_RANK_H_ - diff --git a/ceph/src/mds/OpenFileTable.cc b/ceph/src/mds/OpenFileTable.cc index a438ee1dd..1d52e4b99 100644 --- a/ceph/src/mds/OpenFileTable.cc +++ b/ceph/src/mds/OpenFileTable.cc @@ -1090,7 +1090,7 @@ void OpenFileTable::_prefetch_dirfrags() ceph_assert(dir->get_inode()->dirfragtree.is_leaf(dir->get_frag())); dir->fetch(gather.new_sub()); - if (!(++num_opening_dirfrags % 1000)) + if (!(++num_opening_dirfrags % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } @@ -1164,7 +1164,7 @@ void OpenFileTable::_prefetch_inodes() mdcache->open_ino(ino, pool, fin, false); } - if (!(num_opening_inodes % 1000)) + if (!(num_opening_inodes % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } diff --git a/ceph/src/mds/Server.cc b/ceph/src/mds/Server.cc index e6f7e864f..b6e4e960b 100644 --- a/ceph/src/mds/Server.cc +++ b/ceph/src/mds/Server.cc @@ -267,6 +267,7 @@ Server::Server(MDSRank *m, MetricsHandler *metrics_handler) : dir_max_entries = g_conf().get_val("mds_dir_max_entries"); bal_fragment_size_max = g_conf().get_val("mds_bal_fragment_size_max"); supported_features = feature_bitset_t(CEPHFS_FEATURES_MDS_SUPPORTED); + supported_metric_spec = feature_bitset_t(CEPHFS_METRIC_FEATURES_ALL); } void Server::dispatch(const cref_t &m) @@ -858,8 +859,10 @@ void Server::_session_logged(Session *session, uint64_t state_seq, bool open, ve metrics_handler->add_session(session); ceph_assert(session->get_connection()); auto reply = make_message(CEPH_SESSION_OPEN); - if (session->info.has_feature(CEPHFS_FEATURE_MIMIC)) + if (session->info.has_feature(CEPHFS_FEATURE_MIMIC)) { reply->supported_features = supported_features; + reply->metric_spec = supported_metric_spec; + } mds->send_message_client(reply, session); if (mdcache->is_readonly()) { auto m = make_message(CEPH_SESSION_FORCE_RO); @@ -1012,8 +1015,10 @@ void Server::finish_force_open_sessions(const mapadd_session(session); auto reply = make_message(CEPH_SESSION_OPEN); - if (session->info.has_feature(CEPHFS_FEATURE_MIMIC)) + if (session->info.has_feature(CEPHFS_FEATURE_MIMIC)) { reply->supported_features = supported_features; + reply->metric_spec = supported_metric_spec; + } mds->send_message_client(reply, session); if (mdcache->is_readonly()) @@ -1302,38 +1307,23 @@ void Server::kill_session(Session *session, Context *on_safe) } } -size_t Server::apply_blocklist(const std::set &blocklist) +size_t Server::apply_blocklist() { - bool prenautilus = mds->objecter->with_osdmap( - [&](const OSDMap& o) { - return o.require_osd_release < ceph_release_t::nautilus; - }); - std::vector victims; const auto& sessions = mds->sessionmap.get_sessions(); - for (const auto& p : sessions) { - if (!p.first.is_client()) { - // Do not apply OSDMap blocklist to MDS daemons, we find out - // about their death via MDSMap. - continue; - } - - Session *s = p.second; - auto inst_addr = s->info.inst.addr; - // blocklist entries are always TYPE_ANY for nautilus+ - inst_addr.set_type(entity_addr_t::TYPE_ANY); - if (blocklist.count(inst_addr)) { - victims.push_back(s); - continue; - } - if (prenautilus) { - // ...except pre-nautilus, they were TYPE_LEGACY - inst_addr.set_type(entity_addr_t::TYPE_LEGACY); - if (blocklist.count(inst_addr)) { - victims.push_back(s); + mds->objecter->with_osdmap( + [&](const OSDMap& o) { + for (const auto& p : sessions) { + if (!p.first.is_client()) { + // Do not apply OSDMap blocklist to MDS daemons, we find out + // about their death via MDSMap. + continue; + } + if (o.is_blocklisted(p.second->info.inst.addr)) { + victims.push_back(p.second); + } } - } - } + }); for (const auto& s : victims) { kill_session(s, nullptr); @@ -1495,8 +1485,10 @@ void Server::handle_client_reconnect(const cref_t &m) metrics_handler->add_session(session); // notify client of success with an OPEN auto reply = make_message(CEPH_SESSION_OPEN); - if (session->info.has_feature(CEPHFS_FEATURE_MIMIC)) + if (session->info.has_feature(CEPHFS_FEATURE_MIMIC)) { reply->supported_features = supported_features; + reply->metric_spec = supported_metric_spec; + } mds->send_message_client(reply, session); mds->clog->debug() << "reconnect by " << session->info.inst << " after " << delay; } @@ -2137,6 +2129,9 @@ void Server::early_reply(MDRequestRef& mdr, CInode *tracei, CDentry *tracedn) mds->logger->inc(l_mds_reply); utime_t lat = ceph_clock_now() - req->get_recv_stamp(); mds->logger->tinc(l_mds_reply_latency, lat); + if (lat >= g_conf()->mds_op_complaint_time) { + mds->logger->inc(l_mds_slow_reply); + } if (client_inst.name.is_client()) { mds->sessionmap.hit_session(mdr->session); } @@ -2195,6 +2190,9 @@ void Server::reply_client_request(MDRequestRef& mdr, const ref_t & mds->logger->inc(l_mds_reply); utime_t lat = ceph_clock_now() - mdr->client_request->get_recv_stamp(); mds->logger->tinc(l_mds_reply_latency, lat); + if (lat >= g_conf()->mds_op_complaint_time) { + mds->logger->inc(l_mds_slow_reply); + } if (session && client_inst.name.is_client()) { mds->sessionmap.hit_session(session); } @@ -2698,7 +2696,7 @@ void Server::handle_peer_request(const cref_t &m) CDentry *straydn = NULL; if (m->straybl.length() > 0) { - mdcache->decode_replica_stray(straydn, m->straybl, from); + mdcache->decode_replica_stray(straydn, nullptr, m->straybl, from); ceph_assert(straydn); m->straybl.clear(); } diff --git a/ceph/src/mds/Server.h b/ceph/src/mds/Server.h index 9e5bc45a4..b16106ef5 100644 --- a/ceph/src/mds/Server.h +++ b/ceph/src/mds/Server.h @@ -134,7 +134,7 @@ public: void find_idle_sessions(); void kill_session(Session *session, Context *on_safe); - size_t apply_blocklist(const std::set &blocklist); + size_t apply_blocklist(); void journal_close_session(Session *session, int state, Context *on_safe); size_t get_num_pending_reclaim() const { return client_reclaim_gather.size(); } @@ -487,6 +487,7 @@ private: std::set client_reconnect_denied; // clients whose reconnect msg have been denied . feature_bitset_t supported_features; + feature_bitset_t supported_metric_spec; feature_bitset_t required_client_features; bool forward_all_requests_to_auth = false; diff --git a/ceph/src/mds/journal.cc b/ceph/src/mds/journal.cc index 0c211ee39..cf2d2a658 100644 --- a/ceph/src/mds/journal.cc +++ b/ceph/src/mds/journal.cc @@ -1362,7 +1362,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) in->state_clear(CInode::STATE_AUTH); ceph_assert(g_conf()->mds_kill_journal_replay_at != 2); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } @@ -1397,7 +1397,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) if (lump.is_importing()) dn->state_set(CDentry::STATE_AUTH); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } @@ -1435,7 +1435,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) // Make null dentries the first things we trim dout(10) << "EMetaBlob.replay pushing to bottom of lru " << *dn << dendl; - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } } @@ -1468,7 +1468,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) else dir->state_set(CDir::STATE_AUTH); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } } @@ -1500,7 +1500,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) dir->state_clear(CDir::STATE_AUTH); mds->mdcache->adjust_subtree_auth(dir, CDIR_AUTH_UNDEF); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } } @@ -1513,7 +1513,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) ceph_assert(p->first->is_dir()); mds->mdcache->adjust_subtree_after_rename(p->first, p->second, false); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } } @@ -1531,7 +1531,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) } else mds->mdcache->remove_inode_recursive(in); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } } @@ -1544,7 +1544,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) if (client) client->got_journaled_agree(p.second, logseg); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } @@ -1634,7 +1634,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) ceph_assert(in); mds->mdcache->add_recovered_truncate(in, logseg); - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } for (const auto& p : truncate_finish) { @@ -1645,7 +1645,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) mds->mdcache->remove_recovered_truncate(in, ls); } - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } @@ -1667,7 +1667,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) dout(10) << "EMetaBlob.replay destroyed " << *p << ", not in cache" << dendl; } - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } mds->mdcache->open_file_table.note_destroyed_inos(logseg->seq, destroyed_inodes); @@ -1689,7 +1689,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) } } - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } @@ -1705,7 +1705,7 @@ void EMetaBlob::replay(MDSRank *mds, LogSegment *logseg, MDPeerUpdate *peerup) } } - if (!(++count % 1000)) + if (!(++count % mds->heartbeat_reset_grace())) mds->heartbeat_reset(); } diff --git a/ceph/src/mgr/ActivePyModules.cc b/ceph/src/mgr/ActivePyModules.cc index 712471ca0..58c3d9ee4 100644 --- a/ceph/src/mgr/ActivePyModules.cc +++ b/ceph/src/mgr/ActivePyModules.cc @@ -97,6 +97,7 @@ void ActivePyModules::dump_server(const std::string &hostname, f->open_object_section("service"); f->dump_string("type", key.type); f->dump_string("id", key.name); + f->dump_string("ceph_version", ceph_version); if (!id.empty()) { f->dump_string("name", id); } @@ -1426,6 +1427,11 @@ void ActivePyModules::remove_mds_perf_query(MetricQueryID query_id) } } +void ActivePyModules::reregister_mds_perf_queries() +{ + server.reregister_mds_perf_queries(); +} + PyObject *ActivePyModules::get_mds_perf_counters(MetricQueryID query_id) { MDSPerfCollector collector(query_id); @@ -1468,6 +1474,11 @@ PyObject *ActivePyModules::get_mds_perf_counters(MetricQueryID query_id) f.close_section(); // i } f.close_section(); // counters + + f.open_array_section("last_updated"); + f.dump_float("last_updated_mono", collector.last_updated_mono); + f.close_section(); // last_updated + f.close_section(); // metrics return f.get(); diff --git a/ceph/src/mgr/ActivePyModules.h b/ceph/src/mgr/ActivePyModules.h index 2547c2565..218497c1e 100644 --- a/ceph/src/mgr/ActivePyModules.h +++ b/ceph/src/mgr/ActivePyModules.h @@ -124,6 +124,7 @@ public: const MDSPerfMetricQuery &query, const std::optional &limit); void remove_mds_perf_query(MetricQueryID query_id); + void reregister_mds_perf_queries(); PyObject *get_mds_perf_counters(MetricQueryID query_id); bool get_store(const std::string &module_name, diff --git a/ceph/src/mgr/BaseMgrModule.cc b/ceph/src/mgr/BaseMgrModule.cc index 4c905a6f7..2e894b031 100644 --- a/ceph/src/mgr/BaseMgrModule.cc +++ b/ceph/src/mgr/BaseMgrModule.cc @@ -1321,6 +1321,13 @@ ceph_remove_mds_perf_query(BaseMgrModule *self, PyObject *args) Py_RETURN_NONE; } +static PyObject* +ceph_reregister_mds_perf_queries(BaseMgrModule *self, PyObject *args) +{ + self->py_modules->reregister_mds_perf_queries(); + Py_RETURN_NONE; +} + static PyObject* ceph_get_mds_perf_counters(BaseMgrModule *self, PyObject *args) { @@ -1512,6 +1519,9 @@ PyMethodDef BaseMgrModule_methods[] = { {"_ceph_remove_mds_perf_query", (PyCFunction)ceph_remove_mds_perf_query, METH_VARARGS, "Remove an mds perf query"}, + {"_ceph_reregister_mds_perf_queries", (PyCFunction)ceph_reregister_mds_perf_queries, + METH_NOARGS, "Re-register mds perf queries"}, + {"_ceph_get_mds_perf_counters", (PyCFunction)ceph_get_mds_perf_counters, METH_VARARGS, "Get mds perf counters"}, diff --git a/ceph/src/mgr/DaemonServer.cc b/ceph/src/mgr/DaemonServer.cc index f5c0376c7..fd493a8d9 100644 --- a/ceph/src/mgr/DaemonServer.cc +++ b/ceph/src/mgr/DaemonServer.cc @@ -3084,6 +3084,11 @@ int DaemonServer::remove_mds_perf_query(MetricQueryID query_id) return mds_perf_metric_collector.remove_query(query_id); } +void DaemonServer::reregister_mds_perf_queries() +{ + mds_perf_metric_collector.reregister_queries(); +} + int DaemonServer::get_mds_perf_counters(MDSPerfCollector *collector) { return mds_perf_metric_collector.get_counters(collector); diff --git a/ceph/src/mgr/DaemonServer.h b/ceph/src/mgr/DaemonServer.h index b3c9f191a..d719a96a7 100644 --- a/ceph/src/mgr/DaemonServer.h +++ b/ceph/src/mgr/DaemonServer.h @@ -296,6 +296,7 @@ public: MetricQueryID add_mds_perf_query(const MDSPerfMetricQuery &query, const std::optional &limit); int remove_mds_perf_query(MetricQueryID query_id); + void reregister_mds_perf_queries(); int get_mds_perf_counters(MDSPerfCollector *collector); virtual const char** get_tracked_conf_keys() const override; diff --git a/ceph/src/mgr/MDSPerfMetricCollector.cc b/ceph/src/mgr/MDSPerfMetricCollector.cc index 74404a89d..62298aba3 100644 --- a/ceph/src/mgr/MDSPerfMetricCollector.cc +++ b/ceph/src/mgr/MDSPerfMetricCollector.cc @@ -33,6 +33,8 @@ void MDSPerfMetricCollector::process_reports(const MetricPayload &payload) { // update delayed rank set delayed_ranks = metric_report.rank_metrics_delayed; dout(20) << ": delayed ranks=[" << delayed_ranks << "]" << dendl; + + clock_gettime(CLOCK_MONOTONIC_COARSE, &last_updated_mono); } int MDSPerfMetricCollector::get_counters(PerfCollector *collector) { @@ -47,6 +49,7 @@ int MDSPerfMetricCollector::get_counters(PerfCollector *collector) { get_delayed_ranks(&c->delayed_ranks); + get_last_updated(&c->last_updated_mono); return r; } @@ -54,3 +57,8 @@ void MDSPerfMetricCollector::get_delayed_ranks(std::set *ranks) { ceph_assert(ceph_mutex_is_locked(lock)); *ranks = delayed_ranks; } + +void MDSPerfMetricCollector::get_last_updated(utime_t *ts) { + ceph_assert(ceph_mutex_is_locked(lock)); + *ts = utime_t(last_updated_mono); +} diff --git a/ceph/src/mgr/MDSPerfMetricCollector.h b/ceph/src/mgr/MDSPerfMetricCollector.h index c6b379ec5..c72bce091 100644 --- a/ceph/src/mgr/MDSPerfMetricCollector.h +++ b/ceph/src/mgr/MDSPerfMetricCollector.h @@ -13,9 +13,11 @@ class MDSPerfMetricCollector MDSPerfMetrics> { private: std::set delayed_ranks; + struct timespec last_updated_mono; void get_delayed_ranks(std::set *ranks); + void get_last_updated(utime_t *ts); public: MDSPerfMetricCollector(MetricListener &listener); diff --git a/ceph/src/mgr/MDSPerfMetricTypes.h b/ceph/src/mgr/MDSPerfMetricTypes.h index b778b0347..a965e5fa7 100644 --- a/ceph/src/mgr/MDSPerfMetricTypes.h +++ b/ceph/src/mgr/MDSPerfMetricTypes.h @@ -317,6 +317,7 @@ std::ostream &operator<<(std::ostream &os, const MDSPerfMetricQuery &query); struct MDSPerfCollector : PerfCollector { std::map counters; std::set delayed_ranks; + utime_t last_updated_mono; MDSPerfCollector(MetricQueryID query_id) : PerfCollector(query_id) { diff --git a/ceph/src/mgr/MetricCollector.cc b/ceph/src/mgr/MetricCollector.cc index 7da0dae7f..c31dcf0b9 100644 --- a/ceph/src/mgr/MetricCollector.cc +++ b/ceph/src/mgr/MetricCollector.cc @@ -114,6 +114,12 @@ void MetricCollector::remove_all_queries() { } } +template +void MetricCollector::reregister_queries() { + dout(20) << dendl; + listener.handle_query_updated(); +} + template int MetricCollector::get_counters_generic( MetricQueryID query_id, std::map *c) { diff --git a/ceph/src/mgr/MetricCollector.h b/ceph/src/mgr/MetricCollector.h index 65e71e276..91fa78781 100644 --- a/ceph/src/mgr/MetricCollector.h +++ b/ceph/src/mgr/MetricCollector.h @@ -34,6 +34,8 @@ public: void remove_all_queries(); + void reregister_queries(); + std::map get_queries() const { std::lock_guard locker(lock); diff --git a/ceph/src/mon/LogMonitor.cc b/ceph/src/mon/LogMonitor.cc index 9103ddf7c..eb489e5df 100644 --- a/ceph/src/mon/LogMonitor.cc +++ b/ceph/src/mon/LogMonitor.cc @@ -387,6 +387,10 @@ void LogMonitor::log_external(const LogEntry& le) } if (g_conf()->mon_cluster_log_to_file) { + if (this->log_rotated.exchange(false)) { + this->log_external_close_fds(); + } + auto p = channel_fds.find(channel); int fd; if (p == channel_fds.end()) { diff --git a/ceph/src/mon/LogMonitor.h b/ceph/src/mon/LogMonitor.h index 715f380af..01909264a 100644 --- a/ceph/src/mon/LogMonitor.h +++ b/ceph/src/mon/LogMonitor.h @@ -15,6 +15,7 @@ #ifndef CEPH_LOGMONITOR_H #define CEPH_LOGMONITOR_H +#include #include #include @@ -51,6 +52,7 @@ private: std::map channel_fds; fmt::memory_buffer file_log_buffer; + std::atomic log_rotated = false; struct log_channel_info { @@ -167,6 +169,9 @@ private: void check_subs(); void check_sub(Subscription *s); + void reopen_logs() { + this->log_rotated.store(true); + } void log_external_close_fds(); void log_external(const LogEntry& le); void log_external_backlog(); diff --git a/ceph/src/mon/MonCommands.h b/ceph/src/mon/MonCommands.h index 73314523c..097f3f74e 100644 --- a/ceph/src/mon/MonCommands.h +++ b/ceph/src/mon/MonCommands.h @@ -1016,6 +1016,7 @@ COMMAND("osd new " "Reads secrets from JSON file via `-i ` (see man page).", "osd", "rw") COMMAND("osd blocklist " + "name=range,type=CephString,goodchars=[range],req=false " "name=blocklistop,type=CephChoices,strings=add|rm " "name=addr,type=CephEntityAddr " "name=expire,type=CephFloat,range=0.0,req=false", diff --git a/ceph/src/mon/Monitor.cc b/ceph/src/mon/Monitor.cc index d647316e9..ac6d6632c 100644 --- a/ceph/src/mon/Monitor.cc +++ b/ceph/src/mon/Monitor.cc @@ -501,6 +501,7 @@ void Monitor::handle_signal(int signum) derr << "*** Got Signal " << sig_str(signum) << " ***" << dendl; if (signum == SIGHUP) { sighup_handler(signum); + logmon()->reopen_logs(); } else { ceph_assert(signum == SIGINT || signum == SIGTERM); shutdown(); diff --git a/ceph/src/mon/OSDMonitor.cc b/ceph/src/mon/OSDMonitor.cc index 276efcd04..0d63ba8b5 100644 --- a/ceph/src/mon/OSDMonitor.cc +++ b/ceph/src/mon/OSDMonitor.cc @@ -5253,6 +5253,16 @@ void OSDMonitor::tick() do_propose = true; } } + for (auto p = osdmap.range_blocklist.begin(); + p != osdmap.range_blocklist.end(); + ++p) { + if (p->second < now) { + dout(10) << "expiring range_blocklist item " << p->first + << " expired " << p->second << " < now " << now << dendl; + pending_inc.old_range_blocklist.push_back(p->first); + do_propose = true; + } + } if (try_prune_purged_snaps()) { do_propose = true; @@ -6032,7 +6042,31 @@ bool OSDMonitor::preprocess_command(MonOpRequestRef op) f->close_section(); f->flush(rdata); } - ss << "listed " << osdmap.blocklist.size() << " entries"; + if (f) + f->open_array_section("range_blocklist"); + + for (auto p = osdmap.range_blocklist.begin(); + p != osdmap.range_blocklist.end(); + ++p) { + if (f) { + f->open_object_section("entry"); + f->dump_string("range", p->first.get_legacy_str()); + f->dump_stream("until") << p->second; + f->close_section(); + } else { + stringstream ss; + string s; + ss << p->first << " " << p->second; + getline(ss, s); + s += "\n"; + rdata.append(s); + } + } + if (f) { + f->close_section(); + f->flush(rdata); + } + ss << "listed " << osdmap.blocklist.size() + osdmap.range_blocklist.size() << " entries"; } else if (prefix == "osd pool ls") { string detail; @@ -12676,10 +12710,14 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op, prefix == "osd blacklist clear") { pending_inc.new_blocklist.clear(); std::list > blocklist; - osdmap.get_blocklist(&blocklist); + std::list > range_b; + osdmap.get_blocklist(&blocklist, &range_b); for (const auto &entry : blocklist) { pending_inc.old_blocklist.push_back(entry.first); } + for (const auto &entry : range_b) { + pending_inc.old_range_blocklist.push_back(entry.first); + } ss << " removed all blocklist entries"; getline(ss, rs); wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, @@ -12687,8 +12725,18 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op, return true; } else if (prefix == "osd blocklist" || prefix == "osd blacklist") { - string addrstr; + string addrstr, rangestr; + bool range = false; cmd_getval(cmdmap, "addr", addrstr); + if (cmd_getval(cmdmap, "range", rangestr)) { + if (rangestr == "range") { + range = true; + } else { + ss << "Did you mean to specify \"osd blocklist range\"?"; + err = -EINVAL; + goto reply; + } + } entity_addr_t addr; if (!addr.parse(addrstr)) { ss << "unable to parse address " << addrstr; @@ -12696,11 +12744,31 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op, goto reply; } else { - if (osdmap.require_osd_release >= ceph_release_t::nautilus) { - // always blocklist type ANY - addr.set_type(entity_addr_t::TYPE_ANY); + if (range) { + if (!addr.maybe_cidr()) { + ss << "You specified a range command, but " << addr + << " does not parse as a CIDR range"; + err = -EINVAL; + goto reply; + } + addr.type = entity_addr_t::TYPE_CIDR; + err = check_cluster_features(CEPH_FEATUREMASK_RANGE_BLOCKLIST, ss); + if (err) { + goto reply; + } + if ((addr.is_ipv4() && addr.get_nonce() > 32) || + (addr.is_ipv6() && addr.get_nonce() > 128)) { + ss << "Too many bits in range for that protocol!"; + err = -EINVAL; + goto reply; + } } else { - addr.set_type(entity_addr_t::TYPE_LEGACY); + if (osdmap.require_osd_release >= ceph_release_t::nautilus) { + // always blocklist type ANY + addr.set_type(entity_addr_t::TYPE_ANY); + } else { + addr.set_type(entity_addr_t::TYPE_LEGACY); + } } string blocklistop; @@ -12714,16 +12782,27 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op, g_conf()->mon_osd_blocklist_default_expire); expires += d; - pending_inc.new_blocklist[addr] = expires; + auto add_to_pending_blocklists = [](auto& nb, auto& ob, + const auto& addr, + const auto& expires) { + nb[addr] = expires; + // cancel any pending un-blocklisting request too + auto it = std::find(ob.begin(), + ob.end(), addr); + if (it != ob.end()) { + ob.erase(it); + } + }; + if (range) { + add_to_pending_blocklists(pending_inc.new_range_blocklist, + pending_inc.old_range_blocklist, + addr, expires); - { - // cancel any pending un-blocklisting request too - auto it = std::find(pending_inc.old_blocklist.begin(), - pending_inc.old_blocklist.end(), addr); - if (it != pending_inc.old_blocklist.end()) { - pending_inc.old_blocklist.erase(it); - } - } + } else { + add_to_pending_blocklists(pending_inc.new_blocklist, + pending_inc.old_blocklist, + addr, expires); + } ss << "blocklisting " << addr << " until " << expires << " (" << d << " sec)"; getline(ss, rs); @@ -12731,12 +12810,24 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op, get_last_committed() + 1)); return true; } else if (blocklistop == "rm") { - if (osdmap.is_blocklisted(addr) || - pending_inc.new_blocklist.count(addr)) { - if (osdmap.is_blocklisted(addr)) - pending_inc.old_blocklist.push_back(addr); - else - pending_inc.new_blocklist.erase(addr); + auto rm_from_pending_blocklists = [](const auto& addr, + auto& blocklist, + auto& ob, auto& pb) { + if (blocklist.count(addr)) { + ob.push_back(addr); + return true; + } else if (pb.count(addr)) { + pb.erase(addr); + return true; + } + return false; + }; + if ((!range && rm_from_pending_blocklists(addr, osdmap.blocklist, + pending_inc.old_blocklist, + pending_inc.new_blocklist)) || + (range && rm_from_pending_blocklists(addr, osdmap.range_blocklist, + pending_inc.old_range_blocklist, + pending_inc.new_range_blocklist))) { ss << "un-blocklisting " << addr; getline(ss, rs); wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, @@ -14620,7 +14711,7 @@ void OSDMonitor::trigger_degraded_stretch_mode(const set& dead_buckets, newp.peering_crush_bucket_count = new_site_count; newp.peering_crush_mandatory_member = remaining_site; newp.min_size = pgi.second.min_size / 2; // only support 2 zones now - newp.last_force_op_resend = pending_inc.epoch; + newp.set_last_force_op_resend(pending_inc.epoch); } } propose_pending(); @@ -14640,7 +14731,7 @@ void OSDMonitor::trigger_recovery_stretch_mode() for (auto pgi : osdmap.pools) { if (pgi.second.peering_crush_bucket_count) { pg_pool_t& newp = *pending_inc.get_new_pool(pgi.first, &pgi.second); - newp.last_force_op_resend = pending_inc.epoch; + newp.set_last_force_op_resend(pending_inc.epoch); } } propose_pending(); @@ -14726,7 +14817,7 @@ void OSDMonitor::trigger_healthy_stretch_mode() newp.peering_crush_bucket_count = osdmap.stretch_bucket_count; newp.peering_crush_mandatory_member = CRUSH_ITEM_NONE; newp.min_size = g_conf().get_val("mon_stretch_pool_min_size"); - newp.last_force_op_resend = pending_inc.epoch; + newp.set_last_force_op_resend(pending_inc.epoch); } } propose_pending(); diff --git a/ceph/src/mount.fuse.ceph b/ceph/src/mount.fuse.ceph index c61625a8c..59a296d96 100755 --- a/ceph/src/mount.fuse.ceph +++ b/ceph/src/mount.fuse.ceph @@ -27,6 +27,8 @@ id=myuser,conf=/etc/ceph/foo.conf /mnt/ceph fuse.ceph defaults 0 0 import sys import argparse +import errno +import platform from subprocess import Popen def ceph_options(mntops): @@ -73,7 +75,11 @@ def main(arguments): mount_cmd.communicate() if (mount_cmd.returncode != 0): - print("Mount failed with status code: {}".format(mount_cmd.returncode)) + if (platform.system() == "Linux"): + if (mount_cmd.returncode != errno.EBUSY): + print("Mount failed with status code: {}".format(mount_cmd.returncode)) + else: + print("Mount failed with status code: {}".format(mount_cmd.returncode)) if __name__ == '__main__': sys.exit(main(sys.argv[1:])) diff --git a/ceph/src/mount/conf.cc b/ceph/src/mount/conf.cc index df95912c1..9a24aa53c 100644 --- a/ceph/src/mount/conf.cc +++ b/ceph/src/mount/conf.cc @@ -71,9 +71,7 @@ extern "C" void mount_ceph_get_config_info(const char *config_file, } std::string addr; - addr += eaddr.ip_only_to_str(); - addr += ":"; - addr += std::to_string(eaddr.get_port()); + addr += eaddr.ip_n_port_to_str(); /* If this will overrun cci_mons, stop here */ if (monaddrs.length() + 1 + addr.length() + 1 > sizeof(cci->cci_mons)) break; diff --git a/ceph/src/msg/msg_types.cc b/ceph/src/msg/msg_types.cc index 0c28e915b..1a78cb326 100644 --- a/ceph/src/msg/msg_types.cc +++ b/ceph/src/msg/msg_types.cc @@ -410,3 +410,16 @@ std::string entity_addr_t::ip_only_to_str() const } return host_ip ? host_ip : ""; } + +std::string entity_addr_t::ip_n_port_to_str() const +{ + std::string addr; + addr += ip_only_to_str(); + if (is_ipv6()) { + addr = '[' + addr + ']'; + } + addr += ':'; + addr += std::to_string(get_port()); + return addr; +} + diff --git a/ceph/src/msg/msg_types.h b/ceph/src/msg/msg_types.h index 0d931a7af..6e0ee1261 100644 --- a/ceph/src/msg/msg_types.h +++ b/ceph/src/msg/msg_types.h @@ -197,7 +197,9 @@ static inline void decode(sockaddr_storage& a, * an entity's network address. * includes a random value that prevents it from being reused. * thus identifies a particular process instance. - * ipv4 for now. + * + * This also happens to work to support cidr ranges, in which + * case the nonce contains the netmask. It's great! */ struct entity_addr_t { typedef enum { @@ -205,6 +207,7 @@ struct entity_addr_t { TYPE_LEGACY = 1, ///< legacy msgr1 protocol (ceph jewel and older) TYPE_MSGR2 = 2, ///< msgr2 protocol (new in ceph kraken) TYPE_ANY = 3, ///< ambiguous + TYPE_CIDR = 4, } type_t; static const type_t TYPE_DEFAULT = TYPE_MSGR2; static std::string_view get_type_name(int t) { @@ -213,6 +216,7 @@ struct entity_addr_t { case TYPE_LEGACY: return "v1"; case TYPE_MSGR2: return "v2"; case TYPE_ANY: return "any"; + case TYPE_CIDR: return "cidr"; default: return "???"; } }; @@ -245,6 +249,8 @@ struct entity_addr_t { bool is_legacy() const { return type == TYPE_LEGACY; } bool is_msgr2() const { return type == TYPE_MSGR2; } bool is_any() const { return type == TYPE_ANY; } + // this isn't a guarantee; some client addrs will match it + bool maybe_cidr() const { return get_port() == 0 && nonce != 0; } __u32 get_nonce() const { return nonce; } void set_nonce(__u32 n) { nonce = n; } @@ -400,6 +406,7 @@ struct entity_addr_t { } std::string ip_only_to_str() const; + std::string ip_n_port_to_str() const; std::string get_legacy_str() const { std::ostringstream ss; diff --git a/ceph/src/os/bluestore/BlueStore.cc b/ceph/src/os/bluestore/BlueStore.cc index ce066fa72..6c9e737b4 100644 --- a/ceph/src/os/bluestore/BlueStore.cc +++ b/ceph/src/os/bluestore/BlueStore.cc @@ -11872,10 +11872,10 @@ int BlueStore::_onode_omap_get( o->flush(); { const string& prefix = o->get_omap_prefix(); - KeyValueDB::Iterator it = db->get_iterator(prefix); string head, tail; o->get_omap_header(&head); o->get_omap_tail(&tail); + KeyValueDB::Iterator it = db->get_iterator(prefix, 0, KeyValueDB::IteratorBounds{head, tail}); it->lower_bound(head); while (it->valid()) { if (it->key() == head) { @@ -11957,10 +11957,10 @@ int BlueStore::omap_get_keys( o->flush(); { const string& prefix = o->get_omap_prefix(); - KeyValueDB::Iterator it = db->get_iterator(prefix); string head, tail; o->get_omap_key(string(), &head); o->get_omap_tail(&tail); + KeyValueDB::Iterator it = db->get_iterator(prefix, 0, KeyValueDB::IteratorBounds{head, tail}); it->lower_bound(head); while (it->valid()) { if (it->key() >= tail) { @@ -12145,7 +12145,15 @@ ObjectMap::ObjectMapIterator BlueStore::get_omap_iterator( } o->flush(); dout(10) << __func__ << " has_omap = " << (int)o->onode.has_omap() <get_iterator(o->get_omap_prefix()); + auto bounds = KeyValueDB::IteratorBounds(); + if (o->onode.has_omap()) { + std::string lower_bound, upper_bound; + o->get_omap_key(string(), &lower_bound); + o->get_omap_tail(&upper_bound); + bounds.lower_bound = std::move(lower_bound); + bounds.upper_bound = std::move(upper_bound); + } + KeyValueDB::Iterator it = db->get_iterator(o->get_omap_prefix(), 0, std::move(bounds)); return ObjectMap::ObjectMapIterator(new OmapIteratorImpl(c, o, it)); } @@ -14581,7 +14589,7 @@ void BlueStore::_do_write_small( o->extent_map.punch_hole(c, offset, length, &wctx->old_extents); // Zero detection -- small block - if (!bl.is_zero()) { + if (!cct->_conf->bluestore_zero_block_detection || !bl.is_zero()) { BlobRef b = c->new_blob(); _pad_zeros(&bl, &b_off0, min_alloc_size); wctx->write(offset, b, alloc_len, b_off0, bl, b_off, length, false, true); @@ -14820,7 +14828,7 @@ void BlueStore::_do_write_small( o->extent_map.punch_hole(c, offset, length, &wctx->old_extents); // Zero detection -- small block - if (!bl.is_zero()) { + if (!cct->_conf->bluestore_zero_block_detection || !bl.is_zero()) { _pad_zeros(&bl, &b_off0, chunk_size); dout(20) << __func__ << " reuse blob " << *b << std::hex @@ -14882,7 +14890,7 @@ void BlueStore::_do_write_small( o->extent_map.punch_hole(c, offset, length, &wctx->old_extents); // Zero detection -- small block - if (!bl.is_zero()) { + if (!cct->_conf->bluestore_zero_block_detection || !bl.is_zero()) { uint64_t chunk_size = b->get_blob().get_chunk_size(block_size); _pad_zeros(&bl, &b_off0, chunk_size); @@ -14938,7 +14946,7 @@ void BlueStore::_do_write_small( o->extent_map.punch_hole(c, offset, length, &wctx->old_extents); // Zero detection -- small block - if (!bl.is_zero()) { + if (!cct->_conf->bluestore_zero_block_detection || !bl.is_zero()) { // new blob. BlobRef b = c->new_blob(); _pad_zeros(&bl, &b_off0, block_size); @@ -15271,7 +15279,7 @@ void BlueStore::_do_write_big( blp.copy(l, t); // Zero detection -- big block - if (!t.is_zero()) { + if (!cct->_conf->bluestore_zero_block_detection || !t.is_zero()) { wctx->write(offset, b, l, b_off, t, b_off, l, false, new_blob); dout(20) << __func__ << " schedule write big: 0x" @@ -16658,10 +16666,10 @@ int BlueStore::_clone(TransContext *txc, // otherwise rewrite_omap_key will corrupt data ceph_assert(oldo->onode.flags == newo->onode.flags); const string& prefix = newo->get_omap_prefix(); - KeyValueDB::Iterator it = db->get_iterator(prefix); string head, tail; oldo->get_omap_header(&head); oldo->get_omap_tail(&tail); + KeyValueDB::Iterator it = db->get_iterator(prefix, 0, KeyValueDB::IteratorBounds{head, tail}); it->lower_bound(head); while (it->valid()) { if (it->key() >= tail) { diff --git a/ceph/src/osd/OSD.cc b/ceph/src/osd/OSD.cc index c35ac0b52..141326d45 100644 --- a/ceph/src/osd/OSD.cc +++ b/ceph/src/osd/OSD.cc @@ -2656,10 +2656,11 @@ will start to track new ops received afterwards."; f->close_section(); } else if (prefix == "dump_blocklist") { list > bl; + list > rbl; OSDMapRef curmap = service.get_osdmap(); + curmap->get_blocklist(&bl, &rbl); f->open_array_section("blocklist"); - curmap->get_blocklist(&bl); for (list >::iterator it = bl.begin(); it != bl.end(); ++it) { f->open_object_section("entry"); @@ -2670,6 +2671,17 @@ will start to track new ops received afterwards."; f->close_section(); //entry } f->close_section(); //blocklist + f->open_array_section("range_blocklist"); + for (list >::iterator it = rbl.begin(); + it != rbl.end(); ++it) { + f->open_object_section("entry"); + f->open_object_section("entity_addr_t"); + it->first.dump(f); + f->close_section(); //entity_addr_t + it->second.localtime(f->dump_stream("expire_time")); + f->close_section(); //entry + } + f->close_section(); //blocklist } else if (prefix == "dump_watchers") { list watchers; // scan pg's diff --git a/ceph/src/osd/OSDMap.cc b/ceph/src/osd/OSDMap.cc index 8ef6a1607..01608d38e 100644 --- a/ceph/src/osd/OSDMap.cc +++ b/ceph/src/osd/OSDMap.cc @@ -648,7 +648,7 @@ void OSDMap::Incremental::encode(ceph::buffer::list& bl, uint64_t features) cons } { - uint8_t target_v = 9; // if bumping this, be aware of stretch_mode target_v 10! + uint8_t target_v = 9; // if bumping this, be aware of range_blocklist 11 if (!HAVE_FEATURE(features, SERVER_LUMINOUS)) { target_v = 2; } else if (!HAVE_FEATURE(features, SERVER_NAUTILUS)) { @@ -657,6 +657,10 @@ void OSDMap::Incremental::encode(ceph::buffer::list& bl, uint64_t features) cons if (change_stretch_mode) { target_v = std::max((uint8_t)10, target_v); } + if (!new_range_blocklist.empty() || + !old_range_blocklist.empty()) { + target_v = std::max((uint8_t)11, target_v); + } ENCODE_START(target_v, 1, bl); // extended, osd-only data if (target_v < 7) { encode_addrvec_map_as_addr(new_hb_back_up, bl, features); @@ -706,6 +710,10 @@ void OSDMap::Incremental::encode(ceph::buffer::list& bl, uint64_t features) cons encode(new_stretch_mode_bucket, bl); encode(stretch_mode_enabled, bl); } + if (target_v >= 11) { + encode(new_range_blocklist, bl, features); + encode(old_range_blocklist, bl, features); + } ENCODE_FINISH(bl); // osd-only data } @@ -980,7 +988,10 @@ void OSDMap::Incremental::decode(ceph::buffer::list::const_iterator& bl) decode(new_stretch_mode_bucket, bl); decode(stretch_mode_enabled, bl); } - + if (struct_v >= 11) { + decode(new_range_blocklist, bl); + decode(old_range_blocklist, bl); + } DECODE_FINISH(bl); // osd-only data } @@ -1226,6 +1237,17 @@ void OSDMap::Incremental::dump(Formatter *f) const for (const auto &blist : old_blocklist) f->dump_stream("addr") << blist; f->close_section(); + f->open_array_section("new_range_blocklist"); + for (const auto &blist : new_range_blocklist) { + stringstream ss; + ss << blist.first; + f->dump_stream(ss.str().c_str()) << blist.second; + } + f->close_section(); + f->open_array_section("old_range_blocklist"); + for (const auto &blist : old_range_blocklist) + f->dump_stream("addr") << blist; + f->close_section(); f->open_array_section("new_xinfo"); for (const auto &xinfo : new_xinfo) { @@ -1336,9 +1358,76 @@ void OSDMap::set_epoch(epoch_t e) pool.second.last_change = e; } -bool OSDMap::is_blocklisted(const entity_addr_t& orig) const +OSDMap::range_bits::range_bits() : ipv6(false) { + memset(&bits, 0, sizeof(bits)); +} + +OSDMap::range_bits::range_bits(const entity_addr_t& addr) : ipv6(false) { + memset(&bits, 0, sizeof(bits)); + parse(addr); +} + +void OSDMap::range_bits::get_ipv6_bytes(unsigned const char *addr, + uint64_t *upper, uint64_t *lower) +{ + *upper = ((uint64_t)(ntohl(*(uint32_t*)(addr)))) << 32 | + ((uint64_t)(ntohl(*(uint32_t*)(&addr[4])))); + *lower = ((uint64_t)(ntohl(*(uint32_t*)(&addr[8])))) << 32 | + ((uint64_t)(ntohl(*(uint32_t*)(&addr[12])))); +} + +void OSDMap::range_bits::parse(const entity_addr_t& addr) { + // parse it into meaningful data + if (addr.is_ipv6()) { + get_ipv6_bytes(addr.in6_addr().sin6_addr.s6_addr, + &bits.ipv6.upper_64_bits, &bits.ipv6.lower_64_bits); + int32_t lower_shift = std::min(128- + static_cast(addr.get_nonce()), 64); + int32_t upper_shift = std::max(64- //(128-b.first.get_nonce())-64 + static_cast(addr.get_nonce()), 0); + + auto get_mask = [](int32_t shift) -> uint64_t { + if (shift >= 0 && shift < 64) { + return UINT64_MAX << shift; + } + return 0; + }; + + bits.ipv6.lower_mask = get_mask(lower_shift); + bits.ipv6.upper_mask = get_mask(upper_shift); + ipv6 = true; + } else if (addr.is_ipv4()) { + bits.ipv4.ip_32_bits = ntohl(addr.in4_addr().sin_addr.s_addr); + if (addr.get_nonce() > 0) { + bits.ipv4.mask = UINT32_MAX << (32-addr.get_nonce()); + } else { + bits.ipv4.mask = 0; + } + } else { + // uh... + } +} + +bool OSDMap::range_bits::matches(const entity_addr_t& addr) const { + if (addr.is_ipv4() && !ipv6) { + return ((ntohl(addr.in4_addr().sin_addr.s_addr) & bits.ipv4.mask) == + (bits.ipv4.ip_32_bits & bits.ipv4.mask)); + } else if (addr.is_ipv6() && ipv6) { + uint64_t upper_64, lower_64; + get_ipv6_bytes(addr.in6_addr().sin6_addr.s6_addr, &upper_64, &lower_64); + return (((upper_64 & bits.ipv6.upper_mask) == + (bits.ipv6.upper_64_bits & bits.ipv6.upper_mask)) && + ((lower_64 & bits.ipv6.lower_mask) == + (bits.ipv6.lower_64_bits & bits.ipv6.lower_mask))); + } + return false; +} + +bool OSDMap::is_blocklisted(const entity_addr_t& orig, CephContext *cct) const { - if (blocklist.empty()) { + if (cct) ldout(cct, 25) << "is_blocklisted: " << orig << dendl; + if (blocklist.empty() && range_blocklist.empty()) { + if (cct) ldout(cct, 30) << "not blocklisted: " << orig << dendl; return false; } @@ -1353,6 +1442,7 @@ bool OSDMap::is_blocklisted(const entity_addr_t& orig) const // this specific instance? if (blocklist.count(a)) { + if (cct) ldout(cct, 20) << "blocklist contains " << a << dendl; return true; } @@ -1361,20 +1451,31 @@ bool OSDMap::is_blocklisted(const entity_addr_t& orig) const a.set_port(0); a.set_nonce(0); if (blocklist.count(a)) { + if (cct) ldout(cct, 20) << "blocklist contains " << a << dendl; return true; } } + // is it in a blocklisted range? + for (const auto& i : calculated_ranges) { + bool blocked = i.second.matches(a); + if (blocked) { + if (cct) ldout(cct, 20) << "range_blocklist contains " << a << dendl; + return true; + } + } + + if (cct) ldout(cct, 25) << "not blocklisted: " << orig << dendl; return false; } -bool OSDMap::is_blocklisted(const entity_addrvec_t& av) const +bool OSDMap::is_blocklisted(const entity_addrvec_t& av, CephContext *cct) const { - if (blocklist.empty()) + if (blocklist.empty() && range_blocklist.empty()) return false; for (auto& a : av.v) { - if (is_blocklisted(a)) { + if (is_blocklisted(a, cct)) { return true; } } @@ -1382,16 +1483,23 @@ bool OSDMap::is_blocklisted(const entity_addrvec_t& av) const return false; } -void OSDMap::get_blocklist(list > *bl) const +void OSDMap::get_blocklist(list > *bl, + std::list > *rl) const { std::copy(blocklist.begin(), blocklist.end(), std::back_inserter(*bl)); + std::copy(range_blocklist.begin(), range_blocklist.end(), + std::back_inserter(*rl)); } -void OSDMap::get_blocklist(std::set *bl) const +void OSDMap::get_blocklist(std::set *bl, + std::set *rl) const { for (const auto &i : blocklist) { bl->insert(i.first); } + for (const auto &i : range_blocklist) { + rl->insert(i.first); + } } void OSDMap::set_max_osd(int m) @@ -2287,6 +2395,16 @@ int OSDMap::apply_incremental(const Incremental &inc) for (const auto &addr : inc.old_blocklist) blocklist.erase(addr); + for (const auto& addr_p : inc.new_range_blocklist) { + range_blocklist.insert(addr_p); + calculated_ranges.emplace(addr_p.first, addr_p.first); + new_blocklist_entries = true; + } + for (const auto &addr : inc.old_range_blocklist) { + calculated_ranges.erase(addr); + range_blocklist.erase(addr); + } + for (auto& i : inc.new_crush_node_flags) { if (i.second) { crush_node_flags[i.first] = i.second; @@ -3037,7 +3155,7 @@ void OSDMap::encode(ceph::buffer::list& bl, uint64_t features) const { // NOTE: any new encoding dependencies must be reflected by // SIGNIFICANT_FEATURES - uint8_t target_v = 9; // when bumping this, be aware of stretch_mode target_v 10! + uint8_t target_v = 9; // when bumping this, be aware of range blocklist if (!HAVE_FEATURE(features, SERVER_LUMINOUS)) { target_v = 1; } else if (!HAVE_FEATURE(features, SERVER_MIMIC)) { @@ -3048,6 +3166,9 @@ void OSDMap::encode(ceph::buffer::list& bl, uint64_t features) const if (stretch_mode_enabled) { target_v = std::max((uint8_t)10, target_v); } + if (!range_blocklist.empty()) { + target_v = std::max((uint8_t)11, target_v); + } ENCODE_START(target_v, 1, bl); // extended, osd-only data if (target_v < 7) { encode_addrvec_pvec_as_addr(osd_addrs->hb_back_addrs, bl, features); @@ -3103,6 +3224,9 @@ void OSDMap::encode(ceph::buffer::list& bl, uint64_t features) const encode(recovering_stretch_mode, bl); encode(stretch_mode_bucket, bl); } + if (target_v >= 11) { + ::encode(range_blocklist, bl, features); + } ENCODE_FINISH(bl); // osd-only data } @@ -3442,6 +3566,13 @@ void OSDMap::decode(ceph::buffer::list::const_iterator& bl) recovering_stretch_mode = 0; stretch_mode_bucket = 0; } + if (struct_v >= 11) { + decode(range_blocklist, bl); + calculated_ranges.clear(); + for (const auto& i : range_blocklist) { + calculated_ranges.emplace(i.first, i.first); + } + } DECODE_FINISH(bl); // osd-only data } @@ -3654,6 +3785,13 @@ void OSDMap::dump(Formatter *f) const f->dump_stream(ss.str().c_str()) << addr.second; } f->close_section(); + f->open_object_section("range_blocklist"); + for (const auto &addr : range_blocklist) { + stringstream ss; + ss << addr.first; + f->dump_stream(ss.str().c_str()) << addr.second; + } + f->close_section(); dump_erasure_code_profiles(erasure_code_profiles, f); @@ -3918,6 +4056,8 @@ void OSDMap::print(ostream& out) const for (const auto &addr : blocklist) out << "blocklist " << addr.first << " expires " << addr.second << "\n"; + for (const auto &addr : range_blocklist) + out << "range blocklist " << addr.first << " expires " << addr.second << "\n"; } class OSDTreePlainDumper : public CrushTreeDumper::Dumper { diff --git a/ceph/src/osd/OSDMap.h b/ceph/src/osd/OSDMap.h index 4cf02756c..28449c8b6 100644 --- a/ceph/src/osd/OSDMap.h +++ b/ceph/src/osd/OSDMap.h @@ -397,6 +397,8 @@ public: mempool::osdmap::map new_blocklist; mempool::osdmap::vector old_blocklist; + mempool::osdmap::map new_range_blocklist; + mempool::osdmap::vector old_range_blocklist; mempool::osdmap::map new_hb_back_up; mempool::osdmap::map new_hb_front_up; @@ -582,7 +584,31 @@ private: std::shared_ptr< mempool::osdmap::vector > osd_uuid; mempool::osdmap::vector osd_xinfo; + class range_bits { + struct ip6 { + uint64_t upper_64_bits, lower_64_bits; + uint64_t upper_mask, lower_mask; + }; + struct ip4 { + uint32_t ip_32_bits; + uint32_t mask; + }; + union { + ip6 ipv6; + ip4 ipv4; + } bits; + bool ipv6; + static void get_ipv6_bytes(unsigned const char *addr, + uint64_t *upper, uint64_t *lower); + public: + range_bits(); + range_bits(const entity_addr_t& addr); + void parse(const entity_addr_t& addr); + bool matches(const entity_addr_t& addr) const; + }; mempool::osdmap::unordered_map blocklist; + mempool::osdmap::map range_blocklist; + mempool::osdmap::map calculated_ranges; /// queue of snaps to remove mempool::osdmap::map removed_snaps_queue; @@ -693,10 +719,12 @@ public: const utime_t& get_created() const { return created; } const utime_t& get_modified() const { return modified; } - bool is_blocklisted(const entity_addr_t& a) const; - bool is_blocklisted(const entity_addrvec_t& a) const; - void get_blocklist(std::list > *bl) const; - void get_blocklist(std::set *bl) const; + bool is_blocklisted(const entity_addr_t& a, CephContext *cct=nullptr) const; + bool is_blocklisted(const entity_addrvec_t& a, CephContext *cct=nullptr) const; + void get_blocklist(std::list > *bl, + std::list > *rl) const; + void get_blocklist(std::set *bl, + std::set *rl) const; std::string get_cluster_snapshot() const { if (cluster_snapshot_epoch == epoch) diff --git a/ceph/src/osd/PG.cc b/ceph/src/osd/PG.cc index d3345319a..d77cef0d7 100644 --- a/ceph/src/osd/PG.cc +++ b/ceph/src/osd/PG.cc @@ -441,7 +441,6 @@ void PG::queue_scrub_after_repair() m_scrubber->set_op_parameters(m_planned_scrub); dout(15) << __func__ << ": queueing" << dendl; - m_scrubber->set_queued_or_active(); osd->queue_scrub_after_repair(this, Scrub::scrub_prio_t::high_priority); } @@ -1371,7 +1370,6 @@ Scrub::schedule_result_t PG::sched_scrub() m_scrubber->set_op_parameters(m_planned_scrub); dout(10) << __func__ << ": queueing" << dendl; - m_scrubber->set_queued_or_active(); osd->queue_for_scrub(this, Scrub::scrub_prio_t::low_priority); return Scrub::schedule_result_t::scrub_initiated; } diff --git a/ceph/src/osd/PeeringState.cc b/ceph/src/osd/PeeringState.cc index 0158ed92d..68b8d2225 100644 --- a/ceph/src/osd/PeeringState.cc +++ b/ceph/src/osd/PeeringState.cc @@ -4164,6 +4164,8 @@ void PeeringState::append_log( psdout(10) << __func__ << " approx pg log length = " << pg_log.get_log().approx_size() << dendl; + psdout(10) << __func__ << " dups pg log length = " + << pg_log.get_log().dups.size() << dendl; psdout(10) << __func__ << " transaction_applied = " << transaction_applied << dendl; if (!transaction_applied || async) diff --git a/ceph/src/osd/PrimaryLogPG.cc b/ceph/src/osd/PrimaryLogPG.cc index 677aef165..f7b15a5f8 100644 --- a/ceph/src/osd/PrimaryLogPG.cc +++ b/ceph/src/osd/PrimaryLogPG.cc @@ -3452,6 +3452,12 @@ int PrimaryLogPG::get_manifest_ref_count(ObjectContextRef obc, std::string& fp_o if (osdmap->in_removed_snaps_queue(info.pgid.pgid.pool(), *p)) { return -EBUSY; } + if (is_unreadable_object(clone_oid)) { + dout(10) << __func__ << ": " << clone_oid + << " is unreadable. Need to wait for recovery" << dendl; + wait_for_unreadable_object(clone_oid, op); + return -EAGAIN; + } ObjectContextRef clone_obc = get_object_context(clone_oid, false); if (!clone_obc) { break; @@ -10474,7 +10480,11 @@ int PrimaryLogPG::start_dedup(OpRequestRef op, ObjectContextRef obc) if (pool.info.get_fingerprint_type() == pg_pool_t::TYPE_FINGERPRINT_NONE) { dout(0) << " fingerprint algorithm is not set " << dendl; return -EINVAL; - } + } + if (pool.info.get_dedup_tier() <= 0) { + dout(10) << " dedup tier is not set " << dendl; + return -EINVAL; + } /* * The operations to make dedup chunks are tracked by a ManifestOp. @@ -10610,6 +10620,8 @@ hobject_t PrimaryLogPG::get_fpoid_from_chunk(const hobject_t soid, bufferlist& c pg_t raw_pg; object_locator_t oloc(soid); oloc.pool = pool.info.get_dedup_tier(); + // check if dedup_tier isn't set + ceph_assert(oloc.pool > 0); get_osdmap()->object_locator_to_pg(fp_oid, oloc, raw_pg); hobject_t target(fp_oid, oloc.key, snapid_t(), raw_pg.ps(), raw_pg.pool(), diff --git a/ceph/src/osd/scrubber/pg_scrubber.cc b/ceph/src/osd/scrubber/pg_scrubber.cc index 60582664a..25e4a83d9 100644 --- a/ceph/src/osd/scrubber/pg_scrubber.cc +++ b/ceph/src/osd/scrubber/pg_scrubber.cc @@ -188,9 +188,7 @@ void PgScrubber::initiate_regular_scrub(epoch_t epoch_queued) m_fsm->process_event(StartScrub{}); dout(10) << "scrubber event --<< StartScrub" << dendl; } else { - clear_queued_or_active(); - // and just in case snap trimming was blocked by the aborted scrub - m_pg->snap_trimmer_scrub_complete(); + clear_queued_or_active(); // also restarts snap trimming } } @@ -204,9 +202,7 @@ void PgScrubber::initiate_scrub_after_repair(epoch_t epoch_queued) m_fsm->process_event(AfterRepairScrub{}); dout(10) << "scrubber event --<< AfterRepairScrub" << dendl; } else { - clear_queued_or_active(); - // and just in case snap trimming was blocked by the aborted scrub - m_pg->snap_trimmer_scrub_complete(); + clear_queued_or_active(); // also restarts snap trimming } } @@ -1335,6 +1331,8 @@ void PgScrubber::set_op_parameters(requested_scrub_t& request) { dout(10) << __func__ << " input: " << request << dendl; + set_queued_or_active(); // we are fully committed now. + // write down the epoch of starting a new scrub. Will be used // to discard stale messages from previous aborted scrubs. m_epoch_start = m_pg->get_osdmap_epoch(); @@ -1715,7 +1713,11 @@ void PgScrubber::set_queued_or_active() void PgScrubber::clear_queued_or_active() { - m_queued_or_active = false; + if (m_queued_or_active) { + m_queued_or_active = false; + // and just in case snap trimming was blocked by the aborted scrub + m_pg->snap_trimmer_scrub_complete(); + } } bool PgScrubber::is_queued_or_active() const @@ -1921,9 +1923,6 @@ void PgScrubber::scrub_finish() if (m_pg->is_active() && m_pg->is_primary()) { m_pg->recovery_state.share_pg_info(); } - - // we may have blocked the snap trimmer - m_pg->snap_trimmer_scrub_complete(); } void PgScrubber::on_digest_updates() @@ -2301,6 +2300,11 @@ std::ostream& PgScrubber::gen_prefix(std::ostream& out) const } } +void PgScrubber::log_cluster_warning(const std::string& warning) const +{ + m_osds->clog->do_log(CLOG_WARN, warning); +} + ostream& PgScrubber::show(ostream& out) const { return out << " [ " << m_pg_id << ": " << m_flags << " ] "; diff --git a/ceph/src/osd/scrubber/pg_scrubber.h b/ceph/src/osd/scrubber/pg_scrubber.h index f32fb2319..eeba65bab 100644 --- a/ceph/src/osd/scrubber/pg_scrubber.h +++ b/ceph/src/osd/scrubber/pg_scrubber.h @@ -429,6 +429,7 @@ class PgScrubber : public ScrubPgIF, public ScrubMachineListener { [[nodiscard]] bool was_epoch_changed() const final; void set_queued_or_active() final; + /// Clears `m_queued_or_active` and restarts snaptrimming void clear_queued_or_active() final; void mark_local_map_ready() final; @@ -444,6 +445,8 @@ class PgScrubber : public ScrubPgIF, public ScrubMachineListener { utime_t scrub_begin_stamp; std::ostream& gen_prefix(std::ostream& out) const final; + void log_cluster_warning(const std::string& warning) const final; + protected: bool state_test(uint64_t m) const { return m_pg->state_test(m); } void state_set(uint64_t m) { m_pg->state_set(m); } diff --git a/ceph/src/osd/scrubber/scrub_machine.cc b/ceph/src/osd/scrubber/scrub_machine.cc index 2610bf351..7987559d0 100644 --- a/ceph/src/osd/scrubber/scrub_machine.cc +++ b/ceph/src/osd/scrubber/scrub_machine.cc @@ -435,6 +435,15 @@ sc::result WaitReplicas::react(const GotReplicas&) } } +sc::result WaitReplicas::react(const DigestUpdate&) +{ + DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases + std::string warn_msg = "WaitReplicas::react(const DigestUpdate&): Unexpected DigestUpdate event"; + dout(10) << warn_msg << dendl; + scrbr->log_cluster_warning(warn_msg); + return discard_event(); +} + // ----------------------- WaitDigestUpdate ----------------------------------- WaitDigestUpdate::WaitDigestUpdate(my_context ctx) : my_base(ctx) diff --git a/ceph/src/osd/scrubber/scrub_machine.h b/ceph/src/osd/scrubber/scrub_machine.h index d9132eedc..41669138a 100644 --- a/ceph/src/osd/scrubber/scrub_machine.h +++ b/ceph/src/osd/scrubber/scrub_machine.h @@ -299,11 +299,11 @@ struct WaitReplicas : sc::state { using reactions = mpl::list, // all replicas are accounted for sc::transition, - sc::deferral // might arrive before we've reached WDU + sc::custom_reaction >; sc::result react(const GotReplicas&); - + sc::result react(const DigestUpdate&); bool all_maps_already_called{false}; // see comment in react code }; diff --git a/ceph/src/osd/scrubber/scrub_machine_lstnr.h b/ceph/src/osd/scrubber/scrub_machine_lstnr.h index 2fed0a399..f26d5faeb 100644 --- a/ceph/src/osd/scrubber/scrub_machine_lstnr.h +++ b/ceph/src/osd/scrubber/scrub_machine_lstnr.h @@ -186,4 +186,7 @@ struct ScrubMachineListener { /// exposed to be used by the scrub_machine logger virtual std::ostream& gen_prefix(std::ostream& out) const = 0; + + /// sending cluster-log warnings + virtual void log_cluster_warning(const std::string& msg) const = 0; }; diff --git a/ceph/src/osd/scrubber_common.h b/ceph/src/osd/scrubber_common.h index 2b4d370be..078eb60af 100644 --- a/ceph/src/osd/scrubber_common.h +++ b/ceph/src/osd/scrubber_common.h @@ -191,6 +191,8 @@ struct ScrubPgIF { /** * Manipulate the 'scrubbing request has been queued, or - we are * actually scrubbing' Scrubber's flag + * + * clear_queued_or_active() will also restart any blocked snaptrimming. */ virtual void set_queued_or_active() = 0; virtual void clear_queued_or_active() = 0; diff --git a/ceph/src/osdc/Objecter.cc b/ceph/src/osdc/Objecter.cc index 0050489d6..708c12cc9 100644 --- a/ceph/src/osdc/Objecter.cc +++ b/ceph/src/osdc/Objecter.cc @@ -1426,14 +1426,20 @@ void Objecter::emit_blocklist_events(const OSDMap &old_osd_map, std::set old_set; std::set new_set; + std::set old_range_set; + std::set new_range_set; - old_osd_map.get_blocklist(&old_set); - new_osd_map.get_blocklist(&new_set); + old_osd_map.get_blocklist(&old_set, &old_range_set); + new_osd_map.get_blocklist(&new_set, &new_range_set); std::set delta_set; std::set_difference( new_set.begin(), new_set.end(), old_set.begin(), old_set.end(), std::inserter(delta_set, delta_set.begin())); + std::set_difference( + new_range_set.begin(), new_range_set.end(), + old_range_set.begin(), old_range_set.end(), + std::inserter(delta_set, delta_set.begin())); blocklist_events.insert(delta_set.begin(), delta_set.end()); } diff --git a/ceph/src/pybind/mgr/CMakeLists.txt b/ceph/src/pybind/mgr/CMakeLists.txt index 4580ee2b7..54aa91420 100644 --- a/ceph/src/pybind/mgr/CMakeLists.txt +++ b/ceph/src/pybind/mgr/CMakeLists.txt @@ -58,5 +58,5 @@ set(mgr_modules install(DIRECTORY ${mgr_modules} DESTINATION ${CEPH_INSTALL_DATADIR}/mgr ${mgr_module_install_excludes}) -install(FILES mgr_module.py mgr_util.py +install(FILES mgr_module.py mgr_util.py object_format.py DESTINATION ${CEPH_INSTALL_DATADIR}/mgr) diff --git a/ceph/src/pybind/mgr/alerts/module.py b/ceph/src/pybind/mgr/alerts/module.py index 296bcf938..f20f04716 100644 --- a/ceph/src/pybind/mgr/alerts/module.py +++ b/ceph/src/pybind/mgr/alerts/module.py @@ -4,6 +4,7 @@ A simple cluster health alerting module. """ from mgr_module import CLIReadCommand, HandleCommandResult, MgrModule, Option +from email.utils import formatdate, make_msgid from threading import Event from typing import Any, Optional, Dict, List, TYPE_CHECKING, Union import json @@ -203,12 +204,16 @@ class Alerts(MgrModule): message = ('From: {from_name} <{sender}>\n' 'Subject: {status}\n' 'To: {target}\n' + 'Message-Id: {message_id}\n' + 'Date: {date}\n' '\n' '{status}\n'.format( sender=self.smtp_sender, from_name=self.smtp_from_name, status=status['status'], - target=self.smtp_destination)) + target=self.smtp_destination, + message_id=make_msgid(), + date=formatdate())) if 'new' in diff: message += ('\n--- New ---\n') diff --git a/ceph/src/pybind/mgr/ceph_module.pyi b/ceph/src/pybind/mgr/ceph_module.pyi index ca323870e..171919295 100644 --- a/ceph/src/pybind/mgr/ceph_module.pyi +++ b/ceph/src/pybind/mgr/ceph_module.pyi @@ -110,6 +110,7 @@ class BaseMgrModule(object): def _ceph_get_osd_perf_counters(self, query_id: int) -> Optional[Dict[str, List[PerfCounterT]]]: ... def _ceph_add_mds_perf_query(self, query: Dict[str, Dict[str, Any]]) -> Optional[int]: ... def _ceph_remove_mds_perf_query(self, query_id: int) -> None: ... + def _ceph_reregister_mds_perf_queries(self) -> None: ... def _ceph_get_mds_perf_counters(self, query_id: int) -> Optional[Dict[str, List[PerfCounterT]]]: ... def _ceph_unregister_client(self, addrs: str) -> None: ... def _ceph_register_client(self, addrs: str) -> None: ... diff --git a/ceph/src/pybind/mgr/cephadm/agent.py b/ceph/src/pybind/mgr/cephadm/agent.py index f6672be0b..fa75a8759 100644 --- a/ceph/src/pybind/mgr/cephadm/agent.py +++ b/ceph/src/pybind/mgr/cephadm/agent.py @@ -8,13 +8,15 @@ import tempfile import threading import time -from mgr_util import verify_tls_files +from mgr_module import ServiceInfoT +from mgr_util import verify_tls_files, build_url from orchestrator import DaemonDescriptionStatus, OrchestratorError from orchestrator._interface import daemon_type_to_service from ceph.utils import datetime_now from ceph.deployment.inventory import Devices from ceph.deployment.service_spec import ServiceSpec, PlacementSpec from cephadm.services.cephadmservice import CephadmDaemonDeploySpec +from cephadm.services.ingress import IngressSpec from datetime import datetime, timedelta from cryptography import x509 @@ -24,7 +26,7 @@ from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.backends import default_backend from typing import Any, Dict, List, Set, Tuple, \ - TYPE_CHECKING, Optional + TYPE_CHECKING, Optional, cast, Collection if TYPE_CHECKING: from cephadm.module import CephadmOrchestrator @@ -51,6 +53,30 @@ class CherryPyThread(threading.Thread): self.server_addr = self.mgr.get_mgr_ip() super(CherryPyThread, self).__init__(target=self.run) + def configure_cherrypy(self) -> None: + cherrypy.config.update({ + 'environment': 'production', + 'server.socket_host': self.server_addr, + 'server.socket_port': self.server_port, + 'engine.autoreload.on': False, + 'server.ssl_module': 'builtin', + 'server.ssl_certificate': self.cert_tmp.name, + 'server.ssl_private_key': self.key_tmp.name, + }) + + # configure routes + root = Root(self.mgr) + host_data = HostData(self.mgr) + d = cherrypy.dispatch.RoutesDispatcher() + d.connect(name='index', route='/', controller=root.index) + d.connect(name='sd-config', route='/prometheus/sd-config', controller=root.get_sd_config) + d.connect(name='rules', route='/prometheus/rules', controller=root.get_prometheus_rules) + d.connect(name='host-data', route='/data', controller=host_data.POST, + conditions=dict(method=['POST'])) + + conf = {'/': {'request.dispatch': d}} + cherrypy.tree.mount(None, "/", config=conf) + def run(self) -> None: try: try: @@ -77,18 +103,8 @@ class CherryPyThread(threading.Thread): cert_fname = self.cert_tmp.name verify_tls_files(cert_fname, key_fname) + self.configure_cherrypy() - cherrypy.config.update({ - 'server.socket_host': self.server_addr, - 'server.socket_port': self.server_port, - 'engine.autoreload.on': False, - 'server.ssl_module': 'builtin', - 'server.ssl_certificate': cert_fname, - 'server.ssl_private_key': key_fname, - }) - root_conf = {'/': {'request.dispatch': cherrypy.dispatch.MethodDispatcher(), - 'tools.response_headers.on': True}} - cherrypy.tree.mount(Root(self.mgr), '/', root_conf) self.mgr.log.debug('Starting cherrypy engine...') self.start_engine() self.mgr.log.debug('Cherrypy engine started.') @@ -130,22 +146,104 @@ class CherryPyThread(threading.Thread): self.cherrypy_shutdown_event.set() -class Root: - exposed = True +class Root(object): + + # collapse everything to '/' + def _cp_dispatch(self, vpath: str) -> 'Root': + cherrypy.request.path = '' + return self def __init__(self, mgr: "CephadmOrchestrator"): self.mgr = mgr - self.data = HostData(self.mgr) - def GET(self) -> str: + @cherrypy.expose + def index(self) -> str: return ''' Cephadm HTTP Endpoint -

Cephadm HTTP Endpoint is up and running

+

Cephadm Service Discovery Endpoints

+

mgr/Prometheus http sd-config

+

Alertmanager http sd-config

+

Node exporter http sd-config

+

HAProxy http sd-config

+

Prometheus rules

''' + @cherrypy.expose + @cherrypy.tools.json_out() + def get_sd_config(self, service: str) -> List[Dict[str, Collection[str]]]: + """Return compatible prometheus config for the specified service.""" + if service == 'mgr-prometheus': + return self.prometheus_sd_config() + elif service == 'alertmanager': + return self.alertmgr_sd_config() + elif service == 'node-exporter': + return self.node_exporter_sd_config() + elif service == 'haproxy': + return self.haproxy_sd_config() + else: + return [] + + def prometheus_sd_config(self) -> List[Dict[str, Collection[str]]]: + """Return compatible prometheus config for prometheus service.""" + servers = self.mgr.list_servers() + targets = [] + for server in servers: + hostname = server.get('hostname', '') + for service in cast(List[ServiceInfoT], server.get('services', [])): + if service['type'] != 'mgr': + continue + port = self.mgr.get_module_option_ex('prometheus', 'server_port', 9283) + targets.append(f'{hostname}:{port}') + return [{"targets": targets, "labels": {}}] + + def alertmgr_sd_config(self) -> List[Dict[str, Collection[str]]]: + """Return compatible prometheus config for mgr alertmanager service.""" + srv_entries = [] + for dd in self.mgr.cache.get_daemons_by_service('alertmanager'): + assert dd.hostname is not None + addr = dd.ip if dd.ip else self.mgr.inventory.get_addr(dd.hostname) + port = dd.ports[0] if dd.ports else 9093 + srv_entries.append('{}'.format(build_url(host=addr, port=port).lstrip('/'))) + return [{"targets": srv_entries, "labels": {}}] + + def node_exporter_sd_config(self) -> List[Dict[str, Collection[str]]]: + """Return compatible prometheus config for node-exporter service.""" + srv_entries = [] + for dd in self.mgr.cache.get_daemons_by_service('node-exporter'): + assert dd.hostname is not None + addr = dd.ip if dd.ip else self.mgr.inventory.get_addr(dd.hostname) + port = dd.ports[0] if dd.ports else 9100 + srv_entries.append({ + 'targets': [build_url(host=addr, port=port).lstrip('/')], + 'labels': {'instance': dd.hostname} + }) + return srv_entries + + def haproxy_sd_config(self) -> List[Dict[str, Collection[str]]]: + """Return compatible prometheus config for haproxy service.""" + srv_entries = [] + for dd in self.mgr.cache.get_daemons_by_type('ingress'): + if dd.service_name() in self.mgr.spec_store: + spec = cast(IngressSpec, self.mgr.spec_store[dd.service_name()].spec) + assert dd.hostname is not None + if dd.daemon_type == 'haproxy': + addr = self.mgr.inventory.get_addr(dd.hostname) + srv_entries.append({ + 'targets': [f"{build_url(host=addr, port=spec.monitor_port).lstrip('/')}"], + 'labels': {'instance': dd.service_name()} + }) + return srv_entries + + @cherrypy.expose(alias='prometheus/rules') + def get_prometheus_rules(self) -> str: + """Return currently configured prometheus rules as Yaml.""" + cherrypy.response.headers['Content-Type'] = 'text/plain' + with open(self.mgr.prometheus_alerts_path, 'r', encoding='utf-8') as f: + return f.read() + class HostData: exposed = True diff --git a/ceph/src/pybind/mgr/cephadm/module.py b/ceph/src/pybind/mgr/cephadm/module.py index d9c473163..766ea7726 100644 --- a/ceph/src/pybind/mgr/cephadm/module.py +++ b/ceph/src/pybind/mgr/cephadm/module.py @@ -1,5 +1,6 @@ import json import errno +import ipaddress import logging import re import shlex @@ -52,14 +53,16 @@ from .services.iscsi import IscsiService from .services.nfs import NFSService from .services.osd import OSDRemovalQueue, OSDService, OSD, NotFoundError from .services.monitoring import GrafanaService, AlertmanagerService, PrometheusService, \ - NodeExporterService, SNMPGatewayService + NodeExporterService, SNMPGatewayService, LokiService, PromtailService from .schedule import HostAssignment from .inventory import Inventory, SpecStore, HostCache, AgentCache, EventStore, \ ClientKeyringStore, ClientKeyringSpec from .upgrade import CephadmUpgrade from .template import TemplateMgr -from .utils import CEPH_IMAGE_TYPES, forall_hosts, cephadmNoImage +from .utils import CEPH_IMAGE_TYPES, RESCHEDULE_FROM_OFFLINE_HOSTS_TYPES, forall_hosts, \ + cephadmNoImage, CEPH_UPGRADE_ORDER from .configchecks import CephadmConfigChecks +from .offline_watcher import OfflineHostWatcher try: import asyncssh @@ -93,6 +96,8 @@ os._exit = os_exit_noop # type: ignore DEFAULT_IMAGE = 'quay.io/ceph/ceph' DEFAULT_PROMETHEUS_IMAGE = 'quay.io/prometheus/prometheus:v2.33.4' DEFAULT_NODE_EXPORTER_IMAGE = 'quay.io/prometheus/node-exporter:v1.3.1' +DEFAULT_LOKI_IMAGE = 'docker.io/grafana/loki:2.4.0' +DEFAULT_PROMTAIL_IMAGE = 'docker.io/grafana/promtail:2.4.0' DEFAULT_ALERT_MANAGER_IMAGE = 'quay.io/prometheus/alertmanager:v0.23.0' DEFAULT_GRAFANA_IMAGE = 'quay.io/ceph/ceph-grafana:8.3.5' DEFAULT_HAPROXY_IMAGE = 'docker.io/library/haproxy:2.3' @@ -197,6 +202,16 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, default=DEFAULT_NODE_EXPORTER_IMAGE, desc='Prometheus container image', ), + Option( + 'container_image_loki', + default=DEFAULT_LOKI_IMAGE, + desc='Loki container image', + ), + Option( + 'container_image_promtail', + default=DEFAULT_PROMTAIL_IMAGE, + desc='Promtail container image', + ), Option( 'container_image_haproxy', default=DEFAULT_HAPROXY_IMAGE, @@ -410,6 +425,8 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, self.container_image_grafana = '' self.container_image_alertmanager = '' self.container_image_node_exporter = '' + self.container_image_loki = '' + self.container_image_promtail = '' self.container_image_haproxy = '' self.container_image_keepalived = '' self.container_image_snmp_gateway = '' @@ -505,7 +522,7 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, _service_clses: Sequence[Type[CephadmService]] = [ OSDService, NFSService, MonService, MgrService, MdsService, RgwService, RbdMirrorService, GrafanaService, AlertmanagerService, - PrometheusService, NodeExporterService, CrashService, IscsiService, + PrometheusService, NodeExporterService, LokiService, PromtailService, CrashService, IscsiService, IngressService, CustomContainerService, CephfsMirrorService, CephadmAgent, SNMPGatewayService ] @@ -530,11 +547,15 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, if self.use_agent: self.agent_helpers._apply_agent() + self.offline_watcher = OfflineHostWatcher(self) + self.offline_watcher.start() + def shutdown(self) -> None: self.log.debug('shutdown') self._worker_pool.close() self._worker_pool.join() self.cherrypy_thread.shutdown() + self.offline_watcher.shutdown() self.run = False self.event.set() @@ -657,7 +678,7 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, suffix = daemon_type not in [ 'mon', 'crash', 'prometheus', 'node-exporter', 'grafana', 'alertmanager', - 'container', 'agent', 'snmp-gateway' + 'container', 'agent', 'snmp-gateway', 'loki', 'promtail' ] if forcename: if len([d for d in existing if d.daemon_id == forcename]): @@ -734,6 +755,7 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, sd.memory_usage = d.get('memory_usage') sd.memory_request = d.get('memory_request') sd.memory_limit = d.get('memory_limit') + sd.cpu_percentage = d.get('cpu_percentage') sd._service_name = d.get('service_name') sd.deployed_by = d.get('deployed_by') sd.version = d.get('version') @@ -760,6 +782,12 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, self.cache.save_host(host) return None + def update_watched_hosts(self) -> None: + # currently, we are watching hosts with nfs daemons + hosts_to_watch = [d.hostname for d in self.cache.get_daemons( + ) if d.daemon_type in RESCHEDULE_FROM_OFFLINE_HOSTS_TYPES] + self.offline_watcher.set_hosts(list(set([h for h in hosts_to_watch if h is not None]))) + def offline_hosts_remove(self, host: str) -> None: if host in self.offline_hosts: self.offline_hosts.remove(host) @@ -953,6 +981,9 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, return 0, "value unchanged", "" self._validate_and_set_ssh_val('ssh_user', user, current_user) + current_ssh_config = self._get_ssh_config() + new_ssh_config = re.sub(r"(\s{2}User\s)(.*)", r"\1" + user, current_ssh_config.stdout) + self._set_ssh_config(new_ssh_config) msg = 'ssh user set to %s' % user if user != 'root': @@ -1293,6 +1324,10 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, image = self.container_image_alertmanager elif daemon_type == 'node-exporter': image = self.container_image_node_exporter + elif daemon_type == 'loki': + image = self.container_image_loki + elif daemon_type == 'promtail': + image = self.container_image_promtail elif daemon_type == 'haproxy': image = self.container_image_haproxy elif daemon_type == 'keepalived': @@ -1312,11 +1347,6 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, return image def _check_valid_addr(self, host: str, addr: str) -> str: - # make sure mgr is not resolving own ip - if addr in self.get_mgr_id(): - raise OrchestratorError( - "Can not automatically resolve ip address of host where active mgr is running. Please explicitly provide the address.") - # make sure hostname is resolvable before trying to make a connection try: ip_addr = utils.resolve_ip(addr) @@ -1339,6 +1369,15 @@ Then run the following: > ssh -F ssh_config -i ~/cephadm_private_key {self.ssh_user}@{addr}''' raise OrchestratorError(msg) + if ipaddress.ip_address(ip_addr).is_loopback and host == addr: + # if this is a re-add, use old address. otherwise error + if host not in self.inventory or self.inventory.get_addr(host) == host: + raise OrchestratorError( + (f'Cannot automatically resolve ip address of host {host}. Ip resolved to loopback address: {ip_addr}\n' + + f'Please explicitly provide the address (ceph orch host add {host} --addr )')) + self.log.debug( + f'Received loopback address resolving ip for {host}: {ip_addr}. Falling back to previous address.') + ip_addr = self.inventory.get_addr(host) out, err, code = self.wait_async(CephadmServe(self)._run_cephadm( host, cephadmNoImage, 'check-host', ['--expect-hostname', host], @@ -1361,7 +1400,7 @@ Then run the following: :param host: host name """ - spec.validate() + HostSpec.validate(spec) ip_addr = self._check_valid_addr(spec.hostname, spec.addr) if spec.addr == spec.hostname and ip_addr: spec.addr = ip_addr @@ -1520,7 +1559,19 @@ Then run the following: return 'Added label %s to host %s' % (label, host) @handle_orch_error - def remove_host_label(self, host: str, label: str) -> str: + def remove_host_label(self, host: str, label: str, force: bool = False) -> str: + # if we remove the _admin label from the only host that has it we could end up + # removing the only instance of the config and keyring and cause issues + if not force and label == '_admin': + p = PlacementSpec(label='_admin') + admin_hosts = p.filter_matching_hostspecs(self.inventory.all_specs()) + if len(admin_hosts) == 1 and admin_hosts[0] == host: + raise OrchestratorValidationError(f"Host {host} is the last host with the '_admin'" + " label.\nRemoving the _admin label from this host could cause the removal" + " of the last cluster config/keyring managed by cephadm.\n" + "It is recommended to add the _admin label to another host" + " before completing this operation.\nIf you're certain this is" + " what you want rerun this command with --force.") self.inventory.rm_label(host, label) self.log.info('Removed label %s to host %s' % (label, host)) self._kick_serve_loop() @@ -1963,6 +2014,8 @@ Then run the following: if self.spec_store[service_name].spec.service_type in ('mon', 'mgr'): return f'Unable to remove {service_name} service.\n' \ f'Note, you might want to mark the {service_name} service as "unmanaged"' + else: + return f"Invalid service '{service_name}'. Use 'ceph orch ls' to list available services.\n" # Report list of affected OSDs? if not force and service_name.startswith('osd.'): @@ -2196,6 +2249,10 @@ Then run the following: @handle_orch_error def create_osds(self, drive_group: DriveGroupSpec) -> str: + hosts: List[HostSpec] = self.inventory.all_specs() + filtered_hosts: List[str] = drive_group.placement.filter_matching_hostspecs(hosts) + if not filtered_hosts: + return "Invalid 'host:device' spec: host not found in cluster. Please check 'ceph orch host ls' for available hosts" return self.osd_service.create_from_spec(drive_group) def _preview_osdspecs(self, @@ -2271,6 +2328,8 @@ Then run the following: for dep_type in need.get(daemon_type, []): for dd in self.cache.get_daemons_by_type(dep_type): deps.append(dd.name()) + if daemon_type == 'prometheus': + deps.append(str(self.get_module_option_ex('prometheus', 'server_port', 9283))) return sorted(deps) @forall_hosts @@ -2445,6 +2504,8 @@ Then run the following: 'alertmanager': PlacementSpec(count=1), 'prometheus': PlacementSpec(count=1), 'node-exporter': PlacementSpec(host_pattern='*'), + 'loki': PlacementSpec(count=1), + 'promtail': PlacementSpec(host_pattern='*'), 'crash': PlacementSpec(host_pattern='*'), 'container': PlacementSpec(count=1), 'snmp-gateway': PlacementSpec(count=1), @@ -2540,6 +2601,14 @@ Then run the following: def apply_prometheus(self, spec: ServiceSpec) -> str: return self._apply(spec) + @handle_orch_error + def apply_loki(self, spec: ServiceSpec) -> str: + return self._apply(spec) + + @handle_orch_error + def apply_promtail(self, spec: ServiceSpec) -> str: + return self._apply(spec) + @handle_orch_error def apply_node_exporter(self, spec: ServiceSpec) -> str: return self._apply(spec) @@ -2619,14 +2688,41 @@ Then run the following: return self.upgrade.upgrade_status() @handle_orch_error - def upgrade_ls(self, image: Optional[str], tags: bool) -> Dict[Any, Any]: - return self.upgrade.upgrade_ls(image, tags) + def upgrade_ls(self, image: Optional[str], tags: bool, show_all_versions: Optional[bool]) -> Dict[Any, Any]: + return self.upgrade.upgrade_ls(image, tags, show_all_versions) @handle_orch_error - def upgrade_start(self, image: str, version: str) -> str: + def upgrade_start(self, image: str, version: str, daemon_types: Optional[List[str]] = None, host_placement: Optional[str] = None, + services: Optional[List[str]] = None, limit: Optional[int] = None) -> str: if self.inventory.get_host_with_state("maintenance"): raise OrchestratorError("upgrade aborted - you have host(s) in maintenance state") - return self.upgrade.upgrade_start(image, version) + if daemon_types is not None and services is not None: + raise OrchestratorError('--daemon-types and --services are mutually exclusive') + if daemon_types is not None: + for dtype in daemon_types: + if dtype not in CEPH_UPGRADE_ORDER: + raise OrchestratorError(f'Upgrade aborted - Got unexpected daemon type "{dtype}".\n' + f'Viable daemon types for this command are: {utils.CEPH_TYPES + utils.GATEWAY_TYPES}') + if services is not None: + for service in services: + if service not in self.spec_store: + raise OrchestratorError(f'Upgrade aborted - Got unknown service name "{service}".\n' + f'Known services are: {self.spec_store.all_specs.keys()}') + hosts: Optional[List[str]] = None + if host_placement is not None: + all_hosts = list(self.inventory.all_specs()) + placement = PlacementSpec.from_string(host_placement) + hosts = placement.filter_matching_hostspecs(all_hosts) + if not hosts: + raise OrchestratorError( + f'Upgrade aborted - hosts parameter "{host_placement}" provided did not match any hosts') + + if limit is not None: + if limit < 1: + raise OrchestratorError( + f'Upgrade aborted - --limit arg must be a positive integer, not {limit}') + + return self.upgrade.upgrade_start(image, version, daemon_types, hosts, services, limit) @handle_orch_error def upgrade_pause(self) -> str: @@ -2701,12 +2797,26 @@ Then run the following: return self.to_remove_osds.all_osds() @handle_orch_error - def drain_host(self, hostname): - # type: (str) -> str + def drain_host(self, hostname, force=False): + # type: (str, bool) -> str """ Drain all daemons from a host. :param host: host name """ + + # if we drain the last admin host we could end up removing the only instance + # of the config and keyring and cause issues + if not force: + p = PlacementSpec(label='_admin') + admin_hosts = p.filter_matching_hostspecs(self.inventory.all_specs()) + if len(admin_hosts) == 1 and admin_hosts[0] == hostname: + raise OrchestratorValidationError(f"Host {hostname} is the last host with the '_admin'" + " label.\nDraining this host could cause the removal" + " of the last cluster config/keyring managed by cephadm.\n" + "It is recommended to add the _admin label to another host" + " before completing this operation.\nIf you're certain this is" + " what you want rerun this command with --force.") + self.add_host_label(hostname, '_no_schedule') daemons: List[orchestrator.DaemonDescription] = self.cache.get_daemons_by_host(hostname) diff --git a/ceph/src/pybind/mgr/cephadm/offline_watcher.py b/ceph/src/pybind/mgr/cephadm/offline_watcher.py new file mode 100644 index 000000000..b80f5104e --- /dev/null +++ b/ceph/src/pybind/mgr/cephadm/offline_watcher.py @@ -0,0 +1,60 @@ +import logging +from typing import List, Optional, TYPE_CHECKING + +import multiprocessing as mp +import threading + +if TYPE_CHECKING: + from cephadm.module import CephadmOrchestrator + +logger = logging.getLogger(__name__) + + +class OfflineHostWatcher(threading.Thread): + def __init__(self, mgr: "CephadmOrchestrator") -> None: + self.mgr = mgr + self.hosts: Optional[List[str]] = None + self.new_hosts: Optional[List[str]] = None + self.stop = False + self.event = threading.Event() + super(OfflineHostWatcher, self).__init__(target=self.run) + + def run(self) -> None: + self.thread_pool = mp.pool.ThreadPool(10) + while not self.stop: + # only need to take action if we have hosts to check + if self.hosts or self.new_hosts: + if self.new_hosts: + self.hosts = self.new_hosts + self.new_hosts = None + logger.debug(f'OfflineHostDetector: Checking if hosts: {self.hosts} are offline.') + assert self.hosts is not None + self.thread_pool.map(self.check_host, self.hosts) + self.event.wait(20) + self.event.clear() + self.thread_pool.close() + self.thread_pool.join() + + def check_host(self, host: str) -> None: + if host not in self.mgr.offline_hosts: + try: + self.mgr.ssh.check_execute_command(host, ['true']) + except Exception: + logger.debug(f'OfflineHostDetector: detected {host} to be offline') + # kick serve loop in case corrective action must be taken for offline host + self.mgr._kick_serve_loop() + + def set_hosts(self, hosts: List[str]) -> None: + hosts.sort() + if (not self.hosts or self.hosts != hosts) and hosts: + self.new_hosts = hosts + logger.debug( + f'OfflineHostDetector: Hosts to check if offline swapped to: {self.new_hosts}.') + self.wakeup() + + def wakeup(self) -> None: + self.event.set() + + def shutdown(self) -> None: + self.stop = True + self.wakeup() diff --git a/ceph/src/pybind/mgr/cephadm/schedule.py b/ceph/src/pybind/mgr/cephadm/schedule.py index 9ee0a5e3c..612c55804 100644 --- a/ceph/src/pybind/mgr/cephadm/schedule.py +++ b/ceph/src/pybind/mgr/cephadm/schedule.py @@ -7,6 +7,7 @@ import orchestrator from ceph.deployment.service_spec import ServiceSpec from orchestrator._interface import DaemonDescription from orchestrator import OrchestratorValidationError +from .utils import RESCHEDULE_FROM_OFFLINE_HOSTS_TYPES logger = logging.getLogger(__name__) T = TypeVar('T') @@ -255,6 +256,10 @@ class HostAssignment(object): # get candidate hosts based on [hosts, label, host_pattern] candidates = self.get_candidates() # type: List[DaemonPlacement] + if self.primary_daemon_type in RESCHEDULE_FROM_OFFLINE_HOSTS_TYPES: + # remove unreachable hosts that are not in maintenance so daemons + # on these hosts will be rescheduled + candidates = self.remove_non_maintenance_unreachable_candidates(candidates) def expand_candidates(ls: List[DaemonPlacement], num: int) -> List[DaemonPlacement]: r = [] @@ -433,3 +438,15 @@ class HostAssignment(object): final = sorted(ls) random.Random(seed).shuffle(final) return ls + + def remove_non_maintenance_unreachable_candidates(self, candidates: List[DaemonPlacement]) -> List[DaemonPlacement]: + in_maintenance: Dict[str, bool] = {} + for h in self.hosts: + if h.status.lower() == 'maintenance': + in_maintenance[h.hostname] = True + continue + in_maintenance[h.hostname] = False + unreachable_hosts = [h.hostname for h in self.unreachable_hosts] + candidates = [ + c for c in candidates if c.hostname not in unreachable_hosts or in_maintenance[c.hostname]] + return candidates diff --git a/ceph/src/pybind/mgr/cephadm/serve.py b/ceph/src/pybind/mgr/cephadm/serve.py index ba2d3c65d..207f3c6ba 100644 --- a/ceph/src/pybind/mgr/cephadm/serve.py +++ b/ceph/src/pybind/mgr/cephadm/serve.py @@ -2,6 +2,7 @@ import hashlib import json import logging import uuid +import os from collections import defaultdict from typing import TYPE_CHECKING, Optional, List, cast, Dict, Any, Union, Tuple, Set, \ DefaultDict @@ -513,7 +514,7 @@ class CephadmServe: f"Failed to apply {len(self.mgr.apply_spec_fails)} service(s): {','.join(x[0] for x in self.mgr.apply_spec_fails)}", len(self.mgr.apply_spec_fails), warnings) - + self.mgr.update_watched_hosts() return r def _apply_service_config(self, spec: ServiceSpec) -> None: @@ -697,7 +698,7 @@ class CephadmServe: slot = slot.assign_name(self.mgr.get_unique_name( slot.daemon_type, slot.hostname, - daemons, + [d for d in daemons if d not in daemons_to_remove], prefix=spec.service_id, forcename=slot.name, rank=slot.rank, @@ -727,18 +728,20 @@ class CephadmServe: # create daemons daemon_place_fails = [] for slot in slots_to_add: - # first remove daemon on conflicting port? - if slot.ports: + # first remove daemon with conflicting port or name? + if slot.ports or slot.name in [d.name() for d in daemons_to_remove]: for d in daemons_to_remove: - if d.hostname != slot.hostname: - continue - if not (set(d.ports or []) & set(slot.ports)): - continue - if d.ip and slot.ip and d.ip != slot.ip: + if ( + d.hostname != slot.hostname + or not (set(d.ports or []) & set(slot.ports)) + or (d.ip and slot.ip and d.ip != slot.ip) + and d.name() != slot.name + ): continue - self.log.info( - f'Removing {d.name()} before deploying to {slot} to avoid a port conflict' - ) + if d.name() != slot.name: + self.log.info( + f'Removing {d.name()} before deploying to {slot} to avoid a port or conflict' + ) # NOTE: we don't check ok-to-stop here to avoid starvation if # there is only 1 gateway. self._remove_daemon(d.name(), d.hostname) @@ -797,6 +800,14 @@ class CephadmServe: self.mgr.set_health_warning('CEPHADM_DAEMON_PLACE_FAIL', f'Failed to place {len(daemon_place_fails)} daemon(s)', len( daemon_place_fails), daemon_place_fails) + if service_type == 'mgr': + active_mgr = svc.get_active_daemon(self.mgr.cache.get_daemons_by_type('mgr')) + if active_mgr.daemon_id in [d.daemon_id for d in daemons_to_remove]: + # We can't just remove the active mgr like any other daemon. + # Need to fail over later so it can be removed on next pass. + # This can be accomplished by scheduling a restart of the active mgr. + self.mgr._schedule_daemon_action(active_mgr.name(), 'restart') + # remove any? def _ok_to_stop(remove_daemons: List[orchestrator.DaemonDescription]) -> bool: daemon_ids = [d.daemon_id for d in remove_daemons] @@ -980,6 +991,7 @@ class CephadmServe: # ceph.conf config = self.mgr.get_minimal_ceph_conf().encode('utf-8') config_digest = ''.join('%02x' % c for c in hashlib.sha256(config).digest()) + cluster_cfg_dir = f'/var/lib/ceph/{self.mgr._cluster_fsid}/config' if self.mgr.manage_etc_ceph_ceph_conf: try: @@ -995,9 +1007,9 @@ class CephadmServe: for host in {s.hostname for s in all_slots}: if host not in client_files: client_files[host] = {} - client_files[host]['/etc/ceph/ceph.conf'] = ( - 0o644, 0, 0, bytes(config), str(config_digest) - ) + ceph_conf = (0o644, 0, 0, bytes(config), str(config_digest)) + client_files[host]['/etc/ceph/ceph.conf'] = ceph_conf + client_files[host][f'{cluster_cfg_dir}/ceph.conf'] = ceph_conf except Exception as e: self.mgr.log.warning( f'unable to calc conf hosts: {self.mgr.manage_etc_ceph_ceph_conf_hosts}: {e}') @@ -1025,12 +1037,12 @@ class CephadmServe: for host in {s.hostname for s in all_slots}: if host not in client_files: client_files[host] = {} - client_files[host]['/etc/ceph/ceph.conf'] = ( - 0o644, 0, 0, bytes(config), str(config_digest) - ) - client_files[host][ks.path] = ( - ks.mode, ks.uid, ks.gid, keyring.encode('utf-8'), digest - ) + ceph_conf = (0o644, 0, 0, bytes(config), str(config_digest)) + client_files[host]['/etc/ceph/ceph.conf'] = ceph_conf + client_files[host][f'{cluster_cfg_dir}/ceph.conf'] = ceph_conf + ceph_admin_key = (ks.mode, ks.uid, ks.gid, keyring.encode('utf-8'), digest) + client_files[host][ks.path] = ceph_admin_key + client_files[host][f'{cluster_cfg_dir}/{os.path.basename(ks.path)}'] = ceph_admin_key except Exception as e: self.log.warning( f'unable to calc client keyring {ks.entity} placement {ks.placement}: {e}') @@ -1194,11 +1206,15 @@ class CephadmServe: with set_exception_subject('service', daemon.service_id(), overwrite=True): self.mgr.cephadm_services[daemon_type_to_service(daemon_type)].pre_remove(daemon) - # NOTE: we are passing the 'force' flag here, which means # we can delete a mon instances data. - args = ['--name', name, '--force'] - self.log.info('Removing daemon %s from %s' % (name, host)) + dd = self.mgr.cache.get_daemon(daemon.daemon_name) + if dd.ports: + args = ['--name', name, '--force', '--tcp-ports', ' '.join(map(str, dd.ports))] + else: + args = ['--name', name, '--force'] + + self.log.info('Removing daemon %s from %s -- ports %s' % (name, host, dd.ports)) out, err, code = self.mgr.wait_async(self._run_cephadm( host, name, 'rm-daemon', args)) if not code: diff --git a/ceph/src/pybind/mgr/cephadm/services/cephadmservice.py b/ceph/src/pybind/mgr/cephadm/services/cephadmservice.py index 36b501a64..8abb0e63a 100644 --- a/ceph/src/pybind/mgr/cephadm/services/cephadmservice.py +++ b/ceph/src/pybind/mgr/cephadm/services/cephadmservice.py @@ -2,6 +2,8 @@ import errno import json import logging import re +import socket +import time from abc import ABCMeta, abstractmethod from typing import TYPE_CHECKING, List, Callable, TypeVar, \ Optional, Dict, Any, Tuple, NewType, cast @@ -239,9 +241,14 @@ class CephadmService(metaclass=ABCMeta): self.mgr.log.warning(f"Unable to update caps for {entity}") return keyring - def _inventory_get_addr(self, hostname: str) -> str: - """Get a host's address with its hostname.""" - return self.mgr.inventory.get_addr(hostname) + def _inventory_get_fqdn(self, hostname: str) -> str: + """Get a host's FQDN with its hostname. + + If the FQDN can't be resolved, the address from the inventory will + be returned instead. + """ + addr = self.mgr.inventory.get_addr(hostname) + return socket.getfqdn(addr) def _set_service_url_on_dashboard(self, service_name: str, @@ -666,19 +673,31 @@ class MgrService(CephService): return DaemonDescription() def fail_over(self) -> None: - if not self.mgr_map_has_standby(): - raise OrchestratorError('Need standby mgr daemon', event_kind_subject=( - 'daemon', 'mgr' + self.mgr.get_mgr_id())) - - self.mgr.events.for_daemon('mgr' + self.mgr.get_mgr_id(), - 'INFO', 'Failing over to other MGR') - logger.info('Failing over to other MGR') - - # fail over - ret, out, err = self.mgr.check_mon_command({ - 'prefix': 'mgr fail', - 'who': self.mgr.get_mgr_id(), - }) + # this has been seen to sometimes transiently fail even when there are multiple + # mgr daemons. As long as there are multiple known mgr daemons, we should retry. + class NoStandbyError(OrchestratorError): + pass + no_standby_exc = NoStandbyError('Need standby mgr daemon', event_kind_subject=( + 'daemon', 'mgr' + self.mgr.get_mgr_id())) + for sleep_secs in [2, 8, 15]: + try: + if not self.mgr_map_has_standby(): + raise no_standby_exc + self.mgr.events.for_daemon('mgr' + self.mgr.get_mgr_id(), + 'INFO', 'Failing over to other MGR') + logger.info('Failing over to other MGR') + + # fail over + ret, out, err = self.mgr.check_mon_command({ + 'prefix': 'mgr fail', + 'who': self.mgr.get_mgr_id(), + }) + return + except NoStandbyError: + logger.info( + f'Failed to find standby mgr for failover. Retrying in {sleep_secs} seconds') + time.sleep(sleep_secs) + raise no_standby_exc def mgr_map_has_standby(self) -> bool: """ diff --git a/ceph/src/pybind/mgr/cephadm/services/monitoring.py b/ceph/src/pybind/mgr/cephadm/services/monitoring.py index d67d3057b..0bd388f46 100644 --- a/ceph/src/pybind/mgr/cephadm/services/monitoring.py +++ b/ceph/src/pybind/mgr/cephadm/services/monitoring.py @@ -31,13 +31,22 @@ class GrafanaService(CephadmService): prom_services = [] # type: List[str] for dd in self.mgr.cache.get_daemons_by_service('prometheus'): assert dd.hostname is not None - addr = dd.ip if dd.ip else self._inventory_get_addr(dd.hostname) + addr = dd.ip if dd.ip else self._inventory_get_fqdn(dd.hostname) port = dd.ports[0] if dd.ports else 9095 prom_services.append(build_url(scheme='http', host=addr, port=port)) deps.append(dd.name()) + + daemons = self.mgr.cache.get_daemons_by_service('mgr') + loki_host = '' + assert daemons is not None + if daemons != []: + assert daemons[0].hostname is not None + addr = daemons[0].ip if daemons[0].ip else self._inventory_get_fqdn(daemons[0].hostname) + loki_host = build_url(scheme='http', host=addr, port=3100) + grafana_data_sources = self.mgr.template.render( - 'services/grafana/ceph-dashboard.yml.j2', {'hosts': prom_services}) + 'services/grafana/ceph-dashboard.yml.j2', {'hosts': prom_services, 'loki_host': loki_host}) cert = self.mgr.get_store('grafana_crt') pkey = self.mgr.get_store('grafana_key') @@ -87,7 +96,7 @@ class GrafanaService(CephadmService): # TODO: signed cert dd = self.get_active_daemon(daemon_descrs) assert dd.hostname is not None - addr = dd.ip if dd.ip else self._inventory_get_addr(dd.hostname) + addr = dd.ip if dd.ip else self._inventory_get_fqdn(dd.hostname) port = dd.ports[0] if dd.ports else self.DEFAULT_SERVICE_PORT service_url = build_url(scheme='https', host=addr, port=port) self._set_service_url_on_dashboard( @@ -122,6 +131,10 @@ class AlertmanagerService(CephadmService): default_webhook_urls: List[str] = [] spec = cast(AlertManagerSpec, self.mgr.spec_store[daemon_spec.service_name].spec) + try: + secure = spec.secure + except AttributeError: + secure = False user_data = spec.user_data if 'default_webhook_urls' in user_data and isinstance( user_data['default_webhook_urls'], list): @@ -135,7 +148,7 @@ class AlertmanagerService(CephadmService): proto = None # http: or https: url = mgr_map.get('services', {}).get('dashboard', None) if url: - dashboard_urls.append(url) + dashboard_urls.append(url.rstrip('/')) p_result = urlparse(url) proto = p_result.scheme port = p_result.port @@ -150,13 +163,13 @@ class AlertmanagerService(CephadmService): if dd.daemon_id == self.mgr.get_mgr_id(): continue assert dd.hostname is not None - addr = self.mgr.inventory.get_addr(dd.hostname) - dashboard_urls.append(build_url(scheme=proto, host=addr, port=port)) + addr = self._inventory_get_fqdn(dd.hostname) + dashboard_urls.append(build_url(scheme=proto, host=addr, port=port).rstrip('/')) for dd in self.mgr.cache.get_daemons_by_service('snmp-gateway'): assert dd.hostname is not None assert dd.ports - addr = dd.ip if dd.ip else self._inventory_get_addr(dd.hostname) + addr = dd.ip if dd.ip else self._inventory_get_fqdn(dd.hostname) deps.append(dd.name()) snmp_gateway_urls.append(build_url(scheme='http', host=addr, @@ -166,6 +179,7 @@ class AlertmanagerService(CephadmService): 'dashboard_urls': dashboard_urls, 'default_webhook_urls': default_webhook_urls, 'snmp_gateway_urls': snmp_gateway_urls, + 'secure': secure, } yml = self.mgr.template.render('services/alertmanager/alertmanager.yml.j2', context) @@ -174,7 +188,7 @@ class AlertmanagerService(CephadmService): for dd in self.mgr.cache.get_daemons_by_service('alertmanager'): assert dd.hostname is not None deps.append(dd.name()) - addr = self.mgr.inventory.get_addr(dd.hostname) + addr = self._inventory_get_fqdn(dd.hostname) peers.append(build_url(host=addr, port=port).lstrip('/')) return { @@ -194,7 +208,7 @@ class AlertmanagerService(CephadmService): def config_dashboard(self, daemon_descrs: List[DaemonDescription]) -> None: dd = self.get_active_daemon(daemon_descrs) assert dd.hostname is not None - addr = dd.ip if dd.ip else self._inventory_get_addr(dd.hostname) + addr = dd.ip if dd.ip else self._inventory_get_fqdn(dd.hostname) port = dd.ports[0] if dd.ports else self.DEFAULT_SERVICE_PORT service_url = build_url(scheme='http', host=addr, port=port) self._set_service_url_on_dashboard( @@ -217,6 +231,7 @@ class AlertmanagerService(CephadmService): class PrometheusService(CephadmService): TYPE = 'prometheus' DEFAULT_SERVICE_PORT = 9095 + DEFAULT_MGR_PROMETHEUS_PORT = 9283 def config(self, spec: ServiceSpec) -> None: # make sure module is enabled @@ -247,13 +262,19 @@ class PrometheusService(CephadmService): # scrape mgrs mgr_scrape_list = [] mgr_map = self.mgr.get('mgr_map') - port = None + port = cast(int, self.mgr.get_module_option_ex( + 'prometheus', 'server_port', self.DEFAULT_MGR_PROMETHEUS_PORT)) + deps.append(str(port)) t = mgr_map.get('services', {}).get('prometheus', None) if t: p_result = urlparse(t) - t = t.split('/')[2] - mgr_scrape_list.append(t) - port = p_result.port or 9283 + # urlparse .hostname removes '[]' from the hostname in case + # of ipv6 addresses so if this is the case then we just + # append the brackets when building the final scrape endpoint + if '[' in p_result.netloc and ']' in p_result.netloc: + mgr_scrape_list.append(f"[{p_result.hostname}]:{port}") + else: + mgr_scrape_list.append(f"{p_result.hostname}:{port}") # scan all mgrs to generate deps and to get standbys too. # assume that they are all on the same port as the active mgr. for dd in self.mgr.cache.get_daemons_by_service('mgr'): @@ -265,7 +286,7 @@ class PrometheusService(CephadmService): if dd.daemon_id == self.mgr.get_mgr_id(): continue assert dd.hostname is not None - addr = self.mgr.inventory.get_addr(dd.hostname) + addr = self._inventory_get_fqdn(dd.hostname) mgr_scrape_list.append(build_url(host=addr, port=port).lstrip('/')) # scrape node exporters @@ -273,7 +294,7 @@ class PrometheusService(CephadmService): for dd in self.mgr.cache.get_daemons_by_service('node-exporter'): assert dd.hostname is not None deps.append(dd.name()) - addr = dd.ip if dd.ip else self.mgr.inventory.get_addr(dd.hostname) + addr = dd.ip if dd.ip else self._inventory_get_fqdn(dd.hostname) port = dd.ports[0] if dd.ports else 9100 nodes.append({ 'hostname': dd.hostname, @@ -285,7 +306,7 @@ class PrometheusService(CephadmService): for dd in self.mgr.cache.get_daemons_by_service('alertmanager'): assert dd.hostname is not None deps.append(dd.name()) - addr = dd.ip if dd.ip else self.mgr.inventory.get_addr(dd.hostname) + addr = dd.ip if dd.ip else self._inventory_get_fqdn(dd.hostname) port = dd.ports[0] if dd.ports else 9093 alertmgr_targets.append("'{}'".format(build_url(host=addr, port=port).lstrip('/'))) @@ -297,7 +318,7 @@ class PrometheusService(CephadmService): assert dd.hostname is not None deps.append(dd.name()) if dd.daemon_type == 'haproxy': - addr = self.mgr.inventory.get_addr(dd.hostname) + addr = self._inventory_get_fqdn(dd.hostname) haproxy_targets.append({ "url": f"'{build_url(host=addr, port=spec.monitor_port).lstrip('/')}'", "service": dd.service_name(), @@ -336,7 +357,7 @@ class PrometheusService(CephadmService): def config_dashboard(self, daemon_descrs: List[DaemonDescription]) -> None: dd = self.get_active_daemon(daemon_descrs) assert dd.hostname is not None - addr = dd.ip if dd.ip else self._inventory_get_addr(dd.hostname) + addr = dd.ip if dd.ip else self._inventory_get_fqdn(dd.hostname) port = dd.ports[0] if dd.ports else self.DEFAULT_SERVICE_PORT service_url = build_url(scheme='http', host=addr, port=port) self._set_service_url_on_dashboard( @@ -378,6 +399,57 @@ class NodeExporterService(CephadmService): return HandleCommandResult(0, out, '') +class LokiService(CephadmService): + TYPE = 'loki' + DEFAULT_SERVICE_PORT = 3100 + + def prepare_create(self, daemon_spec: CephadmDaemonDeploySpec) -> CephadmDaemonDeploySpec: + assert self.TYPE == daemon_spec.daemon_type + daemon_spec.final_config, daemon_spec.deps = self.generate_config(daemon_spec) + return daemon_spec + + def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[str, Any], List[str]]: + assert self.TYPE == daemon_spec.daemon_type + deps: List[str] = [] + + yml = self.mgr.template.render('services/loki.yml.j2') + return { + "files": { + "loki.yml": yml + } + }, sorted(deps) + + +class PromtailService(CephadmService): + TYPE = 'promtail' + DEFAULT_SERVICE_PORT = 9080 + + def prepare_create(self, daemon_spec: CephadmDaemonDeploySpec) -> CephadmDaemonDeploySpec: + assert self.TYPE == daemon_spec.daemon_type + daemon_spec.final_config, daemon_spec.deps = self.generate_config(daemon_spec) + return daemon_spec + + def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[str, Any], List[str]]: + assert self.TYPE == daemon_spec.daemon_type + deps: List[str] = [] + hostnames: List[str] = [] + for dd in self.mgr.cache.get_daemons_by_service('mgr'): + assert dd.hostname is not None + addr = self.mgr.inventory.get_addr(dd.hostname) + hostnames.append(addr) + context = { + 'hostnames': hostnames, + 'client_hostname': hostnames[0], + } + + yml = self.mgr.template.render('services/promtail.yml.j2', context) + return { + "files": { + "promtail.yml": yml + } + }, sorted(deps) + + class SNMPGatewayService(CephadmService): TYPE = 'snmp-gateway' diff --git a/ceph/src/pybind/mgr/cephadm/services/osd.py b/ceph/src/pybind/mgr/cephadm/services/osd.py index 234d7a057..31771fb5f 100644 --- a/ceph/src/pybind/mgr/cephadm/services/osd.py +++ b/ceph/src/pybind/mgr/cephadm/services/osd.py @@ -47,9 +47,9 @@ class OSDService(CephService): osd_id_claims_for_host = osd_id_claims.filtered_by_host(host) - cmd = self.driveselection_to_ceph_volume(drive_selection, - osd_id_claims_for_host) - if not cmd: + cmds: List[str] = self.driveselection_to_ceph_volume(drive_selection, + osd_id_claims_for_host) + if not cmds: logger.debug("No data_devices, skipping DriveGroup: {}".format( drive_group.service_id)) return None @@ -60,7 +60,7 @@ class OSDService(CephService): start_ts = datetime_now() env_vars: List[str] = [f"CEPH_VOLUME_OSDSPEC_AFFINITY={drive_group.service_id}"] ret_msg = await self.create_single_host( - drive_group, host, cmd, + drive_group, host, cmds, replace_osd_ids=osd_id_claims_for_host, env_vars=env_vars ) self.mgr.cache.update_osdspec_last_applied( @@ -79,20 +79,20 @@ class OSDService(CephService): async def create_single_host(self, drive_group: DriveGroupSpec, - host: str, cmd: str, replace_osd_ids: List[str], + host: str, cmds: List[str], replace_osd_ids: List[str], env_vars: Optional[List[str]] = None) -> str: - out, err, code = await self._run_ceph_volume_command(host, cmd, env_vars=env_vars) - - if code == 1 and ', it is already prepared' in '\n'.join(err): - # HACK: when we create against an existing LV, ceph-volume - # returns an error and the above message. To make this - # command idempotent, tolerate this "error" and continue. - logger.debug('the device was already prepared; continuing') - code = 0 - if code: - raise RuntimeError( - 'cephadm exited with an error code: %d, stderr:%s' % ( - code, '\n'.join(err))) + for cmd in cmds: + out, err, code = await self._run_ceph_volume_command(host, cmd, env_vars=env_vars) + if code == 1 and ', it is already prepared' in '\n'.join(err): + # HACK: when we create against an existing LV, ceph-volume + # returns an error and the above message. To make this + # command idempotent, tolerate this "error" and continue. + logger.debug('the device was already prepared; continuing') + code = 0 + if code: + raise RuntimeError( + 'cephadm exited with an error code: %d, stderr:%s' % ( + code, '\n'.join(err))) return await self.deploy_osd_daemons_for_existing_osds(host, drive_group.service_name(), replace_osd_ids) @@ -228,18 +228,33 @@ class OSDService(CephService): drive_selection = DriveSelection(drive_group, inventory_for_host, existing_daemons=len(dd_for_spec_and_host)) logger.debug(f"Found drive selection {drive_selection}") + if drive_group.method and drive_group.method == 'raw': + # ceph-volume can currently only handle a 1:1 mapping + # of data/db/wal devices for raw mode osds. If db/wal devices + # are defined and the number does not match the number of data + # devices, we need to bail out + if drive_selection.data_devices() and drive_selection.db_devices(): + if len(drive_selection.data_devices()) != len(drive_selection.db_devices()): + raise OrchestratorError('Raw mode only supports a 1:1 ratio of data to db devices. Found ' + f'{len(drive_selection.data_devices())} potential data device(s) and ' + f'{len(drive_selection.db_devices())} potential db device(s) on host {host}') + if drive_selection.data_devices() and drive_selection.wal_devices(): + if len(drive_selection.data_devices()) != len(drive_selection.wal_devices()): + raise OrchestratorError('Raw mode only supports a 1:1 ratio of data to wal devices. Found ' + f'{len(drive_selection.data_devices())} potential data device(s) and ' + f'{len(drive_selection.wal_devices())} potential wal device(s) on host {host}') host_ds_map.append((host, drive_selection)) return host_ds_map @staticmethod def driveselection_to_ceph_volume(drive_selection: DriveSelection, osd_id_claims: Optional[List[str]] = None, - preview: bool = False) -> Optional[str]: + preview: bool = False) -> List[str]: logger.debug(f"Translating DriveGroup <{drive_selection.spec}> to ceph-volume command") - cmd: Optional[str] = translate.to_ceph_volume(drive_selection, - osd_id_claims, preview=preview).run() - logger.debug(f"Resulting ceph-volume cmd: {cmd}") - return cmd + cmds: List[str] = translate.to_ceph_volume(drive_selection, + osd_id_claims, preview=preview).run() + logger.debug(f"Resulting ceph-volume cmds: {cmds}") + return cmds def get_previews(self, host: str) -> List[Dict[str, Any]]: # Find OSDSpecs that match host. @@ -282,32 +297,34 @@ class OSDService(CephService): continue # driveselection for host - cmd = self.driveselection_to_ceph_volume(ds, - osd_id_claims.filtered_by_host(host), - preview=True) - if not cmd: + cmds: List[str] = self.driveselection_to_ceph_volume(ds, + osd_id_claims.filtered_by_host( + host), + preview=True) + if not cmds: logger.debug("No data_devices, skipping DriveGroup: {}".format( osdspec.service_name())) continue # get preview data from ceph-volume - out, err, code = self.mgr.wait_async(self._run_ceph_volume_command(host, cmd)) - if out: - try: - concat_out: Dict[str, Any] = json.loads(' '.join(out)) - except ValueError: - logger.exception('Cannot decode JSON: \'%s\'' % ' '.join(out)) - concat_out = {} - notes = [] - if osdspec.data_devices is not None and osdspec.data_devices.limit and len(concat_out) < osdspec.data_devices.limit: - found = len(concat_out) - limit = osdspec.data_devices.limit - notes.append( - f'NOTE: Did not find enough disks matching filter on host {host} to reach data device limit (Found: {found} | Limit: {limit})') - ret_all.append({'data': concat_out, - 'osdspec': osdspec.service_id, - 'host': host, - 'notes': notes}) + for cmd in cmds: + out, err, code = self.mgr.wait_async(self._run_ceph_volume_command(host, cmd)) + if out: + try: + concat_out: Dict[str, Any] = json.loads(' '.join(out)) + except ValueError: + logger.exception('Cannot decode JSON: \'%s\'' % ' '.join(out)) + concat_out = {} + notes = [] + if osdspec.data_devices is not None and osdspec.data_devices.limit and len(concat_out) < osdspec.data_devices.limit: + found = len(concat_out) + limit = osdspec.data_devices.limit + notes.append( + f'NOTE: Did not find enough disks matching filter on host {host} to reach data device limit (Found: {found} | Limit: {limit})') + ret_all.append({'data': concat_out, + 'osdspec': osdspec.service_id, + 'host': host, + 'notes': notes}) return ret_all def resolve_hosts_for_osdspecs(self, diff --git a/ceph/src/pybind/mgr/cephadm/ssh.py b/ceph/src/pybind/mgr/cephadm/ssh.py index 034688023..e874ba6d7 100644 --- a/ceph/src/pybind/mgr/cephadm/ssh.py +++ b/ceph/src/pybind/mgr/cephadm/ssh.py @@ -58,7 +58,7 @@ class SSHManager: host: str, addr: Optional[str] = None, ) -> "SSHClientConnection": - if not self.cons.get(host): + if not self.cons.get(host) or host not in self.mgr.inventory: if not addr and host in self.mgr.inventory: addr = self.mgr.inventory.get_addr(host) @@ -75,7 +75,11 @@ class SSHManager: with self.redirect_log(host, addr): try: - conn = await asyncssh.connect(addr, username=self.mgr.ssh_user, client_keys=[self.mgr.tkey.name], known_hosts=None, config=[self.mgr.ssh_config_fname], preferred_auth=['publickey']) + ssh_options = asyncssh.SSHClientConnectionOptions( + keepalive_interval=7, keepalive_count_max=3) + conn = await asyncssh.connect(addr, username=self.mgr.ssh_user, client_keys=[self.mgr.tkey.name], + known_hosts=None, config=[self.mgr.ssh_config_fname], + preferred_auth=['publickey'], options=ssh_options) except OSError: raise except asyncssh.Error: @@ -92,7 +96,7 @@ class SSHManager: def redirect_log(self, host: str, addr: str) -> Iterator[None]: log_string = StringIO() ch = logging.StreamHandler(log_string) - ch.setLevel(logging.DEBUG) + ch.setLevel(logging.INFO) asyncssh_logger.addHandler(ch) try: @@ -100,8 +104,7 @@ class SSHManager: except OSError as e: self.mgr.offline_hosts.add(host) log_content = log_string.getvalue() - msg = f"Can't communicate with remote host `{addr}`, possibly because python3 is not installed there. {str(e)}" + \ - '\n' + f'Log: {log_content}' + msg = f"Can't communicate with remote host `{addr}`, possibly because python3 is not installed there. {str(e)}" logger.exception(msg) raise OrchestratorError(msg) except asyncssh.Error as e: @@ -133,12 +136,14 @@ class SSHManager: addr: Optional[str] = None, ) -> Tuple[str, str, int]: conn = await self._remote_connection(host, addr) - cmd = "sudo " + " ".join(quote(x) for x in cmd) + sudo_prefix = "sudo " if self.mgr.ssh_user != 'root' else "" + cmd = sudo_prefix + " ".join(quote(x) for x in cmd) logger.debug(f'Running command: {cmd}') try: + r = await conn.run('sudo true', check=True, timeout=5) r = await conn.run(cmd, input=stdin) # handle these Exceptions otherwise you might get a weird error like TypeError: __init__() missing 1 required positional argument: 'reason' (due to the asyncssh error interacting with raise_if_exception) - except (asyncssh.ChannelOpenError, Exception) as e: + except (asyncssh.ChannelOpenError, asyncssh.ProcessError, Exception) as e: # SSH connection closed or broken, will create new connection next call logger.debug(f'Connection to {host} failed. {str(e)}') await self._reset_con(host) @@ -205,11 +210,7 @@ class SSHManager: await self._check_execute_command(host, ['mkdir', '-p', '/tmp' + dirname], addr=addr) tmp_path = '/tmp' + path + '.new' await self._check_execute_command(host, ['touch', tmp_path], addr=addr) - if uid is not None and gid is not None and mode is not None: - # shlex quote takes str or byte object, not int - await self._check_execute_command(host, ['chown', '-R', str(uid) + ':' + str(gid), tmp_path], addr=addr) - await self._check_execute_command(host, ['chmod', oct(mode)[2:], tmp_path], addr=addr) - elif self.mgr.ssh_user != 'root': + if self.mgr.ssh_user != 'root': assert self.mgr.ssh_user await self._check_execute_command(host, ['chown', '-R', self.mgr.ssh_user, tmp_path], addr=addr) await self._check_execute_command(host, ['chmod', str(644), tmp_path], addr=addr) @@ -219,6 +220,10 @@ class SSHManager: f.flush() conn = await self._remote_connection(host, addr) await asyncssh.scp(f.name, (conn, tmp_path)) + if uid is not None and gid is not None and mode is not None: + # shlex quote takes str or byte object, not int + await self._check_execute_command(host, ['chown', '-R', str(uid) + ':' + str(gid), tmp_path], addr=addr) + await self._check_execute_command(host, ['chmod', oct(mode)[2:], tmp_path], addr=addr) await self._check_execute_command(host, ['mv', tmp_path, path], addr=addr) except Exception as e: msg = f"Unable to write {host}:{path}: {e}" diff --git a/ceph/src/pybind/mgr/cephadm/templates/services/alertmanager/alertmanager.yml.j2 b/ceph/src/pybind/mgr/cephadm/templates/services/alertmanager/alertmanager.yml.j2 index 4a8f313a7..4e394106f 100644 --- a/ceph/src/pybind/mgr/cephadm/templates/services/alertmanager/alertmanager.yml.j2 +++ b/ceph/src/pybind/mgr/cephadm/templates/services/alertmanager/alertmanager.yml.j2 @@ -3,6 +3,11 @@ global: resolve_timeout: 5m +{% if not secure %} + http_config: + tls_config: + insecure_skip_verify: true +{% endif %} route: receiver: 'default' diff --git a/ceph/src/pybind/mgr/cephadm/templates/services/grafana/ceph-dashboard.yml.j2 b/ceph/src/pybind/mgr/cephadm/templates/services/grafana/ceph-dashboard.yml.j2 index 170e6f246..7e5ffe5ea 100644 --- a/ceph/src/pybind/mgr/cephadm/templates/services/grafana/ceph-dashboard.yml.j2 +++ b/ceph/src/pybind/mgr/cephadm/templates/services/grafana/ceph-dashboard.yml.j2 @@ -5,6 +5,9 @@ deleteDatasources: orgId: 1 {% endfor %} + - name: 'Loki' + orgId: 2 + datasources: {% for host in hosts %} - name: 'Dashboard{{ loop.index }}' @@ -16,3 +19,12 @@ datasources: isDefault: {{ 'true' if loop.first else 'false' }} editable: false {% endfor %} + + - name: 'Loki' + type: 'loki' + access: 'proxy' + orgId: 2 + url: '{{ loki_host }}' + basicAuth: false + isDefault: true + editable: false diff --git a/ceph/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 b/ceph/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 index cf23802d7..e7e81d89a 100644 --- a/ceph/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 +++ b/ceph/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 @@ -12,6 +12,8 @@ cert_key = /etc/grafana/certs/cert_key http_port = {{ http_port }} http_addr = {{ http_addr }} +[snapshots] + external_enabled = false [security] {% if not initial_admin_password %} disable_initial_admin_creation = true diff --git a/ceph/src/pybind/mgr/cephadm/templates/services/loki.yml.j2 b/ceph/src/pybind/mgr/cephadm/templates/services/loki.yml.j2 new file mode 100644 index 000000000..271437231 --- /dev/null +++ b/ceph/src/pybind/mgr/cephadm/templates/services/loki.yml.j2 @@ -0,0 +1,28 @@ +# {{ cephadm_managed }} +auth_enabled: false + +server: + http_listen_port: 3100 + grpc_listen_port: 8080 + +common: + path_prefix: /tmp/loki + storage: + filesystem: + chunks_directory: /tmp/loki/chunks + rules_directory: /tmp/loki/rules + replication_factor: 1 + ring: + instance_addr: 127.0.0.1 + kvstore: + store: inmemory + +schema_config: + configs: + - from: 2020-10-24 + store: boltdb-shipper + object_store: filesystem + schema: v11 + index: + prefix: index_ + period: 24h diff --git a/ceph/src/pybind/mgr/cephadm/templates/services/promtail.yml.j2 b/ceph/src/pybind/mgr/cephadm/templates/services/promtail.yml.j2 new file mode 100644 index 000000000..f500f5d22 --- /dev/null +++ b/ceph/src/pybind/mgr/cephadm/templates/services/promtail.yml.j2 @@ -0,0 +1,21 @@ +# {{ cephadm_managed }} +server: + http_listen_port: 9080 + grpc_listen_port: 0 + +positions: + filename: /tmp/positions.yaml + +clients: + - url: http://{{ client_hostname }}:3100/loki/api/v1/push + +scrape_configs: +- job_name: system + static_configs: + - targets: +{% for url in hostnames %} + - {{ url }} +{% endfor %} + labels: + job: Cluster Logs + __path__: /var/log/ceph/**/*.log diff --git a/ceph/src/pybind/mgr/cephadm/tests/fixtures.py b/ceph/src/pybind/mgr/cephadm/tests/fixtures.py index 33c6d19a4..7a4ac0d87 100644 --- a/ceph/src/pybind/mgr/cephadm/tests/fixtures.py +++ b/ceph/src/pybind/mgr/cephadm/tests/fixtures.py @@ -28,6 +28,13 @@ def get_ceph_option(_, key): return __file__ +def get_module_option_ex(_, module, key, default=None): + if module == 'prometheus': + if key == 'server_port': + return 9283 + return None + + def _run_cephadm(ret): async def foo(s, host, entity, cmd, e, **kwargs): if cmd == 'gather-facts': @@ -85,12 +92,14 @@ def with_cephadm_module(module_options=None, store=None): """ with mock.patch("cephadm.module.CephadmOrchestrator.get_ceph_option", get_ceph_option),\ mock.patch("cephadm.services.osd.RemoveUtil._run_mon_cmd"), \ + mock.patch('cephadm.module.CephadmOrchestrator.get_module_option_ex', get_module_option_ex),\ mock.patch("cephadm.module.CephadmOrchestrator.get_osdmap"), \ mock.patch("cephadm.module.CephadmOrchestrator.remote"), \ mock.patch("cephadm.agent.CephadmAgentHelpers._request_agent_acks"), \ mock.patch("cephadm.agent.CephadmAgentHelpers._apply_agent", return_value=False), \ mock.patch("cephadm.agent.CephadmAgentHelpers._agent_down", return_value=False), \ - mock.patch('cephadm.agent.CherryPyThread.run'): + mock.patch('cephadm.agent.CherryPyThread.run'), \ + mock.patch('cephadm.offline_watcher.OfflineHostWatcher.run'): m = CephadmOrchestrator.__new__(CephadmOrchestrator) if module_options is not None: diff --git a/ceph/src/pybind/mgr/cephadm/tests/test_agent.py b/ceph/src/pybind/mgr/cephadm/tests/test_agent.py new file mode 100644 index 000000000..a4b1dc1b2 --- /dev/null +++ b/ceph/src/pybind/mgr/cephadm/tests/test_agent.py @@ -0,0 +1,157 @@ +from unittest.mock import MagicMock +from cephadm.agent import Root + + +class FakeDaemonDescription: + def __init__(self, ip, ports, hostname, service_name='', daemon_type=''): + self.ip = ip + self.ports = ports + self.hostname = hostname + self._service_name = service_name + self.daemon_type = daemon_type + + def service_name(self): + return self._service_name + + +class FakeCache: + def get_daemons_by_service(self, service_type): + return [FakeDaemonDescription('1.2.3.4', [9100], 'node0'), + FakeDaemonDescription('1.2.3.5', [9200], 'node1')] + + def get_daemons_by_type(self, daemon_type): + return [FakeDaemonDescription('1.2.3.4', [9100], 'node0', 'ingress', 'haproxy'), + FakeDaemonDescription('1.2.3.5', [9200], 'node1', 'ingress', 'haproxy')] + + +class FakeInventory: + def get_addr(self, name: str): + return '1.2.3.4' + + +class FakeServiceSpec: + def __init__(self, port): + self.monitor_port = port + + +class FakeSpecDescription: + def __init__(self, port): + self.spec = FakeServiceSpec(port) + + +class FakeSpecStore(): + def __init__(self, mgr): + self.mgr = mgr + self._specs = {'ingress': FakeSpecDescription(9049)} + + def __contains__(self, name): + return name in self._specs + + def __getitem__(self, name): + return self._specs['ingress'] + + +class FakeMgr: + def __init__(self): + self.config = '' + self.check_mon_command = MagicMock(side_effect=self._check_mon_command) + self.mon_command = MagicMock(side_effect=self._check_mon_command) + self.template = MagicMock() + self.log = MagicMock() + self.inventory = FakeInventory() + self.cache = FakeCache() + self.spec_store = FakeSpecStore(self) + + def list_servers(self): + + servers = [ + {'hostname': 'node0', + 'ceph_version': '16.2', + 'services': [{'type': 'mgr'}, {'type': 'mon'}]}, + {'hostname': 'node1', + 'ceph_version': '16.2', + 'services': [{'type': 'mgr'}, {'type': 'mon'}]} + ] + + return servers + + def _check_mon_command(self, cmd_dict, inbuf=None): + prefix = cmd_dict.get('prefix') + if prefix == 'get-cmd': + return 0, self.config, '' + if prefix == 'set-cmd': + self.config = cmd_dict.get('value') + return 0, 'value set', '' + return -1, '', 'error' + + def get_module_option_ex(self, module, option, default_value): + return "9283" + + +class TestCephadmService: + + def test_get_sd_config_prometheus(self): + mgr = FakeMgr() + root = Root(mgr) + cfg = root.get_sd_config('mgr-prometheus') + + # check response structure + assert cfg + for entry in cfg: + assert 'labels' in entry + assert 'targets' in entry + + # check content + assert cfg[0]['targets'] == ['node0:9283', 'node1:9283'] + + def test_get_sd_config_node_exporter(self): + mgr = FakeMgr() + root = Root(mgr) + cfg = root.get_sd_config('node-exporter') + + # check response structure + assert cfg + for entry in cfg: + assert 'labels' in entry + assert 'targets' in entry + + # check content + assert cfg[0]['targets'] == ['1.2.3.4:9100'] + assert cfg[0]['labels'] == {'instance': 'node0'} + assert cfg[1]['targets'] == ['1.2.3.5:9200'] + assert cfg[1]['labels'] == {'instance': 'node1'} + + def test_get_sd_config_alertmgr(self): + mgr = FakeMgr() + root = Root(mgr) + cfg = root.get_sd_config('alertmanager') + + # check response structure + assert cfg + for entry in cfg: + assert 'labels' in entry + assert 'targets' in entry + + # check content + assert cfg[0]['targets'] == ['1.2.3.4:9100', '1.2.3.5:9200'] + + def test_get_sd_config_haproxy(self): + mgr = FakeMgr() + root = Root(mgr) + cfg = root.get_sd_config('haproxy') + + # check response structure + assert cfg + for entry in cfg: + assert 'labels' in entry + assert 'targets' in entry + + # check content + assert cfg[0]['targets'] == ['1.2.3.4:9049'] + assert cfg[0]['labels'] == {'instance': 'ingress'} + + def test_get_sd_config_invalid_service(self): + mgr = FakeMgr() + root = Root(mgr) + cfg = root.get_sd_config('invalid-service') + assert cfg == [] diff --git a/ceph/src/pybind/mgr/cephadm/tests/test_cephadm.py b/ceph/src/pybind/mgr/cephadm/tests/test_cephadm.py index afe2c2e34..d8eb76b43 100644 --- a/ceph/src/pybind/mgr/cephadm/tests/test_cephadm.py +++ b/ceph/src/pybind/mgr/cephadm/tests/test_cephadm.py @@ -15,7 +15,7 @@ except ImportError: pass from ceph.deployment.service_spec import ServiceSpec, PlacementSpec, RGWSpec, \ - NFSServiceSpec, IscsiServiceSpec, HostPlacementSpec, CustomContainerSpec + NFSServiceSpec, IscsiServiceSpec, HostPlacementSpec, CustomContainerSpec, MDSSpec from ceph.deployment.drive_selection.selector import DriveSelection from ceph.deployment.inventory import Devices, Device from ceph.utils import datetime_to_str, datetime_now @@ -160,13 +160,27 @@ class TestCephadm(object): assert wait(cephadm_module, cephadm_module.get_hosts()) == [HostSpec('test', '1::4')] assert wait(cephadm_module, cephadm_module.get_hosts()) == [] + @mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('[]')) + @mock.patch("cephadm.utils.resolve_ip") + def test_re_add_host_receive_loopback(self, resolve_ip, cephadm_module): + resolve_ip.side_effect = ['192.168.122.1', '127.0.0.1', '127.0.0.1'] + assert wait(cephadm_module, cephadm_module.get_hosts()) == [] + cephadm_module._add_host(HostSpec('test', '192.168.122.1')) + assert wait(cephadm_module, cephadm_module.get_hosts()) == [ + HostSpec('test', '192.168.122.1')] + cephadm_module._add_host(HostSpec('test')) + assert wait(cephadm_module, cephadm_module.get_hosts()) == [ + HostSpec('test', '192.168.122.1')] + with pytest.raises(OrchestratorError): + cephadm_module._add_host(HostSpec('test2')) + @mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('[]')) def test_service_ls(self, cephadm_module): with with_host(cephadm_module, 'test'): c = cephadm_module.list_daemons(refresh=True) assert wait(cephadm_module, c) == [] - with with_service(cephadm_module, ServiceSpec('mds', 'name', unmanaged=True)) as _, \ - with_daemon(cephadm_module, ServiceSpec('mds', 'name'), 'test') as _: + with with_service(cephadm_module, MDSSpec('mds', 'name', unmanaged=True)) as _, \ + with_daemon(cephadm_module, MDSSpec('mds', 'name'), 'test') as _: c = cephadm_module.list_daemons() @@ -227,7 +241,7 @@ class TestCephadm(object): with with_host(cephadm_module, 'host2'): with with_service(cephadm_module, ServiceSpec('mgr', placement=PlacementSpec(count=2)), CephadmOrchestrator.apply_mgr, '', status_running=True): - with with_service(cephadm_module, ServiceSpec('mds', 'test-id', placement=PlacementSpec(count=2)), + with with_service(cephadm_module, MDSSpec('mds', 'test-id', placement=PlacementSpec(count=2)), CephadmOrchestrator.apply_mds, '', status_running=True): # with no service-type. Should provide info fot both services @@ -811,6 +825,11 @@ class TestCephadm(object): c = cephadm_module.create_osds(dg) out = wait(cephadm_module, c) assert out == "Created no osd(s) on host test; already created?" + bad_dg = DriveGroupSpec(placement=PlacementSpec(host_pattern='invalid_hsot'), + data_devices=DeviceSelection(paths=[''])) + c = cephadm_module.create_osds(bad_dg) + out = wait(cephadm_module, c) + assert "Invalid 'host:device' spec: host not found in cluster" in out @mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) def test_create_noncollocated_osd(self, cephadm_module): @@ -855,29 +874,56 @@ class TestCephadm(object): assert isinstance(f1[1], DriveSelection) @pytest.mark.parametrize( - "devices, preview, exp_command", + "devices, preview, exp_commands", [ # no preview and only one disk, prepare is used due the hack that is in place. - (['/dev/sda'], False, "lvm batch --no-auto /dev/sda --yes --no-systemd"), + (['/dev/sda'], False, ["lvm batch --no-auto /dev/sda --yes --no-systemd"]), # no preview and multiple disks, uses batch (['/dev/sda', '/dev/sdb'], False, - "CEPH_VOLUME_OSDSPEC_AFFINITY=test.spec lvm batch --no-auto /dev/sda /dev/sdb --yes --no-systemd"), + ["CEPH_VOLUME_OSDSPEC_AFFINITY=test.spec lvm batch --no-auto /dev/sda /dev/sdb --yes --no-systemd"]), # preview and only one disk needs to use batch again to generate the preview - (['/dev/sda'], True, "lvm batch --no-auto /dev/sda --yes --no-systemd --report --format json"), + (['/dev/sda'], True, ["lvm batch --no-auto /dev/sda --yes --no-systemd --report --format json"]), # preview and multiple disks work the same (['/dev/sda', '/dev/sdb'], True, - "CEPH_VOLUME_OSDSPEC_AFFINITY=test.spec lvm batch --no-auto /dev/sda /dev/sdb --yes --no-systemd --report --format json"), + ["CEPH_VOLUME_OSDSPEC_AFFINITY=test.spec lvm batch --no-auto /dev/sda /dev/sdb --yes --no-systemd --report --format json"]), ] ) @mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) - def test_driveselection_to_ceph_volume(self, cephadm_module, devices, preview, exp_command): + def test_driveselection_to_ceph_volume(self, cephadm_module, devices, preview, exp_commands): with with_host(cephadm_module, 'test'): dg = DriveGroupSpec(service_id='test.spec', placement=PlacementSpec( host_pattern='test'), data_devices=DeviceSelection(paths=devices)) ds = DriveSelection(dg, Devices([Device(path) for path in devices])) preview = preview out = cephadm_module.osd_service.driveselection_to_ceph_volume(ds, [], preview) - assert out in exp_command + assert all(any(cmd in exp_cmd for exp_cmd in exp_commands) + for cmd in out), f'Expected cmds from f{out} in {exp_commands}' + + @pytest.mark.parametrize( + "devices, preview, exp_commands", + [ + # one data device, no preview + (['/dev/sda'], False, ["raw prepare --bluestore --data /dev/sda"]), + # multiple data devices, no preview + (['/dev/sda', '/dev/sdb'], False, + ["raw prepare --bluestore --data /dev/sda", "raw prepare --bluestore --data /dev/sdb"]), + # one data device, preview + (['/dev/sda'], True, ["raw prepare --bluestore --data /dev/sda --report --format json"]), + # multiple data devices, preview + (['/dev/sda', '/dev/sdb'], True, + ["raw prepare --bluestore --data /dev/sda --report --format json", "raw prepare --bluestore --data /dev/sdb --report --format json"]), + ] + ) + @mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) + def test_raw_driveselection_to_ceph_volume(self, cephadm_module, devices, preview, exp_commands): + with with_host(cephadm_module, 'test'): + dg = DriveGroupSpec(service_id='test.spec', method='raw', placement=PlacementSpec( + host_pattern='test'), data_devices=DeviceSelection(paths=devices)) + ds = DriveSelection(dg, Devices([Device(path) for path in devices])) + preview = preview + out = cephadm_module.osd_service.driveselection_to_ceph_volume(ds, [], preview) + assert all(any(cmd in exp_cmd for exp_cmd in exp_commands) + for cmd in out), f'Expected cmds from f{out} in {exp_commands}' @mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm( json.dumps([ @@ -1291,7 +1337,7 @@ class TestCephadm(object): @mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) def test_mds_config_purge(self, cephadm_module: CephadmOrchestrator): - spec = ServiceSpec('mds', service_id='fsname') + spec = MDSSpec('mds', service_id='fsname', config={'test': 'foo'}) with with_host(cephadm_module, 'test'): with with_service(cephadm_module, spec, host='test'): ret, out, err = cephadm_module.check_mon_command({ @@ -1310,10 +1356,11 @@ class TestCephadm(object): @mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) @mock.patch("cephadm.services.cephadmservice.CephadmService.ok_to_stop") def test_daemon_ok_to_stop(self, ok_to_stop, cephadm_module: CephadmOrchestrator): - spec = ServiceSpec( + spec = MDSSpec( 'mds', service_id='fsname', - placement=PlacementSpec(hosts=['host1', 'host2']) + placement=PlacementSpec(hosts=['host1', 'host2']), + config={'test': 'foo'} ) with with_host(cephadm_module, 'host1'), with_host(cephadm_module, 'host2'): c = cephadm_module.apply_mds(spec) @@ -1464,31 +1511,48 @@ class TestCephadm(object): assert cephadm_module.manage_etc_ceph_ceph_conf is True CephadmServe(cephadm_module)._refresh_hosts_and_daemons() - _write_file.assert_called_with('test', '/etc/ceph/ceph.conf', b'', - 0o644, 0, 0, None) - - assert '/etc/ceph/ceph.conf' in cephadm_module.cache.get_host_client_files('test') + # Make sure both ceph conf locations (default and per fsid) are called + _write_file.assert_has_calls([mock.call('test', '/etc/ceph/ceph.conf', b'', + 0o644, 0, 0, None), + mock.call('test', '/var/lib/ceph/fsid/config/ceph.conf', b'', + 0o644, 0, 0, None)] + ) + ceph_conf_files = cephadm_module.cache.get_host_client_files('test') + assert len(ceph_conf_files) == 2 + assert '/etc/ceph/ceph.conf' in ceph_conf_files + assert '/var/lib/ceph/fsid/config/ceph.conf' in ceph_conf_files # set extra config and expect that we deploy another ceph.conf cephadm_module._set_extra_ceph_conf('[mon]\nk=v') CephadmServe(cephadm_module)._refresh_hosts_and_daemons() - _write_file.assert_called_with('test', '/etc/ceph/ceph.conf', - b'\n\n[mon]\nk=v\n', 0o644, 0, 0, None) - + _write_file.assert_has_calls([mock.call('test', + '/etc/ceph/ceph.conf', + b'\n\n[mon]\nk=v\n', 0o644, 0, 0, None), + mock.call('test', + '/var/lib/ceph/fsid/config/ceph.conf', + b'\n\n[mon]\nk=v\n', 0o644, 0, 0, None)]) # reload cephadm_module.cache.last_client_files = {} cephadm_module.cache.load() - assert '/etc/ceph/ceph.conf' in cephadm_module.cache.get_host_client_files('test') + ceph_conf_files = cephadm_module.cache.get_host_client_files('test') + assert len(ceph_conf_files) == 2 + assert '/etc/ceph/ceph.conf' in ceph_conf_files + assert '/var/lib/ceph/fsid/config/ceph.conf' in ceph_conf_files # Make sure, _check_daemons does a redeploy due to monmap change: - before_digest = cephadm_module.cache.get_host_client_files('test')[ + f1_before_digest = cephadm_module.cache.get_host_client_files('test')[ '/etc/ceph/ceph.conf'][0] + f2_before_digest = cephadm_module.cache.get_host_client_files( + 'test')['/var/lib/ceph/fsid/config/ceph.conf'][0] cephadm_module._set_extra_ceph_conf('[mon]\nk2=v2') CephadmServe(cephadm_module)._refresh_hosts_and_daemons() - after_digest = cephadm_module.cache.get_host_client_files('test')[ + f1_after_digest = cephadm_module.cache.get_host_client_files('test')[ '/etc/ceph/ceph.conf'][0] - assert before_digest != after_digest + f2_after_digest = cephadm_module.cache.get_host_client_files( + 'test')['/var/lib/ceph/fsid/config/ceph.conf'][0] + assert f1_before_digest != f1_after_digest + assert f2_before_digest != f2_after_digest def test_etc_ceph_init(self): with with_cephadm_module({'manage_etc_ceph_ceph_conf': True}) as m: diff --git a/ceph/src/pybind/mgr/cephadm/tests/test_scheduling.py b/ceph/src/pybind/mgr/cephadm/tests/test_scheduling.py index ec4b87f59..c70ef9fb5 100644 --- a/ceph/src/pybind/mgr/cephadm/tests/test_scheduling.py +++ b/ceph/src/pybind/mgr/cephadm/tests/test_scheduling.py @@ -1441,3 +1441,79 @@ def test_unreachable_host(service_type, placement, hosts, unreachable_hosts, dae ).place() assert sorted([h.hostname for h in to_add]) in expected_add assert sorted([h.name() for h in to_remove]) in expected_remove + + +class RescheduleFromOfflineTest(NamedTuple): + service_type: str + placement: PlacementSpec + hosts: List[str] + maintenance_hosts: List[str] + offline_hosts: List[str] + daemons: List[DaemonDescription] + expected_add: List[List[str]] + expected_remove: List[List[str]] + + +@pytest.mark.parametrize("service_type,placement,hosts,maintenance_hosts,offline_hosts,daemons,expected_add,expected_remove", + [ + RescheduleFromOfflineTest( + 'nfs', + PlacementSpec(count=2), + 'host1 host2 host3'.split(), + [], + ['host2'], + [ + DaemonDescription('nfs', 'a', 'host1'), + DaemonDescription('nfs', 'b', 'host2'), + ], + [['host3']], + [[]], + ), + RescheduleFromOfflineTest( + 'nfs', + PlacementSpec(count=2), + 'host1 host2 host3'.split(), + ['host2'], + [], + [ + DaemonDescription('nfs', 'a', 'host1'), + DaemonDescription('nfs', 'b', 'host2'), + ], + [[]], + [[]], + ), + RescheduleFromOfflineTest( + 'mon', + PlacementSpec(count=2), + 'host1 host2 host3'.split(), + [], + ['host2'], + [ + DaemonDescription('mon', 'a', 'host1'), + DaemonDescription('mon', 'b', 'host2'), + ], + [[]], + [[]], + ), + ]) +def test_remove_from_offline(service_type, placement, hosts, maintenance_hosts, offline_hosts, daemons, expected_add, expected_remove): + + spec = ServiceSpec(service_type=service_type, + service_id='test', + placement=placement) + + host_specs = [HostSpec(h) for h in hosts] + for h in host_specs: + if h.hostname in offline_hosts: + h.status = 'offline' + if h.hostname in maintenance_hosts: + h.status = 'maintenance' + + hosts, to_add, to_remove = HostAssignment( + spec=spec, + hosts=host_specs, + unreachable_hosts=[h for h in host_specs if h.status], + daemons=daemons, + ).place() + assert sorted([h.hostname for h in to_add]) in expected_add + assert sorted([h.name() for h in to_remove]) in expected_remove diff --git a/ceph/src/pybind/mgr/cephadm/tests/test_services.py b/ceph/src/pybind/mgr/cephadm/tests/test_services.py index 6f0f81c03..e59e95c7b 100644 --- a/ceph/src/pybind/mgr/cephadm/tests/test_services.py +++ b/ceph/src/pybind/mgr/cephadm/tests/test_services.py @@ -13,7 +13,7 @@ from cephadm.services.iscsi import IscsiService from cephadm.services.nfs import NFSService from cephadm.services.osd import OSDService from cephadm.services.monitoring import GrafanaService, AlertmanagerService, PrometheusService, \ - NodeExporterService + NodeExporterService, LokiService, PromtailService from cephadm.module import CephadmOrchestrator from ceph.deployment.service_spec import IscsiServiceSpec, MonitoringSpec, AlertManagerSpec, \ ServiceSpec, RGWSpec, GrafanaSpec, SNMPGatewaySpec, IngressSpec, PlacementSpec @@ -80,6 +80,8 @@ class TestCephadmService: alertmanager_service = AlertmanagerService(mgr) prometheus_service = PrometheusService(mgr) node_exporter_service = NodeExporterService(mgr) + loki_service = LokiService(mgr) + promtail_service = PromtailService(mgr) crash_service = CrashService(mgr) iscsi_service = IscsiService(mgr) cephadm_services = { @@ -94,6 +96,8 @@ class TestCephadmService: 'alertmanager': alertmanager_service, 'prometheus': prometheus_service, 'node-exporter': node_exporter_service, + 'loki': loki_service, + 'promtail': promtail_service, 'crash': crash_service, 'iscsi': iscsi_service, } @@ -135,7 +139,7 @@ class TestCephadmService: # services based on CephadmService shouldn't have get_auth_entity with pytest.raises(AttributeError): - for daemon_type in ['grafana', 'alertmanager', 'prometheus', 'node-exporter']: + for daemon_type in ['grafana', 'alertmanager', 'prometheus', 'node-exporter', 'loki', 'promtail']: cephadm_services[daemon_type].get_auth_entity("id1", "host") cephadm_services[daemon_type].get_auth_entity("id1", "") cephadm_services[daemon_type].get_auth_entity("id1") @@ -247,6 +251,9 @@ class TestMonitoring: global: resolve_timeout: 5m + http_config: + tls_config: + insecure_skip_verify: true route: receiver: 'default' @@ -298,7 +305,7 @@ class TestMonitoring: honor_labels: true static_configs: - targets: - - '[::1]:8081' + - '[::1]:9283' - job_name: 'node' static_configs: @@ -323,16 +330,113 @@ class TestMonitoring: image='') @patch("cephadm.serve.CephadmServe._run_cephadm") - @patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '1::4') - @patch("cephadm.services.monitoring.verify_tls", lambda *_: None) - def test_grafana_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): + def test_loki_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) with with_host(cephadm_module, 'test'): - cephadm_module.set_store('grafana_crt', 'c') - cephadm_module.set_store('grafana_key', 'k') - with with_service(cephadm_module, MonitoringSpec('prometheus')) as _, \ - with_service(cephadm_module, GrafanaSpec('grafana')) as _: + with with_service(cephadm_module, MonitoringSpec('loki')) as _: + + y = dedent(""" + # This file is generated by cephadm. + auth_enabled: false + + server: + http_listen_port: 3100 + grpc_listen_port: 8080 + + common: + path_prefix: /tmp/loki + storage: + filesystem: + chunks_directory: /tmp/loki/chunks + rules_directory: /tmp/loki/rules + replication_factor: 1 + ring: + instance_addr: 127.0.0.1 + kvstore: + store: inmemory + + schema_config: + configs: + - from: 2020-10-24 + store: boltdb-shipper + object_store: filesystem + schema: v11 + index: + prefix: index_ + period: 24h""").lstrip() + + _run_cephadm.assert_called_with( + 'test', + 'loki.test', + 'deploy', + [ + '--name', 'loki.test', + '--meta-json', + '{"service_name": "loki", "ports": [3100], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', + '--config-json', '-', + '--tcp-ports', '3100' + ], + stdin=json.dumps({"files": {"loki.yml": y}}), + image='') + + @patch("cephadm.serve.CephadmServe._run_cephadm") + def test_promtail_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): + _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) + + with with_host(cephadm_module, 'test'): + with with_service(cephadm_module, ServiceSpec('mgr')) as _, \ + with_service(cephadm_module, MonitoringSpec('promtail')) as _: + + y = dedent(""" + # This file is generated by cephadm. + server: + http_listen_port: 9080 + grpc_listen_port: 0 + + positions: + filename: /tmp/positions.yaml + + clients: + - url: http://1::4:3100/loki/api/v1/push + + scrape_configs: + - job_name: system + static_configs: + - targets: + - 1::4 + labels: + job: Cluster Logs + __path__: /var/log/ceph/**/*.log""").lstrip() + + _run_cephadm.assert_called_with( + 'test', + 'promtail.test', + 'deploy', + [ + '--name', 'promtail.test', + '--meta-json', + '{"service_name": "promtail", "ports": [9080], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', + '--config-json', '-', + '--tcp-ports', '9080' + ], + stdin=json.dumps({"files": {"promtail.yml": y}}), + image='') + + @patch("cephadm.serve.CephadmServe._run_cephadm") + @patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '1::4') + @patch("cephadm.services.monitoring.verify_tls", lambda *_: None) + def test_grafana_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): + _run_cephadm.side_effect = async_side_effect(("{}", "", 0)) + + with with_host(cephadm_module, "test"): + cephadm_module.set_store("grafana_crt", "c") + cephadm_module.set_store("grafana_key", "k") + with with_service( + cephadm_module, MonitoringSpec("prometheus") + ) as _, with_service(cephadm_module, ServiceSpec("mgr")) as _, with_service( + cephadm_module, GrafanaSpec("grafana") + ) as _: files = { 'grafana.ini': dedent(""" # This file is generated by cephadm. @@ -349,6 +453,8 @@ class TestMonitoring: cert_key = /etc/grafana/certs/cert_key http_port = 3000 http_addr = + [snapshots] + external_enabled = false [security] disable_initial_admin_creation = true cookie_secure = true @@ -360,6 +466,9 @@ class TestMonitoring: - name: 'Dashboard1' orgId: 1 + - name: 'Loki' + orgId: 2 + datasources: - name: 'Dashboard1' type: 'prometheus' @@ -369,7 +478,15 @@ class TestMonitoring: basicAuth: false isDefault: true editable: false - """).lstrip(), + + - name: 'Loki' + type: 'loki' + access: 'proxy' + orgId: 2 + url: 'http://[1::4]:3100' + basicAuth: false + isDefault: true + editable: false""").lstrip(), 'certs/cert_file': dedent(""" # generated by cephadm c""").lstrip(), @@ -393,15 +510,14 @@ class TestMonitoring: @patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) def test_grafana_initial_admin_pw(self, cephadm_module: CephadmOrchestrator): with with_host(cephadm_module, 'test'): - with with_service(cephadm_module, GrafanaSpec(initial_admin_password='secure')): + with with_service(cephadm_module, ServiceSpec('mgr')) as _, \ + with_service(cephadm_module, GrafanaSpec(initial_admin_password='secure')): out = cephadm_module.cephadm_services['grafana'].generate_config( CephadmDaemonDeploySpec('test', 'daemon', 'grafana')) assert out == ( { 'files': { - 'certs/cert_file': ANY, - 'certs/cert_key': ANY, 'grafana.ini': '# This file is generated by cephadm.\n' '[users]\n' @@ -417,6 +533,8 @@ class TestMonitoring: ' cert_key = /etc/grafana/certs/cert_key\n' ' http_port = 3000\n' ' http_addr = \n' + '[snapshots]\n' + ' external_enabled = false\n' '[security]\n' ' admin_user = admin\n' ' admin_password = secure\n' @@ -424,14 +542,21 @@ class TestMonitoring: ' cookie_samesite = none\n' ' allow_embedding = true', 'provisioning/datasources/ceph-dashboard.yml': - '# This file is generated by cephadm.\n' - 'deleteDatasources:\n' - '\n' - 'datasources:\n' - } - }, - [], - ) + "# This file is generated by cephadm.\n" + 'deleteDatasources:\n\n' + " - name: 'Loki'\n" + ' orgId: 2\n\n' + 'datasources:\n\n' + " - name: 'Loki'\n" + " type: 'loki'\n" + " access: 'proxy'\n" + ' orgId: 2\n' + " url: 'http://[1::4]:3100'\n" + ' basicAuth: false\n' + ' isDefault: true\n' + ' editable: false', + 'certs/cert_file': ANY, + 'certs/cert_key': ANY}}, []) @patch("cephadm.serve.CephadmServe._run_cephadm") def test_monitoring_ports(self, _run_cephadm, cephadm_module: CephadmOrchestrator): diff --git a/ceph/src/pybind/mgr/cephadm/tests/test_upgrade.py b/ceph/src/pybind/mgr/cephadm/tests/test_upgrade.py index 32aacf263..608b68f89 100644 --- a/ceph/src/pybind/mgr/cephadm/tests/test_upgrade.py +++ b/ceph/src/pybind/mgr/cephadm/tests/test_upgrade.py @@ -8,7 +8,9 @@ from cephadm import CephadmOrchestrator from cephadm.upgrade import CephadmUpgrade from orchestrator import OrchestratorError, DaemonDescription from .fixtures import _run_cephadm, wait, with_host, with_service, \ - receive_agent_metadata + receive_agent_metadata, async_side_effect + +from typing import List, Tuple, Optional @mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) @@ -164,3 +166,244 @@ def test_enough_mds_for_ok_to_stop(get, get_daemons_by_service, cephadm_module: get_daemons_by_service.side_effect = [[DaemonDescription(), DaemonDescription()]] assert cephadm_module.upgrade._enough_mds_for_ok_to_stop( DaemonDescription(daemon_type='mds', daemon_id='myfs.test.host1.gfknd', service_name='mds.myfs.test')) + + +@pytest.mark.parametrize("current_version, use_tags, show_all_versions, tags, result", + [ + # several candidate versions (from different major versions) + ( + (16, 1, '16.1.0'), + False, # use_tags + False, # show_all_versions + [ + 'v17.1.0', + 'v16.2.7', + 'v16.2.6', + 'v16.2.5', + 'v16.1.4', + 'v16.1.3', + 'v15.2.0', + ], + ['17.1.0', '16.2.7', '16.2.6', '16.2.5', '16.1.4', '16.1.3'] + ), + # candidate minor versions are available + ( + (16, 1, '16.1.0'), + False, # use_tags + False, # show_all_versions + [ + 'v16.2.2', + 'v16.2.1', + 'v16.1.6', + ], + ['16.2.2', '16.2.1', '16.1.6'] + ), + # all versions are less than the current version + ( + (17, 2, '17.2.0'), + False, # use_tags + False, # show_all_versions + [ + 'v17.1.0', + 'v16.2.7', + 'v16.2.6', + ], + [] + ), + # show all versions (regardless of the current version) + ( + (16, 1, '16.1.0'), + False, # use_tags + True, # show_all_versions + [ + 'v17.1.0', + 'v16.2.7', + 'v16.2.6', + 'v15.1.0', + 'v14.2.0', + ], + ['17.1.0', '16.2.7', '16.2.6', '15.1.0', '14.2.0'] + ), + # show all tags (regardless of the current version and show_all_versions flag) + ( + (16, 1, '16.1.0'), + True, # use_tags + False, # show_all_versions + [ + 'v17.1.0', + 'v16.2.7', + 'v16.2.6', + 'v16.2.5', + 'v16.1.4', + 'v16.1.3', + 'v15.2.0', + ], + ['v15.2.0', 'v16.1.3', 'v16.1.4', 'v16.2.5', + 'v16.2.6', 'v16.2.7', 'v17.1.0'] + ), + ]) +@mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) +def test_upgrade_ls(current_version, use_tags, show_all_versions, tags, result, cephadm_module: CephadmOrchestrator): + with mock.patch('cephadm.upgrade.Registry.get_tags', return_value=tags): + with mock.patch('cephadm.upgrade.CephadmUpgrade._get_current_version', return_value=current_version): + out = cephadm_module.upgrade.upgrade_ls(None, use_tags, show_all_versions) + if use_tags: + assert out['tags'] == result + else: + assert out['versions'] == result + + +@pytest.mark.parametrize( + "upgraded, not_upgraded, daemon_types, hosts, services, should_block", + # [ ([(type, host, id), ... ], [...], [daemon types], [hosts], [services], True/False), ... ] + [ + ( # valid, upgrade mgr daemons + [], + [('mgr', 'a', 'a.x'), ('mon', 'a', 'a')], + ['mgr'], + None, + None, + False + ), + ( # invalid, can't upgrade mons until mgr is upgraded + [], + [('mgr', 'a', 'a.x'), ('mon', 'a', 'a')], + ['mon'], + None, + None, + True + ), + ( # invalid, can't upgrade mon service until all mgr daemons are upgraded + [], + [('mgr', 'a', 'a.x'), ('mon', 'a', 'a')], + None, + None, + ['mon'], + True + ), + ( # valid, upgrade mgr service + [], + [('mgr', 'a', 'a.x'), ('mon', 'a', 'a')], + None, + None, + ['mgr'], + False + ), + ( # valid, mgr is already upgraded so can upgrade mons + [('mgr', 'a', 'a.x')], + [('mon', 'a', 'a')], + ['mon'], + None, + None, + False + ), + ( # invalid, can't upgrade all daemons on b b/c un-upgraded mgr on a + [], + [('mgr', 'b', 'b.y'), ('mon', 'a', 'a')], + None, + ['a'], + None, + True + ), + ( # valid, only daemon on b is a mgr + [], + [('mgr', 'a', 'a.x'), ('mgr', 'b', 'b.y'), ('mon', 'a', 'a')], + None, + ['b'], + None, + False + ), + ( # invalid, can't upgrade mon on a while mgr on b is un-upgraded + [], + [('mgr', 'a', 'a.x'), ('mgr', 'b', 'b.y'), ('mon', 'a', 'a')], + None, + ['a'], + None, + True + ), + ( # valid, only upgrading the mgr on a + [], + [('mgr', 'a', 'a.x'), ('mgr', 'b', 'b.y'), ('mon', 'a', 'a')], + ['mgr'], + ['a'], + None, + False + ), + ( # valid, mgr daemon not on b are upgraded + [('mgr', 'a', 'a.x')], + [('mgr', 'b', 'b.y'), ('mon', 'a', 'a')], + None, + ['b'], + None, + False + ), + ( # valid, all the necessary hosts are covered, mgr on c is already upgraded + [('mgr', 'c', 'c.z')], + [('mgr', 'a', 'a.x'), ('mgr', 'b', 'b.y'), ('mon', 'a', 'a'), ('osd', 'c', '0')], + None, + ['a', 'b'], + None, + False + ), + ( # invalid, can't upgrade mon on a while mgr on b is un-upgraded + [], + [('mgr', 'a', 'a.x'), ('mgr', 'b', 'b.y'), ('mon', 'a', 'a')], + ['mgr', 'mon'], + ['a'], + None, + True + ), + ( # valid, only mon not on "b" is upgraded already. Case hit while making teuthology test + [('mon', 'a', 'a')], + [('mon', 'b', 'x'), ('mon', 'b', 'y'), ('osd', 'a', '1'), ('osd', 'b', '2')], + ['mon', 'osd'], + ['b'], + None, + False + ), + ] +) +@mock.patch("cephadm.module.HostCache.get_daemons") +@mock.patch("cephadm.serve.CephadmServe._get_container_image_info") +@mock.patch('cephadm.module.SpecStore.__getitem__') +def test_staggered_upgrade_validation( + get_spec, + get_image_info, + get_daemons, + upgraded: List[Tuple[str, str, str]], + not_upgraded: List[Tuple[str, str, str, str]], + daemon_types: Optional[str], + hosts: Optional[str], + services: Optional[str], + should_block: bool, + cephadm_module: CephadmOrchestrator, +): + def to_dds(ts: List[Tuple[str, str]], upgraded: bool) -> List[DaemonDescription]: + dds = [] + digest = 'new_image@repo_digest' if upgraded else 'old_image@repo_digest' + for t in ts: + dds.append(DaemonDescription(daemon_type=t[0], + hostname=t[1], + daemon_id=t[2], + container_image_digests=[digest], + deployed_by=[digest],)) + return dds + get_daemons.return_value = to_dds(upgraded, True) + to_dds(not_upgraded, False) + get_image_info.side_effect = async_side_effect( + ('new_id', 'ceph version 99.99.99 (hash)', ['new_image@repo_digest'])) + + class FakeSpecDesc(): + def __init__(self, spec): + self.spec = spec + + def _get_spec(s): + return FakeSpecDesc(ServiceSpec(s)) + + get_spec.side_effect = _get_spec + if should_block: + with pytest.raises(OrchestratorError): + cephadm_module.upgrade._validate_upgrade_filters( + 'new_image_name', daemon_types, hosts, services) + else: + cephadm_module.upgrade._validate_upgrade_filters( + 'new_image_name', daemon_types, hosts, services) diff --git a/ceph/src/pybind/mgr/cephadm/upgrade.py b/ceph/src/pybind/mgr/cephadm/upgrade.py index f59752db9..d41b9286e 100644 --- a/ceph/src/pybind/mgr/cephadm/upgrade.py +++ b/ceph/src/pybind/mgr/cephadm/upgrade.py @@ -8,7 +8,8 @@ import orchestrator from cephadm.registry import Registry from cephadm.serve import CephadmServe from cephadm.services.cephadmservice import CephadmDaemonDeploySpec -from cephadm.utils import ceph_release_to_major, name_to_config_section, CEPH_UPGRADE_ORDER, MONITORING_STACK_TYPES +from cephadm.utils import ceph_release_to_major, name_to_config_section, CEPH_UPGRADE_ORDER, \ + MONITORING_STACK_TYPES, CEPH_TYPES, GATEWAY_TYPES from orchestrator import OrchestratorError, DaemonDescription, DaemonDescriptionStatus, daemon_type_to_service if TYPE_CHECKING: @@ -58,7 +59,12 @@ class UpgradeState: error: Optional[str] = None, paused: Optional[bool] = None, fs_original_max_mds: Optional[Dict[str, int]] = None, - fs_original_allow_standby_replay: Optional[Dict[str, bool]] = None + fs_original_allow_standby_replay: Optional[Dict[str, bool]] = None, + daemon_types: Optional[List[str]] = None, + hosts: Optional[List[str]] = None, + services: Optional[List[str]] = None, + total_count: Optional[int] = None, + remaining_count: Optional[int] = None, ): self._target_name: str = target_name # Use CephadmUpgrade.target_image instead. self.progress_id: str = progress_id @@ -70,6 +76,11 @@ class UpgradeState: self.fs_original_max_mds: Optional[Dict[str, int]] = fs_original_max_mds self.fs_original_allow_standby_replay: Optional[Dict[str, bool]] = fs_original_allow_standby_replay + self.daemon_types = daemon_types + self.hosts = hosts + self.services = services + self.total_count = total_count + self.remaining_count = remaining_count def to_json(self) -> dict: return { @@ -82,12 +93,18 @@ class UpgradeState: 'fs_original_allow_standby_replay': self.fs_original_allow_standby_replay, 'error': self.error, 'paused': self.paused, + 'daemon_types': self.daemon_types, + 'hosts': self.hosts, + 'services': self.services, + 'total_count': self.total_count, + 'remaining_count': self.remaining_count, } @classmethod def from_json(cls, data: dict) -> Optional['UpgradeState']: + valid_params = UpgradeState.__init__.__code__.co_varnames if data: - c = {k: v for k, v in data.items()} + c = {k: v for k, v in data.items() if k in valid_params} if 'repo_digest' in c: c['target_digests'] = [c.pop('repo_digest')] return cls(**c) @@ -130,6 +147,23 @@ class CephadmUpgrade: r.target_image = self.target_image r.in_progress = True r.progress, r.services_complete = self._get_upgrade_info() + + if self.upgrade_state.daemon_types is not None: + which_str = f'Upgrading daemons of type(s) {",".join(self.upgrade_state.daemon_types)}' + if self.upgrade_state.hosts is not None: + which_str += f' on host(s) {",".join(self.upgrade_state.hosts)}' + elif self.upgrade_state.services is not None: + which_str = f'Upgrading daemons in service(s) {",".join(self.upgrade_state.services)}' + if self.upgrade_state.hosts is not None: + which_str += f' on host(s) {",".join(self.upgrade_state.hosts)}' + elif self.upgrade_state.hosts is not None: + which_str = f'Upgrading all daemons on host(s) {",".join(self.upgrade_state.hosts)}' + else: + which_str = 'Upgrading all daemon types on all hosts' + if self.upgrade_state.total_count is not None and self.upgrade_state.remaining_count is not None: + which_str += f'. Upgrade limited to {self.upgrade_state.total_count} daemons ({self.upgrade_state.remaining_count} remaining).' + r.which = which_str + # accessing self.upgrade_info_str will throw an exception if it # has not been set in _do_upgrade yet try: @@ -146,7 +180,7 @@ class CephadmUpgrade: if not self.upgrade_state or not self.upgrade_state.target_digests: return '', [] - daemons = [d for d in self.mgr.cache.get_daemons() if d.daemon_type in CEPH_UPGRADE_ORDER] + daemons = self._get_filtered_daemons() if any(not d.container_image_digests for d in daemons if d.daemon_type == 'mgr'): return '', [] @@ -161,47 +195,72 @@ class CephadmUpgrade: return '%s/%s daemons upgraded' % (done, len(daemons)), completed_types + def _get_filtered_daemons(self) -> List[DaemonDescription]: + # Return the set of daemons set to be upgraded with out current + # filtering parameters (or all daemons in upgrade order if no filtering + # parameter are set). + assert self.upgrade_state is not None + if self.upgrade_state.daemon_types is not None: + daemons = [d for d in self.mgr.cache.get_daemons( + ) if d.daemon_type in self.upgrade_state.daemon_types] + elif self.upgrade_state.services is not None: + daemons = [] + for service in self.upgrade_state.services: + daemons += self.mgr.cache.get_daemons_by_service(service) + else: + daemons = [d for d in self.mgr.cache.get_daemons( + ) if d.daemon_type in CEPH_UPGRADE_ORDER] + if self.upgrade_state.hosts is not None: + daemons = [d for d in daemons if d.hostname in self.upgrade_state.hosts] + return daemons + + def _get_current_version(self) -> Tuple[int, int, str]: + current_version = self.mgr.version.split('ceph version ')[1] + (current_major, current_minor, _) = current_version.split('-')[0].split('.', 2) + return (int(current_major), int(current_minor), current_version) + def _check_target_version(self, version: str) -> Optional[str]: try: - (major, minor, _) = version.split('.', 2) - assert int(minor) >= 0 + v = version.split('.', 2) + (major, minor) = (int(v[0]), int(v[1])) + assert minor >= 0 # patch might be a number or {number}-g{sha1} except ValueError: return 'version must be in the form X.Y.Z (e.g., 15.2.3)' - if int(major) < 15 or (int(major) == 15 and int(minor) < 2): + if major < 15 or (major == 15 and minor < 2): return 'cephadm only supports octopus (15.2.0) or later' # to far a jump? - current_version = self.mgr.version.split('ceph version ')[1] - (current_major, current_minor, _) = current_version.split('-')[0].split('.', 2) - if int(current_major) < int(major) - 2: + (current_major, current_minor, current_version) = self._get_current_version() + if current_major < major - 2: return f'ceph can only upgrade 1 or 2 major versions at a time; {current_version} -> {version} is too big a jump' - if int(current_major) > int(major): + if current_major > major: return f'ceph cannot downgrade major versions (from {current_version} to {version})' - if int(current_major) == int(major): - if int(current_minor) > int(minor): - return f'ceph cannot downgrade to a {"rc" if minor == "1" else "dev"} release' + if current_major == major: + if current_minor > minor: + return f'ceph cannot downgrade to a {"rc" if minor == 1 else "dev"} release' # check mon min monmap = self.mgr.get("mon_map") mon_min = monmap.get("min_mon_release", 0) - if mon_min < int(major) - 2: + if mon_min < major - 2: return f'min_mon_release ({mon_min}) < target {major} - 2; first complete an upgrade to an earlier release' # check osd min osdmap = self.mgr.get("osd_map") osd_min_name = osdmap.get("require_osd_release", "argonaut") osd_min = ceph_release_to_major(osd_min_name) - if osd_min < int(major) - 2: + if osd_min < major - 2: return f'require_osd_release ({osd_min_name} or {osd_min}) < target {major} - 2; first complete an upgrade to an earlier release' return None - def upgrade_ls(self, image: Optional[str], tags: bool) -> Dict: + def upgrade_ls(self, image: Optional[str], tags: bool, show_all_versions: Optional[bool]) -> Dict: if not image: image = self.mgr.container_image_base reg_name, bare_image = image.split('/', 1) reg = Registry(reg_name) + (current_major, current_minor, _) = self._get_current_version() versions = [] r: Dict[Any, Any] = { "image": image, @@ -218,7 +277,12 @@ class CephadmUpgrade: continue if '-' in v[2]: continue - versions.append('.'.join(v)) + v_major = int(v[0]) + v_minor = int(v[1]) + candidate_version = (v_major > current_major + or (v_major == current_major and v_minor >= current_minor)) + if show_all_versions or candidate_version: + versions.append('.'.join(v)) r["versions"] = sorted( versions, key=lambda k: list(map(int, k.split('.'))), @@ -228,7 +292,8 @@ class CephadmUpgrade: r["tags"] = sorted(ls) return r - def upgrade_start(self, image: str, version: str) -> str: + def upgrade_start(self, image: str, version: str, daemon_types: Optional[List[str]] = None, + hosts: Optional[List[str]] = None, services: Optional[List[str]] = None, limit: Optional[int] = None) -> str: if self.mgr.mode != 'root': raise OrchestratorError('upgrade is not supported in %s mode' % ( self.mgr.mode)) @@ -241,6 +306,10 @@ class CephadmUpgrade: target_name = normalize_image_digest(image, self.mgr.default_registry) else: raise OrchestratorError('must specify either image or version') + + if daemon_types is not None or services is not None or hosts is not None: + self._validate_upgrade_filters(target_name, daemon_types, hosts, services) + if self.upgrade_state: if self.upgrade_state._target_name != target_name: raise OrchestratorError( @@ -261,7 +330,12 @@ class CephadmUpgrade: self.mgr.log.info('Upgrade: Started with target %s' % target_name) self.upgrade_state = UpgradeState( target_name=target_name, - progress_id=str(uuid.uuid4()) + progress_id=str(uuid.uuid4()), + daemon_types=daemon_types, + hosts=hosts, + services=services, + total_count=limit, + remaining_count=limit, ) self._update_upgrade_progress(0.0) self._save_upgrade_state() @@ -269,6 +343,98 @@ class CephadmUpgrade: self.mgr.event.set() return 'Initiating upgrade to %s' % (target_name) + def _validate_upgrade_filters(self, target_name: str, daemon_types: Optional[List[str]] = None, hosts: Optional[List[str]] = None, services: Optional[List[str]] = None) -> None: + def _latest_type(dtypes: List[str]) -> str: + # [::-1] gives the list in reverse + for daemon_type in CEPH_UPGRADE_ORDER[::-1]: + if daemon_type in dtypes: + return daemon_type + return '' + + def _get_earlier_daemons(dtypes: List[str], candidates: List[DaemonDescription]) -> List[DaemonDescription]: + # this function takes a list of daemon types and first finds the daemon + # type from that list that is latest in our upgrade order. Then, from + # that latest type, it filters the list of candidate daemons received + # for daemons with types earlier in the upgrade order than the latest + # type found earlier. That filtered list of daemons is returned. The + # purpose of this function is to help in finding daemons that must have + # already been upgraded for the given filtering parameters (--daemon-types, + # --services, --hosts) to be valid. + latest = _latest_type(dtypes) + if not latest: + return [] + earlier_types = '|'.join(CEPH_UPGRADE_ORDER).split(latest)[0].split('|')[:-1] + earlier_types = [t for t in earlier_types if t not in dtypes] + return [d for d in candidates if d.daemon_type in earlier_types] + + if self.upgrade_state: + raise OrchestratorError( + 'Cannot set values for --daemon-types, --services or --hosts when upgrade already in progress.') + try: + target_id, target_version, target_digests = self.mgr.wait_async( + CephadmServe(self.mgr)._get_container_image_info(target_name)) + except OrchestratorError as e: + raise OrchestratorError(f'Failed to pull {target_name}: {str(e)}') + # what we need to do here is build a list of daemons that must already be upgraded + # in order for the user's selection of daemons to upgrade to be valid. for example, + # if they say --daemon-types 'osd,mds' but mons have not been upgraded, we block. + daemons = [d for d in self.mgr.cache.get_daemons( + ) if d.daemon_type not in MONITORING_STACK_TYPES] + err_msg_base = 'Cannot start upgrade. ' + # "dtypes" will later be filled in with the types of daemons that will be upgraded with the given parameters + dtypes = [] + if daemon_types is not None: + dtypes = daemon_types + if hosts is not None: + dtypes = [_latest_type(dtypes)] + other_host_daemons = [ + d for d in daemons if d.hostname is not None and d.hostname not in hosts] + daemons = _get_earlier_daemons(dtypes, other_host_daemons) + else: + daemons = _get_earlier_daemons(dtypes, daemons) + err_msg_base += 'Daemons with types earlier in upgrade order than given types need upgrading.\n' + elif services is not None: + # for our purposes here we can effectively convert our list of services into the + # set of daemon types the services contain. This works because we don't allow --services + # and --daemon-types at the same time and we only allow services of the same type + sspecs = [ + self.mgr.spec_store[s].spec for s in services if self.mgr.spec_store[s].spec is not None] + stypes = list(set([s.service_type for s in sspecs])) + if len(stypes) != 1: + raise OrchestratorError('Doing upgrade by service only support services of one type at ' + f'a time. Found service types: {stypes}') + for stype in stypes: + dtypes += orchestrator.service_to_daemon_types(stype) + dtypes = list(set(dtypes)) + if hosts is not None: + other_host_daemons = [ + d for d in daemons if d.hostname is not None and d.hostname not in hosts] + daemons = _get_earlier_daemons(dtypes, other_host_daemons) + else: + daemons = _get_earlier_daemons(dtypes, daemons) + err_msg_base += 'Daemons with types earlier in upgrade order than daemons from given services need upgrading.\n' + elif hosts is not None: + # hosts must be handled a bit differently. For this, we really need to find all the daemon types + # that reside on hosts in the list of hosts we will upgrade. Then take the type from + # that list that is latest in the upgrade order and check if any daemons on hosts not in the + # provided list of hosts have a daemon with a type earlier in the upgrade order that is not upgraded. + dtypes = list( + set([d.daemon_type for d in daemons if d.daemon_type is not None and d.hostname in hosts])) + other_hosts_daemons = [ + d for d in daemons if d.hostname is not None and d.hostname not in hosts] + daemons = _get_earlier_daemons([_latest_type(dtypes)], other_hosts_daemons) + err_msg_base += 'Daemons with types earlier in upgrade order than daemons on given host need upgrading.\n' + need_upgrade_self, n1, n2, _ = self._detect_need_upgrade(daemons, target_digests) + if need_upgrade_self and ('mgr' not in dtypes or (daemon_types is None and services is None)): + # also report active mgr as needing to be upgraded. It is not included in the resulting list + # by default as it is treated special and handled via the need_upgrade_self bool + n1.insert(0, (self.mgr.mgr_service.get_active_daemon( + self.mgr.cache.get_daemons_by_type('mgr')), True)) + if n1 or n2: + raise OrchestratorError(f'{err_msg_base}Please first upgrade ' + f'{", ".join(list(set([d[0].name() for d in n1] + [d[0].name() for d in n2])))}\n' + f'NOTE: Enforced upgrade order is: {" -> ".join(CEPH_TYPES + GATEWAY_TYPES)}') + def upgrade_pause(self) -> str: if not self.upgrade_state: raise OrchestratorError('No upgrade in progress') @@ -523,6 +689,281 @@ class CephadmUpgrade: return True # if mds has no fs it should pass ok-to-stop + def _detect_need_upgrade(self, daemons: List[DaemonDescription], target_digests: Optional[List[str]] = None) -> Tuple[bool, List[Tuple[DaemonDescription, bool]], List[Tuple[DaemonDescription, bool]], int]: + # this function takes a list of daemons and container digests. The purpose + # is to go through each daemon and check if the current container digests + # for that daemon match the target digests. The purpose being that we determine + # if a daemon is upgraded to a certain container image or not based on what + # container digests it has. By checking the current digests against the + # targets we can determine which daemons still need to be upgraded + need_upgrade_self = False + need_upgrade: List[Tuple[DaemonDescription, bool]] = [] + need_upgrade_deployer: List[Tuple[DaemonDescription, bool]] = [] + done = 0 + if target_digests is None: + target_digests = [] + for d in daemons: + assert d.daemon_type is not None + assert d.daemon_id is not None + assert d.hostname is not None + if self.mgr.use_agent and not self.mgr.cache.host_metadata_up_to_date(d.hostname): + continue + correct_digest = False + if (any(d in target_digests for d in (d.container_image_digests or [])) + or d.daemon_type in MONITORING_STACK_TYPES): + logger.debug('daemon %s.%s container digest correct' % ( + d.daemon_type, d.daemon_id)) + correct_digest = True + if any(d in target_digests for d in (d.deployed_by or [])): + logger.debug('daemon %s.%s deployed by correct version' % ( + d.daemon_type, d.daemon_id)) + done += 1 + continue + + if self.mgr.daemon_is_self(d.daemon_type, d.daemon_id): + logger.info('Upgrade: Need to upgrade myself (mgr.%s)' % + self.mgr.get_mgr_id()) + need_upgrade_self = True + continue + + if correct_digest: + logger.debug('daemon %s.%s not deployed by correct version' % ( + d.daemon_type, d.daemon_id)) + need_upgrade_deployer.append((d, True)) + else: + logger.debug('daemon %s.%s not correct (%s, %s, %s)' % ( + d.daemon_type, d.daemon_id, + d.container_image_name, d.container_image_digests, d.version)) + need_upgrade.append((d, False)) + + return (need_upgrade_self, need_upgrade, need_upgrade_deployer, done) + + def _to_upgrade(self, need_upgrade: List[Tuple[DaemonDescription, bool]], target_image: str) -> Tuple[bool, List[Tuple[DaemonDescription, bool]]]: + to_upgrade: List[Tuple[DaemonDescription, bool]] = [] + known_ok_to_stop: List[str] = [] + for d_entry in need_upgrade: + d = d_entry[0] + assert d.daemon_type is not None + assert d.daemon_id is not None + assert d.hostname is not None + + if not d.container_image_id: + if d.container_image_name == target_image: + logger.debug( + 'daemon %s has unknown container_image_id but has correct image name' % (d.name())) + continue + + if known_ok_to_stop: + if d.name() in known_ok_to_stop: + logger.info(f'Upgrade: {d.name()} is also safe to restart') + to_upgrade.append(d_entry) + continue + + if d.daemon_type == 'osd': + # NOTE: known_ok_to_stop is an output argument for + # _wait_for_ok_to_stop + if not self._wait_for_ok_to_stop(d, known_ok_to_stop): + return False, to_upgrade + + if d.daemon_type == 'mon' and self._enough_mons_for_ok_to_stop(): + if not self._wait_for_ok_to_stop(d, known_ok_to_stop): + return False, to_upgrade + + if d.daemon_type == 'mds' and self._enough_mds_for_ok_to_stop(d): + if not self._wait_for_ok_to_stop(d, known_ok_to_stop): + return False, to_upgrade + + to_upgrade.append(d_entry) + + # if we don't have a list of others to consider, stop now + if d.daemon_type in ['osd', 'mds', 'mon'] and not known_ok_to_stop: + break + return True, to_upgrade + + def _upgrade_daemons(self, to_upgrade: List[Tuple[DaemonDescription, bool]], target_image: str, target_digests: Optional[List[str]] = None) -> None: + assert self.upgrade_state is not None + num = 1 + if target_digests is None: + target_digests = [] + for d_entry in to_upgrade: + if self.upgrade_state.remaining_count is not None and self.upgrade_state.remaining_count <= 0 and not d_entry[1]: + self.mgr.log.info( + f'Hit upgrade limit of {self.upgrade_state.total_count}. Stopping upgrade') + return + d = d_entry[0] + assert d.daemon_type is not None + assert d.daemon_id is not None + assert d.hostname is not None + + # make sure host has latest container image + out, errs, code = self.mgr.wait_async(CephadmServe(self.mgr)._run_cephadm( + d.hostname, '', 'inspect-image', [], + image=target_image, no_fsid=True, error_ok=True)) + if code or not any(d in target_digests for d in json.loads(''.join(out)).get('repo_digests', [])): + logger.info('Upgrade: Pulling %s on %s' % (target_image, + d.hostname)) + self.upgrade_info_str = 'Pulling %s image on host %s' % ( + target_image, d.hostname) + out, errs, code = self.mgr.wait_async(CephadmServe(self.mgr)._run_cephadm( + d.hostname, '', 'pull', [], + image=target_image, no_fsid=True, error_ok=True)) + if code: + self._fail_upgrade('UPGRADE_FAILED_PULL', { + 'severity': 'warning', + 'summary': 'Upgrade: failed to pull target image', + 'count': 1, + 'detail': [ + 'failed to pull %s on host %s' % (target_image, + d.hostname)], + }) + return + r = json.loads(''.join(out)) + if not any(d in target_digests for d in r.get('repo_digests', [])): + logger.info('Upgrade: image %s pull on %s got new digests %s (not %s), restarting' % ( + target_image, d.hostname, r['repo_digests'], target_digests)) + self.upgrade_info_str = 'Image %s pull on %s got new digests %s (not %s), restarting' % ( + target_image, d.hostname, r['repo_digests'], target_digests) + self.upgrade_state.target_digests = r['repo_digests'] + self._save_upgrade_state() + return + + self.upgrade_info_str = 'Currently upgrading %s daemons' % (d.daemon_type) + + if len(to_upgrade) > 1: + logger.info('Upgrade: Updating %s.%s (%d/%d)' % (d.daemon_type, d.daemon_id, num, min(len(to_upgrade), + self.upgrade_state.remaining_count if self.upgrade_state.remaining_count is not None else 9999999))) + else: + logger.info('Upgrade: Updating %s.%s' % + (d.daemon_type, d.daemon_id)) + action = 'Upgrading' if not d_entry[1] else 'Redeploying' + try: + daemon_spec = CephadmDaemonDeploySpec.from_daemon_description(d) + self.mgr._daemon_action( + daemon_spec, + 'redeploy', + image=target_image if not d_entry[1] else None + ) + self.mgr.cache.metadata_up_to_date[d.hostname] = False + except Exception as e: + self._fail_upgrade('UPGRADE_REDEPLOY_DAEMON', { + 'severity': 'warning', + 'summary': f'{action} daemon {d.name()} on host {d.hostname} failed.', + 'count': 1, + 'detail': [ + f'Upgrade daemon: {d.name()}: {e}' + ], + }) + return + num += 1 + if self.upgrade_state.remaining_count is not None and not d_entry[1]: + self.upgrade_state.remaining_count -= 1 + self._save_upgrade_state() + + def _handle_need_upgrade_self(self, need_upgrade_self: bool, upgrading_mgrs: bool) -> None: + if need_upgrade_self: + try: + self.mgr.mgr_service.fail_over() + except OrchestratorError as e: + self._fail_upgrade('UPGRADE_NO_STANDBY_MGR', { + 'severity': 'warning', + 'summary': f'Upgrade: {e}', + 'count': 1, + 'detail': [ + 'The upgrade process needs to upgrade the mgr, ' + 'but it needs at least one standby to proceed.', + ], + }) + return + + return # unreachable code, as fail_over never returns + elif upgrading_mgrs: + if 'UPGRADE_NO_STANDBY_MGR' in self.mgr.health_checks: + del self.mgr.health_checks['UPGRADE_NO_STANDBY_MGR'] + self.mgr.set_health_checks(self.mgr.health_checks) + + def _set_container_images(self, daemon_type: str, target_image: str, image_settings: Dict[str, str]) -> None: + # push down configs + daemon_type_section = name_to_config_section(daemon_type) + if image_settings.get(daemon_type_section) != target_image: + logger.info('Upgrade: Setting container_image for all %s' % + daemon_type) + self.mgr.set_container_image(daemon_type_section, target_image) + to_clean = [] + for section in image_settings.keys(): + if section.startswith(name_to_config_section(daemon_type) + '.'): + to_clean.append(section) + if to_clean: + logger.debug('Upgrade: Cleaning up container_image for %s' % + to_clean) + for section in to_clean: + ret, image, err = self.mgr.check_mon_command({ + 'prefix': 'config rm', + 'name': 'container_image', + 'who': section, + }) + + def _complete_osd_upgrade(self, target_major: str, target_major_name: str) -> None: + osdmap = self.mgr.get("osd_map") + osd_min_name = osdmap.get("require_osd_release", "argonaut") + osd_min = ceph_release_to_major(osd_min_name) + if osd_min < int(target_major): + logger.info( + f'Upgrade: Setting require_osd_release to {target_major} {target_major_name}') + ret, _, err = self.mgr.check_mon_command({ + 'prefix': 'osd require-osd-release', + 'release': target_major_name, + }) + + def _complete_mds_upgrade(self) -> None: + assert self.upgrade_state is not None + if self.upgrade_state.fs_original_max_mds: + for fs in self.mgr.get("fs_map")['filesystems']: + fscid = fs["id"] + fs_name = fs['mdsmap']['fs_name'] + new_max = self.upgrade_state.fs_original_max_mds.get(fscid, 1) + if new_max > 1: + self.mgr.log.info('Upgrade: Scaling up filesystem %s max_mds to %d' % ( + fs_name, new_max + )) + ret, _, err = self.mgr.check_mon_command({ + 'prefix': 'fs set', + 'fs_name': fs_name, + 'var': 'max_mds', + 'val': str(new_max), + }) + + self.upgrade_state.fs_original_max_mds = {} + self._save_upgrade_state() + if self.upgrade_state.fs_original_allow_standby_replay: + for fs in self.mgr.get("fs_map")['filesystems']: + fscid = fs["id"] + fs_name = fs['mdsmap']['fs_name'] + asr = self.upgrade_state.fs_original_allow_standby_replay.get(fscid, False) + if asr: + self.mgr.log.info('Upgrade: Enabling allow_standby_replay on filesystem %s' % ( + fs_name + )) + ret, _, err = self.mgr.check_mon_command({ + 'prefix': 'fs set', + 'fs_name': fs_name, + 'var': 'allow_standby_replay', + 'val': '1' + }) + + self.upgrade_state.fs_original_allow_standby_replay = {} + self._save_upgrade_state() + + def _mark_upgrade_complete(self) -> None: + if not self.upgrade_state: + logger.debug('_mark_upgrade_complete upgrade already marked complete, exiting') + return + logger.info('Upgrade: Complete!') + if self.upgrade_state.progress_id: + self.mgr.remote('progress', 'complete', + self.upgrade_state.progress_id) + self.upgrade_state = None + self._save_upgrade_state() + def _do_upgrade(self): # type: () -> None if not self.upgrade_state: @@ -538,7 +979,7 @@ class CephadmUpgrade: if not target_id or not target_version or not target_digests: # need to learn the container hash logger.info('Upgrade: First pull of %s' % target_image) - self.upgrade_info_str: str = 'Doing first pull of %s image' % (target_image) + self.upgrade_info_str = 'Doing first pull of %s image' % (target_image) try: target_id, target_version, target_digests = self.mgr.wait_async(CephadmServe(self.mgr)._get_container_image_info( target_image)) @@ -605,49 +1046,60 @@ class CephadmUpgrade: 'who': 'mon', }) - daemons = [d for d in self.mgr.cache.get_daemons() if d.daemon_type in CEPH_UPGRADE_ORDER] - done = 0 + if self.upgrade_state.daemon_types is not None: + logger.debug( + f'Filtering daemons to upgrade by daemon types: {self.upgrade_state.daemon_types}') + daemons = [d for d in self.mgr.cache.get_daemons( + ) if d.daemon_type in self.upgrade_state.daemon_types] + elif self.upgrade_state.services is not None: + logger.debug( + f'Filtering daemons to upgrade by services: {self.upgrade_state.daemon_types}') + daemons = [] + for service in self.upgrade_state.services: + daemons += self.mgr.cache.get_daemons_by_service(service) + else: + daemons = [d for d in self.mgr.cache.get_daemons( + ) if d.daemon_type in CEPH_UPGRADE_ORDER] + if self.upgrade_state.hosts is not None: + logger.debug(f'Filtering daemons to upgrade by hosts: {self.upgrade_state.hosts}') + daemons = [d for d in daemons if d.hostname in self.upgrade_state.hosts] + upgraded_daemon_count: int = 0 for daemon_type in CEPH_UPGRADE_ORDER: - logger.debug('Upgrade: Checking %s daemons' % daemon_type) - - need_upgrade_self = False - need_upgrade: List[Tuple[DaemonDescription, bool]] = [] - need_upgrade_deployer: List[Tuple[DaemonDescription, bool]] = [] - for d in daemons: - if d.daemon_type != daemon_type: - continue - assert d.daemon_type is not None - assert d.daemon_id is not None - assert d.hostname is not None - if self.mgr.use_agent and not self.mgr.cache.host_metadata_up_to_date(d.hostname): - continue - correct_digest = False - if (any(d in target_digests for d in (d.container_image_digests or [])) - or d.daemon_type in MONITORING_STACK_TYPES): - logger.debug('daemon %s.%s container digest correct' % ( - daemon_type, d.daemon_id)) - correct_digest = True - if any(d in target_digests for d in (d.deployed_by or [])): - logger.debug('daemon %s.%s deployed by correct version' % ( - d.daemon_type, d.daemon_id)) - done += 1 + if self.upgrade_state.remaining_count is not None and self.upgrade_state.remaining_count <= 0: + # we hit our limit and should end the upgrade + # except for cases where we only need to redeploy, but not actually upgrade + # the image (which we don't count towards our limit). This case only occurs with mgr + # and monitoring stack daemons. Additionally, this case is only valid if + # the active mgr is already upgraded. + if any(d in target_digests for d in self.mgr.get_active_mgr_digests()): + if daemon_type not in MONITORING_STACK_TYPES and daemon_type != 'mgr': continue - - if self.mgr.daemon_is_self(d.daemon_type, d.daemon_id): - logger.info('Upgrade: Need to upgrade myself (mgr.%s)' % - self.mgr.get_mgr_id()) - need_upgrade_self = True - continue - - if correct_digest: - logger.debug('daemon %s.%s not deployed by correct version' % ( - d.daemon_type, d.daemon_id)) - need_upgrade_deployer.append((d, True)) else: - logger.debug('daemon %s.%s not correct (%s, %s, %s)' % ( - daemon_type, d.daemon_id, - d.container_image_name, d.container_image_digests, d.version)) - need_upgrade.append((d, False)) + self._mark_upgrade_complete() + return + logger.debug('Upgrade: Checking %s daemons' % daemon_type) + daemons_of_type = [d for d in daemons if d.daemon_type == daemon_type] + + need_upgrade_self, need_upgrade, need_upgrade_deployer, done = self._detect_need_upgrade( + daemons_of_type, target_digests) + upgraded_daemon_count += done + self._update_upgrade_progress(upgraded_daemon_count / len(daemons)) + + # make sure mgr and monitoring stack daemons are properly redeployed in staggered upgrade scenarios + if daemon_type == 'mgr' or daemon_type in MONITORING_STACK_TYPES: + if any(d in target_digests for d in self.mgr.get_active_mgr_digests()): + need_upgrade_names = [d[0].name() for d in need_upgrade] + \ + [d[0].name() for d in need_upgrade_deployer] + dds = [d for d in self.mgr.cache.get_daemons_by_type( + daemon_type) if d.name() not in need_upgrade_names] + need_upgrade_active, n1, n2, __ = self._detect_need_upgrade(dds, target_digests) + if not n1: + if not need_upgrade_self and need_upgrade_active: + need_upgrade_self = True + need_upgrade_deployer += n2 + else: + # no point in trying to redeploy with new version if active mgr is not on the new version + need_upgrade_deployer = [] if not need_upgrade_self: # only after the mgr itself is upgraded can we expect daemons to have @@ -665,144 +1117,30 @@ class CephadmUpgrade: if need_upgrade: self.upgrade_info_str = 'Currently upgrading %s daemons' % (daemon_type) - to_upgrade: List[Tuple[DaemonDescription, bool]] = [] - known_ok_to_stop: List[str] = [] - for d_entry in need_upgrade: - d = d_entry[0] - assert d.daemon_type is not None - assert d.daemon_id is not None - assert d.hostname is not None - - if not d.container_image_id: - if d.container_image_name == target_image: - logger.debug( - 'daemon %s has unknown container_image_id but has correct image name' % (d.name())) - continue - - if known_ok_to_stop: - if d.name() in known_ok_to_stop: - logger.info(f'Upgrade: {d.name()} is also safe to restart') - to_upgrade.append(d_entry) - continue - - if d.daemon_type == 'osd': - # NOTE: known_ok_to_stop is an output argument for - # _wait_for_ok_to_stop - if not self._wait_for_ok_to_stop(d, known_ok_to_stop): - return - - if d.daemon_type == 'mon' and self._enough_mons_for_ok_to_stop(): - if not self._wait_for_ok_to_stop(d, known_ok_to_stop): - return - - if d.daemon_type == 'mds' and self._enough_mds_for_ok_to_stop(d): - if not self._wait_for_ok_to_stop(d, known_ok_to_stop): - return - - to_upgrade.append(d_entry) - - # if we don't have a list of others to consider, stop now - if d.daemon_type in ['osd', 'mds', 'mon'] and not known_ok_to_stop: - break - - num = 1 - for d_entry in to_upgrade: - d = d_entry[0] - assert d.daemon_type is not None - assert d.daemon_id is not None - assert d.hostname is not None - - self._update_upgrade_progress(done / len(daemons)) - - # make sure host has latest container image - out, errs, code = self.mgr.wait_async(CephadmServe(self.mgr)._run_cephadm( - d.hostname, '', 'inspect-image', [], - image=target_image, no_fsid=True, error_ok=True)) - if code or not any(d in target_digests for d in json.loads(''.join(out)).get('repo_digests', [])): - logger.info('Upgrade: Pulling %s on %s' % (target_image, - d.hostname)) - self.upgrade_info_str = 'Pulling %s image on host %s' % ( - target_image, d.hostname) - out, errs, code = self.mgr.wait_async(CephadmServe(self.mgr)._run_cephadm( - d.hostname, '', 'pull', [], - image=target_image, no_fsid=True, error_ok=True)) - if code: - self._fail_upgrade('UPGRADE_FAILED_PULL', { - 'severity': 'warning', - 'summary': 'Upgrade: failed to pull target image', - 'count': 1, - 'detail': [ - 'failed to pull %s on host %s' % (target_image, - d.hostname)], - }) - return - r = json.loads(''.join(out)) - if not any(d in target_digests for d in r.get('repo_digests', [])): - logger.info('Upgrade: image %s pull on %s got new digests %s (not %s), restarting' % ( - target_image, d.hostname, r['repo_digests'], target_digests)) - self.upgrade_info_str = 'Image %s pull on %s got new digests %s (not %s), restarting' % ( - target_image, d.hostname, r['repo_digests'], target_digests) - self.upgrade_state.target_digests = r['repo_digests'] - self._save_upgrade_state() - return - - self.upgrade_info_str = 'Currently upgrading %s daemons' % (daemon_type) - - if len(to_upgrade) > 1: - logger.info('Upgrade: Updating %s.%s (%d/%d)' % - (d.daemon_type, d.daemon_id, num, len(to_upgrade))) - else: - logger.info('Upgrade: Updating %s.%s' % - (d.daemon_type, d.daemon_id)) - action = 'Upgrading' if not d_entry[1] else 'Redeploying' - try: - daemon_spec = CephadmDaemonDeploySpec.from_daemon_description(d) - self.mgr._daemon_action( - daemon_spec, - 'redeploy', - image=target_image if not d_entry[1] else None - ) - self.mgr.cache.metadata_up_to_date[d.hostname] = False - except Exception as e: - self._fail_upgrade('UPGRADE_REDEPLOY_DAEMON', { - 'severity': 'warning', - 'summary': f'{action} daemon {d.name()} on host {d.hostname} failed.', - 'count': 1, - 'detail': [ - f'Upgrade daemon: {d.name()}: {e}' - ], - }) - return - num += 1 + _continue, to_upgrade = self._to_upgrade(need_upgrade, target_image) + if not _continue: + return + self._upgrade_daemons(to_upgrade, target_image, target_digests) if to_upgrade: return + self._handle_need_upgrade_self(need_upgrade_self, daemon_type == 'mgr') + + # following bits of _do_upgrade are for completing upgrade for given + # types. If we haven't actually finished upgrading all the daemons + # of this type, we should exit the loop here + _, n1, n2, _ = self._detect_need_upgrade( + self.mgr.cache.get_daemons_by_type(daemon_type), target_digests) + if n1 or n2: + continue + # complete mon upgrade? if daemon_type == 'mon': if not self.mgr.get("have_local_config_map"): logger.info('Upgrade: Restarting mgr now that mons are running pacific') need_upgrade_self = True - if need_upgrade_self: - try: - self.mgr.mgr_service.fail_over() - except OrchestratorError as e: - self._fail_upgrade('UPGRADE_NO_STANDBY_MGR', { - 'severity': 'warning', - 'summary': f'Upgrade: {e}', - 'count': 1, - 'detail': [ - 'The upgrade process needs to upgrade the mgr, ' - 'but it needs at least one standby to proceed.', - ], - }) - return - - return # unreachable code, as fail_over never returns - elif daemon_type == 'mgr': - if 'UPGRADE_NO_STANDBY_MGR' in self.mgr.health_checks: - del self.mgr.health_checks['UPGRADE_NO_STANDBY_MGR'] - self.mgr.set_health_checks(self.mgr.health_checks) + self._handle_need_upgrade_self(need_upgrade_self, daemon_type == 'mgr') # make sure 'ceph versions' agrees ret, out_ver, err = self.mgr.check_mon_command({ @@ -816,84 +1154,22 @@ class CephadmUpgrade: 'Upgrade: %d %s daemon(s) are %s != target %s' % (count, daemon_type, short_version, target_version)) - # push down configs - daemon_type_section = name_to_config_section(daemon_type) - if image_settings.get(daemon_type_section) != target_image: - logger.info('Upgrade: Setting container_image for all %s' % - daemon_type) - self.mgr.set_container_image(daemon_type_section, target_image) - to_clean = [] - for section in image_settings.keys(): - if section.startswith(name_to_config_section(daemon_type) + '.'): - to_clean.append(section) - if to_clean: - logger.debug('Upgrade: Cleaning up container_image for %s' % - to_clean) - for section in to_clean: - ret, image, err = self.mgr.check_mon_command({ - 'prefix': 'config rm', - 'name': 'container_image', - 'who': section, - }) + self._set_container_images(daemon_type, target_image, image_settings) # complete osd upgrade? if daemon_type == 'osd': - osdmap = self.mgr.get("osd_map") - osd_min_name = osdmap.get("require_osd_release", "argonaut") - osd_min = ceph_release_to_major(osd_min_name) - if osd_min < int(target_major): - logger.info( - f'Upgrade: Setting require_osd_release to {target_major} {target_major_name}') - ret, _, err = self.mgr.check_mon_command({ - 'prefix': 'osd require-osd-release', - 'release': target_major_name, - }) + self._complete_osd_upgrade(target_major, target_major_name) # complete mds upgrade? if daemon_type == 'mds': - if self.upgrade_state.fs_original_max_mds: - for fs in self.mgr.get("fs_map")['filesystems']: - fscid = fs["id"] - fs_name = fs['mdsmap']['fs_name'] - new_max = self.upgrade_state.fs_original_max_mds.get(fscid, 1) - if new_max > 1: - self.mgr.log.info('Upgrade: Scaling up filesystem %s max_mds to %d' % ( - fs_name, new_max - )) - ret, _, err = self.mgr.check_mon_command({ - 'prefix': 'fs set', - 'fs_name': fs_name, - 'var': 'max_mds', - 'val': str(new_max), - }) - - self.upgrade_state.fs_original_max_mds = {} - self._save_upgrade_state() - if self.upgrade_state.fs_original_allow_standby_replay: - for fs in self.mgr.get("fs_map")['filesystems']: - fscid = fs["id"] - fs_name = fs['mdsmap']['fs_name'] - asr = self.upgrade_state.fs_original_allow_standby_replay.get(fscid, False) - if asr: - self.mgr.log.info('Upgrade: Enabling allow_standby_replay on filesystem %s' % ( - fs_name - )) - ret, _, err = self.mgr.check_mon_command({ - 'prefix': 'fs set', - 'fs_name': fs_name, - 'var': 'allow_standby_replay', - 'val': '1' - }) - - self.upgrade_state.fs_original_allow_standby_replay = {} - self._save_upgrade_state() + self._complete_mds_upgrade() # Make sure all metadata is up to date before saying we are done upgrading this daemon type if self.mgr.use_agent and not self.mgr.cache.all_host_metadata_up_to_date(): self.mgr.agent_helpers._request_ack_all_not_up_to_date() return - logger.debug('Upgrade: All %s daemons are up to date.' % daemon_type) + logger.debug('Upgrade: Upgraded %s daemon(s).' % daemon_type) # clean up logger.info('Upgrade: Finalizing container_image settings') @@ -912,10 +1188,5 @@ class CephadmUpgrade: 'who': 'mon', }) - logger.info('Upgrade: Complete!') - if self.upgrade_state.progress_id: - self.mgr.remote('progress', 'complete', - self.upgrade_state.progress_id) - self.upgrade_state = None - self._save_upgrade_state() + self._mark_upgrade_complete() return diff --git a/ceph/src/pybind/mgr/cephadm/utils.py b/ceph/src/pybind/mgr/cephadm/utils.py index 99e1683ed..28811fc3a 100644 --- a/ceph/src/pybind/mgr/cephadm/utils.py +++ b/ceph/src/pybind/mgr/cephadm/utils.py @@ -23,7 +23,9 @@ class CephadmNoImage(Enum): # NOTE: order important here as these are used for upgrade order CEPH_TYPES = ['mgr', 'mon', 'crash', 'osd', 'mds', 'rgw', 'rbd-mirror', 'cephfs-mirror'] GATEWAY_TYPES = ['iscsi', 'nfs'] -MONITORING_STACK_TYPES = ['node-exporter', 'prometheus', 'alertmanager', 'grafana'] +MONITORING_STACK_TYPES = ['node-exporter', 'prometheus', + 'alertmanager', 'grafana', 'loki', 'promtail'] +RESCHEDULE_FROM_OFFLINE_HOSTS_TYPES = ['nfs'] CEPH_UPGRADE_ORDER = CEPH_TYPES + GATEWAY_TYPES + MONITORING_STACK_TYPES diff --git a/ceph/src/pybind/mgr/dashboard/controllers/grafana.py b/ceph/src/pybind/mgr/dashboard/controllers/grafana.py index d5c9b19f8..79a680671 100644 --- a/ceph/src/pybind/mgr/dashboard/controllers/grafana.py +++ b/ceph/src/pybind/mgr/dashboard/controllers/grafana.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- from .. import mgr -from ..exceptions import DashboardException from ..grafana import GrafanaRestClient, push_local_dashboards from ..security import Scope +from ..services.exception import handle_error from ..settings import Settings from . import APIDoc, APIRouter, BaseController, Endpoint, EndpointDoc, \ ReadPermission, UpdatePermission @@ -31,6 +31,7 @@ class Grafana(BaseController): @Endpoint() @ReadPermission + @handle_error('grafana') def validation(self, params): grafana = GrafanaRestClient() method = 'GET' @@ -41,14 +42,8 @@ class Grafana(BaseController): @Endpoint(method='POST') @UpdatePermission + @handle_error('grafana', 500) def dashboards(self): response = dict() - try: - response['success'] = push_local_dashboards() - except Exception as e: # pylint: disable=broad-except - raise DashboardException( - msg=str(e), - component='grafana', - http_status_code=500, - ) + response['success'] = push_local_dashboards() return response diff --git a/ceph/src/pybind/mgr/dashboard/controllers/home.py b/ceph/src/pybind/mgr/dashboard/controllers/home.py index 32b007134..b79b53ca8 100644 --- a/ceph/src/pybind/mgr/dashboard/controllers/home.py +++ b/ceph/src/pybind/mgr/dashboard/controllers/home.py @@ -14,6 +14,7 @@ import cherrypy from cherrypy.lib.static import serve_file from .. import mgr +from ..services.custom_banner import get_login_banner_mgr from . import BaseController, Endpoint, Proxy, Router, UIRouter logger = logging.getLogger("controllers.home") @@ -139,3 +140,10 @@ class LangsController(BaseController, LanguageMixin): @Endpoint('GET') def __call__(self): return list(self.LANGUAGES) + + +@UIRouter("/login", secure=False) +class LoginController(BaseController): + @Endpoint('GET', 'custom_banner') + def __call__(self): + return get_login_banner_mgr() diff --git a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/block/mirroring.po.ts b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/block/mirroring.po.ts index 8450763d3..e24a3ebc0 100644 --- a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/block/mirroring.po.ts +++ b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/block/mirroring.po.ts @@ -13,7 +13,7 @@ export class MirroringPageHelper extends PageHelper { */ @PageHelper.restrictTo(pages.index.url) editMirror(name: string, option: string) { - // Clicks the pool in the table + // Select the pool in the table this.getFirstTableCell(name).click(); // Clicks the Edit Mode button @@ -28,5 +28,8 @@ export class MirroringPageHelper extends PageHelper { cy.contains('.modal-dialog', 'Edit pool mirror mode').should('not.exist'); const val = option.toLowerCase(); // used since entries in table are lower case this.getFirstTableCell(val).should('be.visible'); + + // unselect the pool in the table + this.getFirstTableCell(name).click(); } } diff --git a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.e2e-spec.ts b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.e2e-spec.ts index c1935a783..e4f9936c3 100644 --- a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.e2e-spec.ts +++ b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.e2e-spec.ts @@ -31,9 +31,5 @@ describe('Hosts page', () => { it('should check at least one host is present', () => { hosts.check_for_host(); }); - - it('should check services link(s) work for first host', () => { - hosts.check_services_links(); - }); }); }); diff --git a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.po.ts b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.po.ts index ffac83ba6..b741749f7 100644 --- a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.po.ts +++ b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.po.ts @@ -19,28 +19,6 @@ export class HostsPageHelper extends PageHelper { this.getTableCount('total').should('not.be.eq', 0); } - // function that checks all services links work for first - // host in table - check_services_links() { - // check that text (links) is present in services box - let links_tested = 0; - - cy.get('cd-hosts a.service-link') - .should('have.length.greaterThan', 0) - .then(($elems) => { - $elems.each((_i, $el) => { - // click link, check it worked by looking for changed breadcrumb, - // navigate back to hosts page, repeat until all links checked - cy.contains('a', $el.innerText).should('exist').click(); - this.expectBreadcrumbText('Performance Counters'); - this.navigateTo(); - links_tested++; - }); - // check if any links were actually tested - expect(links_tested).gt(0); - }); - } - add(hostname: string, exist?: boolean, maintenance?: boolean, labels: string[] = []) { cy.get(`${this.pages.add.id}`).within(() => { cy.get('#hostname').type(hostname); @@ -127,8 +105,8 @@ export class HostsPageHelper extends PageHelper { @PageHelper.restrictTo(pages.index.url) maintenance(hostname: string, exit = false, force = false) { this.clearTableSearchInput(); + this.getTableCell(this.columnIndex.hostname, hostname).click(); if (force) { - this.getTableCell(this.columnIndex.hostname, hostname).click(); this.clickActionButton('enter-maintenance'); cy.get('cd-modal').within(() => { @@ -145,7 +123,6 @@ export class HostsPageHelper extends PageHelper { } if (exit) { this.getTableCell(this.columnIndex.hostname, hostname) - .click() .parent() .find(`datatable-body-cell:nth-child(${this.columnIndex.status})`) .then(($ele) => { @@ -163,7 +140,6 @@ export class HostsPageHelper extends PageHelper { expect(status).to.not.include('maintenance'); }); } else { - this.getTableCell(this.columnIndex.hostname, hostname).click(); this.clickActionButton('enter-maintenance'); this.getTableCell(this.columnIndex.hostname, hostname) @@ -187,5 +163,9 @@ export class HostsPageHelper extends PageHelper { cy.wait(20000); this.expectTableCount('total', 0); }); + + // unselect it to avoid colliding with any other selection + // in different steps + this.getTableCell(this.columnIndex.hostname, hostname).click(); } } diff --git a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/services.po.ts b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/services.po.ts index cbc900813..a7959c7b3 100644 --- a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/services.po.ts +++ b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/services.po.ts @@ -17,9 +17,8 @@ export class ServicesPageHelper extends PageHelper { }; serviceDetailColumnIndex = { - hostname: 1, - daemonType: 2, - status: 8 + daemonName: 2, + status: 4 }; check_for_service() { @@ -114,14 +113,28 @@ export class ServicesPageHelper extends PageHelper { } checkServiceStatus(daemon: string, expectedStatus = 'running') { - cy.get('cd-service-daemon-list').within(() => { - this.getTableCell(this.serviceDetailColumnIndex.daemonType, daemon) - .parent() - .find(`datatable-body-cell:nth-child(${this.serviceDetailColumnIndex.status}) .badge`) - .should(($ele) => { - const status = $ele.toArray().map((v) => v.innerText); - expect(status).to.include(expectedStatus); - }); + let daemonNameIndex = this.serviceDetailColumnIndex.daemonName; + let statusIndex = this.serviceDetailColumnIndex.status; + + // since hostname row is hidden from the hosts details table, + // we'll need to manually override the indexes when this check is being + // done for the daemons in host details page. So we'll get the url and + // verify if the current page is not the services index page + cy.url().then((url) => { + if (!url.includes(pages.index.url)) { + daemonNameIndex = 1; + statusIndex = 3; + } + + cy.get('cd-service-daemon-list').within(() => { + this.getTableCell(daemonNameIndex, daemon, true) + .parent() + .find(`datatable-body-cell:nth-child(${statusIndex}) .badge`) + .should(($ele) => { + const status = $ele.toArray().map((v) => v.innerText); + expect(status).to.include(expectedStatus); + }); + }); }); } @@ -166,6 +179,10 @@ export class ServicesPageHelper extends PageHelper { cy.get('cd-service-daemon-list').within(() => { this.getTableRow(daemon).click(); this.clickActionButton(action); + + // unselect it to avoid colliding with any other selection + // in different steps + this.getTableRow(daemon).click(); }); } } diff --git a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/workflow/09-services.e2e-spec.ts b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/workflow/09-services.e2e-spec.ts index c266c81b7..4349b1ecf 100644 --- a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/workflow/09-services.e2e-spec.ts +++ b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/workflow/09-services.e2e-spec.ts @@ -2,6 +2,7 @@ import { ServicesPageHelper } from 'cypress/integration/cluster/services.po'; describe('Services page', () => { const services = new ServicesPageHelper(); + const mdsDaemonName = 'mds.test'; beforeEach(() => { cy.login(); Cypress.Cookies.preserveOnce('token'); @@ -15,51 +16,51 @@ describe('Services page', () => { it('should create an mds service', () => { services.navigateTo('create'); services.addService('mds', false); - services.checkExist('mds.test', true); + services.checkExist(mdsDaemonName, true); - services.clickServiceTab('mds.test', 'Details'); + services.clickServiceTab(mdsDaemonName, 'Details'); cy.get('cd-service-details').within(() => { - services.checkServiceStatus('mds'); + services.checkServiceStatus(mdsDaemonName); }); }); it('should stop a daemon', () => { - services.clickServiceTab('mds.test', 'Details'); - services.checkServiceStatus('mds'); + services.clickServiceTab(mdsDaemonName, 'Details'); + services.checkServiceStatus(mdsDaemonName); services.daemonAction('mds', 'stop'); - services.checkServiceStatus('mds', 'stopped'); + services.checkServiceStatus(mdsDaemonName, 'stopped'); }); it('should restart a daemon', () => { - services.checkExist('mds.test', true); - services.clickServiceTab('mds.test', 'Details'); + services.checkExist(mdsDaemonName, true); + services.clickServiceTab(mdsDaemonName, 'Details'); services.daemonAction('mds', 'restart'); - services.checkServiceStatus('mds', 'running'); + services.checkServiceStatus(mdsDaemonName, 'running'); }); it('should redeploy a daemon', () => { - services.checkExist('mds.test', true); - services.clickServiceTab('mds.test', 'Details'); + services.checkExist(mdsDaemonName, true); + services.clickServiceTab(mdsDaemonName, 'Details'); services.daemonAction('mds', 'stop'); - services.checkServiceStatus('mds', 'stopped'); + services.checkServiceStatus(mdsDaemonName, 'stopped'); services.daemonAction('mds', 'redeploy'); - services.checkServiceStatus('mds', 'running'); + services.checkServiceStatus(mdsDaemonName, 'running'); }); it('should start a daemon', () => { - services.checkExist('mds.test', true); - services.clickServiceTab('mds.test', 'Details'); + services.checkExist(mdsDaemonName, true); + services.clickServiceTab(mdsDaemonName, 'Details'); services.daemonAction('mds', 'stop'); - services.checkServiceStatus('mds', 'stopped'); + services.checkServiceStatus(mdsDaemonName, 'stopped'); services.daemonAction('mds', 'start'); - services.checkServiceStatus('mds', 'running'); + services.checkServiceStatus(mdsDaemonName, 'running'); }); it('should delete an mds service', () => { - services.deleteService('mds.test'); + services.deleteService(mdsDaemonName); }); it('should create and delete snmp-gateway service with version V2c', () => { diff --git a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/page-helper.po.ts b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/page-helper.po.ts index 72d811065..4531a70bb 100644 --- a/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/page-helper.po.ts +++ b/ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/page-helper.po.ts @@ -196,10 +196,16 @@ export abstract class PageHelper { } } - getTableCell(columnIndex: number, exactContent: string) { + getTableCell(columnIndex: number, exactContent: string, partialMatch = false) { this.waitDataTableToLoad(); this.clearTableSearchInput(); this.searchTable(exactContent); + if (partialMatch) { + return cy.contains( + `datatable-body-row datatable-body-cell:nth-child(${columnIndex})`, + exactContent + ); + } return cy.contains( `datatable-body-row datatable-body-cell:nth-child(${columnIndex})`, new RegExp(`^${exactContent}$`) diff --git a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/281.919d718adfcdc2881381.js b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/281.919d718adfcdc2881381.js deleted file mode 100644 index dda0b36c1..000000000 --- a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/281.919d718adfcdc2881381.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkceph_dashboard=self.webpackChunkceph_dashboard||[]).push([[281],{59281:(si,Oe,r)=>{r.r(Oe),r.d(Oe,{RgwModule:()=>e_,RoutedRgwModule:()=>ii});var f=r(12057),a=r(24751),w=r(6283),M=r(38549),Ue=r(37496),A=r(79512),N_=r(44466),O_=r(66265),U_=r(23815),u=r.n(U_),Y=r(35758),Me=r(95152),We=r(33394),Ze=r(64762),$e=r(58497),me=r(25917),he=r(19773),W_=r(96736),Z_=r(5304),ge=r(20523),$_=r(93523),e=r(74788);let O=class{constructor(o,_){this.http=o,this.rgwDaemonService=_,this.url="api/rgw/user"}list(){return this.enumerate().pipe((0,he.zg)(o=>o.length>0?(0,Y.D)(o.map(_=>this.get(_))):(0,me.of)([])))}enumerate(){return this.rgwDaemonService.request(o=>this.http.get(this.url,{params:o}))}enumerateEmail(){return this.rgwDaemonService.request(o=>this.http.get(`${this.url}/get_emails`,{params:o}))}get(o){return this.rgwDaemonService.request(_=>this.http.get(`${this.url}/${o}`,{params:_}))}getQuota(o){return this.rgwDaemonService.request(_=>this.http.get(`${this.url}/${o}/quota`,{params:_}))}create(o){return this.rgwDaemonService.request(_=>(u().keys(o).forEach(n=>{_=_.append(n,o[n])}),this.http.post(this.url,null,{params:_})))}update(o,_){return this.rgwDaemonService.request(n=>(u().keys(_).forEach(i=>{n=n.append(i,_[i])}),this.http.put(`${this.url}/${o}`,null,{params:n})))}updateQuota(o,_){return this.rgwDaemonService.request(n=>(u().keys(_).forEach(i=>{n=n.append(i,_[i])}),this.http.put(`${this.url}/${o}/quota`,null,{params:n})))}delete(o){return this.rgwDaemonService.request(_=>this.http.delete(`${this.url}/${o}`,{params:_}))}createSubuser(o,_){return this.rgwDaemonService.request(n=>(u().keys(_).forEach(i=>{n=n.append(i,_[i])}),this.http.post(`${this.url}/${o}/subuser`,null,{params:n})))}deleteSubuser(o,_){return this.rgwDaemonService.request(n=>this.http.delete(`${this.url}/${o}/subuser/${_}`,{params:n}))}addCapability(o,_,n){return this.rgwDaemonService.request(i=>(i=(i=i.append("type",_)).append("perm",n),this.http.post(`${this.url}/${o}/capability`,null,{params:i})))}deleteCapability(o,_,n){return this.rgwDaemonService.request(i=>(i=(i=i.append("type",_)).append("perm",n),this.http.delete(`${this.url}/${o}/capability`,{params:i})))}addS3Key(o,_){return this.rgwDaemonService.request(n=>(n=n.append("key_type","s3"),u().keys(_).forEach(i=>{n=n.append(i,_[i])}),this.http.post(`${this.url}/${o}/key`,null,{params:n})))}deleteS3Key(o,_){return this.rgwDaemonService.request(n=>(n=(n=n.append("key_type","s3")).append("access_key",_),this.http.delete(`${this.url}/${o}/key`,{params:n})))}exists(o){return this.get(o).pipe((0,W_.h)(!0),(0,Z_.K)(_=>(u().isFunction(_.preventDefault)&&_.preventDefault(),(0,me.of)(!1))))}emailExists(o){return o=decodeURIComponent(o),this.enumerateEmail().pipe((0,he.zg)(_=>{const n=u().indexOf(_,o);return(0,me.of)(-1!==n)}))}};O.\u0275fac=function(o){return new(o||O)(e.LFG($e.eN),e.LFG(ge.b))},O.\u0275prov=e.Yz7({token:O,factory:O.\u0275fac,providedIn:"root"}),O=(0,Ze.gn)([$_.o,(0,Ze.w6)("design:paramtypes",[$e.eN,ge.b])],O);var D=r(65862),Ae=r(18001),Ie=r(93614),m=r(77205),ve=r(97161),k=(()=>{return(t=k||(k={})).ENABLED="Enabled",t.DISABLED="Disabled",k;var t})(),B=(()=>{return(t=B||(B={})).ENABLED="Enabled",t.SUSPENDED="Suspended",B;var t})(),J=r(62862),Fe=r(63622),V=r(41582),H=r(56310),q=r(87925),X=r(94276),j=r(82945),h_=r(18372),ee=r(30839),K=r(10545);function I_(t,o){1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",35),e.SDv(2,36),e.qZA(),e.TgZ(3,"div",12),e._UZ(4,"input",37),e.qZA(),e.qZA())}function v_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,39),e.qZA())}function F_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,40),e.qZA())}function L_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,41),e.qZA())}function D_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,42),e.qZA())}function x_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,43),e.qZA())}function y_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,44),e.qZA())}function q_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,45),e.qZA())}function w_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,46),e.qZA())}function k_(t,o){1&t&&(e.TgZ(0,"option",47),e.SDv(1,48),e.qZA()),2&t&&e.Q6J("ngValue",null)}function B_(t,o){1&t&&(e.TgZ(0,"option",47),e.SDv(1,49),e.qZA()),2&t&&e.Q6J("ngValue",null)}function H_(t,o){if(1&t&&(e.TgZ(0,"option",50),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.Oqu(_)}}function X_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,51),e.qZA())}function K_(t,o){1&t&&(e.TgZ(0,"option",47),e.SDv(1,53),e.qZA()),2&t&&e.Q6J("ngValue",null)}function z_(t,o){1&t&&(e.TgZ(0,"option",47),e.SDv(1,54),e.qZA()),2&t&&e.Q6J("ngValue",null)}function Q_(t,o){if(1&t&&(e.TgZ(0,"option",50),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_.name),e.xp6(1),e.Oqu(_.description)}}function Y_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,55),e.qZA())}function J_(t,o){if(1&t&&(e.TgZ(0,"select",52),e.YNc(1,K_,2,1,"option",18),e.YNc(2,z_,2,1,"option",18),e.YNc(3,Q_,2,2,"option",19),e.qZA(),e.YNc(4,Y_,2,0,"span",14)),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(1),e.Q6J("ngIf",null===n.placementTargets),e.xp6(1),e.Q6J("ngIf",null!==n.placementTargets),e.xp6(1),e.Q6J("ngForOf",n.placementTargets),e.xp6(1),e.Q6J("ngIf",n.bucketForm.showError("placement-target",_,"required"))}}function V_(t,o){1&t&&(e.ynx(0),e._UZ(1,"input",56),e.BQk())}function j_(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend",25),e.SDv(2,57),e.qZA(),e.TgZ(3,"div",9),e.TgZ(4,"div",27),e.TgZ(5,"div",28),e.TgZ(6,"input",58),e.NdJ("change",function(){return e.CHM(_),e.oxw(2).setMfaDeleteValidators()}),e.qZA(),e.TgZ(7,"label",59),e.SDv(8,60),e.qZA(),e.TgZ(9,"cd-helper"),e.TgZ(10,"span"),e.SDv(11,61),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}}function et(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,70),e.qZA())}function _t(t,o){if(1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",67),e.SDv(2,68),e.qZA(),e.TgZ(3,"div",12),e._UZ(4,"input",69),e.YNc(5,et,2,0,"span",14),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.bucketForm.showError("mfa-token-serial",_,"required"))}}function tt(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,74),e.qZA())}function nt(t,o){if(1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",71),e.SDv(2,72),e.qZA(),e.TgZ(3,"div",12),e._UZ(4,"input",73),e.YNc(5,tt,2,0,"span",14),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.bucketForm.showError("mfa-token-pin",_,"required"))}}function ot(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend",25),e.SDv(2,62),e.qZA(),e.TgZ(3,"div",9),e.TgZ(4,"div",27),e.TgZ(5,"div",28),e.TgZ(6,"input",63),e.NdJ("change",function(){return e.CHM(_),e.oxw(2).setMfaDeleteValidators()}),e.qZA(),e.TgZ(7,"label",64),e.SDv(8,65),e.qZA(),e.TgZ(9,"cd-helper"),e.TgZ(10,"span"),e.SDv(11,66),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(12,_t,6,1,"div",8),e.YNc(13,nt,6,1,"div",8),e.qZA()}if(2&t){const _=e.oxw(2);e.xp6(12),e.Q6J("ngIf",_.areMfaCredentialsRequired()),e.xp6(1),e.Q6J("ngIf",_.areMfaCredentialsRequired())}}function it(t,o){1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",75),e.SDv(2,76),e.qZA(),e.TgZ(3,"div",12),e.TgZ(4,"select",77),e.TgZ(5,"option",78),e.SDv(6,79),e.qZA(),e.TgZ(7,"option",80),e.SDv(8,81),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function st(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,86),e.qZA())}function at(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,87),e.qZA())}function rt(t,o){if(1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",82),e.ynx(2),e.SDv(3,83),e.BQk(),e.TgZ(4,"cd-helper"),e.SDv(5,84),e.qZA(),e.qZA(),e.TgZ(6,"div",12),e._UZ(7,"input",85),e.YNc(8,st,2,0,"span",14),e.YNc(9,at,2,0,"span",14),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(8),e.Q6J("ngIf",n.bucketForm.showError("lock_retention_period_days",_,"pattern")),e.xp6(1),e.Q6J("ngIf",n.bucketForm.showError("lock_retention_period_days",_,"lockDays"))}}const Le=function(t){return{required:t}};function lt(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.YNc(9,I_,5,0,"div",8),e.TgZ(10,"div",9),e.TgZ(11,"label",10),e.SDv(12,11),e.qZA(),e.TgZ(13,"div",12),e._UZ(14,"input",13),e.YNc(15,v_,2,0,"span",14),e.YNc(16,F_,2,0,"span",14),e.YNc(17,L_,2,0,"span",14),e.YNc(18,D_,2,0,"span",14),e.YNc(19,x_,2,0,"span",14),e.YNc(20,y_,2,0,"span",14),e.YNc(21,q_,2,0,"span",14),e.YNc(22,w_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(23,"div",9),e.TgZ(24,"label",15),e.SDv(25,16),e.qZA(),e.TgZ(26,"div",12),e.TgZ(27,"select",17),e.YNc(28,k_,2,1,"option",18),e.YNc(29,B_,2,1,"option",18),e.YNc(30,H_,2,2,"option",19),e.qZA(),e.YNc(31,X_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(32,"div",9),e.TgZ(33,"label",20),e.SDv(34,21),e.qZA(),e.TgZ(35,"div",12),e.YNc(36,J_,5,4,"ng-template",null,22,e.W1O),e.YNc(38,V_,2,0,"ng-container",23),e.qZA(),e.qZA(),e.YNc(39,j_,12,0,"fieldset",24),e.YNc(40,ot,14,2,"fieldset",24),e.TgZ(41,"fieldset"),e.TgZ(42,"legend",25),e.SDv(43,26),e.qZA(),e.TgZ(44,"div",9),e.TgZ(45,"div",27),e.TgZ(46,"div",28),e._UZ(47,"input",29),e.TgZ(48,"label",30),e.SDv(49,31),e.qZA(),e.TgZ(50,"cd-helper"),e.TgZ(51,"span"),e.SDv(52,32),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(53,it,9,0,"div",8),e.YNc(54,rt,10,2,"div",8),e.qZA(),e.qZA(),e.TgZ(55,"div",33),e.TgZ(56,"cd-form-button-panel",34),e.NdJ("submitActionEvent",function(){return e.CHM(_),e.oxw().submit()}),e.ALo(57,"titlecase"),e.ALo(58,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.MAs(2),n=e.MAs(37),i=e.oxw();e.xp6(1),e.Q6J("formGroup",i.bucketForm),e.xp6(6),e.pQV(e.lcZ(6,29,i.action))(e.lcZ(7,31,i.resource)),e.QtT(5),e.xp6(2),e.Q6J("ngIf",i.editing),e.xp6(2),e.Q6J("ngClass",e.VKq(37,Le,!i.editing)),e.xp6(3),e.Q6J("readonly",i.editing)("autofocus",!i.editing),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"required")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"bucketNameInvalid")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"bucketNameNotAllowed")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"containsUpperCase")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"lowerCaseOrNumber")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"ipAddress")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"onlyLowerCaseAndNumbers")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"shouldBeInRange")),e.xp6(5),e.Q6J("autofocus",i.editing),e.xp6(1),e.Q6J("ngIf",null===i.owners),e.xp6(1),e.Q6J("ngIf",null!==i.owners),e.xp6(1),e.Q6J("ngForOf",i.owners),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("owner",_,"required")),e.xp6(2),e.Q6J("ngClass",e.VKq(39,Le,!i.editing)),e.xp6(5),e.Q6J("ngIf",i.editing)("ngIfElse",n),e.xp6(1),e.Q6J("ngIf",i.editing),e.xp6(1),e.Q6J("ngIf",i.editing),e.xp6(13),e.Q6J("ngIf",i.bucketForm.getValue("lock_enabled")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.getValue("lock_enabled")),e.xp6(2),e.Q6J("form",i.bucketForm)("submitText",e.lcZ(57,33,i.action)+" "+e.lcZ(58,35,i.resource))}}let De=(()=>{class t extends Ie.E{constructor(_,n,i,s,c,d,E,g){super(),this.route=_,this.router=n,this.formBuilder=i,this.rgwBucketService=s,this.rgwSiteService=c,this.rgwUserService=d,this.notificationService=E,this.actionLabels=g,this.editing=!1,this.owners=null,this.placementTargets=[],this.isVersioningAlreadyEnabled=!1,this.isMfaDeleteAlreadyEnabled=!1,this.icons=D.P,this.editing=this.router.url.startsWith(`/rgw/bucket/${A.MQ.EDIT}`),this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.CREATE,this.resource="bucket",this.createForm()}get isVersioningEnabled(){return this.bucketForm.getValue("versioning")}get isMfaDeleteEnabled(){return this.bucketForm.getValue("mfa-delete")}createForm(){const _=this,n=m.h.custom("lockDays",()=>{if(!_.bucketForm||!u().get(_.bucketForm.getRawValue(),"lock_enabled"))return!1;const i=Number(_.bucketForm.getValue("lock_retention_period_days"));return!Number.isInteger(i)||0===i});this.bucketForm=this.formBuilder.group({id:[null],bid:[null,[a.kI.required],this.editing?[]:[m.h.bucketName(),m.h.bucketExistence(!1,this.rgwBucketService)]],owner:[null,[a.kI.required]],"placement-target":[null,this.editing?[]:[a.kI.required]],versioning:[null],"mfa-delete":[null],"mfa-token-serial":[""],"mfa-token-pin":[""],lock_enabled:[{value:!1,disabled:this.editing}],lock_mode:["COMPLIANCE"],lock_retention_period_days:[0,[m.h.number(!1),n]]})}ngOnInit(){const _={owners:this.rgwUserService.enumerate()};this.editing||(_.getPlacementTargets=this.rgwSiteService.get("placement-targets")),this.route.params.subscribe(n=>{if(n.hasOwnProperty("bid")){const i=decodeURIComponent(n.bid);_.getBid=this.rgwBucketService.get(i)}(0,Y.D)(_).subscribe(i=>{if(this.owners=i.owners.sort(),i.getPlacementTargets){const s=i.getPlacementTargets;this.zonegroup=s.zonegroup,u().forEach(s.placement_targets,c=>{c.description=`${c.name} (${"pool"}: ${c.data_pool})`,this.placementTargets.push(c)}),1===this.placementTargets.length&&this.bucketForm.get("placement-target").setValue(this.placementTargets[0].name)}if(i.getBid){const s=i.getBid,c=u().clone(this.bucketForm.getRawValue());let d=u().pick(s,u().keys(c));d.lock_retention_period_days=this.rgwBucketService.getLockDays(s),d["placement-target"]=s.placement_rule,d.versioning=s.versioning===B.ENABLED,d["mfa-delete"]=s.mfa_delete===k.ENABLED,d=u().merge(c,d),this.bucketForm.setValue(d),this.editing&&(this.isVersioningAlreadyEnabled=this.isVersioningEnabled,this.isMfaDeleteAlreadyEnabled=this.isMfaDeleteEnabled,this.setMfaDeleteValidators(),d.lock_enabled&&this.bucketForm.controls.versioning.disable())}this.loadingReady()})})}goToListView(){this.router.navigate(["/rgw/bucket"])}submit(){if(this.bucketForm.pristine)return void this.goToListView();const _=this.bucketForm.value;if(this.editing){const n=this.getVersioningStatus(),i=this.getMfaDeleteStatus();this.rgwBucketService.update(_.bid,_.id,_.owner,n,i,_["mfa-token-serial"],_["mfa-token-pin"],_.lock_mode,_.lock_retention_period_days).subscribe(()=>{this.notificationService.show(Ae.k.success,"Updated Object Gateway bucket '" + _.bid + "'."),this.goToListView()},()=>{this.bucketForm.setErrors({cdSubmitButton:!0})})}else this.rgwBucketService.create(_.bid,_.owner,this.zonegroup,_["placement-target"],_.lock_enabled,_.lock_mode,_.lock_retention_period_days).subscribe(()=>{this.notificationService.show(Ae.k.success,"Created Object Gateway bucket '" + _.bid + "'"),this.goToListView()},()=>{this.bucketForm.setErrors({cdSubmitButton:!0})})}areMfaCredentialsRequired(){return this.isMfaDeleteEnabled!==this.isMfaDeleteAlreadyEnabled||this.isMfaDeleteAlreadyEnabled&&this.isVersioningEnabled!==this.isVersioningAlreadyEnabled}setMfaDeleteValidators(){const _=this.bucketForm.get("mfa-token-serial"),n=this.bucketForm.get("mfa-token-pin");this.areMfaCredentialsRequired()?(_.setValidators(a.kI.required),n.setValidators(a.kI.required)):(_.setValidators(null),n.setValidators(null)),_.updateValueAndValidity(),n.updateValueAndValidity()}getVersioningStatus(){return this.isVersioningEnabled?B.ENABLED:B.SUSPENDED}getMfaDeleteStatus(){return this.isMfaDeleteEnabled?k.ENABLED:k.DISABLED}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(w.gz),e.Y36(w.F0),e.Y36(J.O),e.Y36(Me.o),e.Y36(We.I),e.Y36(O),e.Y36(ve.g),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-bucket-form"]],features:[e.qOj],decls:1,vars:1,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F,T,x,y,S,_e,te,ne,oe,ie,se,ae,re,le,ce,de,ue,Re;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Name",n="Name...",i="Owner",s="Placement target",c="Locking",d="Enabled",E="Enables locking for the objects in the bucket. Locking can only be enabled while creating a bucket.",g="Id",C="This field is required.",b="Bucket names can only contain lowercase letters, numbers, periods and hyphens.",P="The chosen name is already in use.",G="Bucket names must not contain uppercase characters or underscores.",N="Each label must start and end with a lowercase letter or a number.",p="Bucket names cannot be formatted as IP address.",U="Bucket labels cannot be empty and can only contain lowercase letters, numbers and hyphens.",W="Bucket names must be 3 to 63 characters long.",Z="Loading...",$="-- Select a user --",h="This field is required.",I="Loading...",v="-- Select a placement target --",F="This field is required.",T="Versioning",x="Enabled",y="Enables versioning for the objects in the bucket.",S="Multi-Factor Authentication",_e="Delete enabled",te="Enables MFA (multi-factor authentication) Delete, which requires additional authentication for changing the bucket versioning state.",ne="Token Serial Number",oe="This field is required.",ie="Token PIN",se="This field is required.",ae="Mode",re="Compliance",le="Governance",ce="Days",de="The number of days that you want to specify for the default retention period that will be applied to new objects placed in this bucket.",ue="The entered value must be a positive integer.",Re="Retention Days must be a positive integer.",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["name","bucketForm","novalidate","",3,"formGroup"],["frm","ngForm"],[1,"card"],[1,"card-header"],o,[1,"card-body"],["class","form-group row",4,"ngIf"],[1,"form-group","row"],["for","bid",1,"cd-col-form-label",3,"ngClass"],_,[1,"cd-col-form-input"],["id","bid","name","bid","type","text","placeholder",n,"formControlName","bid",1,"form-control",3,"readonly","autofocus"],["class","invalid-feedback",4,"ngIf"],["for","owner",1,"cd-col-form-label","required"],i,["id","owner","name","owner","formControlName","owner",1,"form-control","custom-select",3,"autofocus"],[3,"ngValue",4,"ngIf"],[3,"value",4,"ngFor","ngForOf"],["for","placement-target",1,"cd-col-form-label",3,"ngClass"],s,["placementTargetSelect",""],[4,"ngIf","ngIfElse"],[4,"ngIf"],[1,"cd-header"],c,[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["id","lock_enabled","formControlName","lock_enabled","type","checkbox",1,"custom-control-input"],["for","lock_enabled",1,"custom-control-label"],d,E,[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],["for","id",1,"cd-col-form-label"],g,["id","id","name","id","type","text","formControlName","id","readonly","",1,"form-control"],[1,"invalid-feedback"],C,b,P,G,N,p,U,W,[3,"ngValue"],Z,$,[3,"value"],h,["id","placement-target","name","placement-target","formControlName","placement-target",1,"form-control","custom-select"],I,v,F,["id","placement-target","name","placement-target","formControlName","placement-target","type","text","readonly","",1,"form-control"],T,["type","checkbox","id","versioning","name","versioning","formControlName","versioning",1,"custom-control-input",3,"change"],["for","versioning",1,"custom-control-label"],x,y,S,["type","checkbox","id","mfa-delete","name","mfa-delete","formControlName","mfa-delete",1,"custom-control-input",3,"change"],["for","mfa-delete",1,"custom-control-label"],_e,te,["for","mfa-token-serial",1,"cd-col-form-label"],ne,["type","text","id","mfa-token-serial","name","mfa-token-serial","formControlName","mfa-token-serial",1,"form-control"],oe,["for","mfa-token-pin",1,"cd-col-form-label"],ie,["type","text","id","mfa-token-pin","name","mfa-token-pin","formControlName","mfa-token-pin",1,"form-control"],se,["for","lock_mode",1,"cd-col-form-label"],ae,["formControlName","lock_mode","name","lock_mode","id","lock_mode",1,"form-control","custom-select"],["value","COMPLIANCE"],re,["value","GOVERNANCE"],le,["for","lock_retention_period_days",1,"cd-col-form-label"],ce,de,["type","number","id","lock_retention_period_days","formControlName","lock_retention_period_days","min","0",1,"form-control"],ue,Re]},template:function(_,n){1&_&&e.YNc(0,lt,59,41,"div",0),2&_&&e.Q6J("cdFormLoading",n.loading)},directives:[Fe.y,a._Y,a.JL,V.V,a.sg,f.O5,H.P,f.mk,q.o,a.Fj,X.b,a.JJ,a.u,j.U,a.EJ,f.sg,a.Wl,h_.S,ee.p,a.YN,a.Kr,a.wV,a.qQ],pipes:[f.rS,K.m],styles:[""]}),t})();var xe=r(18891),be=r(68136),ye=r(30982),z=r(64337),Pe=r(68774),qe=r(47557),we=r(66369),Q=r(51847),Ee=r(74937),Te=r(63285),ke=r(94928),ct=r(96102),Be=r(68962);function dt(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,24),e.qZA())}function ut(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.ALo(2,"dimless"),e.qZA()),2&t){const _=e.oxw(3);e.xp6(1),e.hij(" ",e.lcZ(2,1,_.selection.bucket_quota.max_size)," ")}}function Rt(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,25),e.qZA())}function gt(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.qZA()),2&t){const _=e.oxw(3);e.xp6(1),e.hij(" ",_.selection.bucket_quota.max_objects," ")}}function Et(t,o){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"legend"),e.SDv(2,20),e.qZA(),e.TgZ(3,"table",1),e.TgZ(4,"tbody"),e.TgZ(5,"tr"),e.TgZ(6,"td",2),e.SDv(7,21),e.qZA(),e.TgZ(8,"td",4),e._uU(9),e.ALo(10,"booleanText"),e.qZA(),e.qZA(),e.TgZ(11,"tr"),e.TgZ(12,"td",5),e.SDv(13,22),e.qZA(),e.YNc(14,dt,2,0,"td",0),e.YNc(15,ut,3,3,"td",0),e.qZA(),e.TgZ(16,"tr"),e.TgZ(17,"td",5),e.SDv(18,23),e.qZA(),e.YNc(19,Rt,2,0,"td",0),e.YNc(20,gt,2,1,"td",0),e.qZA(),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(2);e.xp6(9),e.Oqu(e.lcZ(10,5,_.selection.bucket_quota.enabled)),e.xp6(5),e.Q6J("ngIf",_.selection.bucket_quota.max_size<=-1),e.xp6(1),e.Q6J("ngIf",_.selection.bucket_quota.max_size>-1),e.xp6(4),e.Q6J("ngIf",_.selection.bucket_quota.max_objects<=-1),e.xp6(1),e.Q6J("ngIf",_.selection.bucket_quota.max_objects>-1)}}function Tt(t,o){if(1&t&&(e.ynx(0),e.TgZ(1,"tr"),e.TgZ(2,"td",5),e.SDv(3,26),e.qZA(),e.TgZ(4,"td"),e._uU(5),e.qZA(),e.qZA(),e.TgZ(6,"tr"),e.TgZ(7,"td",5),e.SDv(8,27),e.qZA(),e.TgZ(9,"td"),e._uU(10),e.qZA(),e.qZA(),e.BQk()),2&t){const _=e.oxw(2);e.xp6(5),e.Oqu(_.selection.lock_mode),e.xp6(5),e.Oqu(_.selection.lock_retention_period_days)}}function St(t,o){if(1&t&&(e.ynx(0),e.TgZ(1,"table",1),e.TgZ(2,"tbody"),e.TgZ(3,"tr"),e.TgZ(4,"td",2),e.SDv(5,3),e.qZA(),e.TgZ(6,"td",4),e._uU(7),e.qZA(),e.qZA(),e.TgZ(8,"tr"),e.TgZ(9,"td",5),e.SDv(10,6),e.qZA(),e.TgZ(11,"td"),e._uU(12),e.qZA(),e.qZA(),e.TgZ(13,"tr"),e.TgZ(14,"td",5),e.SDv(15,7),e.qZA(),e.TgZ(16,"td"),e._uU(17),e.qZA(),e.qZA(),e.TgZ(18,"tr"),e.TgZ(19,"td",5),e.SDv(20,8),e.qZA(),e.TgZ(21,"td"),e._uU(22),e.qZA(),e.qZA(),e.TgZ(23,"tr"),e.TgZ(24,"td",5),e.SDv(25,9),e.qZA(),e.TgZ(26,"td"),e._uU(27),e.qZA(),e.qZA(),e.TgZ(28,"tr"),e.TgZ(29,"td",5),e.SDv(30,10),e.qZA(),e.TgZ(31,"td"),e._uU(32),e.qZA(),e.qZA(),e.TgZ(33,"tr"),e.TgZ(34,"td",5),e.SDv(35,11),e.qZA(),e.TgZ(36,"td"),e._uU(37),e.qZA(),e.qZA(),e.TgZ(38,"tr"),e.TgZ(39,"td",5),e.SDv(40,12),e.qZA(),e.TgZ(41,"td"),e._uU(42),e.qZA(),e.qZA(),e.TgZ(43,"tr"),e.TgZ(44,"td",5),e.SDv(45,13),e.qZA(),e.TgZ(46,"td"),e._uU(47),e.qZA(),e.qZA(),e.TgZ(48,"tr"),e.TgZ(49,"td",5),e.SDv(50,14),e.qZA(),e.TgZ(51,"td"),e._uU(52),e.ALo(53,"cdDate"),e.qZA(),e.qZA(),e.TgZ(54,"tr"),e.TgZ(55,"td",5),e.SDv(56,15),e.qZA(),e.TgZ(57,"td"),e._uU(58),e.qZA(),e.qZA(),e.TgZ(59,"tr"),e.TgZ(60,"td",5),e.SDv(61,16),e.qZA(),e.TgZ(62,"td"),e._uU(63),e.qZA(),e.qZA(),e.TgZ(64,"tr"),e.TgZ(65,"td",5),e.SDv(66,17),e.qZA(),e.TgZ(67,"td"),e._uU(68),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(69,Et,21,7,"div",0),e.TgZ(70,"legend"),e.SDv(71,18),e.qZA(),e.TgZ(72,"table",1),e.TgZ(73,"tbody"),e.TgZ(74,"tr"),e.TgZ(75,"td",2),e.SDv(76,19),e.qZA(),e.TgZ(77,"td",4),e._uU(78),e.ALo(79,"booleanText"),e.qZA(),e.qZA(),e.YNc(80,Tt,11,2,"ng-container",0),e.qZA(),e.qZA(),e.BQk()),2&t){const _=e.oxw();e.xp6(7),e.Oqu(_.selection.bid),e.xp6(5),e.Oqu(_.selection.id),e.xp6(5),e.Oqu(_.selection.owner),e.xp6(5),e.Oqu(_.selection.index_type),e.xp6(5),e.Oqu(_.selection.placement_rule),e.xp6(5),e.Oqu(_.selection.marker),e.xp6(5),e.Oqu(_.selection.max_marker),e.xp6(5),e.Oqu(_.selection.ver),e.xp6(5),e.Oqu(_.selection.master_ver),e.xp6(5),e.Oqu(e.lcZ(53,16,_.selection.mtime)),e.xp6(6),e.Oqu(_.selection.zonegroup),e.xp6(5),e.Oqu(_.selection.versioning),e.xp6(5),e.Oqu(_.selection.mfa_delete),e.xp6(1),e.Q6J("ngIf",_.selection.bucket_quota),e.xp6(9),e.Oqu(e.lcZ(79,18,_.selection.lock_enabled)),e.xp6(2),e.Q6J("ngIf",_.selection.lock_enabled)}}let ft=(()=>{class t{constructor(_){this.rgwBucketService=_}ngOnChanges(){this.selection&&this.rgwBucketService.get(this.selection.bid).subscribe(_=>{_.lock_retention_period_days=this.rgwBucketService.getLockDays(_),this.selection=_})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(Me.o))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-bucket-details"]],inputs:{selection:"selection"},features:[e.TTD],decls:1,vars:1,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F;return o="Name",_="ID",n="Owner",i="Index type",s="Placement rule",c="Marker",d="Maximum marker",E="Version",g="Master version",C="Modification time",b="Zonegroup",P="Versioning",G="MFA Delete",N="Locking",p="Enabled",U="Bucket quota",W="Enabled",Z="Maximum size",$="Maximum objects",h="Unlimited",I="Unlimited",v="Mode",F="Days",[[4,"ngIf"],[1,"table","table-striped","table-bordered"],[1,"bold","w-25"],o,[1,"w-75"],[1,"bold"],_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F]},template:function(_,n){1&_&&e.YNc(0,St,81,20,"ng-container",0),2&_&&e.Q6J("ngIf",n.selection)},directives:[f.O5],pipes:[ct.N,Be.T,we.n],styles:["table[_ngcontent-%COMP%]{table-layout:fixed}table[_ngcontent-%COMP%] td[_ngcontent-%COMP%]{word-wrap:break-word}"]}),t})();var He=r(60251);const Ct=["bucketSizeTpl"],pt=["bucketObjectTpl"];function Mt(t,o){if(1&t&&e._UZ(0,"cd-usage-bar",8),2&t){const _=e.oxw().row;e.Q6J("total",_.bucket_quota.max_size)("used",_.bucket_size)}}function mt(t,o){1&t&&e.SDv(0,9)}function At(t,o){if(1&t&&(e.YNc(0,Mt,1,2,"cd-usage-bar",6),e.YNc(1,mt,1,0,"ng-template",null,7,e.W1O)),2&t){const _=o.row,n=e.MAs(2);e.Q6J("ngIf",_.bucket_quota.max_size>0&&_.bucket_quota.enabled)("ngIfElse",n)}}function bt(t,o){if(1&t&&e._UZ(0,"cd-usage-bar",12),2&t){const _=e.oxw().row;e.Q6J("total",_.bucket_quota.max_objects)("used",_.num_objects)("isBinary",!1)}}function Pt(t,o){1&t&&e.SDv(0,13)}function Gt(t,o){if(1&t&&(e.YNc(0,bt,1,3,"cd-usage-bar",10),e.YNc(1,Pt,1,0,"ng-template",null,11,e.W1O)),2&t){const _=o.row,n=e.MAs(2);e.Q6J("ngIf",_.bucket_quota.max_objects>0&&_.bucket_quota.enabled)("ngIfElse",n)}}let Ot=(()=>{class t extends be.o{constructor(_,n,i,s,c,d,E,g){super(g),this.authStorageService=_,this.dimlessBinaryPipe=n,this.dimlessPipe=i,this.rgwBucketService=s,this.modalService=c,this.urlBuilder=d,this.actionLabels=E,this.ngZone=g,this.columns=[],this.buckets=[],this.selection=new Pe.r}ngOnInit(){this.permission=this.authStorageService.getPermissions().rgw,this.columns=[{name:"Name",prop:"bid",flexGrow:2},{name:"Owner",prop:"owner",flexGrow:2.5},{name:"Used Capacity",prop:"bucket_size",flexGrow:.6,pipe:this.dimlessBinaryPipe},{name:"Capacity Limit %",prop:"size_usage",cellTemplate:this.bucketSizeTpl,flexGrow:.8},{name:"Objects",prop:"num_objects",flexGrow:.6,pipe:this.dimlessPipe},{name:"Object Limit %",prop:"object_usage",cellTemplate:this.bucketObjectTpl,flexGrow:.8}];const _=()=>this.selection.first()&&`${encodeURIComponent(this.selection.first().bid)}`;this.tableActions=[{permission:"create",icon:D.P.add,routerLink:()=>this.urlBuilder.getCreate(),name:this.actionLabels.CREATE,canBePrimary:c=>!c.hasSelection},{permission:"update",icon:D.P.edit,routerLink:()=>this.urlBuilder.getEdit(_()),name:this.actionLabels.EDIT},{permission:"delete",icon:D.P.destroy,click:()=>this.deleteAction(),disable:()=>!this.selection.hasSelection,name:this.actionLabels.DELETE,canBePrimary:c=>c.hasMultiSelection}],this.setTableRefreshTimeout()}transformBucketData(){u().forEach(this.buckets,_=>{const n=_.bucket_quota.max_size,i=_.bucket_quota.max_objects;_.bucket_size=0,_.num_objects=0,u().isEmpty(_.usage)||(_.bucket_size=_.usage["rgw.main"].size_actual,_.num_objects=_.usage["rgw.main"].num_objects),_.size_usage=n>0?_.bucket_size/n:void 0,_.object_usage=i>0?_.num_objects/i:void 0})}getBucketList(_){this.setTableRefreshTimeout(),this.rgwBucketService.list(!0).subscribe(n=>{this.buckets=n,this.transformBucketData()},()=>{_.error()})}updateSelection(_){this.selection=_}deleteAction(){this.modalService.show(ye.M,{itemDescription:this.selection.hasSingleSelection?"bucket":"buckets",itemNames:this.selection.selected.map(_=>_.bid),submitActionObservable:()=>new xe.y(_=>{(0,Y.D)(this.selection.selected.map(n=>this.rgwBucketService.delete(n.bid))).subscribe({error:n=>{_.error(n),this.table.refreshBtn()},complete:()=>{_.complete(),this.table.refreshBtn()}})})})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(Ee.j),e.Y36(qe.$),e.Y36(we.n),e.Y36(Me.o),e.Y36(Te.Z),e.Y36(Q.F),e.Y36(A.p4),e.Y36(e.R0b))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-bucket-list"]],viewQuery:function(_,n){if(1&_&&(e.Gf(z.a,7),e.Gf(Ct,7),e.Gf(pt,7)),2&_){let i;e.iGM(i=e.CRH())&&(n.table=i.first),e.iGM(i=e.CRH())&&(n.bucketSizeTpl=i.first),e.iGM(i=e.CRH())&&(n.bucketObjectTpl=i.first)}},features:[e._Bn([{provide:Q.F,useValue:new Q.F("rgw/bucket")}]),e.qOj],decls:8,vars:9,consts:function(){let o,_;return o="No Limit",_="No Limit",[["columnMode","flex","selectionType","multiClick","identifier","bid",3,"autoReload","data","columns","hasDetails","status","setExpandedRow","updateSelection","fetchData"],["table",""],[1,"table-actions",3,"permission","selection","tableActions"],["cdTableDetail","",3,"selection"],["bucketSizeTpl",""],["bucketObjectTpl",""],[3,"total","used",4,"ngIf","ngIfElse"],["noSizeQuota",""],[3,"total","used"],o,[3,"total","used","isBinary",4,"ngIf","ngIfElse"],["noObjectQuota",""],[3,"total","used","isBinary"],_]},template:function(_,n){1&_&&(e.TgZ(0,"cd-table",0,1),e.NdJ("setExpandedRow",function(s){return n.setExpandedRow(s)})("updateSelection",function(s){return n.updateSelection(s)})("fetchData",function(s){return n.getBucketList(s)}),e._UZ(2,"cd-table-actions",2),e._UZ(3,"cd-rgw-bucket-details",3),e.qZA(),e.YNc(4,At,3,2,"ng-template",null,4,e.W1O),e.YNc(6,Gt,3,2,"ng-template",null,5,e.W1O)),2&_&&(e.Q6J("autoReload",!1)("data",n.buckets)("columns",n.columns)("hasDetails",!0)("status",n.tableStatus),e.xp6(2),e.Q6J("permission",n.permission)("selection",n.selection)("tableActions",n.tableActions),e.xp6(1),e.Q6J("selection",n.expandedRow))},directives:[z.a,ke.K,ft,f.O5,He.O],styles:[""]}),t})();var Ut=r(58111),Xe=r(59376),Wt=r(61350),Zt=r(98056),Ke=r(76317);function $t(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"cd-table-key-value",11),e.NdJ("fetchData",function(){return e.CHM(_),e.oxw(2).getMetaData()}),e.qZA()}if(2&t){const _=e.oxw(2);e.Q6J("data",_.metadata)}}function ht(t,o){if(1&t&&e._UZ(0,"cd-table-performance-counter",12),2&t){const _=e.oxw(2);e.Q6J("serviceId",_.serviceMapId)}}function It(t,o){if(1&t&&e._UZ(0,"cd-grafana",15),2&t){const _=e.oxw(3);e.Q6J("grafanaPath","rgw-instance-detail?var-rgw_servers=rgw."+_.serviceId)}}function vt(t,o){1&t&&(e.TgZ(0,"li",13),e.TgZ(1,"a",4),e.SDv(2,14),e.qZA(),e.YNc(3,It,1,1,"ng-template",6),e.qZA())}function Ft(t,o){if(1&t&&(e.ynx(0),e.TgZ(1,"ul",1,2),e.TgZ(3,"li",3),e.TgZ(4,"a",4),e.SDv(5,5),e.qZA(),e.YNc(6,$t,1,1,"ng-template",6),e.qZA(),e.TgZ(7,"li",7),e.TgZ(8,"a",4),e.SDv(9,8),e.qZA(),e.YNc(10,ht,1,1,"ng-template",6),e.qZA(),e.YNc(11,vt,4,0,"li",9),e.qZA(),e._UZ(12,"div",10),e.BQk()),2&t){const _=e.MAs(2),n=e.oxw();e.xp6(11),e.Q6J("ngIf",n.grafanaPermission.read),e.xp6(1),e.Q6J("ngbNavOutlet",_)}}let Lt=(()=>{class t{constructor(_,n){this.rgwDaemonService=_,this.authStorageService=n,this.serviceId="",this.serviceMapId="",this.grafanaPermission=this.authStorageService.getPermissions().grafana}ngOnChanges(){this.selection&&(this.serviceId=this.selection.id,this.serviceMapId=this.selection.service_map_id)}getMetaData(){u().isEmpty(this.serviceId)||this.rgwDaemonService.get(this.serviceId).subscribe(_=>{this.metadata=_.rgw_metadata})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ge.b),e.Y36(Ee.j))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-daemon-details"]],inputs:{selection:"selection"},features:[e.TTD],decls:1,vars:1,consts:function(){let o,_,n;return o="Details",_="Performance Counters",n="Performance Details",[[4,"ngIf"],["ngbNav","","cdStatefulTab","rgw-daemon-details",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","details"],["ngbNavLink",""],o,["ngbNavContent",""],["ngbNavItem","performance-counters"],_,["ngbNavItem","performance-details",4,"ngIf"],[3,"ngbNavOutlet"],[3,"data","fetchData"],["serviceType","rgw",3,"serviceId"],["ngbNavItem","performance-details"],n,["uid","x5ARzZtmk","grafanaStyle","one",3,"grafanaPath"]]},template:function(_,n){1&_&&e.YNc(0,Ft,13,2,"ng-container",0),2&_&&e.Q6J("ngIf",n.selection)},directives:[f.O5,M.Pz,Xe.m,M.nv,M.Vx,M.uN,M.tO,Wt.b,Zt.p,Ke.F],styles:[""]}),t})();function Dt(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"cd-table",8),e.NdJ("setExpandedRow",function(i){return e.CHM(_),e.oxw().setExpandedRow(i)})("fetchData",function(i){return e.CHM(_),e.oxw().getDaemonList(i)}),e._UZ(1,"cd-rgw-daemon-details",9),e.qZA()}if(2&t){const _=e.oxw();e.Q6J("data",_.daemons)("columns",_.columns)("hasDetails",!0),e.xp6(1),e.Q6J("selection",_.expandedRow)}}function xt(t,o){1&t&&e._UZ(0,"cd-grafana",11),2&t&&e.Q6J("grafanaPath","rgw-overview?")}function yt(t,o){1&t&&(e.TgZ(0,"li",2),e.TgZ(1,"a",3),e.SDv(2,10),e.qZA(),e.YNc(3,xt,1,1,"ng-template",5),e.qZA())}function qt(t,o){1&t&&e._UZ(0,"cd-grafana",13),2&t&&e.Q6J("grafanaPath","radosgw-sync-overview?")}function wt(t,o){1&t&&(e.TgZ(0,"li",2),e.TgZ(1,"a",3),e.SDv(2,12),e.qZA(),e.YNc(3,qt,1,1,"ng-template",5),e.qZA())}let kt=(()=>{class t extends be.o{constructor(_,n,i,s){super(),this.rgwDaemonService=_,this.authStorageService=n,this.cephShortVersionPipe=i,this.rgwSiteService=s,this.columns=[],this.daemons=[],this.updateDaemons=c=>{this.daemons=c}}ngOnInit(){this.grafanaPermission=this.authStorageService.getPermissions().grafana,this.columns=[{name:"ID",prop:"id",flexGrow:2},{name:"Hostname",prop:"server_hostname",flexGrow:2},{name:"Zone",prop:"zone_name",flexGrow:2},{name:"Zone Group",prop:"zonegroup_name",flexGrow:2},{name:"Realm",prop:"realm_name",flexGrow:2},{name:"Version",prop:"version",flexGrow:1,pipe:this.cephShortVersionPipe}],this.rgwSiteService.get("realms").subscribe(_=>this.isMultiSite=_.length>0)}getDaemonList(_){this.rgwDaemonService.list().subscribe(this.updateDaemons,()=>{_.error()})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ge.b),e.Y36(Ee.j),e.Y36(Ut.F),e.Y36(We.I))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-daemon-list"]],features:[e.qOj],decls:9,vars:3,consts:function(){let o,_,n;return o="Daemons List",_="Overall Performance",n="Sync Performance",[["ngbNav","",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem",""],["ngbNavLink",""],o,["ngbNavContent",""],["ngbNavItem","",4,"ngIf"],[3,"ngbNavOutlet"],["columnMode","flex",3,"data","columns","hasDetails","setExpandedRow","fetchData"],["cdTableDetail","",3,"selection"],_,["uid","WAkugZpiz","grafanaStyle","two",3,"grafanaPath"],n,["uid","rgw-sync-overview","grafanaStyle","two",3,"grafanaPath"]]},template:function(_,n){if(1&_&&(e.TgZ(0,"ul",0,1),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.YNc(5,Dt,2,4,"ng-template",5),e.qZA(),e.YNc(6,yt,4,0,"li",6),e.YNc(7,wt,4,0,"li",6),e.qZA(),e._UZ(8,"div",7)),2&_){const i=e.MAs(1);e.xp6(6),e.Q6J("ngIf",n.grafanaPermission.read),e.xp6(1),e.Q6J("ngIf",n.grafanaPermission.read&&n.isMultiSite),e.xp6(1),e.Q6J("ngbNavOutlet",i)}},directives:[M.Pz,M.nv,M.Vx,M.uN,f.O5,M.tO,z.a,Lt,Ke.F],styles:[""]}),t})();var Bt=r(58071),Ge=r(28211),Se=(()=>{return(t=Se||(Se={})).USERS="users",t.BUCKETS="buckets",t.METADATA="metadata",t.USAGE="usage",t.ZONE="zone",Se;var t})();let ze=(()=>{class t{static getAll(){return Object.values(t.capabilities)}}return t.capabilities=Se,t})();var fe=r(60312);function Ht(t,o){1&t&&e._UZ(0,"input",22),2&t&&e.Q6J("readonly",!0)}function Xt(t,o){1&t&&(e.TgZ(0,"option",17),e.SDv(1,25),e.qZA()),2&t&&e.Q6J("ngValue",null)}function Kt(t,o){if(1&t&&(e.TgZ(0,"option",26),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.Oqu(_)}}function zt(t,o){if(1&t&&(e.TgZ(0,"select",23),e.YNc(1,Xt,2,1,"option",24),e.YNc(2,Kt,2,2,"option",19),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.Q6J("ngIf",null!==_.types),e.xp6(1),e.Q6J("ngForOf",_.types)}}function Qt(t,o){1&t&&(e.TgZ(0,"span",27),e.SDv(1,28),e.qZA())}function Yt(t,o){if(1&t&&(e.TgZ(0,"option",26),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Jt(t,o){1&t&&(e.TgZ(0,"span",27),e.SDv(1,29),e.qZA())}const Vt=function(t){return{required:t}},jt=function(){return["read","write","*"]};let en=(()=>{class t{constructor(_,n,i){this.formBuilder=_,this.activeModal=n,this.actionLabels=i,this.submitAction=new e.vpe,this.editing=!0,this.types=[],this.resource="capability",this.createForm()}createForm(){this.formGroup=this.formBuilder.group({type:[null,[a.kI.required]],perm:[null,[a.kI.required]]})}setEditing(_=!0){this.editing=_,this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.ADD}setValues(_,n){this.formGroup.setValue({type:_,perm:n})}setCapabilities(_){const n=[];_.forEach(i=>{n.push(i.type)}),this.types=[],ze.getAll().forEach(i=>{-1===u().indexOf(n,i)&&this.types.push(i)})}onSubmit(){this.submitAction.emit(this.formGroup.value),this.activeModal.close()}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(J.O),e.Y36(M.Kz),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-capability-modal"]],outputs:{submitAction:"submitAction"},decls:29,vars:24,consts:function(){let o,_,n,i,s,c,d;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Type",n="Permission",i="-- Select a permission --",s="-- Select a type --",c="This field is required.",d="This field is required.",[[3,"modalRef"],[1,"modal-title"],o,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","type",1,"cd-col-form-label",3,"ngClass"],_,[1,"cd-col-form-input"],["id","type","class","form-control","type","text","formControlName","type",3,"readonly",4,"ngIf"],["id","type","class","form-control custom-select","formControlName","type","autofocus","",4,"ngIf"],["class","invalid-feedback",4,"ngIf"],["for","perm",1,"cd-col-form-label","required"],n,["id","perm","formControlName","perm",1,"form-control","custom-select"],[3,"ngValue"],i,[3,"value",4,"ngFor","ngForOf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],["id","type","type","text","formControlName","type",1,"form-control",3,"readonly"],["id","type","formControlName","type","autofocus","",1,"form-control","custom-select"],[3,"ngValue",4,"ngIf"],s,[3,"value"],[1,"invalid-feedback"],c,d]},template:function(_,n){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e.YNc(13,Ht,1,1,"input",11),e.YNc(14,zt,3,2,"select",12),e.YNc(15,Qt,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(16,"div",7),e.TgZ(17,"label",14),e.SDv(18,15),e.qZA(),e.TgZ(19,"div",10),e.TgZ(20,"select",16),e.TgZ(21,"option",17),e.SDv(22,18),e.qZA(),e.YNc(23,Yt,2,2,"option",19),e.qZA(),e.YNc(24,Jt,2,0,"span",13),e.qZA(),e.qZA(),e.qZA(),e.TgZ(25,"div",20),e.TgZ(26,"cd-form-button-panel",21),e.NdJ("submitActionEvent",function(){return n.onSubmit()}),e.ALo(27,"titlecase"),e.ALo(28,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const i=e.MAs(7);e.Q6J("modalRef",n.activeModal),e.xp6(4),e.pQV(e.lcZ(3,13,n.action))(e.lcZ(4,15,n.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",n.formGroup),e.xp6(4),e.Q6J("ngClass",e.VKq(21,Vt,!n.editing)),e.xp6(3),e.Q6J("ngIf",n.editing),e.xp6(1),e.Q6J("ngIf",!n.editing),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("type",i,"required")),e.xp6(6),e.Q6J("ngValue",null),e.xp6(2),e.Q6J("ngForOf",e.DdM(23,jt)),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("perm",i,"required")),e.xp6(2),e.Q6J("form",n.formGroup)("submitText",e.lcZ(27,17,n.action)+" "+e.lcZ(28,19,n.resource))}},directives:[fe.z,a._Y,a.JL,V.V,a.sg,H.P,f.mk,f.O5,q.o,a.EJ,X.b,a.JJ,a.u,a.YN,a.Kr,f.sg,ee.p,a.Fj,j.U],pipes:[f.rS,K.m],styles:[""]}),t})();var Ce=r(4416),pe=r(58039);function _n(t,o){1&t&&e._UZ(0,"input",17),2&t&&e.Q6J("readonly",!0)}function tn(t,o){1&t&&(e.TgZ(0,"option",21),e.SDv(1,22),e.qZA()),2&t&&e.Q6J("ngValue",null)}function nn(t,o){if(1&t&&(e.TgZ(0,"option",23),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.Oqu(_)}}function on(t,o){if(1&t&&(e.TgZ(0,"select",18),e.YNc(1,tn,2,1,"option",19),e.YNc(2,nn,2,2,"option",20),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.Q6J("ngIf",null!==_.userCandidates),e.xp6(1),e.Q6J("ngForOf",_.userCandidates)}}function sn(t,o){1&t&&(e.TgZ(0,"span",24),e.SDv(1,25),e.qZA())}function an(t,o){1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"div",26),e.TgZ(2,"div",27),e._UZ(3,"input",28),e.TgZ(4,"label",29),e.SDv(5,30),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function rn(t,o){1&t&&(e.TgZ(0,"span",24),e.SDv(1,38),e.qZA())}const Ne=function(t){return{required:t}};function ln(t,o){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",31),e.SDv(2,32),e.qZA(),e.TgZ(3,"div",10),e.TgZ(4,"div",33),e._UZ(5,"input",34),e.TgZ(6,"span",35),e._UZ(7,"button",36),e._UZ(8,"cd-copy-2-clipboard-button",37),e.qZA(),e.qZA(),e.YNc(9,rn,2,0,"span",13),e.qZA(),e.qZA()),2&t){const _=e.oxw(),n=e.MAs(7);e.xp6(1),e.Q6J("ngClass",e.VKq(3,Ne,!_.viewing)),e.xp6(4),e.Q6J("readonly",_.viewing),e.xp6(4),e.Q6J("ngIf",_.formGroup.showError("access_key",n,"required"))}}function cn(t,o){1&t&&(e.TgZ(0,"span",24),e.SDv(1,44),e.qZA())}function dn(t,o){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",39),e.SDv(2,40),e.qZA(),e.TgZ(3,"div",10),e.TgZ(4,"div",33),e._UZ(5,"input",41),e.TgZ(6,"span",35),e._UZ(7,"button",42),e._UZ(8,"cd-copy-2-clipboard-button",43),e.qZA(),e.qZA(),e.YNc(9,cn,2,0,"span",13),e.qZA(),e.qZA()),2&t){const _=e.oxw(),n=e.MAs(7);e.xp6(1),e.Q6J("ngClass",e.VKq(3,Ne,!_.viewing)),e.xp6(4),e.Q6J("readonly",_.viewing),e.xp6(4),e.Q6J("ngIf",_.formGroup.showError("secret_key",n,"required"))}}let Qe=(()=>{class t{constructor(_,n,i){this.formBuilder=_,this.activeModal=n,this.actionLabels=i,this.submitAction=new e.vpe,this.viewing=!0,this.userCandidates=[],this.resource="S3 Key",this.createForm()}createForm(){this.formGroup=this.formBuilder.group({user:[null,[a.kI.required]],generate_key:[!0],access_key:[null,[m.h.requiredIf({generate_key:!1})]],secret_key:[null,[m.h.requiredIf({generate_key:!1})]]})}setViewing(_=!0){this.viewing=_,this.action=this.viewing?this.actionLabels.SHOW:this.actionLabels.CREATE}setValues(_,n,i){this.formGroup.setValue({user:_,generate_key:u().isEmpty(n),access_key:n,secret_key:i})}setUserCandidates(_){this.userCandidates=_}onSubmit(){this.submitAction.emit(this.formGroup.value),this.activeModal.close()}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(J.O),e.Y36(M.Kz),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-s3-key-modal"]],outputs:{submitAction:"submitAction"},decls:23,vars:24,consts:function(){let o,_,n,i,s,c,d,E,g;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Username",n="-- Select a username --",i="This field is required.",s="Auto-generate key",c="Access key",d="This field is required.",E="Secret key",g="This field is required.",[[3,"modalRef"],[1,"modal-title"],o,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","user",1,"cd-col-form-label",3,"ngClass"],_,[1,"cd-col-form-input"],["id","user","class","form-control","type","text","formControlName","user",3,"readonly",4,"ngIf"],["id","user","class","form-control custom-select","formControlName","user","autofocus","",4,"ngIf"],["class","invalid-feedback",4,"ngIf"],["class","form-group row",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","showSubmit","submitActionEvent"],["id","user","type","text","formControlName","user",1,"form-control",3,"readonly"],["id","user","formControlName","user","autofocus","",1,"form-control","custom-select"],[3,"ngValue",4,"ngIf"],[3,"value",4,"ngFor","ngForOf"],[3,"ngValue"],n,[3,"value"],[1,"invalid-feedback"],i,[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["id","generate_key","type","checkbox","formControlName","generate_key",1,"custom-control-input"],["for","generate_key",1,"custom-control-label"],s,["for","access_key",1,"cd-col-form-label",3,"ngClass"],c,[1,"input-group"],["id","access_key","type","password","formControlName","access_key",1,"form-control",3,"readonly"],[1,"input-group-append"],["type","button","cdPasswordButton","access_key",1,"btn","btn-light"],["source","access_key"],d,["for","secret_key",1,"cd-col-form-label",3,"ngClass"],E,["id","secret_key","type","password","formControlName","secret_key",1,"form-control",3,"readonly"],["type","button","cdPasswordButton","secret_key",1,"btn","btn-light"],["source","secret_key"],g]},template:function(_,n){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e.YNc(13,_n,1,1,"input",11),e.YNc(14,on,3,2,"select",12),e.YNc(15,sn,2,0,"span",13),e.qZA(),e.qZA(),e.YNc(16,an,6,0,"div",14),e.YNc(17,ln,10,5,"div",14),e.YNc(18,dn,10,5,"div",14),e.qZA(),e.TgZ(19,"div",15),e.TgZ(20,"cd-form-button-panel",16),e.NdJ("submitActionEvent",function(){return n.onSubmit()}),e.ALo(21,"titlecase"),e.ALo(22,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const i=e.MAs(7);e.Q6J("modalRef",n.activeModal),e.xp6(4),e.pQV(e.lcZ(3,14,n.action))(e.lcZ(4,16,n.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",n.formGroup),e.xp6(4),e.Q6J("ngClass",e.VKq(22,Ne,!n.viewing)),e.xp6(3),e.Q6J("ngIf",n.viewing),e.xp6(1),e.Q6J("ngIf",!n.viewing),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("user",i,"required")),e.xp6(1),e.Q6J("ngIf",!n.viewing),e.xp6(1),e.Q6J("ngIf",!n.formGroup.getValue("generate_key")),e.xp6(1),e.Q6J("ngIf",!n.formGroup.getValue("generate_key")),e.xp6(2),e.Q6J("form",n.formGroup)("submitText",e.lcZ(21,18,n.action)+" "+e.lcZ(22,20,n.resource))("showSubmit",!n.viewing)}},directives:[fe.z,a._Y,a.JL,V.V,a.sg,H.P,f.mk,f.O5,ee.p,q.o,a.Fj,X.b,a.JJ,a.u,a.EJ,j.U,f.sg,a.YN,a.Kr,a.Wl,Ce.C,pe.s],pipes:[f.rS,K.m],styles:[""]}),t})();class un{}function Rn(t,o){1&t&&(e.TgZ(0,"span",29),e.SDv(1,30),e.qZA())}function gn(t,o){1&t&&(e.TgZ(0,"span",29),e.SDv(1,31),e.qZA())}function En(t,o){if(1&t&&(e.TgZ(0,"option",32),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Tn(t,o){1&t&&(e.TgZ(0,"span",29),e.SDv(1,33),e.qZA())}function Sn(t,o){1&t&&(e.TgZ(0,"span",29),e.SDv(1,48),e.qZA())}function fn(t,o){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",41),e.SDv(2,42),e.qZA(),e.TgZ(3,"div",10),e.TgZ(4,"div",43),e._UZ(5,"input",44),e.TgZ(6,"span",45),e._UZ(7,"button",46),e._UZ(8,"cd-copy-2-clipboard-button",47),e.qZA(),e.qZA(),e.YNc(9,Sn,2,0,"span",15),e.qZA(),e.qZA()),2&t){const _=e.oxw(2),n=e.MAs(7);e.xp6(9),e.Q6J("ngIf",_.formGroup.showError("secret_key",n,"required"))}}function Cn(t,o){if(1&t&&(e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,34),e.qZA(),e.TgZ(3,"div",7),e.TgZ(4,"div",35),e.TgZ(5,"div",36),e._UZ(6,"input",37),e.TgZ(7,"label",38),e.SDv(8,39),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(9,fn,10,1,"div",40),e.qZA()),2&t){const _=e.oxw();e.xp6(9),e.Q6J("ngIf",!_.editing&&!_.formGroup.getValue("generate_secret"))}}const pn=function(t){return{required:t}},Mn=function(){return["read","write"]};let mn=(()=>{class t{constructor(_,n,i){this.formBuilder=_,this.bsModalRef=n,this.actionLabels=i,this.submitAction=new e.vpe,this.editing=!0,this.subusers=[],this.resource="Subuser",this.createForm()}createForm(){this.formGroup=this.formBuilder.group({uid:[null],subuid:[null,[a.kI.required,this.subuserValidator()]],perm:[null,[a.kI.required]],generate_secret:[!0],secret_key:[null,[m.h.requiredIf({generate_secret:!1})]]})}subuserValidator(){const _=this;return n=>_.editing||(0,m.P)(n.value)?null:_.subusers.some(s=>u().isEqual(_.getSubuserName(s.id),n.value))?{subuserIdExists:!0}:null}getSubuserName(_){if(u().isEmpty(_))return _;const n=_.match(/([^:]+)(:(.+))?/);return u().isUndefined(n[3])?n[1]:n[3]}setEditing(_=!0){this.editing=_,this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.CREATE}setValues(_,n="",i=""){this.formGroup.setValue({uid:_,subuid:this.getSubuserName(n),perm:i,generate_secret:!0,secret_key:null})}setSubusers(_){this.subusers=_}onSubmit(){const _=this.formGroup.value,n=new un;n.id=`${_.uid}:${_.subuid}`,n.permissions=_.perm,n.generate_secret=_.generate_secret,n.secret_key=_.secret_key,this.submitAction.emit(n),this.bsModalRef.close()}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(J.O),e.Y36(M.Kz),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-subuser-modal"]],outputs:{submitAction:"submitAction"},decls:39,vars:26,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Username",n="Subuser",i="Permission",s="-- Select a permission --",c="read, write",d="full",E="This field is required.",g="The chosen subuser ID is already in use.",C="This field is required.",b="Swift key",P="Auto-generate secret",G="Secret key",N="This field is required.",[[3,"modalRef"],[1,"modal-title"],o,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","uid",1,"cd-col-form-label"],_,[1,"cd-col-form-input"],["id","uid","type","text","formControlName","uid",1,"form-control",3,"readonly"],["for","subuid",1,"cd-col-form-label",3,"ngClass"],n,["id","subuid","type","text","formControlName","subuid","autofocus","",1,"form-control",3,"readonly"],["class","invalid-feedback",4,"ngIf"],["for","perm",1,"cd-col-form-label","required"],i,["id","perm","formControlName","perm",1,"form-control","custom-select"],[3,"ngValue"],s,[3,"value",4,"ngFor","ngForOf"],["value","read-write"],c,["value","full-control"],d,[4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],E,g,[3,"value"],C,b,[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["id","generate_secret","type","checkbox","formControlName","generate_secret",1,"custom-control-input"],["for","generate_secret",1,"custom-control-label"],P,["class","form-group row",4,"ngIf"],["for","secret_key",1,"cd-col-form-label","required"],G,[1,"input-group"],["id","secret_key","type","password","formControlName","secret_key",1,"form-control"],[1,"input-group-append"],["type","button","cdPasswordButton","secret_key",1,"btn","btn-light"],["source","secret_key"],N]},template:function(_,n){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e._UZ(13,"input",11),e.qZA(),e.qZA(),e.TgZ(14,"div",7),e.TgZ(15,"label",12),e.SDv(16,13),e.qZA(),e.TgZ(17,"div",10),e._UZ(18,"input",14),e.YNc(19,Rn,2,0,"span",15),e.YNc(20,gn,2,0,"span",15),e.qZA(),e.qZA(),e.TgZ(21,"div",7),e.TgZ(22,"label",16),e.SDv(23,17),e.qZA(),e.TgZ(24,"div",10),e.TgZ(25,"select",18),e.TgZ(26,"option",19),e.SDv(27,20),e.qZA(),e.YNc(28,En,2,2,"option",21),e.TgZ(29,"option",22),e.SDv(30,23),e.qZA(),e.TgZ(31,"option",24),e.SDv(32,25),e.qZA(),e.qZA(),e.YNc(33,Tn,2,0,"span",15),e.qZA(),e.qZA(),e.YNc(34,Cn,10,1,"fieldset",26),e.qZA(),e.TgZ(35,"div",27),e.TgZ(36,"cd-form-button-panel",28),e.NdJ("submitActionEvent",function(){return n.onSubmit()}),e.ALo(37,"titlecase"),e.ALo(38,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const i=e.MAs(7);e.Q6J("modalRef",n.bsModalRef),e.xp6(4),e.pQV(e.lcZ(3,15,n.action))(e.lcZ(4,17,n.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",n.formGroup),e.xp6(7),e.Q6J("readonly",!0),e.xp6(2),e.Q6J("ngClass",e.VKq(23,pn,!n.editing)),e.xp6(3),e.Q6J("readonly",n.editing),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("subuid",i,"required")),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("subuid",i,"subuserIdExists")),e.xp6(6),e.Q6J("ngValue",null),e.xp6(2),e.Q6J("ngForOf",e.DdM(25,Mn)),e.xp6(5),e.Q6J("ngIf",n.formGroup.showError("perm",i,"required")),e.xp6(1),e.Q6J("ngIf",!n.editing),e.xp6(2),e.Q6J("form",n.formGroup)("submitText",e.lcZ(37,19,n.action)+" "+e.lcZ(38,21,n.resource))}},directives:[fe.z,a._Y,a.JL,V.V,a.sg,H.P,q.o,a.Fj,X.b,a.JJ,a.u,f.mk,j.U,f.O5,a.EJ,a.YN,a.Kr,f.sg,ee.p,a.Wl,Ce.C,pe.s],pipes:[f.rS,K.m],styles:[""]}),t})();var An=r(13472);let Ye=(()=>{class t{constructor(_,n){this.activeModal=_,this.actionLabels=n,this.resource="Swift Key",this.action=this.actionLabels.SHOW}setValues(_,n){this.user=_,this.secret_key=n}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(M.Kz),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-swift-key-modal"]],decls:24,vars:11,consts:function(){let o,_,n;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Username",n="Secret key",[[3,"modalRef"],[1,"modal-title"],o,[1,"modal-content"],[1,"modal-body"],["novalidate",""],[1,"form-group","row"],["for","user",1,"cd-col-form-label"],_,[1,"cd-col-form-input"],["id","user","name","user","type","text",1,"form-control",3,"readonly","ngModel","ngModelChange"],["for","secret_key",1,"cd-col-form-label"],n,[1,"input-group"],["id","secret_key","name","secret_key","type","password",1,"form-control",3,"ngModel","readonly","ngModelChange"],[1,"input-group-append"],["type","button","cdPasswordButton","secret_key",1,"btn","btn-light"],["source","secret_key"],[1,"modal-footer"],[3,"backAction"]]},template:function(_,n){1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"div",4),e.TgZ(7,"form",5),e.TgZ(8,"div",6),e.TgZ(9,"label",7),e.SDv(10,8),e.qZA(),e.TgZ(11,"div",9),e.TgZ(12,"input",10),e.NdJ("ngModelChange",function(s){return n.user=s}),e.qZA(),e.qZA(),e.qZA(),e.TgZ(13,"div",6),e.TgZ(14,"label",11),e.SDv(15,12),e.qZA(),e.TgZ(16,"div",9),e.TgZ(17,"div",13),e.TgZ(18,"input",14),e.NdJ("ngModelChange",function(s){return n.secret_key=s}),e.qZA(),e.TgZ(19,"span",15),e._UZ(20,"button",16),e._UZ(21,"cd-copy-2-clipboard-button",17),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.TgZ(22,"div",18),e.TgZ(23,"cd-back-button",19),e.NdJ("backAction",function(){return n.activeModal.close()}),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_&&(e.Q6J("modalRef",n.activeModal),e.xp6(4),e.pQV(e.lcZ(3,7,n.action))(e.lcZ(4,9,n.resource)),e.QtT(2),e.xp6(8),e.Q6J("readonly",!0)("ngModel",n.user),e.xp6(6),e.Q6J("ngModel",n.secret_key)("readonly",!0))},directives:[fe.z,a._Y,a.JL,a.F,H.P,q.o,a.Fj,X.b,a.JJ,a.On,Ce.C,pe.s,An.W],pipes:[f.rS,K.m],styles:[""]}),t})();var bn=r(17932);function Pn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,50),e.qZA())}function Gn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,51),e.qZA())}function Nn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,52),e.qZA())}function On(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,56),e.qZA())}function Un(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,57),e.qZA())}function Wn(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",53),e.SDv(2,54),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",55),e.YNc(5,On,2,0,"span",13),e.YNc(6,Un,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(4),e.Q6J("readonly",n.editing),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("tenant",_,"pattern")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("tenant",_,"notUnique"))}}function Zn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,58),e.qZA())}function $n(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,59),e.qZA())}function hn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,60),e.qZA())}function In(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,63),e.qZA())}function vn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,64),e.qZA())}function Fn(t,o){if(1&t&&(e.TgZ(0,"div",8),e._UZ(1,"label",61),e.TgZ(2,"div",11),e._UZ(3,"input",62),e.YNc(4,In,2,0,"span",13),e.YNc(5,vn,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(4),e.Q6J("ngIf",n.userForm.showError("max_buckets",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("max_buckets",_,"min"))}}function Ln(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,76),e.qZA())}function Dn(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",69),e.SDv(2,70),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"div",71),e._UZ(5,"input",72),e.TgZ(6,"span",73),e._UZ(7,"button",74),e._UZ(8,"cd-copy-2-clipboard-button",75),e.qZA(),e.qZA(),e.YNc(9,Ln,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),n=e.oxw();e.xp6(9),e.Q6J("ngIf",n.userForm.showError("access_key",_,"required"))}}function xn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,82),e.qZA())}function yn(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",77),e.SDv(2,78),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"div",71),e._UZ(5,"input",79),e.TgZ(6,"span",73),e._UZ(7,"button",80),e._UZ(8,"cd-copy-2-clipboard-button",81),e.qZA(),e.qZA(),e.YNc(9,xn,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),n=e.oxw();e.xp6(9),e.Q6J("ngIf",n.userForm.showError("secret_key",_,"required"))}}function qn(t,o){if(1&t&&(e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,65),e.qZA(),e.TgZ(3,"div",8),e.TgZ(4,"div",14),e.TgZ(5,"div",15),e._UZ(6,"input",66),e.TgZ(7,"label",67),e.SDv(8,68),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(9,Dn,10,1,"div",19),e.YNc(10,yn,10,1,"div",19),e.qZA()),2&t){const _=e.oxw(2);e.xp6(9),e.Q6J("ngIf",!_.editing&&!_.userForm.getValue("generate_key")),e.xp6(1),e.Q6J("ngIf",!_.editing&&!_.userForm.getValue("generate_key"))}}function wn(t,o){1&t&&(e.TgZ(0,"span",92),e.TgZ(1,"span",93),e.SDv(2,94),e.qZA(),e.qZA())}const L=function(t){return[t]};function kn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"span"),e.TgZ(1,"div",71),e.TgZ(2,"div",95),e.TgZ(3,"span",96),e._UZ(4,"i"),e.qZA(),e.qZA(),e._UZ(5,"input",97),e.TgZ(6,"div",98),e.TgZ(7,"span",96),e._UZ(8,"i"),e.qZA(),e.qZA(),e._UZ(9,"input",97),e.TgZ(10,"span",73),e.TgZ(11,"button",99),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).showSubuserModal(s)}),e._UZ(12,"i",89),e.qZA(),e.TgZ(13,"button",100),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).deleteSubuser(s)}),e._UZ(14,"i",89),e.qZA(),e.qZA(),e.qZA(),e._UZ(15,"span",93),e.qZA()}if(2&t){const _=o.$implicit,n=e.oxw(3);e.xp6(4),e.Tol(n.icons.user),e.xp6(1),e.s9C("value",_.id),e.xp6(3),e.Tol(n.icons.share),e.xp6(1),e.s9C("value","full-control"===_.permissions?"full":_.permissions),e.xp6(3),e.Q6J("ngClass",e.VKq(10,L,n.icons.edit)),e.xp6(2),e.Q6J("ngClass",e.VKq(12,L,n.icons.destroy))}}function Bn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,83),e.qZA(),e.TgZ(3,"div",84),e.TgZ(4,"div",14),e.YNc(5,wn,3,0,"span",85),e.YNc(6,kn,16,14,"span",86),e.TgZ(7,"div",84),e.TgZ(8,"div",87),e.TgZ(9,"button",88),e.NdJ("click",function(){return e.CHM(_),e.oxw(2).showSubuserModal()}),e._UZ(10,"i",89),e.ynx(11),e.SDv(12,90),e.ALo(13,"titlecase"),e.ALo(14,"upperFirst"),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(15,"span",91),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(2);e.xp6(5),e.Q6J("ngIf",0===_.subusers.length),e.xp6(1),e.Q6J("ngForOf",_.subusers),e.xp6(4),e.Q6J("ngClass",e.VKq(9,L,_.icons.add)),e.xp6(4),e.pQV(e.lcZ(13,5,_.actionLabels.CREATE))(e.lcZ(14,7,_.subuserLabel)),e.QtT(12)}}function Hn(t,o){1&t&&(e.TgZ(0,"span",92),e.TgZ(1,"span",93),e.SDv(2,106),e.qZA(),e.qZA())}function Xn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"span"),e.TgZ(1,"div",71),e.TgZ(2,"div",95),e.TgZ(3,"div",96),e._UZ(4,"i"),e.qZA(),e.qZA(),e._UZ(5,"input",97),e.TgZ(6,"span",73),e.TgZ(7,"button",107),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).showS3KeyModal(s)}),e._UZ(8,"i",89),e.qZA(),e.TgZ(9,"button",108),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).deleteS3Key(s)}),e._UZ(10,"i",89),e.qZA(),e.qZA(),e.qZA(),e._UZ(11,"span",93),e.qZA()}if(2&t){const _=o.$implicit,n=e.oxw(3);e.xp6(4),e.Tol(n.icons.key),e.xp6(1),e.s9C("value",_.user),e.xp6(3),e.Q6J("ngClass",e.VKq(6,L,n.icons.show)),e.xp6(2),e.Q6J("ngClass",e.VKq(8,L,n.icons.destroy))}}function Kn(t,o){1&t&&(e.TgZ(0,"span",92),e.TgZ(1,"span",93),e.SDv(2,109),e.qZA(),e.qZA())}function zn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"span"),e.TgZ(1,"div",71),e.TgZ(2,"div",95),e.TgZ(3,"span",96),e._UZ(4,"i"),e.qZA(),e.qZA(),e._UZ(5,"input",97),e.TgZ(6,"span",73),e.TgZ(7,"button",110),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).showSwiftKeyModal(s)}),e._UZ(8,"i",89),e.qZA(),e.qZA(),e.qZA(),e._UZ(9,"span",93),e.qZA()}if(2&t){const _=o.$implicit,n=e.oxw(3);e.xp6(4),e.Tol(n.icons.key),e.xp6(1),e.s9C("value",_.user),e.xp6(3),e.Q6J("ngClass",e.VKq(5,L,n.icons.show))}}function Qn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,101),e.qZA(),e.TgZ(3,"div",8),e.TgZ(4,"label",61),e.SDv(5,102),e.qZA(),e.TgZ(6,"div",11),e.YNc(7,Hn,3,0,"span",85),e.YNc(8,Xn,12,10,"span",86),e.TgZ(9,"div",84),e.TgZ(10,"div",87),e.TgZ(11,"button",103),e.NdJ("click",function(){return e.CHM(_),e.oxw(2).showS3KeyModal()}),e._UZ(12,"i",89),e.ynx(13),e.SDv(14,104),e.ALo(15,"titlecase"),e.ALo(16,"upperFirst"),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(17,"span",91),e.qZA(),e._UZ(18,"hr"),e.qZA(),e.TgZ(19,"div",8),e.TgZ(20,"label",61),e.SDv(21,105),e.qZA(),e.TgZ(22,"div",11),e.YNc(23,Kn,3,0,"span",85),e.YNc(24,zn,10,7,"span",86),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(2);e.xp6(7),e.Q6J("ngIf",0===_.s3Keys.length),e.xp6(1),e.Q6J("ngForOf",_.s3Keys),e.xp6(4),e.Q6J("ngClass",e.VKq(11,L,_.icons.add)),e.xp6(4),e.pQV(e.lcZ(15,7,_.actionLabels.CREATE))(e.lcZ(16,9,_.s3keyLabel)),e.QtT(14),e.xp6(7),e.Q6J("ngIf",0===_.swiftKeys.length),e.xp6(1),e.Q6J("ngForOf",_.swiftKeys)}}function Yn(t,o){1&t&&(e.TgZ(0,"span",92),e.TgZ(1,"span",93),e.SDv(2,114),e.qZA(),e.qZA())}function Jn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"span"),e.TgZ(1,"div",71),e.TgZ(2,"span",95),e.TgZ(3,"div",96),e._UZ(4,"i"),e.qZA(),e.qZA(),e._UZ(5,"input",97),e.TgZ(6,"span",73),e.TgZ(7,"button",115),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).showCapabilityModal(s)}),e._UZ(8,"i",89),e.qZA(),e.TgZ(9,"button",116),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).deleteCapability(s)}),e._UZ(10,"i",89),e.qZA(),e.qZA(),e.qZA(),e._UZ(11,"span",93),e.qZA()}if(2&t){const _=o.$implicit,n=e.oxw(3);e.xp6(4),e.Tol(n.icons.share),e.xp6(1),e.hYB("value","",_.type,":",_.perm,""),e.xp6(3),e.Q6J("ngClass",e.VKq(7,L,n.icons.edit)),e.xp6(2),e.Q6J("ngClass",e.VKq(9,L,n.icons.destroy))}}function Vn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,111),e.qZA(),e.TgZ(3,"div",8),e.TgZ(4,"div",14),e.YNc(5,Yn,3,0,"span",85),e.YNc(6,Jn,12,11,"span",86),e.TgZ(7,"div",84),e.TgZ(8,"div",87),e.TgZ(9,"button",112),e.NdJ("click",function(){return e.CHM(_),e.oxw(2).showCapabilityModal()}),e.ALo(10,"pipeFunction"),e.ALo(11,"pipeFunction"),e._UZ(12,"i",89),e.ynx(13),e.SDv(14,113),e.ALo(15,"titlecase"),e.ALo(16,"upperFirst"),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(17,"span",91),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(2);e.xp6(5),e.Q6J("ngIf",0===_.capabilities.length),e.xp6(1),e.Q6J("ngForOf",_.capabilities),e.xp6(3),e.Q6J("disabled",e.xi3(10,7,_.capabilities,_.hasAllCapabilities))("disableTooltip",!e.xi3(11,10,_.capabilities,_.hasAllCapabilities)),e.xp6(3),e.Q6J("ngClass",e.VKq(17,L,_.icons.add)),e.xp6(4),e.pQV(e.lcZ(15,13,_.actionLabels.ADD))(e.lcZ(16,15,_.capabilityLabel)),e.QtT(14)}}function jn(t,o){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"div",14),e.TgZ(2,"div",15),e._UZ(3,"input",117),e.TgZ(4,"label",118),e.SDv(5,119),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function eo(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,123),e.qZA())}function _o(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,124),e.qZA())}function to(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",120),e.SDv(2,121),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",122),e.YNc(5,eo,2,0,"span",13),e.YNc(6,_o,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.userForm.showError("user_quota_max_size",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("user_quota_max_size",_,"quotaMaxSize"))}}function no(t,o){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"div",14),e.TgZ(2,"div",15),e._UZ(3,"input",125),e.TgZ(4,"label",126),e.SDv(5,127),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function oo(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,131),e.qZA())}function io(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,132),e.qZA())}function so(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",128),e.SDv(2,129),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",130),e.YNc(5,oo,2,0,"span",13),e.YNc(6,io,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.userForm.showError("user_quota_max_objects",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("user_quota_max_objects",_,"min"))}}function ao(t,o){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"div",14),e.TgZ(2,"div",15),e._UZ(3,"input",133),e.TgZ(4,"label",134),e.SDv(5,135),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function ro(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,139),e.qZA())}function lo(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,140),e.qZA())}function co(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",136),e.SDv(2,137),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",138),e.YNc(5,ro,2,0,"span",13),e.YNc(6,lo,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.userForm.showError("bucket_quota_max_size",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("bucket_quota_max_size",_,"quotaMaxSize"))}}function uo(t,o){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"div",14),e.TgZ(2,"div",15),e._UZ(3,"input",141),e.TgZ(4,"label",142),e.SDv(5,143),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function Ro(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,147),e.qZA())}function go(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,148),e.qZA())}function Eo(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",144),e.SDv(2,145),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",146),e.YNc(5,Ro,2,0,"span",13),e.YNc(6,go,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.userForm.showError("bucket_quota_max_objects",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("bucket_quota_max_objects",_,"min"))}}const Je=function(t){return{required:t}};function To(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.TgZ(9,"div",8),e.TgZ(10,"label",9),e.SDv(11,10),e.qZA(),e.TgZ(12,"div",11),e._UZ(13,"input",12),e.YNc(14,Pn,2,0,"span",13),e.YNc(15,Gn,2,0,"span",13),e.YNc(16,Nn,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(17,"div",8),e.TgZ(18,"div",14),e.TgZ(19,"div",15),e.TgZ(20,"input",16),e.NdJ("click",function(){return e.CHM(_),e.oxw().updateFieldsWhenTenanted()}),e.qZA(),e.TgZ(21,"label",17),e.SDv(22,18),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(23,Wn,7,3,"div",19),e.TgZ(24,"div",8),e.TgZ(25,"label",20),e.SDv(26,21),e.qZA(),e.TgZ(27,"div",11),e._UZ(28,"input",22),e.YNc(29,Zn,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(30,"div",8),e.TgZ(31,"label",23),e.SDv(32,24),e.qZA(),e.TgZ(33,"div",11),e._UZ(34,"input",25),e.YNc(35,$n,2,0,"span",13),e.YNc(36,hn,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(37,"div",8),e.TgZ(38,"label",26),e.SDv(39,27),e.qZA(),e.TgZ(40,"div",11),e.TgZ(41,"select",28),e.NdJ("change",function(i){return e.CHM(_),e.oxw().onMaxBucketsModeChange(i.target.value)}),e.TgZ(42,"option",29),e.SDv(43,30),e.qZA(),e.TgZ(44,"option",31),e.SDv(45,32),e.qZA(),e.TgZ(46,"option",33),e.SDv(47,34),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(48,Fn,6,2,"div",19),e.TgZ(49,"div",8),e.TgZ(50,"div",14),e.TgZ(51,"div",15),e._UZ(52,"input",35),e.TgZ(53,"label",36),e.SDv(54,37),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(55,qn,11,2,"fieldset",38),e.YNc(56,Bn,16,11,"fieldset",38),e.YNc(57,Qn,25,13,"fieldset",38),e.YNc(58,Vn,18,19,"fieldset",38),e.TgZ(59,"fieldset"),e.TgZ(60,"legend"),e.SDv(61,39),e.qZA(),e.TgZ(62,"div",8),e.TgZ(63,"div",14),e.TgZ(64,"div",15),e._UZ(65,"input",40),e.TgZ(66,"label",41),e.SDv(67,42),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(68,jn,6,0,"div",19),e.YNc(69,to,7,2,"div",19),e.YNc(70,no,6,0,"div",19),e.YNc(71,so,7,2,"div",19),e.qZA(),e.TgZ(72,"fieldset"),e.TgZ(73,"legend"),e.SDv(74,43),e.qZA(),e.TgZ(75,"div",8),e.TgZ(76,"div",14),e.TgZ(77,"div",15),e._UZ(78,"input",44),e.TgZ(79,"label",45),e.SDv(80,46),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(81,ao,6,0,"div",19),e.YNc(82,co,7,2,"div",19),e.YNc(83,uo,6,0,"div",19),e.YNc(84,Eo,7,2,"div",19),e.qZA(),e.qZA(),e.TgZ(85,"div",47),e.TgZ(86,"cd-form-button-panel",48),e.NdJ("submitActionEvent",function(){return e.CHM(_),e.oxw().onSubmit()}),e.ALo(87,"titlecase"),e.ALo(88,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.MAs(2),n=e.oxw();e.xp6(1),e.Q6J("formGroup",n.userForm),e.xp6(6),e.pQV(e.lcZ(6,29,n.action))(e.lcZ(7,31,n.resource)),e.QtT(5),e.xp6(3),e.Q6J("ngClass",e.VKq(37,Je,!n.editing)),e.xp6(3),e.Q6J("readonly",n.editing),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("user_id",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("user_id",_,"pattern")),e.xp6(1),e.Q6J("ngIf",!n.userForm.getValue("show_tenant")&&n.userForm.showError("user_id",_,"notUnique")),e.xp6(4),e.Q6J("readonly",!0),e.xp6(3),e.Q6J("ngIf",n.userForm.getValue("show_tenant")),e.xp6(2),e.Q6J("ngClass",e.VKq(39,Je,!n.editing)),e.xp6(4),e.Q6J("ngIf",n.userForm.showError("display_name",_,"required")),e.xp6(6),e.Q6J("ngIf",n.userForm.showError("email",_,"email")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("email",_,"notUnique")),e.xp6(12),e.Q6J("ngIf",1==n.userForm.get("max_buckets_mode").value),e.xp6(7),e.Q6J("ngIf",!n.editing),e.xp6(1),e.Q6J("ngIf",n.editing),e.xp6(1),e.Q6J("ngIf",n.editing),e.xp6(1),e.Q6J("ngIf",n.editing),e.xp6(10),e.Q6J("ngIf",n.userForm.controls.user_quota_enabled.value),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.user_quota_enabled.value&&!n.userForm.getValue("user_quota_max_size_unlimited")),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.user_quota_enabled.value),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.user_quota_enabled.value&&!n.userForm.getValue("user_quota_max_objects_unlimited")),e.xp6(10),e.Q6J("ngIf",n.userForm.controls.bucket_quota_enabled.value),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.bucket_quota_enabled.value&&!n.userForm.getValue("bucket_quota_max_size_unlimited")),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.bucket_quota_enabled.value),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.bucket_quota_enabled.value&&!n.userForm.getValue("bucket_quota_max_objects_unlimited")),e.xp6(2),e.Q6J("form",n.userForm)("submitText",e.lcZ(87,33,n.action)+" "+e.lcZ(88,35,n.resource))}}let Ve=(()=>{class t extends Ie.E{constructor(_,n,i,s,c,d,E){super(),this.formBuilder=_,this.route=n,this.router=i,this.rgwUserService=s,this.modalService=c,this.notificationService=d,this.actionLabels=E,this.editing=!1,this.submitObservables=[],this.icons=D.P,this.subusers=[],this.s3Keys=[],this.swiftKeys=[],this.capabilities=[],this.showTenant=!1,this.previousTenant=null,this.resource="user",this.subuserLabel="subuser",this.s3keyLabel="S3 Key",this.capabilityLabel="capability",this.editing=this.router.url.startsWith(`/rgw/user/${A.MQ.EDIT}`),this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.CREATE,this.createForm()}createForm(){this.userForm=this.formBuilder.group({user_id:[null,[a.kI.required,a.kI.pattern(/^[a-zA-Z0-9!@#%^&*()_-]+$/)],this.editing?[]:[m.h.unique(this.rgwUserService.exists,this.rgwUserService,()=>this.userForm.getValue("tenant"))]],show_tenant:[this.editing],tenant:[null,[a.kI.pattern(/^[a-zA-Z0-9!@#%^&*()_-]+$/)],this.editing?[]:[m.h.unique(this.rgwUserService.exists,this.rgwUserService,()=>this.userForm.getValue("user_id"),!0)]],display_name:[null,[a.kI.required]],email:[null,[m.h.email],[m.h.unique(this.rgwUserService.emailExists,this.rgwUserService)]],max_buckets_mode:[1],max_buckets:[1e3,[m.h.requiredIf({max_buckets_mode:"1"}),m.h.number(!1)]],suspended:[!1],generate_key:[!0],access_key:[null,[m.h.requiredIf({generate_key:!1})]],secret_key:[null,[m.h.requiredIf({generate_key:!1})]],user_quota_enabled:[!1],user_quota_max_size_unlimited:[!0],user_quota_max_size:[null,[m.h.composeIf({user_quota_enabled:!0,user_quota_max_size_unlimited:!1},[a.kI.required,this.quotaMaxSizeValidator])]],user_quota_max_objects_unlimited:[!0],user_quota_max_objects:[null,[m.h.requiredIf({user_quota_enabled:!0,user_quota_max_objects_unlimited:!1})]],bucket_quota_enabled:[!1],bucket_quota_max_size_unlimited:[!0],bucket_quota_max_size:[null,[m.h.composeIf({bucket_quota_enabled:!0,bucket_quota_max_size_unlimited:!1},[a.kI.required,this.quotaMaxSizeValidator])]],bucket_quota_max_objects_unlimited:[!0],bucket_quota_max_objects:[null,[m.h.requiredIf({bucket_quota_enabled:!0,bucket_quota_max_objects_unlimited:!1})]]})}ngOnInit(){this.route.params.subscribe(_=>{if(!_.hasOwnProperty("uid"))return void this.loadingReady();const n=decodeURIComponent(_.uid),i=[];i.push(this.rgwUserService.get(n)),i.push(this.rgwUserService.getQuota(n)),(0,Y.D)(i).subscribe(s=>{const c=u().clone(this.userForm.value);let d=u().pick(s[0],u().keys(this.userForm.value));switch(d.max_buckets){case-1:d.max_buckets_mode=-1,d.max_buckets="";break;case 0:d.max_buckets_mode=0,d.max_buckets="";break;default:d.max_buckets_mode=1}["user","bucket"].forEach(g=>{const C=s[1][g+"_quota"];d[g+"_quota_enabled"]=C.enabled,C.max_size<0?(d[g+"_quota_max_size_unlimited"]=!0,d[g+"_quota_max_size"]=null):(d[g+"_quota_max_size_unlimited"]=!1,d[g+"_quota_max_size"]=`${C.max_size} B`),C.max_objects<0?(d[g+"_quota_max_objects_unlimited"]=!0,d[g+"_quota_max_objects"]=null):(d[g+"_quota_max_objects_unlimited"]=!1,d[g+"_quota_max_objects"]=C.max_objects)}),d=u().merge(c,d),this.userForm.setValue(d),this.subusers=s[0].subusers,this.s3Keys=s[0].keys,this.swiftKeys=s[0].swift_keys;const E={"read, write":"*"};s[0].caps.forEach(g=>{g.perm in E&&(g.perm=E[g.perm])}),this.capabilities=s[0].caps,this.loadingReady()},()=>{this.loadingError()})})}goToListView(){this.router.navigate(["/rgw/user"])}onSubmit(){let _;if(this.userForm.pristine)return void this.goToListView();const n=this.getUID();if(this.editing){if(this._isGeneralDirty()){const i=this._getUpdateArgs();this.submitObservables.push(this.rgwUserService.update(n,i))}_="Updated Object Gateway user '" + n + "'"}else{const i=this._getCreateArgs();this.submitObservables.push(this.rgwUserService.create(i)),_="Created Object Gateway user '" + n + "'"}if(this._isUserQuotaDirty()){const i=this._getUserQuotaArgs();this.submitObservables.push(this.rgwUserService.updateQuota(n,i))}if(this._isBucketQuotaDirty()){const i=this._getBucketQuotaArgs();this.submitObservables.push(this.rgwUserService.updateQuota(n,i))}(0,Bt.z)(...this.submitObservables).subscribe({error:()=>{this.userForm.setErrors({cdSubmitButton:!0})},complete:()=>{this.notificationService.show(Ae.k.success,_),this.goToListView()}})}updateFieldsWhenTenanted(){this.showTenant=this.userForm.getValue("show_tenant"),this.showTenant?(this.userForm.get("user_id").markAsTouched(),this.previousTenant=this.userForm.get("tenant").value,this.userForm.get("tenant").patchValue(null)):(this.userForm.get("user_id").markAsUntouched(),this.userForm.get("tenant").patchValue(this.previousTenant))}getUID(){var _;let n=this.userForm.getValue("user_id");const i=null===(_=this.userForm)||void 0===_?void 0:_.getValue("tenant");return i&&i.length>0&&(n=`${this.userForm.getValue("tenant")}$${n}`),n}quotaMaxSizeValidator(_){return(0,m.P)(_.value)?null:null===RegExp("^(\\d+(\\.\\d+)?)\\s*(B|K(B|iB)?|M(B|iB)?|G(B|iB)?|T(B|iB)?)?$","i").exec(_.value)||(new Ge.H).toBytes(_.value)<1024?{quotaMaxSize:!0}:null}setSubuser(_,n){const i={"full-control":"full","read-write":"readwrite"},s=this.getUID();this.submitObservables.push(this.rgwUserService.createSubuser(s,{subuser:_.id,access:_.permissions in i?i[_.permissions]:_.permissions,key_type:"swift",secret_key:_.secret_key,generate_secret:_.generate_secret?"true":"false"})),u().isNumber(n)?this.subusers[n]=_:(this.subusers.push(_),this.swiftKeys.push({user:_.id,secret_key:_.generate_secret?"Apply your changes first...":_.secret_key})),this.userForm.markAsDirty()}deleteSubuser(_){const n=this.subusers[_];this.submitObservables.push(this.rgwUserService.deleteSubuser(this.getUID(),n.id)),this.s3Keys=this.s3Keys.filter(i=>i.user!==n.id),this.swiftKeys=this.swiftKeys.filter(i=>i.user!==n.id),this.subusers.splice(_,1),this.userForm.markAsDirty()}setCapability(_,n){const i=this.getUID();if(u().isNumber(n)){const s=this.capabilities[n];this.submitObservables.push(this.rgwUserService.deleteCapability(i,s.type,s.perm)),this.submitObservables.push(this.rgwUserService.addCapability(i,_.type,_.perm)),this.capabilities[n]=_}else this.submitObservables.push(this.rgwUserService.addCapability(i,_.type,_.perm)),this.capabilities=[...this.capabilities,_];this.userForm.markAsDirty()}deleteCapability(_){const n=this.capabilities[_];this.submitObservables.push(this.rgwUserService.deleteCapability(this.getUID(),n.type,n.perm)),this.capabilities.splice(_,1),this.capabilities=[...this.capabilities],this.userForm.markAsDirty()}hasAllCapabilities(_){return!u().difference(ze.getAll(),u().map(_,"type")).length}setS3Key(_,n){if(!u().isNumber(n)){const i=_.user.match(/([^:]+)(:(.+))?/),s=i[1],c={subuser:i[2]?i[3]:"",generate_key:_.generate_key?"true":"false"};"false"===c.generate_key&&(u().isNil(_.access_key)||(c.access_key=_.access_key),u().isNil(_.secret_key)||(c.secret_key=_.secret_key)),this.submitObservables.push(this.rgwUserService.addS3Key(s,c)),this.s3Keys.push({user:_.user,access_key:_.generate_key?"Apply your changes first...":_.access_key,secret_key:_.generate_key?"Apply your changes first...":_.secret_key})}this.userForm.markAsDirty()}deleteS3Key(_){const n=this.s3Keys[_];this.submitObservables.push(this.rgwUserService.deleteS3Key(this.getUID(),n.access_key)),this.s3Keys.splice(_,1),this.userForm.markAsDirty()}showSubuserModal(_){const n=this.getUID(),i=this.modalService.show(mn);if(u().isNumber(_)){const s=this.subusers[_];i.componentInstance.setEditing(),i.componentInstance.setValues(n,s.id,s.permissions)}else i.componentInstance.setEditing(!1),i.componentInstance.setValues(n),i.componentInstance.setSubusers(this.subusers);i.componentInstance.submitAction.subscribe(s=>{this.setSubuser(s,_)})}showS3KeyModal(_){const n=this.modalService.show(Qe);if(u().isNumber(_)){const i=this.s3Keys[_];n.componentInstance.setViewing(),n.componentInstance.setValues(i.user,i.access_key,i.secret_key)}else{const i=this._getS3KeyUserCandidates();n.componentInstance.setViewing(!1),n.componentInstance.setUserCandidates(i),n.componentInstance.submitAction.subscribe(s=>{this.setS3Key(s)})}}showSwiftKeyModal(_){const n=this.modalService.show(Ye),i=this.swiftKeys[_];n.componentInstance.setValues(i.user,i.secret_key)}showCapabilityModal(_){const n=this.modalService.show(en);if(u().isNumber(_)){const i=this.capabilities[_];n.componentInstance.setEditing(),n.componentInstance.setValues(i.type,i.perm)}else n.componentInstance.setEditing(!1),n.componentInstance.setCapabilities(this.capabilities);n.componentInstance.submitAction.subscribe(i=>{this.setCapability(i,_)})}_isGeneralDirty(){return["display_name","email","max_buckets_mode","max_buckets","suspended"].some(_=>this.userForm.get(_).dirty)}_isUserQuotaDirty(){return["user_quota_enabled","user_quota_max_size_unlimited","user_quota_max_size","user_quota_max_objects_unlimited","user_quota_max_objects"].some(_=>this.userForm.get(_).dirty)}_isBucketQuotaDirty(){return["bucket_quota_enabled","bucket_quota_max_size_unlimited","bucket_quota_max_size","bucket_quota_max_objects_unlimited","bucket_quota_max_objects"].some(_=>this.userForm.get(_).dirty)}_getCreateArgs(){const _={uid:this.getUID(),display_name:this.userForm.getValue("display_name"),suspended:this.userForm.getValue("suspended"),email:"",max_buckets:this.userForm.getValue("max_buckets"),generate_key:this.userForm.getValue("generate_key"),access_key:"",secret_key:""},n=this.userForm.getValue("email");u().isString(n)&&n.length>0&&u().merge(_,{email:n}),this.userForm.getValue("generate_key")||u().merge(_,{generate_key:!1,access_key:this.userForm.getValue("access_key"),secret_key:this.userForm.getValue("secret_key")});const s=parseInt(this.userForm.getValue("max_buckets_mode"),10);return u().includes([-1,0],s)&&u().merge(_,{max_buckets:s}),_}_getUpdateArgs(){const _={},n=["display_name","email","max_buckets","suspended"];for(const s of n)_[s]=this.userForm.getValue(s);const i=parseInt(this.userForm.getValue("max_buckets_mode"),10);return u().includes([-1,0],i)&&(_.max_buckets=i),_}_getUserQuotaArgs(){const _={quota_type:"user",enabled:this.userForm.getValue("user_quota_enabled"),max_size_kb:-1,max_objects:-1};if(!this.userForm.getValue("user_quota_max_size_unlimited")){const n=(new Ge.H).toBytes(this.userForm.getValue("user_quota_max_size"));_.max_size_kb=(n/1024).toFixed(0)}return this.userForm.getValue("user_quota_max_objects_unlimited")||(_.max_objects=this.userForm.getValue("user_quota_max_objects")),_}_getBucketQuotaArgs(){const _={quota_type:"bucket",enabled:this.userForm.getValue("bucket_quota_enabled"),max_size_kb:-1,max_objects:-1};if(!this.userForm.getValue("bucket_quota_max_size_unlimited")){const n=(new Ge.H).toBytes(this.userForm.getValue("bucket_quota_max_size"));_.max_size_kb=(n/1024).toFixed(0)}return this.userForm.getValue("bucket_quota_max_objects_unlimited")||(_.max_objects=this.userForm.getValue("bucket_quota_max_objects")),_}_getS3KeyUserCandidates(){let _=[];const n=this.getUID();return u().isString(n)&&!u().isEmpty(n)&&_.push(n),this.subusers.forEach(i=>{_.push(i.id)}),this.s3Keys.forEach(i=>{_.push(i.user)}),_=u().uniq(_),_}onMaxBucketsModeChange(_){"1"===_&&(this.userForm.get("max_buckets").valid||this.userForm.patchValue({max_buckets:1e3}))}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(J.O),e.Y36(w.gz),e.Y36(w.F0),e.Y36(O),e.Y36(Te.Z),e.Y36(ve.g),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-form"]],features:[e.qOj],decls:1,vars:1,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F,T,x,y,S,_e,te,ne,oe,ie,se,ae,re,le,ce,de,ue,Re,R,__,t_,n_,o_,i_,s_,a_,r_,l_,c_,d_,u_,R_,g_,E_,T_,S_,f_,C_,p_,M_,m_,A_,b_,P_,G_;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="User ID",n="Show Tenant",i="Full name",s="Email address",c="Max. buckets",d="Disabled",E="Unlimited",g="Custom",C="Suspended",b="User quota",P="Enabled",G="Bucket quota",N="Enabled",p="This field is required.",U="The value is not valid.",W="The chosen user ID is already in use.",Z="Tenant",$="The value is not valid.",h="The chosen user ID exists in this tenant.",I="This field is required.",v="This is not a valid email address.",F="The chosen email address is already in use.",T="This field is required.",x="The entered value must be >= 1.",y="S3 key",S="Auto-generate key",_e="Access key",te="This field is required.",ne="Secret key",oe="This field is required.",ie="Subusers",se="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",ae="There are no subusers.",re="Edit",le="Delete",ce="Keys",de="S3",ue="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",Re="Swift",R="There are no keys.",__="Show",t_="Delete",n_="There are no keys.",o_="Show",i_="Capabilities",s_="All capabilities are already added.",a_="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",r_="There are no capabilities.",l_="Edit",c_="Delete",d_="Unlimited size",u_="Max. size",R_="This field is required.",g_="The value is not valid.",E_="Unlimited objects",T_="Max. objects",S_="This field is required.",f_="The entered value must be >= 0.",C_="Unlimited size",p_="Max. size",M_="This field is required.",m_="The value is not valid.",A_="Unlimited objects",b_="Max. objects",P_="This field is required.",G_="The entered value must be >= 0.",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"card"],[1,"card-header"],o,[1,"card-body"],[1,"form-group","row"],["for","user_id",1,"cd-col-form-label",3,"ngClass"],_,[1,"cd-col-form-input"],["id","user_id","type","text","formControlName","user_id",1,"form-control",3,"readonly"],["class","invalid-feedback",4,"ngIf"],[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["id","show_tenant","type","checkbox","formControlName","show_tenant",1,"custom-control-input",3,"readonly","click"],["for","show_tenant",1,"custom-control-label"],n,["class","form-group row",4,"ngIf"],["for","display_name",1,"cd-col-form-label",3,"ngClass"],i,["id","display_name","type","text","formControlName","display_name",1,"form-control"],["for","email",1,"cd-col-form-label"],s,["id","email","type","text","formControlName","email",1,"form-control"],["for","max_buckets_mode",1,"cd-col-form-label"],c,["formControlName","max_buckets_mode","name","max_buckets_mode","id","max_buckets_mode",1,"form-control","custom-select",3,"change"],["value","-1"],d,["value","0"],E,["value","1"],g,["id","suspended","type","checkbox","formControlName","suspended",1,"custom-control-input"],["for","suspended",1,"custom-control-label"],C,[4,"ngIf"],b,["id","user_quota_enabled","type","checkbox","formControlName","user_quota_enabled",1,"custom-control-input"],["for","user_quota_enabled",1,"custom-control-label"],P,G,["id","bucket_quota_enabled","type","checkbox","formControlName","bucket_quota_enabled",1,"custom-control-input"],["for","bucket_quota_enabled",1,"custom-control-label"],N,[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],p,U,W,["for","tenant",1,"cd-col-form-label"],Z,["id","tenant","type","text","formControlName","tenant","autofocus","",1,"form-control",3,"readonly"],$,h,I,v,F,[1,"cd-col-form-label"],["id","max_buckets","type","number","formControlName","max_buckets","min","1",1,"form-control"],T,x,y,["id","generate_key","type","checkbox","formControlName","generate_key",1,"custom-control-input"],["for","generate_key",1,"custom-control-label"],S,["for","access_key",1,"cd-col-form-label","required"],_e,[1,"input-group"],["id","access_key","type","password","formControlName","access_key",1,"form-control"],[1,"input-group-append"],["type","button","cdPasswordButton","access_key",1,"btn","btn-light"],["source","access_key"],te,["for","secret_key",1,"cd-col-form-label","required"],ne,["id","secret_key","type","password","formControlName","secret_key",1,"form-control"],["type","button","cdPasswordButton","secret_key",1,"btn","btn-light"],["source","secret_key"],oe,ie,[1,"row"],["class","no-border",4,"ngIf"],[4,"ngFor","ngForOf"],[1,"col-12"],["type","button",1,"btn","btn-light","float-right","tc_addSubuserButton",3,"click"],[3,"ngClass"],se,[1,"help-block"],[1,"no-border"],[1,"form-text","text-muted"],ae,[1,"input-group-prepend"],[1,"input-group-text"],["type","text","readonly","",1,"cd-form-control",3,"value"],[1,"input-group-prepend","border-left-0","border-right-0"],["type","button","ngbTooltip",re,1,"btn","btn-light","tc_showSubuserButton",3,"click"],["type","button","ngbTooltip",le,1,"btn","btn-light","tc_deleteSubuserButton",3,"click"],ce,de,["type","button",1,"btn","btn-light","float-right","tc_addS3KeyButton",3,"click"],ue,Re,R,["type","button","ngbTooltip",__,1,"btn","btn-light","tc_showS3KeyButton",3,"click"],["type","button","ngbTooltip",t_,1,"btn","btn-light","tc_deleteS3KeyButton",3,"click"],n_,["type","button","ngbTooltip",o_,1,"btn","btn-light","tc_showSwiftKeyButton",3,"click"],i_,["type","button","ngbTooltip",s_,"triggers","pointerenter:pointerleave",1,"btn","btn-light","float-right","tc_addCapButton",3,"disabled","disableTooltip","click"],a_,r_,["type","button","ngbTooltip",l_,1,"btn","btn-light","tc_editCapButton",3,"click"],["type","button","ngbTooltip",c_,1,"btn","btn-light","tc_deleteCapButton",3,"click"],["id","user_quota_max_size_unlimited","type","checkbox","formControlName","user_quota_max_size_unlimited",1,"custom-control-input"],["for","user_quota_max_size_unlimited",1,"custom-control-label"],d_,["for","user_quota_max_size",1,"cd-col-form-label","required"],u_,["id","user_quota_max_size","type","text","formControlName","user_quota_max_size","cdDimlessBinary","",1,"form-control"],R_,g_,["id","user_quota_max_objects_unlimited","type","checkbox","formControlName","user_quota_max_objects_unlimited",1,"custom-control-input"],["for","user_quota_max_objects_unlimited",1,"custom-control-label"],E_,["for","user_quota_max_objects",1,"cd-col-form-label","required"],T_,["id","user_quota_max_objects","type","number","formControlName","user_quota_max_objects","min","0",1,"form-control"],S_,f_,["id","bucket_quota_max_size_unlimited","type","checkbox","formControlName","bucket_quota_max_size_unlimited",1,"custom-control-input"],["for","bucket_quota_max_size_unlimited",1,"custom-control-label"],C_,["for","bucket_quota_max_size",1,"cd-col-form-label","required"],p_,["id","bucket_quota_max_size","type","text","formControlName","bucket_quota_max_size","cdDimlessBinary","",1,"form-control"],M_,m_,["id","bucket_quota_max_objects_unlimited","type","checkbox","formControlName","bucket_quota_max_objects_unlimited",1,"custom-control-input"],["for","bucket_quota_max_objects_unlimited",1,"custom-control-label"],A_,["for","bucket_quota_max_objects",1,"cd-col-form-label","required"],b_,["id","bucket_quota_max_objects","type","number","formControlName","bucket_quota_max_objects","min","0",1,"form-control"],P_,G_]},template:function(_,n){1&_&&e.YNc(0,To,89,41,"div",0),2&_&&e.Q6J("cdFormLoading",n.loading)},directives:[Fe.y,a._Y,a.JL,V.V,a.sg,H.P,f.mk,q.o,a.Fj,X.b,a.JJ,a.u,f.O5,a.Wl,a.EJ,a.YN,a.Kr,ee.p,j.U,a.wV,a.qQ,Ce.C,pe.s,f.sg,M._L,bn.Q],pipes:[f.rS,K.m,Ue.i],styles:[""]}),t})();var je=r(99466),So=r(78877),fo=r(86969);const Co=["accessKeyTpl"],po=["secretKeyTpl"];function Mo(t,o){if(1&t&&(e.TgZ(0,"tr"),e.TgZ(1,"td",15),e.SDv(2,20),e.qZA(),e.TgZ(3,"td"),e._uU(4),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(4),e.Oqu(_.user.email)}}function mo(t,o){if(1&t&&(e.TgZ(0,"div"),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.xp6(1),e.AsE(" ",_.id," (",_.permissions,") ")}}function Ao(t,o){if(1&t&&(e.TgZ(0,"tr"),e.TgZ(1,"td",15),e.SDv(2,21),e.qZA(),e.TgZ(3,"td"),e.YNc(4,mo,2,2,"div",22),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(4),e.Q6J("ngForOf",_.user.subusers)}}function bo(t,o){if(1&t&&(e.TgZ(0,"div"),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.xp6(1),e.AsE(" ",_.type," (",_.perm,") ")}}function Po(t,o){if(1&t&&(e.TgZ(0,"tr"),e.TgZ(1,"td",15),e.SDv(2,23),e.qZA(),e.TgZ(3,"td"),e.YNc(4,bo,2,2,"div",22),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(4),e.Q6J("ngForOf",_.user.caps)}}function Go(t,o){if(1&t&&(e.TgZ(0,"tr"),e.TgZ(1,"td",15),e.SDv(2,24),e.qZA(),e.TgZ(3,"td"),e._uU(4),e.ALo(5,"join"),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(4),e.Oqu(e.lcZ(5,1,_.user.mfa_ids))}}function No(t,o){1&t&&(e.TgZ(0,"td"),e._uU(1,"-"),e.qZA())}function Oo(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,29),e.qZA())}function Uo(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.ALo(2,"dimlessBinary"),e.qZA()),2&t){const _=e.oxw(5);e.xp6(1),e.hij(" ",e.lcZ(2,1,_.user.user_quota.max_size)," ")}}function Wo(t,o){1&t&&(e.TgZ(0,"td"),e._uU(1,"-"),e.qZA())}function Zo(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,30),e.qZA())}function $o(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.qZA()),2&t){const _=e.oxw(5);e.xp6(1),e.hij(" ",_.user.user_quota.max_objects," ")}}function ho(t,o){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"legend"),e.SDv(2,25),e.qZA(),e.TgZ(3,"table",9),e.TgZ(4,"tbody"),e.TgZ(5,"tr"),e.TgZ(6,"td",10),e.SDv(7,26),e.qZA(),e.TgZ(8,"td",12),e._uU(9),e.ALo(10,"booleanText"),e.qZA(),e.qZA(),e.TgZ(11,"tr"),e.TgZ(12,"td",15),e.SDv(13,27),e.qZA(),e.YNc(14,No,2,0,"td",0),e.YNc(15,Oo,2,0,"td",0),e.YNc(16,Uo,3,3,"td",0),e.qZA(),e.TgZ(17,"tr"),e.TgZ(18,"td",15),e.SDv(19,28),e.qZA(),e.YNc(20,Wo,2,0,"td",0),e.YNc(21,Zo,2,0,"td",0),e.YNc(22,$o,2,1,"td",0),e.qZA(),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(9),e.Oqu(e.lcZ(10,7,_.user.user_quota.enabled)),e.xp6(5),e.Q6J("ngIf",!_.user.user_quota.enabled),e.xp6(1),e.Q6J("ngIf",_.user.user_quota.enabled&&_.user.user_quota.max_size<=-1),e.xp6(1),e.Q6J("ngIf",_.user.user_quota.enabled&&_.user.user_quota.max_size>-1),e.xp6(4),e.Q6J("ngIf",!_.user.user_quota.enabled),e.xp6(1),e.Q6J("ngIf",_.user.user_quota.enabled&&_.user.user_quota.max_objects<=-1),e.xp6(1),e.Q6J("ngIf",_.user.user_quota.enabled&&_.user.user_quota.max_objects>-1)}}function Io(t,o){1&t&&(e.TgZ(0,"td"),e._uU(1,"-"),e.qZA())}function vo(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,35),e.qZA())}function Fo(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.ALo(2,"dimlessBinary"),e.qZA()),2&t){const _=e.oxw(5);e.xp6(1),e.hij(" ",e.lcZ(2,1,_.user.bucket_quota.max_size)," ")}}function Lo(t,o){1&t&&(e.TgZ(0,"td"),e._uU(1,"-"),e.qZA())}function Do(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,36),e.qZA())}function xo(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.qZA()),2&t){const _=e.oxw(5);e.xp6(1),e.hij(" ",_.user.bucket_quota.max_objects," ")}}function yo(t,o){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"legend"),e.SDv(2,31),e.qZA(),e.TgZ(3,"table",9),e.TgZ(4,"tbody"),e.TgZ(5,"tr"),e.TgZ(6,"td",10),e.SDv(7,32),e.qZA(),e.TgZ(8,"td",12),e._uU(9),e.ALo(10,"booleanText"),e.qZA(),e.qZA(),e.TgZ(11,"tr"),e.TgZ(12,"td",15),e.SDv(13,33),e.qZA(),e.YNc(14,Io,2,0,"td",0),e.YNc(15,vo,2,0,"td",0),e.YNc(16,Fo,3,3,"td",0),e.qZA(),e.TgZ(17,"tr"),e.TgZ(18,"td",15),e.SDv(19,34),e.qZA(),e.YNc(20,Lo,2,0,"td",0),e.YNc(21,Do,2,0,"td",0),e.YNc(22,xo,2,1,"td",0),e.qZA(),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(9),e.Oqu(e.lcZ(10,7,_.user.bucket_quota.enabled)),e.xp6(5),e.Q6J("ngIf",!_.user.bucket_quota.enabled),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota.enabled&&_.user.bucket_quota.max_size<=-1),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota.enabled&&_.user.bucket_quota.max_size>-1),e.xp6(4),e.Q6J("ngIf",!_.user.bucket_quota.enabled),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota.enabled&&_.user.bucket_quota.max_objects<=-1),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota.enabled&&_.user.bucket_quota.max_objects>-1)}}function qo(t,o){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"table",9),e.TgZ(2,"tbody"),e.TgZ(3,"tr"),e.TgZ(4,"td",10),e.SDv(5,11),e.qZA(),e.TgZ(6,"td",12),e._uU(7),e.qZA(),e.qZA(),e.TgZ(8,"tr"),e.TgZ(9,"td",10),e.SDv(10,13),e.qZA(),e.TgZ(11,"td",12),e._uU(12),e.qZA(),e.qZA(),e.TgZ(13,"tr"),e.TgZ(14,"td",10),e.SDv(15,14),e.qZA(),e.TgZ(16,"td",12),e._uU(17),e.qZA(),e.qZA(),e.TgZ(18,"tr"),e.TgZ(19,"td",15),e.SDv(20,16),e.qZA(),e.TgZ(21,"td"),e._uU(22),e.qZA(),e.qZA(),e.YNc(23,Mo,5,1,"tr",0),e.TgZ(24,"tr"),e.TgZ(25,"td",15),e.SDv(26,17),e.qZA(),e.TgZ(27,"td"),e._uU(28),e.ALo(29,"booleanText"),e.qZA(),e.qZA(),e.TgZ(30,"tr"),e.TgZ(31,"td",15),e.SDv(32,18),e.qZA(),e.TgZ(33,"td"),e._uU(34),e.ALo(35,"booleanText"),e.qZA(),e.qZA(),e.TgZ(36,"tr"),e.TgZ(37,"td",15),e.SDv(38,19),e.qZA(),e.TgZ(39,"td"),e._uU(40),e.ALo(41,"map"),e.qZA(),e.qZA(),e.YNc(42,Ao,5,1,"tr",0),e.YNc(43,Po,5,1,"tr",0),e.YNc(44,Go,6,3,"tr",0),e.qZA(),e.qZA(),e.YNc(45,ho,23,9,"div",0),e.YNc(46,yo,23,9,"div",0),e.qZA()),2&t){const _=e.oxw(3);e.xp6(7),e.Oqu(_.user.tenant),e.xp6(5),e.Oqu(_.user.user_id),e.xp6(5),e.Oqu(_.user.uid),e.xp6(5),e.Oqu(_.user.display_name),e.xp6(1),e.Q6J("ngIf",null==_.user.email?null:_.user.email.length),e.xp6(5),e.Oqu(e.lcZ(29,13,_.user.suspended)),e.xp6(6),e.Oqu(e.lcZ(35,15,"true"===_.user.system)),e.xp6(6),e.Oqu(e.xi3(41,17,_.user.max_buckets,_.maxBucketsMap)),e.xp6(2),e.Q6J("ngIf",_.user.subusers&&_.user.subusers.length),e.xp6(1),e.Q6J("ngIf",_.user.caps&&_.user.caps.length),e.xp6(1),e.Q6J("ngIf",null==_.user.mfa_ids?null:_.user.mfa_ids.length),e.xp6(1),e.Q6J("ngIf",_.user.user_quota),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota)}}function wo(t,o){if(1&t&&e.YNc(0,qo,47,20,"div",0),2&t){const _=e.oxw(2);e.Q6J("ngIf",_.user)}}const ko=function(t){return[t]};function Bo(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"cd-table",39),e.NdJ("updateSelection",function(i){return e.CHM(_),e.oxw(3).updateKeysSelection(i)}),e.TgZ(1,"div",40),e.TgZ(2,"div",41),e.TgZ(3,"button",42),e.NdJ("click",function(){return e.CHM(_),e.oxw(3).showKeyModal()}),e._UZ(4,"i",43),e.ynx(5),e.SDv(6,44),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(3);e.Q6J("data",_.keys)("columns",_.keysColumns),e.xp6(3),e.Q6J("disabled",!_.keysSelection.hasSingleSelection),e.xp6(1),e.Q6J("ngClass",e.VKq(4,ko,_.icons.show))}}function Ho(t,o){1&t&&(e.TgZ(0,"li",37),e.TgZ(1,"a",4),e.SDv(2,38),e.qZA(),e.YNc(3,Bo,7,6,"ng-template",6),e.qZA())}function Xo(t,o){if(1&t&&(e.ynx(0),e.TgZ(1,"ul",1,2),e.TgZ(3,"li",3),e.TgZ(4,"a",4),e.SDv(5,5),e.qZA(),e.YNc(6,wo,1,1,"ng-template",6),e.qZA(),e.YNc(7,Ho,4,0,"li",7),e.qZA(),e._UZ(8,"div",8),e.BQk()),2&t){const _=e.MAs(2),n=e.oxw();e.xp6(7),e.Q6J("ngIf",n.keys.length),e.xp6(1),e.Q6J("ngbNavOutlet",_)}}let Ko=(()=>{class t{constructor(_,n){this.rgwUserService=_,this.modalService=n,this.keys=[],this.keysColumns=[],this.keysSelection=new Pe.r,this.icons=D.P}ngOnInit(){this.keysColumns=[{name:"Username",prop:"username",flexGrow:1},{name:"Type",prop:"type",flexGrow:1}],this.maxBucketsMap={"-1":"Disabled",0:"Unlimited"}}ngOnChanges(){this.selection&&(this.user=this.selection,this.user.subusers=u().sortBy(this.user.subusers,"id"),this.user.caps=u().sortBy(this.user.caps,"type"),this.rgwUserService.getQuota(this.user.uid).subscribe(_=>{u().extend(this.user,_)}),this.keys=[],this.user.keys&&this.user.keys.forEach(_=>{this.keys.push({id:this.keys.length+1,type:"S3",username:_.user,ref:_})}),this.user.swift_keys&&this.user.swift_keys.forEach(_=>{this.keys.push({id:this.keys.length+1,type:"Swift",username:_.user,ref:_})}),this.keys=u().sortBy(this.keys,"user"))}updateKeysSelection(_){this.keysSelection=_}showKeyModal(){const _=this.keysSelection.first(),n=this.modalService.show("S3"===_.type?Qe:Ye);switch(_.type){case"S3":n.componentInstance.setViewing(),n.componentInstance.setValues(_.ref.user,_.ref.access_key,_.ref.secret_key);break;case"Swift":n.componentInstance.setValues(_.ref.user,_.ref.secret_key)}}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(O),e.Y36(Te.Z))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-details"]],viewQuery:function(_,n){if(1&_&&(e.Gf(Co,5),e.Gf(po,5)),2&_){let i;e.iGM(i=e.CRH())&&(n.accessKeyTpl=i.first),e.iGM(i=e.CRH())&&(n.secretKeyTpl=i.first)}},inputs:{selection:"selection"},features:[e.TTD],decls:1,vars:1,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F,T,x,y;return o="Details",_="Tenant",n="User ID",i="Username",s="Full name",c="Suspended",d="System",E="Maximum buckets",g="Email address",C="Subusers",b="Capabilities",P="MFAs(Id)",G="User quota",N="Enabled",p="Maximum size",U="Maximum objects",W="Unlimited",Z="Unlimited",$="Bucket quota",h="Enabled",I="Maximum size",v="Maximum objects",F="Unlimited",T="Unlimited",x="Keys",y="Show",[[4,"ngIf"],["ngbNav","","cdStatefulTab","rgw-user-details",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","details"],["ngbNavLink",""],o,["ngbNavContent",""],["ngbNavItem","keys",4,"ngIf"],[3,"ngbNavOutlet"],[1,"table","table-striped","table-bordered"],[1,"bold","w-25"],_,[1,"w-75"],n,i,[1,"bold"],s,c,d,E,g,C,[4,"ngFor","ngForOf"],b,P,G,N,p,U,W,Z,$,h,I,v,F,T,["ngbNavItem","keys"],x,["columnMode","flex","selectionType","multi","forceIdentifier","true",3,"data","columns","updateSelection"],[1,"table-actions"],["dropdown","",1,"btn-group"],["type","button",1,"btn","btn-accent",3,"disabled","click"],[3,"ngClass"],y]},template:function(_,n){1&_&&e.YNc(0,Xo,9,2,"ng-container",0),2&_&&e.Q6J("ngIf",n.selection)},directives:[f.O5,M.Pz,Xe.m,M.nv,M.Vx,M.uN,M.tO,f.sg,z.a,q.o,f.mk],pipes:[Be.T,So.b,fo.A,qe.$],styles:[""]}),t})();const zo=["userSizeTpl"],Qo=["userObjectTpl"];function Yo(t,o){if(1&t&&e._UZ(0,"cd-usage-bar",8),2&t){const _=e.oxw().row;e.Q6J("total",_.user_quota.max_size)("used",_.stats.size_actual)}}function Jo(t,o){1&t&&e.SDv(0,9)}function Vo(t,o){if(1&t&&(e.YNc(0,Yo,1,2,"cd-usage-bar",6),e.YNc(1,Jo,1,0,"ng-template",null,7,e.W1O)),2&t){const _=o.row,n=e.MAs(2);e.Q6J("ngIf",_.user_quota.max_size>0&&_.user_quota.enabled)("ngIfElse",n)}}function jo(t,o){if(1&t&&e._UZ(0,"cd-usage-bar",12),2&t){const _=e.oxw().row;e.Q6J("total",_.user_quota.max_objects)("used",_.stats.num_objects)("isBinary",!1)}}function ei(t,o){1&t&&e.SDv(0,13)}function _i(t,o){if(1&t&&(e.YNc(0,jo,1,3,"cd-usage-bar",10),e.YNc(1,ei,1,0,"ng-template",null,11,e.W1O)),2&t){const _=o.row,n=e.MAs(2);e.Q6J("ngIf",_.user_quota.max_objects>0&&_.user_quota.enabled)("ngIfElse",n)}}let ni=(()=>{class t extends be.o{constructor(_,n,i,s,c,d){super(d),this.authStorageService=_,this.rgwUserService=n,this.modalService=i,this.urlBuilder=s,this.actionLabels=c,this.ngZone=d,this.columns=[],this.users=[],this.selection=new Pe.r}ngOnInit(){this.permission=this.authStorageService.getPermissions().rgw,this.columns=[{name:"Username",prop:"uid",flexGrow:1},{name:"Tenant",prop:"tenant",flexGrow:1},{name:"Full name",prop:"display_name",flexGrow:1},{name:"Email address",prop:"email",flexGrow:1},{name:"Suspended",prop:"suspended",flexGrow:1,cellClass:"text-center",cellTransformation:je.e.checkIcon},{name:"Max. buckets",prop:"max_buckets",flexGrow:1,cellTransformation:je.e.map,customTemplateConfig:{"-1":"Disabled",0:"Unlimited"}},{name:"Capacity Limit %",prop:"size_usage",cellTemplate:this.userSizeTpl,flexGrow:.8},{name:"Object Limit %",prop:"object_usage",cellTemplate:this.userObjectTpl,flexGrow:.8}];const _=()=>this.selection.first()&&`${encodeURIComponent(this.selection.first().uid)}`;this.tableActions=[{permission:"create",icon:D.P.add,routerLink:()=>this.urlBuilder.getCreate(),name:this.actionLabels.CREATE,canBePrimary:c=>!c.hasSelection},{permission:"update",icon:D.P.edit,routerLink:()=>this.urlBuilder.getEdit(_()),name:this.actionLabels.EDIT},{permission:"delete",icon:D.P.destroy,click:()=>this.deleteAction(),disable:()=>!this.selection.hasSelection,name:this.actionLabels.DELETE,canBePrimary:c=>c.hasMultiSelection}],this.setTableRefreshTimeout()}getUserList(_){this.setTableRefreshTimeout(),this.rgwUserService.list().subscribe(n=>{this.users=n},()=>{_.error()})}updateSelection(_){this.selection=_}deleteAction(){this.modalService.show(ye.M,{itemDescription:this.selection.hasSingleSelection?"user":"users",itemNames:this.selection.selected.map(_=>_.uid),submitActionObservable:()=>new xe.y(_=>{(0,Y.D)(this.selection.selected.map(n=>this.rgwUserService.delete(n.uid))).subscribe({error:n=>{_.error(n),this.table.refreshBtn()},complete:()=>{_.complete(),this.table.refreshBtn()}})})})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(Ee.j),e.Y36(O),e.Y36(Te.Z),e.Y36(Q.F),e.Y36(A.p4),e.Y36(e.R0b))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-list"]],viewQuery:function(_,n){if(1&_&&(e.Gf(z.a,7),e.Gf(zo,7),e.Gf(Qo,7)),2&_){let i;e.iGM(i=e.CRH())&&(n.table=i.first),e.iGM(i=e.CRH())&&(n.userSizeTpl=i.first),e.iGM(i=e.CRH())&&(n.userObjectTpl=i.first)}},features:[e._Bn([{provide:Q.F,useValue:new Q.F("rgw/user")}]),e.qOj],decls:8,vars:9,consts:function(){let o,_;return o="No Limit",_="No Limit",[["columnMode","flex","selectionType","multiClick","identifier","uid",3,"autoReload","data","columns","hasDetails","status","setExpandedRow","updateSelection","fetchData"],["table",""],[1,"table-actions",3,"permission","selection","tableActions"],["cdTableDetail","",3,"selection"],["userSizeTpl",""],["userObjectTpl",""],[3,"total","used",4,"ngIf","ngIfElse"],["noSizeQuota",""],[3,"total","used"],o,[3,"total","used","isBinary",4,"ngIf","ngIfElse"],["noObjectQuota",""],[3,"total","used","isBinary"],_]},template:function(_,n){1&_&&(e.TgZ(0,"cd-table",0,1),e.NdJ("setExpandedRow",function(s){return n.setExpandedRow(s)})("updateSelection",function(s){return n.updateSelection(s)})("fetchData",function(s){return n.getUserList(s)}),e._UZ(2,"cd-table-actions",2),e._UZ(3,"cd-rgw-user-details",3),e.qZA(),e.YNc(4,Vo,3,2,"ng-template",null,4,e.W1O),e.YNc(6,_i,3,2,"ng-template",null,5,e.W1O)),2&_&&(e.Q6J("autoReload",!1)("data",n.users)("columns",n.columns)("hasDetails",!0)("status",n.tableStatus),e.xp6(2),e.Q6J("permission",n.permission)("selection",n.selection)("tableActions",n.tableActions),e.xp6(1),e.Q6J("selection",n.expandedRow))},directives:[z.a,ke.K,Ko,f.O5,He.O],styles:[""]}),t})(),e_=(()=>{class t{}return t.\u0275fac=function(_){return new(_||t)},t.\u0275mod=e.oAB({type:t}),t.\u0275inj=e.cJS({imports:[[f.ez,N_.m,a.u5,a.UX,O_.B,M.Oz,w.Bz,M.HK,Ue.b]]}),t})();const oi=[{path:""},{path:"daemon",component:kt,data:{breadcrumbs:"Daemons"}},{path:"user",data:{breadcrumbs:"Users"},children:[{path:"",component:ni},{path:A.MQ.CREATE,component:Ve,data:{breadcrumbs:A.Qn.CREATE}},{path:`${A.MQ.EDIT}/:uid`,component:Ve,data:{breadcrumbs:A.Qn.EDIT}}]},{path:"bucket",data:{breadcrumbs:"Buckets"},children:[{path:"",component:Ot},{path:A.MQ.CREATE,component:De,data:{breadcrumbs:A.Qn.CREATE}},{path:`${A.MQ.EDIT}/:bid`,component:De,data:{breadcrumbs:A.Qn.EDIT}}]}];let ii=(()=>{class t{}return t.\u0275fac=function(_){return new(_||t)},t.\u0275mod=e.oAB({type:t}),t.\u0275inj=e.cJS({imports:[[e_,w.Bz.forChild(oi)]]}),t})()}}]); \ No newline at end of file diff --git a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/281.cd14092ccedeaf2d7d79.js b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/281.cd14092ccedeaf2d7d79.js new file mode 100644 index 000000000..a064d6a98 --- /dev/null +++ b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/281.cd14092ccedeaf2d7d79.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkceph_dashboard=self.webpackChunkceph_dashboard||[]).push([[281],{59281:(si,Oe,r)=>{r.r(Oe),r.d(Oe,{RgwModule:()=>e_,RoutedRgwModule:()=>ii});var f=r(12057),a=r(24751),w=r(6283),M=r(38549),Ue=r(37496),A=r(79512),N_=r(44466),O_=r(66265),U_=r(23815),u=r.n(U_),Y=r(35758),Me=r(95152),We=r(33394),Ze=r(64762),$e=r(58497),me=r(25917),he=r(19773),W_=r(96736),Z_=r(5304),ge=r(20523),$_=r(93523),e=r(74788);let O=class{constructor(o,_){this.http=o,this.rgwDaemonService=_,this.url="api/rgw/user"}list(){return this.enumerate().pipe((0,he.zg)(o=>o.length>0?(0,Y.D)(o.map(_=>this.get(_))):(0,me.of)([])))}enumerate(){return this.rgwDaemonService.request(o=>this.http.get(this.url,{params:o}))}enumerateEmail(){return this.rgwDaemonService.request(o=>this.http.get(`${this.url}/get_emails`,{params:o}))}get(o){return this.rgwDaemonService.request(_=>this.http.get(`${this.url}/${o}`,{params:_}))}getQuota(o){return this.rgwDaemonService.request(_=>this.http.get(`${this.url}/${o}/quota`,{params:_}))}create(o){return this.rgwDaemonService.request(_=>(u().keys(o).forEach(n=>{_=_.append(n,o[n])}),this.http.post(this.url,null,{params:_})))}update(o,_){return this.rgwDaemonService.request(n=>(u().keys(_).forEach(i=>{n=n.append(i,_[i])}),this.http.put(`${this.url}/${o}`,null,{params:n})))}updateQuota(o,_){return this.rgwDaemonService.request(n=>(u().keys(_).forEach(i=>{n=n.append(i,_[i])}),this.http.put(`${this.url}/${o}/quota`,null,{params:n})))}delete(o){return this.rgwDaemonService.request(_=>this.http.delete(`${this.url}/${o}`,{params:_}))}createSubuser(o,_){return this.rgwDaemonService.request(n=>(u().keys(_).forEach(i=>{n=n.append(i,_[i])}),this.http.post(`${this.url}/${o}/subuser`,null,{params:n})))}deleteSubuser(o,_){return this.rgwDaemonService.request(n=>this.http.delete(`${this.url}/${o}/subuser/${_}`,{params:n}))}addCapability(o,_,n){return this.rgwDaemonService.request(i=>(i=(i=i.append("type",_)).append("perm",n),this.http.post(`${this.url}/${o}/capability`,null,{params:i})))}deleteCapability(o,_,n){return this.rgwDaemonService.request(i=>(i=(i=i.append("type",_)).append("perm",n),this.http.delete(`${this.url}/${o}/capability`,{params:i})))}addS3Key(o,_){return this.rgwDaemonService.request(n=>(n=n.append("key_type","s3"),u().keys(_).forEach(i=>{n=n.append(i,_[i])}),this.http.post(`${this.url}/${o}/key`,null,{params:n})))}deleteS3Key(o,_){return this.rgwDaemonService.request(n=>(n=(n=n.append("key_type","s3")).append("access_key",_),this.http.delete(`${this.url}/${o}/key`,{params:n})))}exists(o){return this.get(o).pipe((0,W_.h)(!0),(0,Z_.K)(_=>(u().isFunction(_.preventDefault)&&_.preventDefault(),(0,me.of)(!1))))}emailExists(o){return o=decodeURIComponent(o),this.enumerateEmail().pipe((0,he.zg)(_=>{const n=u().indexOf(_,o);return(0,me.of)(-1!==n)}))}};O.\u0275fac=function(o){return new(o||O)(e.LFG($e.eN),e.LFG(ge.b))},O.\u0275prov=e.Yz7({token:O,factory:O.\u0275fac,providedIn:"root"}),O=(0,Ze.gn)([$_.o,(0,Ze.w6)("design:paramtypes",[$e.eN,ge.b])],O);var D=r(65862),Ae=r(18001),Ie=r(93614),m=r(77205),ve=r(97161),k=(()=>{return(t=k||(k={})).ENABLED="Enabled",t.DISABLED="Disabled",k;var t})(),B=(()=>{return(t=B||(B={})).ENABLED="Enabled",t.SUSPENDED="Suspended",B;var t})(),J=r(62862),Fe=r(63622),V=r(41582),H=r(56310),q=r(87925),X=r(94276),j=r(82945),h_=r(18372),ee=r(30839),K=r(10545);function I_(t,o){1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",35),e.SDv(2,36),e.qZA(),e.TgZ(3,"div",12),e._UZ(4,"input",37),e.qZA(),e.qZA())}function v_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,39),e.qZA())}function F_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,40),e.qZA())}function L_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,41),e.qZA())}function D_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,42),e.qZA())}function x_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,43),e.qZA())}function y_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,44),e.qZA())}function q_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,45),e.qZA())}function w_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,46),e.qZA())}function k_(t,o){1&t&&(e.TgZ(0,"option",47),e.SDv(1,48),e.qZA()),2&t&&e.Q6J("ngValue",null)}function B_(t,o){1&t&&(e.TgZ(0,"option",47),e.SDv(1,49),e.qZA()),2&t&&e.Q6J("ngValue",null)}function H_(t,o){if(1&t&&(e.TgZ(0,"option",50),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.Oqu(_)}}function X_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,51),e.qZA())}function K_(t,o){1&t&&(e.TgZ(0,"option",47),e.SDv(1,53),e.qZA()),2&t&&e.Q6J("ngValue",null)}function z_(t,o){1&t&&(e.TgZ(0,"option",47),e.SDv(1,54),e.qZA()),2&t&&e.Q6J("ngValue",null)}function Q_(t,o){if(1&t&&(e.TgZ(0,"option",50),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_.name),e.xp6(1),e.Oqu(_.description)}}function Y_(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,55),e.qZA())}function J_(t,o){if(1&t&&(e.TgZ(0,"select",52),e.YNc(1,K_,2,1,"option",18),e.YNc(2,z_,2,1,"option",18),e.YNc(3,Q_,2,2,"option",19),e.qZA(),e.YNc(4,Y_,2,0,"span",14)),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(1),e.Q6J("ngIf",null===n.placementTargets),e.xp6(1),e.Q6J("ngIf",null!==n.placementTargets),e.xp6(1),e.Q6J("ngForOf",n.placementTargets),e.xp6(1),e.Q6J("ngIf",n.bucketForm.showError("placement-target",_,"required"))}}function V_(t,o){1&t&&(e.ynx(0),e._UZ(1,"input",56),e.BQk())}function j_(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend",25),e.SDv(2,57),e.qZA(),e.TgZ(3,"div",9),e.TgZ(4,"div",27),e.TgZ(5,"div",28),e.TgZ(6,"input",58),e.NdJ("change",function(){return e.CHM(_),e.oxw(2).setMfaDeleteValidators()}),e.qZA(),e.TgZ(7,"label",59),e.SDv(8,60),e.qZA(),e.TgZ(9,"cd-helper"),e.TgZ(10,"span"),e.SDv(11,61),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}}function et(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,70),e.qZA())}function _t(t,o){if(1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",67),e.SDv(2,68),e.qZA(),e.TgZ(3,"div",12),e._UZ(4,"input",69),e.YNc(5,et,2,0,"span",14),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.bucketForm.showError("mfa-token-serial",_,"required"))}}function tt(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,74),e.qZA())}function nt(t,o){if(1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",71),e.SDv(2,72),e.qZA(),e.TgZ(3,"div",12),e._UZ(4,"input",73),e.YNc(5,tt,2,0,"span",14),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.bucketForm.showError("mfa-token-pin",_,"required"))}}function ot(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend",25),e.SDv(2,62),e.qZA(),e.TgZ(3,"div",9),e.TgZ(4,"div",27),e.TgZ(5,"div",28),e.TgZ(6,"input",63),e.NdJ("change",function(){return e.CHM(_),e.oxw(2).setMfaDeleteValidators()}),e.qZA(),e.TgZ(7,"label",64),e.SDv(8,65),e.qZA(),e.TgZ(9,"cd-helper"),e.TgZ(10,"span"),e.SDv(11,66),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(12,_t,6,1,"div",8),e.YNc(13,nt,6,1,"div",8),e.qZA()}if(2&t){const _=e.oxw(2);e.xp6(12),e.Q6J("ngIf",_.areMfaCredentialsRequired()),e.xp6(1),e.Q6J("ngIf",_.areMfaCredentialsRequired())}}function it(t,o){1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",75),e.SDv(2,76),e.qZA(),e.TgZ(3,"div",12),e.TgZ(4,"select",77),e.TgZ(5,"option",78),e.SDv(6,79),e.qZA(),e.TgZ(7,"option",80),e.SDv(8,81),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function st(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,86),e.qZA())}function at(t,o){1&t&&(e.TgZ(0,"span",38),e.SDv(1,87),e.qZA())}function rt(t,o){if(1&t&&(e.TgZ(0,"div",9),e.TgZ(1,"label",82),e.ynx(2),e.SDv(3,83),e.BQk(),e.TgZ(4,"cd-helper"),e.SDv(5,84),e.qZA(),e.qZA(),e.TgZ(6,"div",12),e._UZ(7,"input",85),e.YNc(8,st,2,0,"span",14),e.YNc(9,at,2,0,"span",14),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(8),e.Q6J("ngIf",n.bucketForm.showError("lock_retention_period_days",_,"pattern")),e.xp6(1),e.Q6J("ngIf",n.bucketForm.showError("lock_retention_period_days",_,"lockDays"))}}const Le=function(t){return{required:t}};function lt(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.YNc(9,I_,5,0,"div",8),e.TgZ(10,"div",9),e.TgZ(11,"label",10),e.SDv(12,11),e.qZA(),e.TgZ(13,"div",12),e._UZ(14,"input",13),e.YNc(15,v_,2,0,"span",14),e.YNc(16,F_,2,0,"span",14),e.YNc(17,L_,2,0,"span",14),e.YNc(18,D_,2,0,"span",14),e.YNc(19,x_,2,0,"span",14),e.YNc(20,y_,2,0,"span",14),e.YNc(21,q_,2,0,"span",14),e.YNc(22,w_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(23,"div",9),e.TgZ(24,"label",15),e.SDv(25,16),e.qZA(),e.TgZ(26,"div",12),e.TgZ(27,"select",17),e.YNc(28,k_,2,1,"option",18),e.YNc(29,B_,2,1,"option",18),e.YNc(30,H_,2,2,"option",19),e.qZA(),e.YNc(31,X_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(32,"div",9),e.TgZ(33,"label",20),e.SDv(34,21),e.qZA(),e.TgZ(35,"div",12),e.YNc(36,J_,5,4,"ng-template",null,22,e.W1O),e.YNc(38,V_,2,0,"ng-container",23),e.qZA(),e.qZA(),e.YNc(39,j_,12,0,"fieldset",24),e.YNc(40,ot,14,2,"fieldset",24),e.TgZ(41,"fieldset"),e.TgZ(42,"legend",25),e.SDv(43,26),e.qZA(),e.TgZ(44,"div",9),e.TgZ(45,"div",27),e.TgZ(46,"div",28),e._UZ(47,"input",29),e.TgZ(48,"label",30),e.SDv(49,31),e.qZA(),e.TgZ(50,"cd-helper"),e.TgZ(51,"span"),e.SDv(52,32),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(53,it,9,0,"div",8),e.YNc(54,rt,10,2,"div",8),e.qZA(),e.qZA(),e.TgZ(55,"div",33),e.TgZ(56,"cd-form-button-panel",34),e.NdJ("submitActionEvent",function(){return e.CHM(_),e.oxw().submit()}),e.ALo(57,"titlecase"),e.ALo(58,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.MAs(2),n=e.MAs(37),i=e.oxw();e.xp6(1),e.Q6J("formGroup",i.bucketForm),e.xp6(6),e.pQV(e.lcZ(6,29,i.action))(e.lcZ(7,31,i.resource)),e.QtT(5),e.xp6(2),e.Q6J("ngIf",i.editing),e.xp6(2),e.Q6J("ngClass",e.VKq(37,Le,!i.editing)),e.xp6(3),e.Q6J("readonly",i.editing)("autofocus",!i.editing),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"required")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"bucketNameInvalid")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"bucketNameNotAllowed")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"containsUpperCase")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"lowerCaseOrNumber")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"ipAddress")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"onlyLowerCaseAndNumbers")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("bid",_,"shouldBeInRange")),e.xp6(5),e.Q6J("autofocus",i.editing),e.xp6(1),e.Q6J("ngIf",null===i.owners),e.xp6(1),e.Q6J("ngIf",null!==i.owners),e.xp6(1),e.Q6J("ngForOf",i.owners),e.xp6(1),e.Q6J("ngIf",i.bucketForm.showError("owner",_,"required")),e.xp6(2),e.Q6J("ngClass",e.VKq(39,Le,!i.editing)),e.xp6(5),e.Q6J("ngIf",i.editing)("ngIfElse",n),e.xp6(1),e.Q6J("ngIf",i.editing),e.xp6(1),e.Q6J("ngIf",i.editing),e.xp6(13),e.Q6J("ngIf",i.bucketForm.getValue("lock_enabled")),e.xp6(1),e.Q6J("ngIf",i.bucketForm.getValue("lock_enabled")),e.xp6(2),e.Q6J("form",i.bucketForm)("submitText",e.lcZ(57,33,i.action)+" "+e.lcZ(58,35,i.resource))}}let De=(()=>{class t extends Ie.E{constructor(_,n,i,s,c,d,E,g){super(),this.route=_,this.router=n,this.formBuilder=i,this.rgwBucketService=s,this.rgwSiteService=c,this.rgwUserService=d,this.notificationService=E,this.actionLabels=g,this.editing=!1,this.owners=null,this.placementTargets=[],this.isVersioningAlreadyEnabled=!1,this.isMfaDeleteAlreadyEnabled=!1,this.icons=D.P,this.editing=this.router.url.startsWith(`/rgw/bucket/${A.MQ.EDIT}`),this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.CREATE,this.resource="bucket",this.createForm()}get isVersioningEnabled(){return this.bucketForm.getValue("versioning")}get isMfaDeleteEnabled(){return this.bucketForm.getValue("mfa-delete")}createForm(){const _=this,n=m.h.custom("lockDays",()=>{if(!_.bucketForm||!u().get(_.bucketForm.getRawValue(),"lock_enabled"))return!1;const i=Number(_.bucketForm.getValue("lock_retention_period_days"));return!Number.isInteger(i)||0===i});this.bucketForm=this.formBuilder.group({id:[null],bid:[null,[a.kI.required],this.editing?[]:[m.h.bucketName(),m.h.bucketExistence(!1,this.rgwBucketService)]],owner:[null,[a.kI.required]],"placement-target":[null,this.editing?[]:[a.kI.required]],versioning:[null],"mfa-delete":[null],"mfa-token-serial":[""],"mfa-token-pin":[""],lock_enabled:[{value:!1,disabled:this.editing}],lock_mode:["COMPLIANCE"],lock_retention_period_days:[0,[m.h.number(!1),n]]})}ngOnInit(){const _={owners:this.rgwUserService.enumerate()};this.editing||(_.getPlacementTargets=this.rgwSiteService.get("placement-targets")),this.route.params.subscribe(n=>{if(n.hasOwnProperty("bid")){const i=decodeURIComponent(n.bid);_.getBid=this.rgwBucketService.get(i)}(0,Y.D)(_).subscribe(i=>{if(this.owners=i.owners.sort(),i.getPlacementTargets){const s=i.getPlacementTargets;this.zonegroup=s.zonegroup,u().forEach(s.placement_targets,c=>{c.description=`${c.name} (${"pool"}: ${c.data_pool})`,this.placementTargets.push(c)}),1===this.placementTargets.length&&this.bucketForm.get("placement-target").setValue(this.placementTargets[0].name)}if(i.getBid){const s=i.getBid,c=u().clone(this.bucketForm.getRawValue());let d=u().pick(s,u().keys(c));d.lock_retention_period_days=this.rgwBucketService.getLockDays(s),d["placement-target"]=s.placement_rule,d.versioning=s.versioning===B.ENABLED,d["mfa-delete"]=s.mfa_delete===k.ENABLED,d=u().merge(c,d),this.bucketForm.setValue(d),this.editing&&(this.isVersioningAlreadyEnabled=this.isVersioningEnabled,this.isMfaDeleteAlreadyEnabled=this.isMfaDeleteEnabled,this.setMfaDeleteValidators(),d.lock_enabled&&this.bucketForm.controls.versioning.disable())}this.loadingReady()})})}goToListView(){this.router.navigate(["/rgw/bucket"])}submit(){if(this.bucketForm.pristine)return void this.goToListView();const _=this.bucketForm.value;if(this.editing){const n=this.getVersioningStatus(),i=this.getMfaDeleteStatus();this.rgwBucketService.update(_.bid,_.id,_.owner,n,i,_["mfa-token-serial"],_["mfa-token-pin"],_.lock_mode,_.lock_retention_period_days).subscribe(()=>{this.notificationService.show(Ae.k.success,"Updated Object Gateway bucket '" + _.bid + "'."),this.goToListView()},()=>{this.bucketForm.setErrors({cdSubmitButton:!0})})}else this.rgwBucketService.create(_.bid,_.owner,this.zonegroup,_["placement-target"],_.lock_enabled,_.lock_mode,_.lock_retention_period_days).subscribe(()=>{this.notificationService.show(Ae.k.success,"Created Object Gateway bucket '" + _.bid + "'"),this.goToListView()},()=>{this.bucketForm.setErrors({cdSubmitButton:!0})})}areMfaCredentialsRequired(){return this.isMfaDeleteEnabled!==this.isMfaDeleteAlreadyEnabled||this.isMfaDeleteAlreadyEnabled&&this.isVersioningEnabled!==this.isVersioningAlreadyEnabled}setMfaDeleteValidators(){const _=this.bucketForm.get("mfa-token-serial"),n=this.bucketForm.get("mfa-token-pin");this.areMfaCredentialsRequired()?(_.setValidators(a.kI.required),n.setValidators(a.kI.required)):(_.setValidators(null),n.setValidators(null)),_.updateValueAndValidity(),n.updateValueAndValidity()}getVersioningStatus(){return this.isVersioningEnabled?B.ENABLED:B.SUSPENDED}getMfaDeleteStatus(){return this.isMfaDeleteEnabled?k.ENABLED:k.DISABLED}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(w.gz),e.Y36(w.F0),e.Y36(J.O),e.Y36(Me.o),e.Y36(We.I),e.Y36(O),e.Y36(ve.g),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-bucket-form"]],features:[e.qOj],decls:1,vars:1,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F,T,x,y,S,_e,te,ne,oe,ie,se,ae,re,le,ce,de,ue,Re;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Name",n="Name...",i="Owner",s="Placement target",c="Locking",d="Enabled",E="Enables locking for the objects in the bucket. Locking can only be enabled while creating a bucket.",g="Id",C="This field is required.",b="Bucket names can only contain lowercase letters, numbers, periods and hyphens.",P="The chosen name is already in use.",G="Bucket names must not contain uppercase characters or underscores.",N="Each label must start and end with a lowercase letter or a number.",p="Bucket names cannot be formatted as IP address.",U="Bucket labels cannot be empty and can only contain lowercase letters, numbers and hyphens.",W="Bucket names must be 3 to 63 characters long.",Z="Loading...",$="-- Select a user --",h="This field is required.",I="Loading...",v="-- Select a placement target --",F="This field is required.",T="Versioning",x="Enabled",y="Enables versioning for the objects in the bucket.",S="Multi-Factor Authentication",_e="Delete enabled",te="Enables MFA (multi-factor authentication) Delete, which requires additional authentication for changing the bucket versioning state.",ne="Token Serial Number",oe="This field is required.",ie="Token PIN",se="This field is required.",ae="Mode",re="Compliance",le="Governance",ce="Days",de="The number of days that you want to specify for the default retention period that will be applied to new objects placed in this bucket.",ue="The entered value must be a positive integer.",Re="Retention Days must be a positive integer.",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["name","bucketForm","novalidate","",3,"formGroup"],["frm","ngForm"],[1,"card"],[1,"card-header"],o,[1,"card-body"],["class","form-group row",4,"ngIf"],[1,"form-group","row"],["for","bid",1,"cd-col-form-label",3,"ngClass"],_,[1,"cd-col-form-input"],["id","bid","name","bid","type","text","placeholder",n,"formControlName","bid",1,"form-control",3,"readonly","autofocus"],["class","invalid-feedback",4,"ngIf"],["for","owner",1,"cd-col-form-label","required"],i,["id","owner","name","owner","formControlName","owner",1,"form-control",3,"autofocus"],[3,"ngValue",4,"ngIf"],[3,"value",4,"ngFor","ngForOf"],["for","placement-target",1,"cd-col-form-label",3,"ngClass"],s,["placementTargetSelect",""],[4,"ngIf","ngIfElse"],[4,"ngIf"],[1,"cd-header"],c,[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["id","lock_enabled","formControlName","lock_enabled","type","checkbox",1,"custom-control-input"],["for","lock_enabled",1,"custom-control-label"],d,E,[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],["for","id",1,"cd-col-form-label"],g,["id","id","name","id","type","text","formControlName","id","readonly","",1,"form-control"],[1,"invalid-feedback"],C,b,P,G,N,p,U,W,[3,"ngValue"],Z,$,[3,"value"],h,["id","placement-target","name","placement-target","formControlName","placement-target",1,"form-control"],I,v,F,["id","placement-target","name","placement-target","formControlName","placement-target","type","text","readonly","",1,"form-control"],T,["type","checkbox","id","versioning","name","versioning","formControlName","versioning",1,"custom-control-input",3,"change"],["for","versioning",1,"custom-control-label"],x,y,S,["type","checkbox","id","mfa-delete","name","mfa-delete","formControlName","mfa-delete",1,"custom-control-input",3,"change"],["for","mfa-delete",1,"custom-control-label"],_e,te,["for","mfa-token-serial",1,"cd-col-form-label"],ne,["type","text","id","mfa-token-serial","name","mfa-token-serial","formControlName","mfa-token-serial",1,"form-control"],oe,["for","mfa-token-pin",1,"cd-col-form-label"],ie,["type","text","id","mfa-token-pin","name","mfa-token-pin","formControlName","mfa-token-pin",1,"form-control"],se,["for","lock_mode",1,"cd-col-form-label"],ae,["formControlName","lock_mode","name","lock_mode","id","lock_mode",1,"form-control"],["value","COMPLIANCE"],re,["value","GOVERNANCE"],le,["for","lock_retention_period_days",1,"cd-col-form-label"],ce,de,["type","number","id","lock_retention_period_days","formControlName","lock_retention_period_days","min","0",1,"form-control"],ue,Re]},template:function(_,n){1&_&&e.YNc(0,lt,59,41,"div",0),2&_&&e.Q6J("cdFormLoading",n.loading)},directives:[Fe.y,a._Y,a.JL,V.V,a.sg,f.O5,H.P,f.mk,q.o,a.Fj,X.b,a.JJ,a.u,j.U,a.EJ,f.sg,a.Wl,h_.S,ee.p,a.YN,a.Kr,a.wV,a.qQ],pipes:[f.rS,K.m],styles:[""]}),t})();var xe=r(18891),be=r(68136),ye=r(30982),z=r(64337),Pe=r(68774),qe=r(47557),we=r(66369),Q=r(51847),Ee=r(74937),Te=r(63285),ke=r(94928),ct=r(96102),Be=r(68962);function dt(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,24),e.qZA())}function ut(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.ALo(2,"dimless"),e.qZA()),2&t){const _=e.oxw(3);e.xp6(1),e.hij(" ",e.lcZ(2,1,_.selection.bucket_quota.max_size)," ")}}function Rt(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,25),e.qZA())}function gt(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.qZA()),2&t){const _=e.oxw(3);e.xp6(1),e.hij(" ",_.selection.bucket_quota.max_objects," ")}}function Et(t,o){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"legend"),e.SDv(2,20),e.qZA(),e.TgZ(3,"table",1),e.TgZ(4,"tbody"),e.TgZ(5,"tr"),e.TgZ(6,"td",2),e.SDv(7,21),e.qZA(),e.TgZ(8,"td",4),e._uU(9),e.ALo(10,"booleanText"),e.qZA(),e.qZA(),e.TgZ(11,"tr"),e.TgZ(12,"td",5),e.SDv(13,22),e.qZA(),e.YNc(14,dt,2,0,"td",0),e.YNc(15,ut,3,3,"td",0),e.qZA(),e.TgZ(16,"tr"),e.TgZ(17,"td",5),e.SDv(18,23),e.qZA(),e.YNc(19,Rt,2,0,"td",0),e.YNc(20,gt,2,1,"td",0),e.qZA(),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(2);e.xp6(9),e.Oqu(e.lcZ(10,5,_.selection.bucket_quota.enabled)),e.xp6(5),e.Q6J("ngIf",_.selection.bucket_quota.max_size<=-1),e.xp6(1),e.Q6J("ngIf",_.selection.bucket_quota.max_size>-1),e.xp6(4),e.Q6J("ngIf",_.selection.bucket_quota.max_objects<=-1),e.xp6(1),e.Q6J("ngIf",_.selection.bucket_quota.max_objects>-1)}}function Tt(t,o){if(1&t&&(e.ynx(0),e.TgZ(1,"tr"),e.TgZ(2,"td",5),e.SDv(3,26),e.qZA(),e.TgZ(4,"td"),e._uU(5),e.qZA(),e.qZA(),e.TgZ(6,"tr"),e.TgZ(7,"td",5),e.SDv(8,27),e.qZA(),e.TgZ(9,"td"),e._uU(10),e.qZA(),e.qZA(),e.BQk()),2&t){const _=e.oxw(2);e.xp6(5),e.Oqu(_.selection.lock_mode),e.xp6(5),e.Oqu(_.selection.lock_retention_period_days)}}function St(t,o){if(1&t&&(e.ynx(0),e.TgZ(1,"table",1),e.TgZ(2,"tbody"),e.TgZ(3,"tr"),e.TgZ(4,"td",2),e.SDv(5,3),e.qZA(),e.TgZ(6,"td",4),e._uU(7),e.qZA(),e.qZA(),e.TgZ(8,"tr"),e.TgZ(9,"td",5),e.SDv(10,6),e.qZA(),e.TgZ(11,"td"),e._uU(12),e.qZA(),e.qZA(),e.TgZ(13,"tr"),e.TgZ(14,"td",5),e.SDv(15,7),e.qZA(),e.TgZ(16,"td"),e._uU(17),e.qZA(),e.qZA(),e.TgZ(18,"tr"),e.TgZ(19,"td",5),e.SDv(20,8),e.qZA(),e.TgZ(21,"td"),e._uU(22),e.qZA(),e.qZA(),e.TgZ(23,"tr"),e.TgZ(24,"td",5),e.SDv(25,9),e.qZA(),e.TgZ(26,"td"),e._uU(27),e.qZA(),e.qZA(),e.TgZ(28,"tr"),e.TgZ(29,"td",5),e.SDv(30,10),e.qZA(),e.TgZ(31,"td"),e._uU(32),e.qZA(),e.qZA(),e.TgZ(33,"tr"),e.TgZ(34,"td",5),e.SDv(35,11),e.qZA(),e.TgZ(36,"td"),e._uU(37),e.qZA(),e.qZA(),e.TgZ(38,"tr"),e.TgZ(39,"td",5),e.SDv(40,12),e.qZA(),e.TgZ(41,"td"),e._uU(42),e.qZA(),e.qZA(),e.TgZ(43,"tr"),e.TgZ(44,"td",5),e.SDv(45,13),e.qZA(),e.TgZ(46,"td"),e._uU(47),e.qZA(),e.qZA(),e.TgZ(48,"tr"),e.TgZ(49,"td",5),e.SDv(50,14),e.qZA(),e.TgZ(51,"td"),e._uU(52),e.ALo(53,"cdDate"),e.qZA(),e.qZA(),e.TgZ(54,"tr"),e.TgZ(55,"td",5),e.SDv(56,15),e.qZA(),e.TgZ(57,"td"),e._uU(58),e.qZA(),e.qZA(),e.TgZ(59,"tr"),e.TgZ(60,"td",5),e.SDv(61,16),e.qZA(),e.TgZ(62,"td"),e._uU(63),e.qZA(),e.qZA(),e.TgZ(64,"tr"),e.TgZ(65,"td",5),e.SDv(66,17),e.qZA(),e.TgZ(67,"td"),e._uU(68),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(69,Et,21,7,"div",0),e.TgZ(70,"legend"),e.SDv(71,18),e.qZA(),e.TgZ(72,"table",1),e.TgZ(73,"tbody"),e.TgZ(74,"tr"),e.TgZ(75,"td",2),e.SDv(76,19),e.qZA(),e.TgZ(77,"td",4),e._uU(78),e.ALo(79,"booleanText"),e.qZA(),e.qZA(),e.YNc(80,Tt,11,2,"ng-container",0),e.qZA(),e.qZA(),e.BQk()),2&t){const _=e.oxw();e.xp6(7),e.Oqu(_.selection.bid),e.xp6(5),e.Oqu(_.selection.id),e.xp6(5),e.Oqu(_.selection.owner),e.xp6(5),e.Oqu(_.selection.index_type),e.xp6(5),e.Oqu(_.selection.placement_rule),e.xp6(5),e.Oqu(_.selection.marker),e.xp6(5),e.Oqu(_.selection.max_marker),e.xp6(5),e.Oqu(_.selection.ver),e.xp6(5),e.Oqu(_.selection.master_ver),e.xp6(5),e.Oqu(e.lcZ(53,16,_.selection.mtime)),e.xp6(6),e.Oqu(_.selection.zonegroup),e.xp6(5),e.Oqu(_.selection.versioning),e.xp6(5),e.Oqu(_.selection.mfa_delete),e.xp6(1),e.Q6J("ngIf",_.selection.bucket_quota),e.xp6(9),e.Oqu(e.lcZ(79,18,_.selection.lock_enabled)),e.xp6(2),e.Q6J("ngIf",_.selection.lock_enabled)}}let ft=(()=>{class t{constructor(_){this.rgwBucketService=_}ngOnChanges(){this.selection&&this.rgwBucketService.get(this.selection.bid).subscribe(_=>{_.lock_retention_period_days=this.rgwBucketService.getLockDays(_),this.selection=_})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(Me.o))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-bucket-details"]],inputs:{selection:"selection"},features:[e.TTD],decls:1,vars:1,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F;return o="Name",_="ID",n="Owner",i="Index type",s="Placement rule",c="Marker",d="Maximum marker",E="Version",g="Master version",C="Modification time",b="Zonegroup",P="Versioning",G="MFA Delete",N="Locking",p="Enabled",U="Bucket quota",W="Enabled",Z="Maximum size",$="Maximum objects",h="Unlimited",I="Unlimited",v="Mode",F="Days",[[4,"ngIf"],[1,"table","table-striped","table-bordered"],[1,"bold","w-25"],o,[1,"w-75"],[1,"bold"],_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F]},template:function(_,n){1&_&&e.YNc(0,St,81,20,"ng-container",0),2&_&&e.Q6J("ngIf",n.selection)},directives:[f.O5],pipes:[ct.N,Be.T,we.n],styles:["table[_ngcontent-%COMP%]{table-layout:fixed}table[_ngcontent-%COMP%] td[_ngcontent-%COMP%]{word-wrap:break-word}"]}),t})();var He=r(60251);const Ct=["bucketSizeTpl"],pt=["bucketObjectTpl"];function Mt(t,o){if(1&t&&e._UZ(0,"cd-usage-bar",8),2&t){const _=e.oxw().row;e.Q6J("total",_.bucket_quota.max_size)("used",_.bucket_size)}}function mt(t,o){1&t&&e.SDv(0,9)}function At(t,o){if(1&t&&(e.YNc(0,Mt,1,2,"cd-usage-bar",6),e.YNc(1,mt,1,0,"ng-template",null,7,e.W1O)),2&t){const _=o.row,n=e.MAs(2);e.Q6J("ngIf",_.bucket_quota.max_size>0&&_.bucket_quota.enabled)("ngIfElse",n)}}function bt(t,o){if(1&t&&e._UZ(0,"cd-usage-bar",12),2&t){const _=e.oxw().row;e.Q6J("total",_.bucket_quota.max_objects)("used",_.num_objects)("isBinary",!1)}}function Pt(t,o){1&t&&e.SDv(0,13)}function Gt(t,o){if(1&t&&(e.YNc(0,bt,1,3,"cd-usage-bar",10),e.YNc(1,Pt,1,0,"ng-template",null,11,e.W1O)),2&t){const _=o.row,n=e.MAs(2);e.Q6J("ngIf",_.bucket_quota.max_objects>0&&_.bucket_quota.enabled)("ngIfElse",n)}}let Ot=(()=>{class t extends be.o{constructor(_,n,i,s,c,d,E,g){super(g),this.authStorageService=_,this.dimlessBinaryPipe=n,this.dimlessPipe=i,this.rgwBucketService=s,this.modalService=c,this.urlBuilder=d,this.actionLabels=E,this.ngZone=g,this.columns=[],this.buckets=[],this.selection=new Pe.r}ngOnInit(){this.permission=this.authStorageService.getPermissions().rgw,this.columns=[{name:"Name",prop:"bid",flexGrow:2},{name:"Owner",prop:"owner",flexGrow:2.5},{name:"Used Capacity",prop:"bucket_size",flexGrow:.6,pipe:this.dimlessBinaryPipe},{name:"Capacity Limit %",prop:"size_usage",cellTemplate:this.bucketSizeTpl,flexGrow:.8},{name:"Objects",prop:"num_objects",flexGrow:.6,pipe:this.dimlessPipe},{name:"Object Limit %",prop:"object_usage",cellTemplate:this.bucketObjectTpl,flexGrow:.8}];const _=()=>this.selection.first()&&`${encodeURIComponent(this.selection.first().bid)}`;this.tableActions=[{permission:"create",icon:D.P.add,routerLink:()=>this.urlBuilder.getCreate(),name:this.actionLabels.CREATE,canBePrimary:c=>!c.hasSelection},{permission:"update",icon:D.P.edit,routerLink:()=>this.urlBuilder.getEdit(_()),name:this.actionLabels.EDIT},{permission:"delete",icon:D.P.destroy,click:()=>this.deleteAction(),disable:()=>!this.selection.hasSelection,name:this.actionLabels.DELETE,canBePrimary:c=>c.hasMultiSelection}],this.setTableRefreshTimeout()}transformBucketData(){u().forEach(this.buckets,_=>{const n=_.bucket_quota.max_size,i=_.bucket_quota.max_objects;_.bucket_size=0,_.num_objects=0,u().isEmpty(_.usage)||(_.bucket_size=_.usage["rgw.main"].size_actual,_.num_objects=_.usage["rgw.main"].num_objects),_.size_usage=n>0?_.bucket_size/n:void 0,_.object_usage=i>0?_.num_objects/i:void 0})}getBucketList(_){this.setTableRefreshTimeout(),this.rgwBucketService.list(!0).subscribe(n=>{this.buckets=n,this.transformBucketData()},()=>{_.error()})}updateSelection(_){this.selection=_}deleteAction(){this.modalService.show(ye.M,{itemDescription:this.selection.hasSingleSelection?"bucket":"buckets",itemNames:this.selection.selected.map(_=>_.bid),submitActionObservable:()=>new xe.y(_=>{(0,Y.D)(this.selection.selected.map(n=>this.rgwBucketService.delete(n.bid))).subscribe({error:n=>{_.error(n),this.table.refreshBtn()},complete:()=>{_.complete(),this.table.refreshBtn()}})})})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(Ee.j),e.Y36(qe.$),e.Y36(we.n),e.Y36(Me.o),e.Y36(Te.Z),e.Y36(Q.F),e.Y36(A.p4),e.Y36(e.R0b))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-bucket-list"]],viewQuery:function(_,n){if(1&_&&(e.Gf(z.a,7),e.Gf(Ct,7),e.Gf(pt,7)),2&_){let i;e.iGM(i=e.CRH())&&(n.table=i.first),e.iGM(i=e.CRH())&&(n.bucketSizeTpl=i.first),e.iGM(i=e.CRH())&&(n.bucketObjectTpl=i.first)}},features:[e._Bn([{provide:Q.F,useValue:new Q.F("rgw/bucket")}]),e.qOj],decls:8,vars:9,consts:function(){let o,_;return o="No Limit",_="No Limit",[["columnMode","flex","selectionType","multiClick","identifier","bid",3,"autoReload","data","columns","hasDetails","status","setExpandedRow","updateSelection","fetchData"],["table",""],[1,"table-actions",3,"permission","selection","tableActions"],["cdTableDetail","",3,"selection"],["bucketSizeTpl",""],["bucketObjectTpl",""],[3,"total","used",4,"ngIf","ngIfElse"],["noSizeQuota",""],[3,"total","used"],o,[3,"total","used","isBinary",4,"ngIf","ngIfElse"],["noObjectQuota",""],[3,"total","used","isBinary"],_]},template:function(_,n){1&_&&(e.TgZ(0,"cd-table",0,1),e.NdJ("setExpandedRow",function(s){return n.setExpandedRow(s)})("updateSelection",function(s){return n.updateSelection(s)})("fetchData",function(s){return n.getBucketList(s)}),e._UZ(2,"cd-table-actions",2),e._UZ(3,"cd-rgw-bucket-details",3),e.qZA(),e.YNc(4,At,3,2,"ng-template",null,4,e.W1O),e.YNc(6,Gt,3,2,"ng-template",null,5,e.W1O)),2&_&&(e.Q6J("autoReload",!1)("data",n.buckets)("columns",n.columns)("hasDetails",!0)("status",n.tableStatus),e.xp6(2),e.Q6J("permission",n.permission)("selection",n.selection)("tableActions",n.tableActions),e.xp6(1),e.Q6J("selection",n.expandedRow))},directives:[z.a,ke.K,ft,f.O5,He.O],styles:[""]}),t})();var Ut=r(58111),Xe=r(59376),Wt=r(61350),Zt=r(98056),Ke=r(76317);function $t(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"cd-table-key-value",11),e.NdJ("fetchData",function(){return e.CHM(_),e.oxw(2).getMetaData()}),e.qZA()}if(2&t){const _=e.oxw(2);e.Q6J("data",_.metadata)}}function ht(t,o){if(1&t&&e._UZ(0,"cd-table-performance-counter",12),2&t){const _=e.oxw(2);e.Q6J("serviceId",_.serviceMapId)}}function It(t,o){if(1&t&&e._UZ(0,"cd-grafana",15),2&t){const _=e.oxw(3);e.Q6J("grafanaPath","rgw-instance-detail?var-rgw_servers=rgw."+_.serviceId)}}function vt(t,o){1&t&&(e.TgZ(0,"li",13),e.TgZ(1,"a",4),e.SDv(2,14),e.qZA(),e.YNc(3,It,1,1,"ng-template",6),e.qZA())}function Ft(t,o){if(1&t&&(e.ynx(0),e.TgZ(1,"ul",1,2),e.TgZ(3,"li",3),e.TgZ(4,"a",4),e.SDv(5,5),e.qZA(),e.YNc(6,$t,1,1,"ng-template",6),e.qZA(),e.TgZ(7,"li",7),e.TgZ(8,"a",4),e.SDv(9,8),e.qZA(),e.YNc(10,ht,1,1,"ng-template",6),e.qZA(),e.YNc(11,vt,4,0,"li",9),e.qZA(),e._UZ(12,"div",10),e.BQk()),2&t){const _=e.MAs(2),n=e.oxw();e.xp6(11),e.Q6J("ngIf",n.grafanaPermission.read),e.xp6(1),e.Q6J("ngbNavOutlet",_)}}let Lt=(()=>{class t{constructor(_,n){this.rgwDaemonService=_,this.authStorageService=n,this.serviceId="",this.serviceMapId="",this.grafanaPermission=this.authStorageService.getPermissions().grafana}ngOnChanges(){this.selection&&(this.serviceId=this.selection.id,this.serviceMapId=this.selection.service_map_id)}getMetaData(){u().isEmpty(this.serviceId)||this.rgwDaemonService.get(this.serviceId).subscribe(_=>{this.metadata=_.rgw_metadata})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ge.b),e.Y36(Ee.j))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-daemon-details"]],inputs:{selection:"selection"},features:[e.TTD],decls:1,vars:1,consts:function(){let o,_,n;return o="Details",_="Performance Counters",n="Performance Details",[[4,"ngIf"],["ngbNav","","cdStatefulTab","rgw-daemon-details",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","details"],["ngbNavLink",""],o,["ngbNavContent",""],["ngbNavItem","performance-counters"],_,["ngbNavItem","performance-details",4,"ngIf"],[3,"ngbNavOutlet"],[3,"data","fetchData"],["serviceType","rgw",3,"serviceId"],["ngbNavItem","performance-details"],n,["uid","x5ARzZtmk","grafanaStyle","one",3,"grafanaPath"]]},template:function(_,n){1&_&&e.YNc(0,Ft,13,2,"ng-container",0),2&_&&e.Q6J("ngIf",n.selection)},directives:[f.O5,M.Pz,Xe.m,M.nv,M.Vx,M.uN,M.tO,Wt.b,Zt.p,Ke.F],styles:[""]}),t})();function Dt(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"cd-table",8),e.NdJ("setExpandedRow",function(i){return e.CHM(_),e.oxw().setExpandedRow(i)})("fetchData",function(i){return e.CHM(_),e.oxw().getDaemonList(i)}),e._UZ(1,"cd-rgw-daemon-details",9),e.qZA()}if(2&t){const _=e.oxw();e.Q6J("data",_.daemons)("columns",_.columns)("hasDetails",!0),e.xp6(1),e.Q6J("selection",_.expandedRow)}}function xt(t,o){1&t&&e._UZ(0,"cd-grafana",11),2&t&&e.Q6J("grafanaPath","rgw-overview?")}function yt(t,o){1&t&&(e.TgZ(0,"li",2),e.TgZ(1,"a",3),e.SDv(2,10),e.qZA(),e.YNc(3,xt,1,1,"ng-template",5),e.qZA())}function qt(t,o){1&t&&e._UZ(0,"cd-grafana",13),2&t&&e.Q6J("grafanaPath","radosgw-sync-overview?")}function wt(t,o){1&t&&(e.TgZ(0,"li",2),e.TgZ(1,"a",3),e.SDv(2,12),e.qZA(),e.YNc(3,qt,1,1,"ng-template",5),e.qZA())}let kt=(()=>{class t extends be.o{constructor(_,n,i,s){super(),this.rgwDaemonService=_,this.authStorageService=n,this.cephShortVersionPipe=i,this.rgwSiteService=s,this.columns=[],this.daemons=[],this.updateDaemons=c=>{this.daemons=c}}ngOnInit(){this.grafanaPermission=this.authStorageService.getPermissions().grafana,this.columns=[{name:"ID",prop:"id",flexGrow:2},{name:"Hostname",prop:"server_hostname",flexGrow:2},{name:"Zone",prop:"zone_name",flexGrow:2},{name:"Zone Group",prop:"zonegroup_name",flexGrow:2},{name:"Realm",prop:"realm_name",flexGrow:2},{name:"Version",prop:"version",flexGrow:1,pipe:this.cephShortVersionPipe}],this.rgwSiteService.get("realms").subscribe(_=>this.isMultiSite=_.length>0)}getDaemonList(_){this.rgwDaemonService.list().subscribe(this.updateDaemons,()=>{_.error()})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ge.b),e.Y36(Ee.j),e.Y36(Ut.F),e.Y36(We.I))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-daemon-list"]],features:[e.qOj],decls:9,vars:3,consts:function(){let o,_,n;return o="Daemons List",_="Overall Performance",n="Sync Performance",[["ngbNav","",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem",""],["ngbNavLink",""],o,["ngbNavContent",""],["ngbNavItem","",4,"ngIf"],[3,"ngbNavOutlet"],["columnMode","flex",3,"data","columns","hasDetails","setExpandedRow","fetchData"],["cdTableDetail","",3,"selection"],_,["uid","WAkugZpiz","grafanaStyle","two",3,"grafanaPath"],n,["uid","rgw-sync-overview","grafanaStyle","two",3,"grafanaPath"]]},template:function(_,n){if(1&_&&(e.TgZ(0,"ul",0,1),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.YNc(5,Dt,2,4,"ng-template",5),e.qZA(),e.YNc(6,yt,4,0,"li",6),e.YNc(7,wt,4,0,"li",6),e.qZA(),e._UZ(8,"div",7)),2&_){const i=e.MAs(1);e.xp6(6),e.Q6J("ngIf",n.grafanaPermission.read),e.xp6(1),e.Q6J("ngIf",n.grafanaPermission.read&&n.isMultiSite),e.xp6(1),e.Q6J("ngbNavOutlet",i)}},directives:[M.Pz,M.nv,M.Vx,M.uN,f.O5,M.tO,z.a,Lt,Ke.F],styles:[""]}),t})();var Bt=r(58071),Ge=r(28211),Se=(()=>{return(t=Se||(Se={})).USERS="users",t.BUCKETS="buckets",t.METADATA="metadata",t.USAGE="usage",t.ZONE="zone",Se;var t})();let ze=(()=>{class t{static getAll(){return Object.values(t.capabilities)}}return t.capabilities=Se,t})();var fe=r(60312);function Ht(t,o){1&t&&e._UZ(0,"input",22),2&t&&e.Q6J("readonly",!0)}function Xt(t,o){1&t&&(e.TgZ(0,"option",17),e.SDv(1,25),e.qZA()),2&t&&e.Q6J("ngValue",null)}function Kt(t,o){if(1&t&&(e.TgZ(0,"option",26),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.Oqu(_)}}function zt(t,o){if(1&t&&(e.TgZ(0,"select",23),e.YNc(1,Xt,2,1,"option",24),e.YNc(2,Kt,2,2,"option",19),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.Q6J("ngIf",null!==_.types),e.xp6(1),e.Q6J("ngForOf",_.types)}}function Qt(t,o){1&t&&(e.TgZ(0,"span",27),e.SDv(1,28),e.qZA())}function Yt(t,o){if(1&t&&(e.TgZ(0,"option",26),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Jt(t,o){1&t&&(e.TgZ(0,"span",27),e.SDv(1,29),e.qZA())}const Vt=function(t){return{required:t}},jt=function(){return["read","write","*"]};let en=(()=>{class t{constructor(_,n,i){this.formBuilder=_,this.activeModal=n,this.actionLabels=i,this.submitAction=new e.vpe,this.editing=!0,this.types=[],this.resource="capability",this.createForm()}createForm(){this.formGroup=this.formBuilder.group({type:[null,[a.kI.required]],perm:[null,[a.kI.required]]})}setEditing(_=!0){this.editing=_,this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.ADD}setValues(_,n){this.formGroup.setValue({type:_,perm:n})}setCapabilities(_){const n=[];_.forEach(i=>{n.push(i.type)}),this.types=[],ze.getAll().forEach(i=>{-1===u().indexOf(n,i)&&this.types.push(i)})}onSubmit(){this.submitAction.emit(this.formGroup.value),this.activeModal.close()}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(J.O),e.Y36(M.Kz),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-capability-modal"]],outputs:{submitAction:"submitAction"},decls:29,vars:24,consts:function(){let o,_,n,i,s,c,d;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Type",n="Permission",i="-- Select a permission --",s="-- Select a type --",c="This field is required.",d="This field is required.",[[3,"modalRef"],[1,"modal-title"],o,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","type",1,"cd-col-form-label",3,"ngClass"],_,[1,"cd-col-form-input"],["id","type","class","form-control","type","text","formControlName","type",3,"readonly",4,"ngIf"],["id","type","class","form-control","formControlName","type","autofocus","",4,"ngIf"],["class","invalid-feedback",4,"ngIf"],["for","perm",1,"cd-col-form-label","required"],n,["id","perm","formControlName","perm",1,"form-control"],[3,"ngValue"],i,[3,"value",4,"ngFor","ngForOf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],["id","type","type","text","formControlName","type",1,"form-control",3,"readonly"],["id","type","formControlName","type","autofocus","",1,"form-control"],[3,"ngValue",4,"ngIf"],s,[3,"value"],[1,"invalid-feedback"],c,d]},template:function(_,n){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e.YNc(13,Ht,1,1,"input",11),e.YNc(14,zt,3,2,"select",12),e.YNc(15,Qt,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(16,"div",7),e.TgZ(17,"label",14),e.SDv(18,15),e.qZA(),e.TgZ(19,"div",10),e.TgZ(20,"select",16),e.TgZ(21,"option",17),e.SDv(22,18),e.qZA(),e.YNc(23,Yt,2,2,"option",19),e.qZA(),e.YNc(24,Jt,2,0,"span",13),e.qZA(),e.qZA(),e.qZA(),e.TgZ(25,"div",20),e.TgZ(26,"cd-form-button-panel",21),e.NdJ("submitActionEvent",function(){return n.onSubmit()}),e.ALo(27,"titlecase"),e.ALo(28,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const i=e.MAs(7);e.Q6J("modalRef",n.activeModal),e.xp6(4),e.pQV(e.lcZ(3,13,n.action))(e.lcZ(4,15,n.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",n.formGroup),e.xp6(4),e.Q6J("ngClass",e.VKq(21,Vt,!n.editing)),e.xp6(3),e.Q6J("ngIf",n.editing),e.xp6(1),e.Q6J("ngIf",!n.editing),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("type",i,"required")),e.xp6(6),e.Q6J("ngValue",null),e.xp6(2),e.Q6J("ngForOf",e.DdM(23,jt)),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("perm",i,"required")),e.xp6(2),e.Q6J("form",n.formGroup)("submitText",e.lcZ(27,17,n.action)+" "+e.lcZ(28,19,n.resource))}},directives:[fe.z,a._Y,a.JL,V.V,a.sg,H.P,f.mk,f.O5,q.o,a.EJ,X.b,a.JJ,a.u,a.YN,a.Kr,f.sg,ee.p,a.Fj,j.U],pipes:[f.rS,K.m],styles:[""]}),t})();var Ce=r(4416),pe=r(58039);function _n(t,o){1&t&&e._UZ(0,"input",17),2&t&&e.Q6J("readonly",!0)}function tn(t,o){1&t&&(e.TgZ(0,"option",21),e.SDv(1,22),e.qZA()),2&t&&e.Q6J("ngValue",null)}function nn(t,o){if(1&t&&(e.TgZ(0,"option",23),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.Oqu(_)}}function on(t,o){if(1&t&&(e.TgZ(0,"select",18),e.YNc(1,tn,2,1,"option",19),e.YNc(2,nn,2,2,"option",20),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.Q6J("ngIf",null!==_.userCandidates),e.xp6(1),e.Q6J("ngForOf",_.userCandidates)}}function sn(t,o){1&t&&(e.TgZ(0,"span",24),e.SDv(1,25),e.qZA())}function an(t,o){1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"div",26),e.TgZ(2,"div",27),e._UZ(3,"input",28),e.TgZ(4,"label",29),e.SDv(5,30),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function rn(t,o){1&t&&(e.TgZ(0,"span",24),e.SDv(1,38),e.qZA())}const Ne=function(t){return{required:t}};function ln(t,o){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",31),e.SDv(2,32),e.qZA(),e.TgZ(3,"div",10),e.TgZ(4,"div",33),e._UZ(5,"input",34),e.TgZ(6,"span",35),e._UZ(7,"button",36),e._UZ(8,"cd-copy-2-clipboard-button",37),e.qZA(),e.qZA(),e.YNc(9,rn,2,0,"span",13),e.qZA(),e.qZA()),2&t){const _=e.oxw(),n=e.MAs(7);e.xp6(1),e.Q6J("ngClass",e.VKq(3,Ne,!_.viewing)),e.xp6(4),e.Q6J("readonly",_.viewing),e.xp6(4),e.Q6J("ngIf",_.formGroup.showError("access_key",n,"required"))}}function cn(t,o){1&t&&(e.TgZ(0,"span",24),e.SDv(1,44),e.qZA())}function dn(t,o){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",39),e.SDv(2,40),e.qZA(),e.TgZ(3,"div",10),e.TgZ(4,"div",33),e._UZ(5,"input",41),e.TgZ(6,"span",35),e._UZ(7,"button",42),e._UZ(8,"cd-copy-2-clipboard-button",43),e.qZA(),e.qZA(),e.YNc(9,cn,2,0,"span",13),e.qZA(),e.qZA()),2&t){const _=e.oxw(),n=e.MAs(7);e.xp6(1),e.Q6J("ngClass",e.VKq(3,Ne,!_.viewing)),e.xp6(4),e.Q6J("readonly",_.viewing),e.xp6(4),e.Q6J("ngIf",_.formGroup.showError("secret_key",n,"required"))}}let Qe=(()=>{class t{constructor(_,n,i){this.formBuilder=_,this.activeModal=n,this.actionLabels=i,this.submitAction=new e.vpe,this.viewing=!0,this.userCandidates=[],this.resource="S3 Key",this.createForm()}createForm(){this.formGroup=this.formBuilder.group({user:[null,[a.kI.required]],generate_key:[!0],access_key:[null,[m.h.requiredIf({generate_key:!1})]],secret_key:[null,[m.h.requiredIf({generate_key:!1})]]})}setViewing(_=!0){this.viewing=_,this.action=this.viewing?this.actionLabels.SHOW:this.actionLabels.CREATE}setValues(_,n,i){this.formGroup.setValue({user:_,generate_key:u().isEmpty(n),access_key:n,secret_key:i})}setUserCandidates(_){this.userCandidates=_}onSubmit(){this.submitAction.emit(this.formGroup.value),this.activeModal.close()}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(J.O),e.Y36(M.Kz),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-s3-key-modal"]],outputs:{submitAction:"submitAction"},decls:23,vars:24,consts:function(){let o,_,n,i,s,c,d,E,g;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Username",n="-- Select a username --",i="This field is required.",s="Auto-generate key",c="Access key",d="This field is required.",E="Secret key",g="This field is required.",[[3,"modalRef"],[1,"modal-title"],o,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","user",1,"cd-col-form-label",3,"ngClass"],_,[1,"cd-col-form-input"],["id","user","class","form-control","type","text","formControlName","user",3,"readonly",4,"ngIf"],["id","user","class","form-control","formControlName","user","autofocus","",4,"ngIf"],["class","invalid-feedback",4,"ngIf"],["class","form-group row",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","showSubmit","submitActionEvent"],["id","user","type","text","formControlName","user",1,"form-control",3,"readonly"],["id","user","formControlName","user","autofocus","",1,"form-control"],[3,"ngValue",4,"ngIf"],[3,"value",4,"ngFor","ngForOf"],[3,"ngValue"],n,[3,"value"],[1,"invalid-feedback"],i,[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["id","generate_key","type","checkbox","formControlName","generate_key",1,"custom-control-input"],["for","generate_key",1,"custom-control-label"],s,["for","access_key",1,"cd-col-form-label",3,"ngClass"],c,[1,"input-group"],["id","access_key","type","password","formControlName","access_key",1,"form-control",3,"readonly"],[1,"input-group-append"],["type","button","cdPasswordButton","access_key",1,"btn","btn-light"],["source","access_key"],d,["for","secret_key",1,"cd-col-form-label",3,"ngClass"],E,["id","secret_key","type","password","formControlName","secret_key",1,"form-control",3,"readonly"],["type","button","cdPasswordButton","secret_key",1,"btn","btn-light"],["source","secret_key"],g]},template:function(_,n){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e.YNc(13,_n,1,1,"input",11),e.YNc(14,on,3,2,"select",12),e.YNc(15,sn,2,0,"span",13),e.qZA(),e.qZA(),e.YNc(16,an,6,0,"div",14),e.YNc(17,ln,10,5,"div",14),e.YNc(18,dn,10,5,"div",14),e.qZA(),e.TgZ(19,"div",15),e.TgZ(20,"cd-form-button-panel",16),e.NdJ("submitActionEvent",function(){return n.onSubmit()}),e.ALo(21,"titlecase"),e.ALo(22,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const i=e.MAs(7);e.Q6J("modalRef",n.activeModal),e.xp6(4),e.pQV(e.lcZ(3,14,n.action))(e.lcZ(4,16,n.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",n.formGroup),e.xp6(4),e.Q6J("ngClass",e.VKq(22,Ne,!n.viewing)),e.xp6(3),e.Q6J("ngIf",n.viewing),e.xp6(1),e.Q6J("ngIf",!n.viewing),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("user",i,"required")),e.xp6(1),e.Q6J("ngIf",!n.viewing),e.xp6(1),e.Q6J("ngIf",!n.formGroup.getValue("generate_key")),e.xp6(1),e.Q6J("ngIf",!n.formGroup.getValue("generate_key")),e.xp6(2),e.Q6J("form",n.formGroup)("submitText",e.lcZ(21,18,n.action)+" "+e.lcZ(22,20,n.resource))("showSubmit",!n.viewing)}},directives:[fe.z,a._Y,a.JL,V.V,a.sg,H.P,f.mk,f.O5,ee.p,q.o,a.Fj,X.b,a.JJ,a.u,a.EJ,j.U,f.sg,a.YN,a.Kr,a.Wl,Ce.C,pe.s],pipes:[f.rS,K.m],styles:[""]}),t})();class un{}function Rn(t,o){1&t&&(e.TgZ(0,"span",29),e.SDv(1,30),e.qZA())}function gn(t,o){1&t&&(e.TgZ(0,"span",29),e.SDv(1,31),e.qZA())}function En(t,o){if(1&t&&(e.TgZ(0,"option",32),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Tn(t,o){1&t&&(e.TgZ(0,"span",29),e.SDv(1,33),e.qZA())}function Sn(t,o){1&t&&(e.TgZ(0,"span",29),e.SDv(1,48),e.qZA())}function fn(t,o){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",41),e.SDv(2,42),e.qZA(),e.TgZ(3,"div",10),e.TgZ(4,"div",43),e._UZ(5,"input",44),e.TgZ(6,"span",45),e._UZ(7,"button",46),e._UZ(8,"cd-copy-2-clipboard-button",47),e.qZA(),e.qZA(),e.YNc(9,Sn,2,0,"span",15),e.qZA(),e.qZA()),2&t){const _=e.oxw(2),n=e.MAs(7);e.xp6(9),e.Q6J("ngIf",_.formGroup.showError("secret_key",n,"required"))}}function Cn(t,o){if(1&t&&(e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,34),e.qZA(),e.TgZ(3,"div",7),e.TgZ(4,"div",35),e.TgZ(5,"div",36),e._UZ(6,"input",37),e.TgZ(7,"label",38),e.SDv(8,39),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(9,fn,10,1,"div",40),e.qZA()),2&t){const _=e.oxw();e.xp6(9),e.Q6J("ngIf",!_.editing&&!_.formGroup.getValue("generate_secret"))}}const pn=function(t){return{required:t}},Mn=function(){return["read","write"]};let mn=(()=>{class t{constructor(_,n,i){this.formBuilder=_,this.bsModalRef=n,this.actionLabels=i,this.submitAction=new e.vpe,this.editing=!0,this.subusers=[],this.resource="Subuser",this.createForm()}createForm(){this.formGroup=this.formBuilder.group({uid:[null],subuid:[null,[a.kI.required,this.subuserValidator()]],perm:[null,[a.kI.required]],generate_secret:[!0],secret_key:[null,[m.h.requiredIf({generate_secret:!1})]]})}subuserValidator(){const _=this;return n=>_.editing||(0,m.P)(n.value)?null:_.subusers.some(s=>u().isEqual(_.getSubuserName(s.id),n.value))?{subuserIdExists:!0}:null}getSubuserName(_){if(u().isEmpty(_))return _;const n=_.match(/([^:]+)(:(.+))?/);return u().isUndefined(n[3])?n[1]:n[3]}setEditing(_=!0){this.editing=_,this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.CREATE}setValues(_,n="",i=""){this.formGroup.setValue({uid:_,subuid:this.getSubuserName(n),perm:i,generate_secret:!0,secret_key:null})}setSubusers(_){this.subusers=_}onSubmit(){const _=this.formGroup.value,n=new un;n.id=`${_.uid}:${_.subuid}`,n.permissions=_.perm,n.generate_secret=_.generate_secret,n.secret_key=_.secret_key,this.submitAction.emit(n),this.bsModalRef.close()}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(J.O),e.Y36(M.Kz),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-subuser-modal"]],outputs:{submitAction:"submitAction"},decls:39,vars:26,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Username",n="Subuser",i="Permission",s="-- Select a permission --",c="read, write",d="full",E="This field is required.",g="The chosen subuser ID is already in use.",C="This field is required.",b="Swift key",P="Auto-generate secret",G="Secret key",N="This field is required.",[[3,"modalRef"],[1,"modal-title"],o,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","uid",1,"cd-col-form-label"],_,[1,"cd-col-form-input"],["id","uid","type","text","formControlName","uid",1,"form-control",3,"readonly"],["for","subuid",1,"cd-col-form-label",3,"ngClass"],n,["id","subuid","type","text","formControlName","subuid","autofocus","",1,"form-control",3,"readonly"],["class","invalid-feedback",4,"ngIf"],["for","perm",1,"cd-col-form-label","required"],i,["id","perm","formControlName","perm",1,"form-control"],[3,"ngValue"],s,[3,"value",4,"ngFor","ngForOf"],["value","read-write"],c,["value","full-control"],d,[4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],E,g,[3,"value"],C,b,[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["id","generate_secret","type","checkbox","formControlName","generate_secret",1,"custom-control-input"],["for","generate_secret",1,"custom-control-label"],P,["class","form-group row",4,"ngIf"],["for","secret_key",1,"cd-col-form-label","required"],G,[1,"input-group"],["id","secret_key","type","password","formControlName","secret_key",1,"form-control"],[1,"input-group-append"],["type","button","cdPasswordButton","secret_key",1,"btn","btn-light"],["source","secret_key"],N]},template:function(_,n){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e._UZ(13,"input",11),e.qZA(),e.qZA(),e.TgZ(14,"div",7),e.TgZ(15,"label",12),e.SDv(16,13),e.qZA(),e.TgZ(17,"div",10),e._UZ(18,"input",14),e.YNc(19,Rn,2,0,"span",15),e.YNc(20,gn,2,0,"span",15),e.qZA(),e.qZA(),e.TgZ(21,"div",7),e.TgZ(22,"label",16),e.SDv(23,17),e.qZA(),e.TgZ(24,"div",10),e.TgZ(25,"select",18),e.TgZ(26,"option",19),e.SDv(27,20),e.qZA(),e.YNc(28,En,2,2,"option",21),e.TgZ(29,"option",22),e.SDv(30,23),e.qZA(),e.TgZ(31,"option",24),e.SDv(32,25),e.qZA(),e.qZA(),e.YNc(33,Tn,2,0,"span",15),e.qZA(),e.qZA(),e.YNc(34,Cn,10,1,"fieldset",26),e.qZA(),e.TgZ(35,"div",27),e.TgZ(36,"cd-form-button-panel",28),e.NdJ("submitActionEvent",function(){return n.onSubmit()}),e.ALo(37,"titlecase"),e.ALo(38,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const i=e.MAs(7);e.Q6J("modalRef",n.bsModalRef),e.xp6(4),e.pQV(e.lcZ(3,15,n.action))(e.lcZ(4,17,n.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",n.formGroup),e.xp6(7),e.Q6J("readonly",!0),e.xp6(2),e.Q6J("ngClass",e.VKq(23,pn,!n.editing)),e.xp6(3),e.Q6J("readonly",n.editing),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("subuid",i,"required")),e.xp6(1),e.Q6J("ngIf",n.formGroup.showError("subuid",i,"subuserIdExists")),e.xp6(6),e.Q6J("ngValue",null),e.xp6(2),e.Q6J("ngForOf",e.DdM(25,Mn)),e.xp6(5),e.Q6J("ngIf",n.formGroup.showError("perm",i,"required")),e.xp6(1),e.Q6J("ngIf",!n.editing),e.xp6(2),e.Q6J("form",n.formGroup)("submitText",e.lcZ(37,19,n.action)+" "+e.lcZ(38,21,n.resource))}},directives:[fe.z,a._Y,a.JL,V.V,a.sg,H.P,q.o,a.Fj,X.b,a.JJ,a.u,f.mk,j.U,f.O5,a.EJ,a.YN,a.Kr,f.sg,ee.p,a.Wl,Ce.C,pe.s],pipes:[f.rS,K.m],styles:[""]}),t})();var An=r(13472);let Ye=(()=>{class t{constructor(_,n){this.activeModal=_,this.actionLabels=n,this.resource="Swift Key",this.action=this.actionLabels.SHOW}setValues(_,n){this.user=_,this.secret_key=n}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(M.Kz),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-swift-key-modal"]],decls:24,vars:11,consts:function(){let o,_,n;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Username",n="Secret key",[[3,"modalRef"],[1,"modal-title"],o,[1,"modal-content"],[1,"modal-body"],["novalidate",""],[1,"form-group","row"],["for","user",1,"cd-col-form-label"],_,[1,"cd-col-form-input"],["id","user","name","user","type","text",1,"form-control",3,"readonly","ngModel","ngModelChange"],["for","secret_key",1,"cd-col-form-label"],n,[1,"input-group"],["id","secret_key","name","secret_key","type","password",1,"form-control",3,"ngModel","readonly","ngModelChange"],[1,"input-group-append"],["type","button","cdPasswordButton","secret_key",1,"btn","btn-light"],["source","secret_key"],[1,"modal-footer"],[3,"backAction"]]},template:function(_,n){1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"div",4),e.TgZ(7,"form",5),e.TgZ(8,"div",6),e.TgZ(9,"label",7),e.SDv(10,8),e.qZA(),e.TgZ(11,"div",9),e.TgZ(12,"input",10),e.NdJ("ngModelChange",function(s){return n.user=s}),e.qZA(),e.qZA(),e.qZA(),e.TgZ(13,"div",6),e.TgZ(14,"label",11),e.SDv(15,12),e.qZA(),e.TgZ(16,"div",9),e.TgZ(17,"div",13),e.TgZ(18,"input",14),e.NdJ("ngModelChange",function(s){return n.secret_key=s}),e.qZA(),e.TgZ(19,"span",15),e._UZ(20,"button",16),e._UZ(21,"cd-copy-2-clipboard-button",17),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.TgZ(22,"div",18),e.TgZ(23,"cd-back-button",19),e.NdJ("backAction",function(){return n.activeModal.close()}),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_&&(e.Q6J("modalRef",n.activeModal),e.xp6(4),e.pQV(e.lcZ(3,7,n.action))(e.lcZ(4,9,n.resource)),e.QtT(2),e.xp6(8),e.Q6J("readonly",!0)("ngModel",n.user),e.xp6(6),e.Q6J("ngModel",n.secret_key)("readonly",!0))},directives:[fe.z,a._Y,a.JL,a.F,H.P,q.o,a.Fj,X.b,a.JJ,a.On,Ce.C,pe.s,An.W],pipes:[f.rS,K.m],styles:[""]}),t})();var bn=r(17932);function Pn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,50),e.qZA())}function Gn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,51),e.qZA())}function Nn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,52),e.qZA())}function On(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,56),e.qZA())}function Un(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,57),e.qZA())}function Wn(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",53),e.SDv(2,54),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",55),e.YNc(5,On,2,0,"span",13),e.YNc(6,Un,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(4),e.Q6J("readonly",n.editing),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("tenant",_,"pattern")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("tenant",_,"notUnique"))}}function Zn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,58),e.qZA())}function $n(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,59),e.qZA())}function hn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,60),e.qZA())}function In(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,63),e.qZA())}function vn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,64),e.qZA())}function Fn(t,o){if(1&t&&(e.TgZ(0,"div",8),e._UZ(1,"label",61),e.TgZ(2,"div",11),e._UZ(3,"input",62),e.YNc(4,In,2,0,"span",13),e.YNc(5,vn,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(4),e.Q6J("ngIf",n.userForm.showError("max_buckets",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("max_buckets",_,"min"))}}function Ln(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,76),e.qZA())}function Dn(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",69),e.SDv(2,70),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"div",71),e._UZ(5,"input",72),e.TgZ(6,"span",73),e._UZ(7,"button",74),e._UZ(8,"cd-copy-2-clipboard-button",75),e.qZA(),e.qZA(),e.YNc(9,Ln,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),n=e.oxw();e.xp6(9),e.Q6J("ngIf",n.userForm.showError("access_key",_,"required"))}}function xn(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,82),e.qZA())}function yn(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",77),e.SDv(2,78),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"div",71),e._UZ(5,"input",79),e.TgZ(6,"span",73),e._UZ(7,"button",80),e._UZ(8,"cd-copy-2-clipboard-button",81),e.qZA(),e.qZA(),e.YNc(9,xn,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),n=e.oxw();e.xp6(9),e.Q6J("ngIf",n.userForm.showError("secret_key",_,"required"))}}function qn(t,o){if(1&t&&(e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,65),e.qZA(),e.TgZ(3,"div",8),e.TgZ(4,"div",14),e.TgZ(5,"div",15),e._UZ(6,"input",66),e.TgZ(7,"label",67),e.SDv(8,68),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(9,Dn,10,1,"div",19),e.YNc(10,yn,10,1,"div",19),e.qZA()),2&t){const _=e.oxw(2);e.xp6(9),e.Q6J("ngIf",!_.editing&&!_.userForm.getValue("generate_key")),e.xp6(1),e.Q6J("ngIf",!_.editing&&!_.userForm.getValue("generate_key"))}}function wn(t,o){1&t&&(e.TgZ(0,"span",92),e.TgZ(1,"span",93),e.SDv(2,94),e.qZA(),e.qZA())}const L=function(t){return[t]};function kn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"span"),e.TgZ(1,"div",71),e.TgZ(2,"div",95),e.TgZ(3,"span",96),e._UZ(4,"i"),e.qZA(),e.qZA(),e._UZ(5,"input",97),e.TgZ(6,"div",98),e.TgZ(7,"span",96),e._UZ(8,"i"),e.qZA(),e.qZA(),e._UZ(9,"input",97),e.TgZ(10,"span",73),e.TgZ(11,"button",99),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).showSubuserModal(s)}),e._UZ(12,"i",89),e.qZA(),e.TgZ(13,"button",100),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).deleteSubuser(s)}),e._UZ(14,"i",89),e.qZA(),e.qZA(),e.qZA(),e._UZ(15,"span",93),e.qZA()}if(2&t){const _=o.$implicit,n=e.oxw(3);e.xp6(4),e.Tol(n.icons.user),e.xp6(1),e.s9C("value",_.id),e.xp6(3),e.Tol(n.icons.share),e.xp6(1),e.s9C("value","full-control"===_.permissions?"full":_.permissions),e.xp6(3),e.Q6J("ngClass",e.VKq(10,L,n.icons.edit)),e.xp6(2),e.Q6J("ngClass",e.VKq(12,L,n.icons.destroy))}}function Bn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,83),e.qZA(),e.TgZ(3,"div",84),e.TgZ(4,"div",14),e.YNc(5,wn,3,0,"span",85),e.YNc(6,kn,16,14,"span",86),e.TgZ(7,"div",84),e.TgZ(8,"div",87),e.TgZ(9,"button",88),e.NdJ("click",function(){return e.CHM(_),e.oxw(2).showSubuserModal()}),e._UZ(10,"i",89),e.ynx(11),e.SDv(12,90),e.ALo(13,"titlecase"),e.ALo(14,"upperFirst"),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(15,"span",91),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(2);e.xp6(5),e.Q6J("ngIf",0===_.subusers.length),e.xp6(1),e.Q6J("ngForOf",_.subusers),e.xp6(4),e.Q6J("ngClass",e.VKq(9,L,_.icons.add)),e.xp6(4),e.pQV(e.lcZ(13,5,_.actionLabels.CREATE))(e.lcZ(14,7,_.subuserLabel)),e.QtT(12)}}function Hn(t,o){1&t&&(e.TgZ(0,"span",92),e.TgZ(1,"span",93),e.SDv(2,106),e.qZA(),e.qZA())}function Xn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"span"),e.TgZ(1,"div",71),e.TgZ(2,"div",95),e.TgZ(3,"div",96),e._UZ(4,"i"),e.qZA(),e.qZA(),e._UZ(5,"input",97),e.TgZ(6,"span",73),e.TgZ(7,"button",107),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).showS3KeyModal(s)}),e._UZ(8,"i",89),e.qZA(),e.TgZ(9,"button",108),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).deleteS3Key(s)}),e._UZ(10,"i",89),e.qZA(),e.qZA(),e.qZA(),e._UZ(11,"span",93),e.qZA()}if(2&t){const _=o.$implicit,n=e.oxw(3);e.xp6(4),e.Tol(n.icons.key),e.xp6(1),e.s9C("value",_.user),e.xp6(3),e.Q6J("ngClass",e.VKq(6,L,n.icons.show)),e.xp6(2),e.Q6J("ngClass",e.VKq(8,L,n.icons.destroy))}}function Kn(t,o){1&t&&(e.TgZ(0,"span",92),e.TgZ(1,"span",93),e.SDv(2,109),e.qZA(),e.qZA())}function zn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"span"),e.TgZ(1,"div",71),e.TgZ(2,"div",95),e.TgZ(3,"span",96),e._UZ(4,"i"),e.qZA(),e.qZA(),e._UZ(5,"input",97),e.TgZ(6,"span",73),e.TgZ(7,"button",110),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).showSwiftKeyModal(s)}),e._UZ(8,"i",89),e.qZA(),e.qZA(),e.qZA(),e._UZ(9,"span",93),e.qZA()}if(2&t){const _=o.$implicit,n=e.oxw(3);e.xp6(4),e.Tol(n.icons.key),e.xp6(1),e.s9C("value",_.user),e.xp6(3),e.Q6J("ngClass",e.VKq(5,L,n.icons.show))}}function Qn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,101),e.qZA(),e.TgZ(3,"div",8),e.TgZ(4,"label",61),e.SDv(5,102),e.qZA(),e.TgZ(6,"div",11),e.YNc(7,Hn,3,0,"span",85),e.YNc(8,Xn,12,10,"span",86),e.TgZ(9,"div",84),e.TgZ(10,"div",87),e.TgZ(11,"button",103),e.NdJ("click",function(){return e.CHM(_),e.oxw(2).showS3KeyModal()}),e._UZ(12,"i",89),e.ynx(13),e.SDv(14,104),e.ALo(15,"titlecase"),e.ALo(16,"upperFirst"),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(17,"span",91),e.qZA(),e._UZ(18,"hr"),e.qZA(),e.TgZ(19,"div",8),e.TgZ(20,"label",61),e.SDv(21,105),e.qZA(),e.TgZ(22,"div",11),e.YNc(23,Kn,3,0,"span",85),e.YNc(24,zn,10,7,"span",86),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(2);e.xp6(7),e.Q6J("ngIf",0===_.s3Keys.length),e.xp6(1),e.Q6J("ngForOf",_.s3Keys),e.xp6(4),e.Q6J("ngClass",e.VKq(11,L,_.icons.add)),e.xp6(4),e.pQV(e.lcZ(15,7,_.actionLabels.CREATE))(e.lcZ(16,9,_.s3keyLabel)),e.QtT(14),e.xp6(7),e.Q6J("ngIf",0===_.swiftKeys.length),e.xp6(1),e.Q6J("ngForOf",_.swiftKeys)}}function Yn(t,o){1&t&&(e.TgZ(0,"span",92),e.TgZ(1,"span",93),e.SDv(2,114),e.qZA(),e.qZA())}function Jn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"span"),e.TgZ(1,"div",71),e.TgZ(2,"span",95),e.TgZ(3,"div",96),e._UZ(4,"i"),e.qZA(),e.qZA(),e._UZ(5,"input",97),e.TgZ(6,"span",73),e.TgZ(7,"button",115),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).showCapabilityModal(s)}),e._UZ(8,"i",89),e.qZA(),e.TgZ(9,"button",116),e.NdJ("click",function(){const s=e.CHM(_).index;return e.oxw(3).deleteCapability(s)}),e._UZ(10,"i",89),e.qZA(),e.qZA(),e.qZA(),e._UZ(11,"span",93),e.qZA()}if(2&t){const _=o.$implicit,n=e.oxw(3);e.xp6(4),e.Tol(n.icons.share),e.xp6(1),e.hYB("value","",_.type,":",_.perm,""),e.xp6(3),e.Q6J("ngClass",e.VKq(7,L,n.icons.edit)),e.xp6(2),e.Q6J("ngClass",e.VKq(9,L,n.icons.destroy))}}function Vn(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"fieldset"),e.TgZ(1,"legend"),e.SDv(2,111),e.qZA(),e.TgZ(3,"div",8),e.TgZ(4,"div",14),e.YNc(5,Yn,3,0,"span",85),e.YNc(6,Jn,12,11,"span",86),e.TgZ(7,"div",84),e.TgZ(8,"div",87),e.TgZ(9,"button",112),e.NdJ("click",function(){return e.CHM(_),e.oxw(2).showCapabilityModal()}),e.ALo(10,"pipeFunction"),e.ALo(11,"pipeFunction"),e._UZ(12,"i",89),e.ynx(13),e.SDv(14,113),e.ALo(15,"titlecase"),e.ALo(16,"upperFirst"),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(17,"span",91),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(2);e.xp6(5),e.Q6J("ngIf",0===_.capabilities.length),e.xp6(1),e.Q6J("ngForOf",_.capabilities),e.xp6(3),e.Q6J("disabled",e.xi3(10,7,_.capabilities,_.hasAllCapabilities))("disableTooltip",!e.xi3(11,10,_.capabilities,_.hasAllCapabilities)),e.xp6(3),e.Q6J("ngClass",e.VKq(17,L,_.icons.add)),e.xp6(4),e.pQV(e.lcZ(15,13,_.actionLabels.ADD))(e.lcZ(16,15,_.capabilityLabel)),e.QtT(14)}}function jn(t,o){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"div",14),e.TgZ(2,"div",15),e._UZ(3,"input",117),e.TgZ(4,"label",118),e.SDv(5,119),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function eo(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,123),e.qZA())}function _o(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,124),e.qZA())}function to(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",120),e.SDv(2,121),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",122),e.YNc(5,eo,2,0,"span",13),e.YNc(6,_o,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.userForm.showError("user_quota_max_size",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("user_quota_max_size",_,"quotaMaxSize"))}}function no(t,o){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"div",14),e.TgZ(2,"div",15),e._UZ(3,"input",125),e.TgZ(4,"label",126),e.SDv(5,127),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function oo(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,131),e.qZA())}function io(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,132),e.qZA())}function so(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",128),e.SDv(2,129),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",130),e.YNc(5,oo,2,0,"span",13),e.YNc(6,io,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.userForm.showError("user_quota_max_objects",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("user_quota_max_objects",_,"min"))}}function ao(t,o){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"div",14),e.TgZ(2,"div",15),e._UZ(3,"input",133),e.TgZ(4,"label",134),e.SDv(5,135),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function ro(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,139),e.qZA())}function lo(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,140),e.qZA())}function co(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",136),e.SDv(2,137),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",138),e.YNc(5,ro,2,0,"span",13),e.YNc(6,lo,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.userForm.showError("bucket_quota_max_size",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("bucket_quota_max_size",_,"quotaMaxSize"))}}function uo(t,o){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"div",14),e.TgZ(2,"div",15),e._UZ(3,"input",141),e.TgZ(4,"label",142),e.SDv(5,143),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function Ro(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,147),e.qZA())}function go(t,o){1&t&&(e.TgZ(0,"span",49),e.SDv(1,148),e.qZA())}function Eo(t,o){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",144),e.SDv(2,145),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",146),e.YNc(5,Ro,2,0,"span",13),e.YNc(6,go,2,0,"span",13),e.qZA(),e.qZA()),2&t){e.oxw();const _=e.MAs(2),n=e.oxw();e.xp6(5),e.Q6J("ngIf",n.userForm.showError("bucket_quota_max_objects",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("bucket_quota_max_objects",_,"min"))}}const Je=function(t){return{required:t}};function To(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.TgZ(9,"div",8),e.TgZ(10,"label",9),e.SDv(11,10),e.qZA(),e.TgZ(12,"div",11),e._UZ(13,"input",12),e.YNc(14,Pn,2,0,"span",13),e.YNc(15,Gn,2,0,"span",13),e.YNc(16,Nn,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(17,"div",8),e.TgZ(18,"div",14),e.TgZ(19,"div",15),e.TgZ(20,"input",16),e.NdJ("click",function(){return e.CHM(_),e.oxw().updateFieldsWhenTenanted()}),e.qZA(),e.TgZ(21,"label",17),e.SDv(22,18),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(23,Wn,7,3,"div",19),e.TgZ(24,"div",8),e.TgZ(25,"label",20),e.SDv(26,21),e.qZA(),e.TgZ(27,"div",11),e._UZ(28,"input",22),e.YNc(29,Zn,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(30,"div",8),e.TgZ(31,"label",23),e.SDv(32,24),e.qZA(),e.TgZ(33,"div",11),e._UZ(34,"input",25),e.YNc(35,$n,2,0,"span",13),e.YNc(36,hn,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(37,"div",8),e.TgZ(38,"label",26),e.SDv(39,27),e.qZA(),e.TgZ(40,"div",11),e.TgZ(41,"select",28),e.NdJ("change",function(i){return e.CHM(_),e.oxw().onMaxBucketsModeChange(i.target.value)}),e.TgZ(42,"option",29),e.SDv(43,30),e.qZA(),e.TgZ(44,"option",31),e.SDv(45,32),e.qZA(),e.TgZ(46,"option",33),e.SDv(47,34),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(48,Fn,6,2,"div",19),e.TgZ(49,"div",8),e.TgZ(50,"div",14),e.TgZ(51,"div",15),e._UZ(52,"input",35),e.TgZ(53,"label",36),e.SDv(54,37),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(55,qn,11,2,"fieldset",38),e.YNc(56,Bn,16,11,"fieldset",38),e.YNc(57,Qn,25,13,"fieldset",38),e.YNc(58,Vn,18,19,"fieldset",38),e.TgZ(59,"fieldset"),e.TgZ(60,"legend"),e.SDv(61,39),e.qZA(),e.TgZ(62,"div",8),e.TgZ(63,"div",14),e.TgZ(64,"div",15),e._UZ(65,"input",40),e.TgZ(66,"label",41),e.SDv(67,42),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(68,jn,6,0,"div",19),e.YNc(69,to,7,2,"div",19),e.YNc(70,no,6,0,"div",19),e.YNc(71,so,7,2,"div",19),e.qZA(),e.TgZ(72,"fieldset"),e.TgZ(73,"legend"),e.SDv(74,43),e.qZA(),e.TgZ(75,"div",8),e.TgZ(76,"div",14),e.TgZ(77,"div",15),e._UZ(78,"input",44),e.TgZ(79,"label",45),e.SDv(80,46),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.YNc(81,ao,6,0,"div",19),e.YNc(82,co,7,2,"div",19),e.YNc(83,uo,6,0,"div",19),e.YNc(84,Eo,7,2,"div",19),e.qZA(),e.qZA(),e.TgZ(85,"div",47),e.TgZ(86,"cd-form-button-panel",48),e.NdJ("submitActionEvent",function(){return e.CHM(_),e.oxw().onSubmit()}),e.ALo(87,"titlecase"),e.ALo(88,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.MAs(2),n=e.oxw();e.xp6(1),e.Q6J("formGroup",n.userForm),e.xp6(6),e.pQV(e.lcZ(6,29,n.action))(e.lcZ(7,31,n.resource)),e.QtT(5),e.xp6(3),e.Q6J("ngClass",e.VKq(37,Je,!n.editing)),e.xp6(3),e.Q6J("readonly",n.editing),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("user_id",_,"required")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("user_id",_,"pattern")),e.xp6(1),e.Q6J("ngIf",!n.userForm.getValue("show_tenant")&&n.userForm.showError("user_id",_,"notUnique")),e.xp6(4),e.Q6J("readonly",!0),e.xp6(3),e.Q6J("ngIf",n.userForm.getValue("show_tenant")),e.xp6(2),e.Q6J("ngClass",e.VKq(39,Je,!n.editing)),e.xp6(4),e.Q6J("ngIf",n.userForm.showError("display_name",_,"required")),e.xp6(6),e.Q6J("ngIf",n.userForm.showError("email",_,"email")),e.xp6(1),e.Q6J("ngIf",n.userForm.showError("email",_,"notUnique")),e.xp6(12),e.Q6J("ngIf",1==n.userForm.get("max_buckets_mode").value),e.xp6(7),e.Q6J("ngIf",!n.editing),e.xp6(1),e.Q6J("ngIf",n.editing),e.xp6(1),e.Q6J("ngIf",n.editing),e.xp6(1),e.Q6J("ngIf",n.editing),e.xp6(10),e.Q6J("ngIf",n.userForm.controls.user_quota_enabled.value),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.user_quota_enabled.value&&!n.userForm.getValue("user_quota_max_size_unlimited")),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.user_quota_enabled.value),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.user_quota_enabled.value&&!n.userForm.getValue("user_quota_max_objects_unlimited")),e.xp6(10),e.Q6J("ngIf",n.userForm.controls.bucket_quota_enabled.value),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.bucket_quota_enabled.value&&!n.userForm.getValue("bucket_quota_max_size_unlimited")),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.bucket_quota_enabled.value),e.xp6(1),e.Q6J("ngIf",n.userForm.controls.bucket_quota_enabled.value&&!n.userForm.getValue("bucket_quota_max_objects_unlimited")),e.xp6(2),e.Q6J("form",n.userForm)("submitText",e.lcZ(87,33,n.action)+" "+e.lcZ(88,35,n.resource))}}let Ve=(()=>{class t extends Ie.E{constructor(_,n,i,s,c,d,E){super(),this.formBuilder=_,this.route=n,this.router=i,this.rgwUserService=s,this.modalService=c,this.notificationService=d,this.actionLabels=E,this.editing=!1,this.submitObservables=[],this.icons=D.P,this.subusers=[],this.s3Keys=[],this.swiftKeys=[],this.capabilities=[],this.showTenant=!1,this.previousTenant=null,this.resource="user",this.subuserLabel="subuser",this.s3keyLabel="S3 Key",this.capabilityLabel="capability",this.editing=this.router.url.startsWith(`/rgw/user/${A.MQ.EDIT}`),this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.CREATE,this.createForm()}createForm(){this.userForm=this.formBuilder.group({user_id:[null,[a.kI.required,a.kI.pattern(/^[a-zA-Z0-9!@#%^&*()_-]+$/)],this.editing?[]:[m.h.unique(this.rgwUserService.exists,this.rgwUserService,()=>this.userForm.getValue("tenant"))]],show_tenant:[this.editing],tenant:[null,[a.kI.pattern(/^[a-zA-Z0-9!@#%^&*()_-]+$/)],this.editing?[]:[m.h.unique(this.rgwUserService.exists,this.rgwUserService,()=>this.userForm.getValue("user_id"),!0)]],display_name:[null,[a.kI.required]],email:[null,[m.h.email],[m.h.unique(this.rgwUserService.emailExists,this.rgwUserService)]],max_buckets_mode:[1],max_buckets:[1e3,[m.h.requiredIf({max_buckets_mode:"1"}),m.h.number(!1)]],suspended:[!1],generate_key:[!0],access_key:[null,[m.h.requiredIf({generate_key:!1})]],secret_key:[null,[m.h.requiredIf({generate_key:!1})]],user_quota_enabled:[!1],user_quota_max_size_unlimited:[!0],user_quota_max_size:[null,[m.h.composeIf({user_quota_enabled:!0,user_quota_max_size_unlimited:!1},[a.kI.required,this.quotaMaxSizeValidator])]],user_quota_max_objects_unlimited:[!0],user_quota_max_objects:[null,[m.h.requiredIf({user_quota_enabled:!0,user_quota_max_objects_unlimited:!1})]],bucket_quota_enabled:[!1],bucket_quota_max_size_unlimited:[!0],bucket_quota_max_size:[null,[m.h.composeIf({bucket_quota_enabled:!0,bucket_quota_max_size_unlimited:!1},[a.kI.required,this.quotaMaxSizeValidator])]],bucket_quota_max_objects_unlimited:[!0],bucket_quota_max_objects:[null,[m.h.requiredIf({bucket_quota_enabled:!0,bucket_quota_max_objects_unlimited:!1})]]})}ngOnInit(){this.route.params.subscribe(_=>{if(!_.hasOwnProperty("uid"))return void this.loadingReady();const n=decodeURIComponent(_.uid),i=[];i.push(this.rgwUserService.get(n)),i.push(this.rgwUserService.getQuota(n)),(0,Y.D)(i).subscribe(s=>{const c=u().clone(this.userForm.value);let d=u().pick(s[0],u().keys(this.userForm.value));switch(d.max_buckets){case-1:d.max_buckets_mode=-1,d.max_buckets="";break;case 0:d.max_buckets_mode=0,d.max_buckets="";break;default:d.max_buckets_mode=1}["user","bucket"].forEach(g=>{const C=s[1][g+"_quota"];d[g+"_quota_enabled"]=C.enabled,C.max_size<0?(d[g+"_quota_max_size_unlimited"]=!0,d[g+"_quota_max_size"]=null):(d[g+"_quota_max_size_unlimited"]=!1,d[g+"_quota_max_size"]=`${C.max_size} B`),C.max_objects<0?(d[g+"_quota_max_objects_unlimited"]=!0,d[g+"_quota_max_objects"]=null):(d[g+"_quota_max_objects_unlimited"]=!1,d[g+"_quota_max_objects"]=C.max_objects)}),d=u().merge(c,d),this.userForm.setValue(d),this.subusers=s[0].subusers,this.s3Keys=s[0].keys,this.swiftKeys=s[0].swift_keys;const E={"read, write":"*"};s[0].caps.forEach(g=>{g.perm in E&&(g.perm=E[g.perm])}),this.capabilities=s[0].caps,this.loadingReady()},()=>{this.loadingError()})})}goToListView(){this.router.navigate(["/rgw/user"])}onSubmit(){let _;if(this.userForm.pristine)return void this.goToListView();const n=this.getUID();if(this.editing){if(this._isGeneralDirty()){const i=this._getUpdateArgs();this.submitObservables.push(this.rgwUserService.update(n,i))}_="Updated Object Gateway user '" + n + "'"}else{const i=this._getCreateArgs();this.submitObservables.push(this.rgwUserService.create(i)),_="Created Object Gateway user '" + n + "'"}if(this._isUserQuotaDirty()){const i=this._getUserQuotaArgs();this.submitObservables.push(this.rgwUserService.updateQuota(n,i))}if(this._isBucketQuotaDirty()){const i=this._getBucketQuotaArgs();this.submitObservables.push(this.rgwUserService.updateQuota(n,i))}(0,Bt.z)(...this.submitObservables).subscribe({error:()=>{this.userForm.setErrors({cdSubmitButton:!0})},complete:()=>{this.notificationService.show(Ae.k.success,_),this.goToListView()}})}updateFieldsWhenTenanted(){this.showTenant=this.userForm.getValue("show_tenant"),this.showTenant?(this.userForm.get("user_id").markAsTouched(),this.previousTenant=this.userForm.get("tenant").value,this.userForm.get("tenant").patchValue(null)):(this.userForm.get("user_id").markAsUntouched(),this.userForm.get("tenant").patchValue(this.previousTenant))}getUID(){var _;let n=this.userForm.getValue("user_id");const i=null===(_=this.userForm)||void 0===_?void 0:_.getValue("tenant");return i&&i.length>0&&(n=`${this.userForm.getValue("tenant")}$${n}`),n}quotaMaxSizeValidator(_){return(0,m.P)(_.value)?null:null===RegExp("^(\\d+(\\.\\d+)?)\\s*(B|K(B|iB)?|M(B|iB)?|G(B|iB)?|T(B|iB)?)?$","i").exec(_.value)||(new Ge.H).toBytes(_.value)<1024?{quotaMaxSize:!0}:null}setSubuser(_,n){const i={"full-control":"full","read-write":"readwrite"},s=this.getUID();this.submitObservables.push(this.rgwUserService.createSubuser(s,{subuser:_.id,access:_.permissions in i?i[_.permissions]:_.permissions,key_type:"swift",secret_key:_.secret_key,generate_secret:_.generate_secret?"true":"false"})),u().isNumber(n)?this.subusers[n]=_:(this.subusers.push(_),this.swiftKeys.push({user:_.id,secret_key:_.generate_secret?"Apply your changes first...":_.secret_key})),this.userForm.markAsDirty()}deleteSubuser(_){const n=this.subusers[_];this.submitObservables.push(this.rgwUserService.deleteSubuser(this.getUID(),n.id)),this.s3Keys=this.s3Keys.filter(i=>i.user!==n.id),this.swiftKeys=this.swiftKeys.filter(i=>i.user!==n.id),this.subusers.splice(_,1),this.userForm.markAsDirty()}setCapability(_,n){const i=this.getUID();if(u().isNumber(n)){const s=this.capabilities[n];this.submitObservables.push(this.rgwUserService.deleteCapability(i,s.type,s.perm)),this.submitObservables.push(this.rgwUserService.addCapability(i,_.type,_.perm)),this.capabilities[n]=_}else this.submitObservables.push(this.rgwUserService.addCapability(i,_.type,_.perm)),this.capabilities=[...this.capabilities,_];this.userForm.markAsDirty()}deleteCapability(_){const n=this.capabilities[_];this.submitObservables.push(this.rgwUserService.deleteCapability(this.getUID(),n.type,n.perm)),this.capabilities.splice(_,1),this.capabilities=[...this.capabilities],this.userForm.markAsDirty()}hasAllCapabilities(_){return!u().difference(ze.getAll(),u().map(_,"type")).length}setS3Key(_,n){if(!u().isNumber(n)){const i=_.user.match(/([^:]+)(:(.+))?/),s=i[1],c={subuser:i[2]?i[3]:"",generate_key:_.generate_key?"true":"false"};"false"===c.generate_key&&(u().isNil(_.access_key)||(c.access_key=_.access_key),u().isNil(_.secret_key)||(c.secret_key=_.secret_key)),this.submitObservables.push(this.rgwUserService.addS3Key(s,c)),this.s3Keys.push({user:_.user,access_key:_.generate_key?"Apply your changes first...":_.access_key,secret_key:_.generate_key?"Apply your changes first...":_.secret_key})}this.userForm.markAsDirty()}deleteS3Key(_){const n=this.s3Keys[_];this.submitObservables.push(this.rgwUserService.deleteS3Key(this.getUID(),n.access_key)),this.s3Keys.splice(_,1),this.userForm.markAsDirty()}showSubuserModal(_){const n=this.getUID(),i=this.modalService.show(mn);if(u().isNumber(_)){const s=this.subusers[_];i.componentInstance.setEditing(),i.componentInstance.setValues(n,s.id,s.permissions)}else i.componentInstance.setEditing(!1),i.componentInstance.setValues(n),i.componentInstance.setSubusers(this.subusers);i.componentInstance.submitAction.subscribe(s=>{this.setSubuser(s,_)})}showS3KeyModal(_){const n=this.modalService.show(Qe);if(u().isNumber(_)){const i=this.s3Keys[_];n.componentInstance.setViewing(),n.componentInstance.setValues(i.user,i.access_key,i.secret_key)}else{const i=this._getS3KeyUserCandidates();n.componentInstance.setViewing(!1),n.componentInstance.setUserCandidates(i),n.componentInstance.submitAction.subscribe(s=>{this.setS3Key(s)})}}showSwiftKeyModal(_){const n=this.modalService.show(Ye),i=this.swiftKeys[_];n.componentInstance.setValues(i.user,i.secret_key)}showCapabilityModal(_){const n=this.modalService.show(en);if(u().isNumber(_)){const i=this.capabilities[_];n.componentInstance.setEditing(),n.componentInstance.setValues(i.type,i.perm)}else n.componentInstance.setEditing(!1),n.componentInstance.setCapabilities(this.capabilities);n.componentInstance.submitAction.subscribe(i=>{this.setCapability(i,_)})}_isGeneralDirty(){return["display_name","email","max_buckets_mode","max_buckets","suspended"].some(_=>this.userForm.get(_).dirty)}_isUserQuotaDirty(){return["user_quota_enabled","user_quota_max_size_unlimited","user_quota_max_size","user_quota_max_objects_unlimited","user_quota_max_objects"].some(_=>this.userForm.get(_).dirty)}_isBucketQuotaDirty(){return["bucket_quota_enabled","bucket_quota_max_size_unlimited","bucket_quota_max_size","bucket_quota_max_objects_unlimited","bucket_quota_max_objects"].some(_=>this.userForm.get(_).dirty)}_getCreateArgs(){const _={uid:this.getUID(),display_name:this.userForm.getValue("display_name"),suspended:this.userForm.getValue("suspended"),email:"",max_buckets:this.userForm.getValue("max_buckets"),generate_key:this.userForm.getValue("generate_key"),access_key:"",secret_key:""},n=this.userForm.getValue("email");u().isString(n)&&n.length>0&&u().merge(_,{email:n}),this.userForm.getValue("generate_key")||u().merge(_,{generate_key:!1,access_key:this.userForm.getValue("access_key"),secret_key:this.userForm.getValue("secret_key")});const s=parseInt(this.userForm.getValue("max_buckets_mode"),10);return u().includes([-1,0],s)&&u().merge(_,{max_buckets:s}),_}_getUpdateArgs(){const _={},n=["display_name","email","max_buckets","suspended"];for(const s of n)_[s]=this.userForm.getValue(s);const i=parseInt(this.userForm.getValue("max_buckets_mode"),10);return u().includes([-1,0],i)&&(_.max_buckets=i),_}_getUserQuotaArgs(){const _={quota_type:"user",enabled:this.userForm.getValue("user_quota_enabled"),max_size_kb:-1,max_objects:-1};if(!this.userForm.getValue("user_quota_max_size_unlimited")){const n=(new Ge.H).toBytes(this.userForm.getValue("user_quota_max_size"));_.max_size_kb=(n/1024).toFixed(0)}return this.userForm.getValue("user_quota_max_objects_unlimited")||(_.max_objects=this.userForm.getValue("user_quota_max_objects")),_}_getBucketQuotaArgs(){const _={quota_type:"bucket",enabled:this.userForm.getValue("bucket_quota_enabled"),max_size_kb:-1,max_objects:-1};if(!this.userForm.getValue("bucket_quota_max_size_unlimited")){const n=(new Ge.H).toBytes(this.userForm.getValue("bucket_quota_max_size"));_.max_size_kb=(n/1024).toFixed(0)}return this.userForm.getValue("bucket_quota_max_objects_unlimited")||(_.max_objects=this.userForm.getValue("bucket_quota_max_objects")),_}_getS3KeyUserCandidates(){let _=[];const n=this.getUID();return u().isString(n)&&!u().isEmpty(n)&&_.push(n),this.subusers.forEach(i=>{_.push(i.id)}),this.s3Keys.forEach(i=>{_.push(i.user)}),_=u().uniq(_),_}onMaxBucketsModeChange(_){"1"===_&&(this.userForm.get("max_buckets").valid||this.userForm.patchValue({max_buckets:1e3}))}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(J.O),e.Y36(w.gz),e.Y36(w.F0),e.Y36(O),e.Y36(Te.Z),e.Y36(ve.g),e.Y36(A.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-form"]],features:[e.qOj],decls:1,vars:1,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F,T,x,y,S,_e,te,ne,oe,ie,se,ae,re,le,ce,de,ue,Re,R,__,t_,n_,o_,i_,s_,a_,r_,l_,c_,d_,u_,R_,g_,E_,T_,S_,f_,C_,p_,M_,m_,A_,b_,P_,G_;return o="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="User ID",n="Show Tenant",i="Full name",s="Email address",c="Max. buckets",d="Disabled",E="Unlimited",g="Custom",C="Suspended",b="User quota",P="Enabled",G="Bucket quota",N="Enabled",p="This field is required.",U="The value is not valid.",W="The chosen user ID is already in use.",Z="Tenant",$="The value is not valid.",h="The chosen user ID exists in this tenant.",I="This field is required.",v="This is not a valid email address.",F="The chosen email address is already in use.",T="This field is required.",x="The entered value must be >= 1.",y="S3 key",S="Auto-generate key",_e="Access key",te="This field is required.",ne="Secret key",oe="This field is required.",ie="Subusers",se="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",ae="There are no subusers.",re="Edit",le="Delete",ce="Keys",de="S3",ue="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",Re="Swift",R="There are no keys.",__="Show",t_="Delete",n_="There are no keys.",o_="Show",i_="Capabilities",s_="All capabilities are already added.",a_="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",r_="There are no capabilities.",l_="Edit",c_="Delete",d_="Unlimited size",u_="Max. size",R_="This field is required.",g_="The value is not valid.",E_="Unlimited objects",T_="Max. objects",S_="This field is required.",f_="The entered value must be >= 0.",C_="Unlimited size",p_="Max. size",M_="This field is required.",m_="The value is not valid.",A_="Unlimited objects",b_="Max. objects",P_="This field is required.",G_="The entered value must be >= 0.",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"card"],[1,"card-header"],o,[1,"card-body"],[1,"form-group","row"],["for","user_id",1,"cd-col-form-label",3,"ngClass"],_,[1,"cd-col-form-input"],["id","user_id","type","text","formControlName","user_id",1,"form-control",3,"readonly"],["class","invalid-feedback",4,"ngIf"],[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["id","show_tenant","type","checkbox","formControlName","show_tenant",1,"custom-control-input",3,"readonly","click"],["for","show_tenant",1,"custom-control-label"],n,["class","form-group row",4,"ngIf"],["for","display_name",1,"cd-col-form-label",3,"ngClass"],i,["id","display_name","type","text","formControlName","display_name",1,"form-control"],["for","email",1,"cd-col-form-label"],s,["id","email","type","text","formControlName","email",1,"form-control"],["for","max_buckets_mode",1,"cd-col-form-label"],c,["formControlName","max_buckets_mode","name","max_buckets_mode","id","max_buckets_mode",1,"form-control",3,"change"],["value","-1"],d,["value","0"],E,["value","1"],g,["id","suspended","type","checkbox","formControlName","suspended",1,"custom-control-input"],["for","suspended",1,"custom-control-label"],C,[4,"ngIf"],b,["id","user_quota_enabled","type","checkbox","formControlName","user_quota_enabled",1,"custom-control-input"],["for","user_quota_enabled",1,"custom-control-label"],P,G,["id","bucket_quota_enabled","type","checkbox","formControlName","bucket_quota_enabled",1,"custom-control-input"],["for","bucket_quota_enabled",1,"custom-control-label"],N,[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],p,U,W,["for","tenant",1,"cd-col-form-label"],Z,["id","tenant","type","text","formControlName","tenant","autofocus","",1,"form-control",3,"readonly"],$,h,I,v,F,[1,"cd-col-form-label"],["id","max_buckets","type","number","formControlName","max_buckets","min","1",1,"form-control"],T,x,y,["id","generate_key","type","checkbox","formControlName","generate_key",1,"custom-control-input"],["for","generate_key",1,"custom-control-label"],S,["for","access_key",1,"cd-col-form-label","required"],_e,[1,"input-group"],["id","access_key","type","password","formControlName","access_key",1,"form-control"],[1,"input-group-append"],["type","button","cdPasswordButton","access_key",1,"btn","btn-light"],["source","access_key"],te,["for","secret_key",1,"cd-col-form-label","required"],ne,["id","secret_key","type","password","formControlName","secret_key",1,"form-control"],["type","button","cdPasswordButton","secret_key",1,"btn","btn-light"],["source","secret_key"],oe,ie,[1,"row"],["class","no-border",4,"ngIf"],[4,"ngFor","ngForOf"],[1,"col-12"],["type","button",1,"btn","btn-light","float-right","tc_addSubuserButton",3,"click"],[3,"ngClass"],se,[1,"help-block"],[1,"no-border"],[1,"form-text","text-muted"],ae,[1,"input-group-prepend"],[1,"input-group-text"],["type","text","readonly","",1,"cd-form-control",3,"value"],[1,"input-group-prepend","border-left-0","border-right-0"],["type","button","ngbTooltip",re,1,"btn","btn-light","tc_showSubuserButton",3,"click"],["type","button","ngbTooltip",le,1,"btn","btn-light","tc_deleteSubuserButton",3,"click"],ce,de,["type","button",1,"btn","btn-light","float-right","tc_addS3KeyButton",3,"click"],ue,Re,R,["type","button","ngbTooltip",__,1,"btn","btn-light","tc_showS3KeyButton",3,"click"],["type","button","ngbTooltip",t_,1,"btn","btn-light","tc_deleteS3KeyButton",3,"click"],n_,["type","button","ngbTooltip",o_,1,"btn","btn-light","tc_showSwiftKeyButton",3,"click"],i_,["type","button","ngbTooltip",s_,"triggers","pointerenter:pointerleave",1,"btn","btn-light","float-right","tc_addCapButton",3,"disabled","disableTooltip","click"],a_,r_,["type","button","ngbTooltip",l_,1,"btn","btn-light","tc_editCapButton",3,"click"],["type","button","ngbTooltip",c_,1,"btn","btn-light","tc_deleteCapButton",3,"click"],["id","user_quota_max_size_unlimited","type","checkbox","formControlName","user_quota_max_size_unlimited",1,"custom-control-input"],["for","user_quota_max_size_unlimited",1,"custom-control-label"],d_,["for","user_quota_max_size",1,"cd-col-form-label","required"],u_,["id","user_quota_max_size","type","text","formControlName","user_quota_max_size","cdDimlessBinary","",1,"form-control"],R_,g_,["id","user_quota_max_objects_unlimited","type","checkbox","formControlName","user_quota_max_objects_unlimited",1,"custom-control-input"],["for","user_quota_max_objects_unlimited",1,"custom-control-label"],E_,["for","user_quota_max_objects",1,"cd-col-form-label","required"],T_,["id","user_quota_max_objects","type","number","formControlName","user_quota_max_objects","min","0",1,"form-control"],S_,f_,["id","bucket_quota_max_size_unlimited","type","checkbox","formControlName","bucket_quota_max_size_unlimited",1,"custom-control-input"],["for","bucket_quota_max_size_unlimited",1,"custom-control-label"],C_,["for","bucket_quota_max_size",1,"cd-col-form-label","required"],p_,["id","bucket_quota_max_size","type","text","formControlName","bucket_quota_max_size","cdDimlessBinary","",1,"form-control"],M_,m_,["id","bucket_quota_max_objects_unlimited","type","checkbox","formControlName","bucket_quota_max_objects_unlimited",1,"custom-control-input"],["for","bucket_quota_max_objects_unlimited",1,"custom-control-label"],A_,["for","bucket_quota_max_objects",1,"cd-col-form-label","required"],b_,["id","bucket_quota_max_objects","type","number","formControlName","bucket_quota_max_objects","min","0",1,"form-control"],P_,G_]},template:function(_,n){1&_&&e.YNc(0,To,89,41,"div",0),2&_&&e.Q6J("cdFormLoading",n.loading)},directives:[Fe.y,a._Y,a.JL,V.V,a.sg,H.P,f.mk,q.o,a.Fj,X.b,a.JJ,a.u,f.O5,a.Wl,a.EJ,a.YN,a.Kr,ee.p,j.U,a.wV,a.qQ,Ce.C,pe.s,f.sg,M._L,bn.Q],pipes:[f.rS,K.m,Ue.i],styles:[""]}),t})();var je=r(99466),So=r(78877),fo=r(86969);const Co=["accessKeyTpl"],po=["secretKeyTpl"];function Mo(t,o){if(1&t&&(e.TgZ(0,"tr"),e.TgZ(1,"td",15),e.SDv(2,20),e.qZA(),e.TgZ(3,"td"),e._uU(4),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(4),e.Oqu(_.user.email)}}function mo(t,o){if(1&t&&(e.TgZ(0,"div"),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.xp6(1),e.AsE(" ",_.id," (",_.permissions,") ")}}function Ao(t,o){if(1&t&&(e.TgZ(0,"tr"),e.TgZ(1,"td",15),e.SDv(2,21),e.qZA(),e.TgZ(3,"td"),e.YNc(4,mo,2,2,"div",22),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(4),e.Q6J("ngForOf",_.user.subusers)}}function bo(t,o){if(1&t&&(e.TgZ(0,"div"),e._uU(1),e.qZA()),2&t){const _=o.$implicit;e.xp6(1),e.AsE(" ",_.type," (",_.perm,") ")}}function Po(t,o){if(1&t&&(e.TgZ(0,"tr"),e.TgZ(1,"td",15),e.SDv(2,23),e.qZA(),e.TgZ(3,"td"),e.YNc(4,bo,2,2,"div",22),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(4),e.Q6J("ngForOf",_.user.caps)}}function Go(t,o){if(1&t&&(e.TgZ(0,"tr"),e.TgZ(1,"td",15),e.SDv(2,24),e.qZA(),e.TgZ(3,"td"),e._uU(4),e.ALo(5,"join"),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(4),e.Oqu(e.lcZ(5,1,_.user.mfa_ids))}}function No(t,o){1&t&&(e.TgZ(0,"td"),e._uU(1,"-"),e.qZA())}function Oo(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,29),e.qZA())}function Uo(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.ALo(2,"dimlessBinary"),e.qZA()),2&t){const _=e.oxw(5);e.xp6(1),e.hij(" ",e.lcZ(2,1,_.user.user_quota.max_size)," ")}}function Wo(t,o){1&t&&(e.TgZ(0,"td"),e._uU(1,"-"),e.qZA())}function Zo(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,30),e.qZA())}function $o(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.qZA()),2&t){const _=e.oxw(5);e.xp6(1),e.hij(" ",_.user.user_quota.max_objects," ")}}function ho(t,o){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"legend"),e.SDv(2,25),e.qZA(),e.TgZ(3,"table",9),e.TgZ(4,"tbody"),e.TgZ(5,"tr"),e.TgZ(6,"td",10),e.SDv(7,26),e.qZA(),e.TgZ(8,"td",12),e._uU(9),e.ALo(10,"booleanText"),e.qZA(),e.qZA(),e.TgZ(11,"tr"),e.TgZ(12,"td",15),e.SDv(13,27),e.qZA(),e.YNc(14,No,2,0,"td",0),e.YNc(15,Oo,2,0,"td",0),e.YNc(16,Uo,3,3,"td",0),e.qZA(),e.TgZ(17,"tr"),e.TgZ(18,"td",15),e.SDv(19,28),e.qZA(),e.YNc(20,Wo,2,0,"td",0),e.YNc(21,Zo,2,0,"td",0),e.YNc(22,$o,2,1,"td",0),e.qZA(),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(9),e.Oqu(e.lcZ(10,7,_.user.user_quota.enabled)),e.xp6(5),e.Q6J("ngIf",!_.user.user_quota.enabled),e.xp6(1),e.Q6J("ngIf",_.user.user_quota.enabled&&_.user.user_quota.max_size<=-1),e.xp6(1),e.Q6J("ngIf",_.user.user_quota.enabled&&_.user.user_quota.max_size>-1),e.xp6(4),e.Q6J("ngIf",!_.user.user_quota.enabled),e.xp6(1),e.Q6J("ngIf",_.user.user_quota.enabled&&_.user.user_quota.max_objects<=-1),e.xp6(1),e.Q6J("ngIf",_.user.user_quota.enabled&&_.user.user_quota.max_objects>-1)}}function Io(t,o){1&t&&(e.TgZ(0,"td"),e._uU(1,"-"),e.qZA())}function vo(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,35),e.qZA())}function Fo(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.ALo(2,"dimlessBinary"),e.qZA()),2&t){const _=e.oxw(5);e.xp6(1),e.hij(" ",e.lcZ(2,1,_.user.bucket_quota.max_size)," ")}}function Lo(t,o){1&t&&(e.TgZ(0,"td"),e._uU(1,"-"),e.qZA())}function Do(t,o){1&t&&(e.TgZ(0,"td"),e.SDv(1,36),e.qZA())}function xo(t,o){if(1&t&&(e.TgZ(0,"td"),e._uU(1),e.qZA()),2&t){const _=e.oxw(5);e.xp6(1),e.hij(" ",_.user.bucket_quota.max_objects," ")}}function yo(t,o){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"legend"),e.SDv(2,31),e.qZA(),e.TgZ(3,"table",9),e.TgZ(4,"tbody"),e.TgZ(5,"tr"),e.TgZ(6,"td",10),e.SDv(7,32),e.qZA(),e.TgZ(8,"td",12),e._uU(9),e.ALo(10,"booleanText"),e.qZA(),e.qZA(),e.TgZ(11,"tr"),e.TgZ(12,"td",15),e.SDv(13,33),e.qZA(),e.YNc(14,Io,2,0,"td",0),e.YNc(15,vo,2,0,"td",0),e.YNc(16,Fo,3,3,"td",0),e.qZA(),e.TgZ(17,"tr"),e.TgZ(18,"td",15),e.SDv(19,34),e.qZA(),e.YNc(20,Lo,2,0,"td",0),e.YNc(21,Do,2,0,"td",0),e.YNc(22,xo,2,1,"td",0),e.qZA(),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(9),e.Oqu(e.lcZ(10,7,_.user.bucket_quota.enabled)),e.xp6(5),e.Q6J("ngIf",!_.user.bucket_quota.enabled),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota.enabled&&_.user.bucket_quota.max_size<=-1),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota.enabled&&_.user.bucket_quota.max_size>-1),e.xp6(4),e.Q6J("ngIf",!_.user.bucket_quota.enabled),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota.enabled&&_.user.bucket_quota.max_objects<=-1),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota.enabled&&_.user.bucket_quota.max_objects>-1)}}function qo(t,o){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"table",9),e.TgZ(2,"tbody"),e.TgZ(3,"tr"),e.TgZ(4,"td",10),e.SDv(5,11),e.qZA(),e.TgZ(6,"td",12),e._uU(7),e.qZA(),e.qZA(),e.TgZ(8,"tr"),e.TgZ(9,"td",10),e.SDv(10,13),e.qZA(),e.TgZ(11,"td",12),e._uU(12),e.qZA(),e.qZA(),e.TgZ(13,"tr"),e.TgZ(14,"td",10),e.SDv(15,14),e.qZA(),e.TgZ(16,"td",12),e._uU(17),e.qZA(),e.qZA(),e.TgZ(18,"tr"),e.TgZ(19,"td",15),e.SDv(20,16),e.qZA(),e.TgZ(21,"td"),e._uU(22),e.qZA(),e.qZA(),e.YNc(23,Mo,5,1,"tr",0),e.TgZ(24,"tr"),e.TgZ(25,"td",15),e.SDv(26,17),e.qZA(),e.TgZ(27,"td"),e._uU(28),e.ALo(29,"booleanText"),e.qZA(),e.qZA(),e.TgZ(30,"tr"),e.TgZ(31,"td",15),e.SDv(32,18),e.qZA(),e.TgZ(33,"td"),e._uU(34),e.ALo(35,"booleanText"),e.qZA(),e.qZA(),e.TgZ(36,"tr"),e.TgZ(37,"td",15),e.SDv(38,19),e.qZA(),e.TgZ(39,"td"),e._uU(40),e.ALo(41,"map"),e.qZA(),e.qZA(),e.YNc(42,Ao,5,1,"tr",0),e.YNc(43,Po,5,1,"tr",0),e.YNc(44,Go,6,3,"tr",0),e.qZA(),e.qZA(),e.YNc(45,ho,23,9,"div",0),e.YNc(46,yo,23,9,"div",0),e.qZA()),2&t){const _=e.oxw(3);e.xp6(7),e.Oqu(_.user.tenant),e.xp6(5),e.Oqu(_.user.user_id),e.xp6(5),e.Oqu(_.user.uid),e.xp6(5),e.Oqu(_.user.display_name),e.xp6(1),e.Q6J("ngIf",null==_.user.email?null:_.user.email.length),e.xp6(5),e.Oqu(e.lcZ(29,13,_.user.suspended)),e.xp6(6),e.Oqu(e.lcZ(35,15,"true"===_.user.system)),e.xp6(6),e.Oqu(e.xi3(41,17,_.user.max_buckets,_.maxBucketsMap)),e.xp6(2),e.Q6J("ngIf",_.user.subusers&&_.user.subusers.length),e.xp6(1),e.Q6J("ngIf",_.user.caps&&_.user.caps.length),e.xp6(1),e.Q6J("ngIf",null==_.user.mfa_ids?null:_.user.mfa_ids.length),e.xp6(1),e.Q6J("ngIf",_.user.user_quota),e.xp6(1),e.Q6J("ngIf",_.user.bucket_quota)}}function wo(t,o){if(1&t&&e.YNc(0,qo,47,20,"div",0),2&t){const _=e.oxw(2);e.Q6J("ngIf",_.user)}}const ko=function(t){return[t]};function Bo(t,o){if(1&t){const _=e.EpF();e.TgZ(0,"cd-table",39),e.NdJ("updateSelection",function(i){return e.CHM(_),e.oxw(3).updateKeysSelection(i)}),e.TgZ(1,"div",40),e.TgZ(2,"div",41),e.TgZ(3,"button",42),e.NdJ("click",function(){return e.CHM(_),e.oxw(3).showKeyModal()}),e._UZ(4,"i",43),e.ynx(5),e.SDv(6,44),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(3);e.Q6J("data",_.keys)("columns",_.keysColumns),e.xp6(3),e.Q6J("disabled",!_.keysSelection.hasSingleSelection),e.xp6(1),e.Q6J("ngClass",e.VKq(4,ko,_.icons.show))}}function Ho(t,o){1&t&&(e.TgZ(0,"li",37),e.TgZ(1,"a",4),e.SDv(2,38),e.qZA(),e.YNc(3,Bo,7,6,"ng-template",6),e.qZA())}function Xo(t,o){if(1&t&&(e.ynx(0),e.TgZ(1,"ul",1,2),e.TgZ(3,"li",3),e.TgZ(4,"a",4),e.SDv(5,5),e.qZA(),e.YNc(6,wo,1,1,"ng-template",6),e.qZA(),e.YNc(7,Ho,4,0,"li",7),e.qZA(),e._UZ(8,"div",8),e.BQk()),2&t){const _=e.MAs(2),n=e.oxw();e.xp6(7),e.Q6J("ngIf",n.keys.length),e.xp6(1),e.Q6J("ngbNavOutlet",_)}}let Ko=(()=>{class t{constructor(_,n){this.rgwUserService=_,this.modalService=n,this.keys=[],this.keysColumns=[],this.keysSelection=new Pe.r,this.icons=D.P}ngOnInit(){this.keysColumns=[{name:"Username",prop:"username",flexGrow:1},{name:"Type",prop:"type",flexGrow:1}],this.maxBucketsMap={"-1":"Disabled",0:"Unlimited"}}ngOnChanges(){this.selection&&(this.user=this.selection,this.user.subusers=u().sortBy(this.user.subusers,"id"),this.user.caps=u().sortBy(this.user.caps,"type"),this.rgwUserService.getQuota(this.user.uid).subscribe(_=>{u().extend(this.user,_)}),this.keys=[],this.user.keys&&this.user.keys.forEach(_=>{this.keys.push({id:this.keys.length+1,type:"S3",username:_.user,ref:_})}),this.user.swift_keys&&this.user.swift_keys.forEach(_=>{this.keys.push({id:this.keys.length+1,type:"Swift",username:_.user,ref:_})}),this.keys=u().sortBy(this.keys,"user"))}updateKeysSelection(_){this.keysSelection=_}showKeyModal(){const _=this.keysSelection.first(),n=this.modalService.show("S3"===_.type?Qe:Ye);switch(_.type){case"S3":n.componentInstance.setViewing(),n.componentInstance.setValues(_.ref.user,_.ref.access_key,_.ref.secret_key);break;case"Swift":n.componentInstance.setValues(_.ref.user,_.ref.secret_key)}}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(O),e.Y36(Te.Z))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-details"]],viewQuery:function(_,n){if(1&_&&(e.Gf(Co,5),e.Gf(po,5)),2&_){let i;e.iGM(i=e.CRH())&&(n.accessKeyTpl=i.first),e.iGM(i=e.CRH())&&(n.secretKeyTpl=i.first)}},inputs:{selection:"selection"},features:[e.TTD],decls:1,vars:1,consts:function(){let o,_,n,i,s,c,d,E,g,C,b,P,G,N,p,U,W,Z,$,h,I,v,F,T,x,y;return o="Details",_="Tenant",n="User ID",i="Username",s="Full name",c="Suspended",d="System",E="Maximum buckets",g="Email address",C="Subusers",b="Capabilities",P="MFAs(Id)",G="User quota",N="Enabled",p="Maximum size",U="Maximum objects",W="Unlimited",Z="Unlimited",$="Bucket quota",h="Enabled",I="Maximum size",v="Maximum objects",F="Unlimited",T="Unlimited",x="Keys",y="Show",[[4,"ngIf"],["ngbNav","","cdStatefulTab","rgw-user-details",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","details"],["ngbNavLink",""],o,["ngbNavContent",""],["ngbNavItem","keys",4,"ngIf"],[3,"ngbNavOutlet"],[1,"table","table-striped","table-bordered"],[1,"bold","w-25"],_,[1,"w-75"],n,i,[1,"bold"],s,c,d,E,g,C,[4,"ngFor","ngForOf"],b,P,G,N,p,U,W,Z,$,h,I,v,F,T,["ngbNavItem","keys"],x,["columnMode","flex","selectionType","multi","forceIdentifier","true",3,"data","columns","updateSelection"],[1,"table-actions"],["dropdown","",1,"btn-group"],["type","button",1,"btn","btn-accent",3,"disabled","click"],[3,"ngClass"],y]},template:function(_,n){1&_&&e.YNc(0,Xo,9,2,"ng-container",0),2&_&&e.Q6J("ngIf",n.selection)},directives:[f.O5,M.Pz,Xe.m,M.nv,M.Vx,M.uN,M.tO,f.sg,z.a,q.o,f.mk],pipes:[Be.T,So.b,fo.A,qe.$],styles:[""]}),t})();const zo=["userSizeTpl"],Qo=["userObjectTpl"];function Yo(t,o){if(1&t&&e._UZ(0,"cd-usage-bar",8),2&t){const _=e.oxw().row;e.Q6J("total",_.user_quota.max_size)("used",_.stats.size_actual)}}function Jo(t,o){1&t&&e.SDv(0,9)}function Vo(t,o){if(1&t&&(e.YNc(0,Yo,1,2,"cd-usage-bar",6),e.YNc(1,Jo,1,0,"ng-template",null,7,e.W1O)),2&t){const _=o.row,n=e.MAs(2);e.Q6J("ngIf",_.user_quota.max_size>0&&_.user_quota.enabled)("ngIfElse",n)}}function jo(t,o){if(1&t&&e._UZ(0,"cd-usage-bar",12),2&t){const _=e.oxw().row;e.Q6J("total",_.user_quota.max_objects)("used",_.stats.num_objects)("isBinary",!1)}}function ei(t,o){1&t&&e.SDv(0,13)}function _i(t,o){if(1&t&&(e.YNc(0,jo,1,3,"cd-usage-bar",10),e.YNc(1,ei,1,0,"ng-template",null,11,e.W1O)),2&t){const _=o.row,n=e.MAs(2);e.Q6J("ngIf",_.user_quota.max_objects>0&&_.user_quota.enabled)("ngIfElse",n)}}let ni=(()=>{class t extends be.o{constructor(_,n,i,s,c,d){super(d),this.authStorageService=_,this.rgwUserService=n,this.modalService=i,this.urlBuilder=s,this.actionLabels=c,this.ngZone=d,this.columns=[],this.users=[],this.selection=new Pe.r}ngOnInit(){this.permission=this.authStorageService.getPermissions().rgw,this.columns=[{name:"Username",prop:"uid",flexGrow:1},{name:"Tenant",prop:"tenant",flexGrow:1},{name:"Full name",prop:"display_name",flexGrow:1},{name:"Email address",prop:"email",flexGrow:1},{name:"Suspended",prop:"suspended",flexGrow:1,cellClass:"text-center",cellTransformation:je.e.checkIcon},{name:"Max. buckets",prop:"max_buckets",flexGrow:1,cellTransformation:je.e.map,customTemplateConfig:{"-1":"Disabled",0:"Unlimited"}},{name:"Capacity Limit %",prop:"size_usage",cellTemplate:this.userSizeTpl,flexGrow:.8},{name:"Object Limit %",prop:"object_usage",cellTemplate:this.userObjectTpl,flexGrow:.8}];const _=()=>this.selection.first()&&`${encodeURIComponent(this.selection.first().uid)}`;this.tableActions=[{permission:"create",icon:D.P.add,routerLink:()=>this.urlBuilder.getCreate(),name:this.actionLabels.CREATE,canBePrimary:c=>!c.hasSelection},{permission:"update",icon:D.P.edit,routerLink:()=>this.urlBuilder.getEdit(_()),name:this.actionLabels.EDIT},{permission:"delete",icon:D.P.destroy,click:()=>this.deleteAction(),disable:()=>!this.selection.hasSelection,name:this.actionLabels.DELETE,canBePrimary:c=>c.hasMultiSelection}],this.setTableRefreshTimeout()}getUserList(_){this.setTableRefreshTimeout(),this.rgwUserService.list().subscribe(n=>{this.users=n},()=>{_.error()})}updateSelection(_){this.selection=_}deleteAction(){this.modalService.show(ye.M,{itemDescription:this.selection.hasSingleSelection?"user":"users",itemNames:this.selection.selected.map(_=>_.uid),submitActionObservable:()=>new xe.y(_=>{(0,Y.D)(this.selection.selected.map(n=>this.rgwUserService.delete(n.uid))).subscribe({error:n=>{_.error(n),this.table.refreshBtn()},complete:()=>{_.complete(),this.table.refreshBtn()}})})})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(Ee.j),e.Y36(O),e.Y36(Te.Z),e.Y36(Q.F),e.Y36(A.p4),e.Y36(e.R0b))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-rgw-user-list"]],viewQuery:function(_,n){if(1&_&&(e.Gf(z.a,7),e.Gf(zo,7),e.Gf(Qo,7)),2&_){let i;e.iGM(i=e.CRH())&&(n.table=i.first),e.iGM(i=e.CRH())&&(n.userSizeTpl=i.first),e.iGM(i=e.CRH())&&(n.userObjectTpl=i.first)}},features:[e._Bn([{provide:Q.F,useValue:new Q.F("rgw/user")}]),e.qOj],decls:8,vars:9,consts:function(){let o,_;return o="No Limit",_="No Limit",[["columnMode","flex","selectionType","multiClick","identifier","uid",3,"autoReload","data","columns","hasDetails","status","setExpandedRow","updateSelection","fetchData"],["table",""],[1,"table-actions",3,"permission","selection","tableActions"],["cdTableDetail","",3,"selection"],["userSizeTpl",""],["userObjectTpl",""],[3,"total","used",4,"ngIf","ngIfElse"],["noSizeQuota",""],[3,"total","used"],o,[3,"total","used","isBinary",4,"ngIf","ngIfElse"],["noObjectQuota",""],[3,"total","used","isBinary"],_]},template:function(_,n){1&_&&(e.TgZ(0,"cd-table",0,1),e.NdJ("setExpandedRow",function(s){return n.setExpandedRow(s)})("updateSelection",function(s){return n.updateSelection(s)})("fetchData",function(s){return n.getUserList(s)}),e._UZ(2,"cd-table-actions",2),e._UZ(3,"cd-rgw-user-details",3),e.qZA(),e.YNc(4,Vo,3,2,"ng-template",null,4,e.W1O),e.YNc(6,_i,3,2,"ng-template",null,5,e.W1O)),2&_&&(e.Q6J("autoReload",!1)("data",n.users)("columns",n.columns)("hasDetails",!0)("status",n.tableStatus),e.xp6(2),e.Q6J("permission",n.permission)("selection",n.selection)("tableActions",n.tableActions),e.xp6(1),e.Q6J("selection",n.expandedRow))},directives:[z.a,ke.K,Ko,f.O5,He.O],styles:[""]}),t})(),e_=(()=>{class t{}return t.\u0275fac=function(_){return new(_||t)},t.\u0275mod=e.oAB({type:t}),t.\u0275inj=e.cJS({imports:[[f.ez,N_.m,a.u5,a.UX,O_.B,M.Oz,w.Bz,M.HK,Ue.b]]}),t})();const oi=[{path:""},{path:"daemon",component:kt,data:{breadcrumbs:"Daemons"}},{path:"user",data:{breadcrumbs:"Users"},children:[{path:"",component:ni},{path:A.MQ.CREATE,component:Ve,data:{breadcrumbs:A.Qn.CREATE}},{path:`${A.MQ.EDIT}/:uid`,component:Ve,data:{breadcrumbs:A.Qn.EDIT}}]},{path:"bucket",data:{breadcrumbs:"Buckets"},children:[{path:"",component:Ot},{path:A.MQ.CREATE,component:De,data:{breadcrumbs:A.Qn.CREATE}},{path:`${A.MQ.EDIT}/:bid`,component:De,data:{breadcrumbs:A.Qn.EDIT}}]}];let ii=(()=>{class t{}return t.\u0275fac=function(_){return new(_||t)},t.\u0275mod=e.oAB({type:t}),t.\u0275inj=e.cJS({imports:[[e_,w.Bz.forChild(oi)]]}),t})()}}]); \ No newline at end of file diff --git a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/330.070e111fc5b7315b4eac.js b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/330.070e111fc5b7315b4eac.js deleted file mode 100644 index cbbf16408..000000000 --- a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/330.070e111fc5b7315b4eac.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkceph_dashboard=self.webpackChunkceph_dashboard||[]).push([[330],{91330:(it,Oe,p)=>{p.r(Oe),p.d(Oe,{BlockModule:()=>Mt,RoutedBlockModule:()=>$a});var l=p(12057),r=p(24751),m=p(6283),ne=p(20356),I=p(38549),Ie=p(37496),D=p(79512),j=p(4222),_e=p(44466),be=p(23815),C=p.n(be),W=p(35758),b=p(64762),ie=p(58497),Y=p(93523),e=p(74788);let k=class{constructor(_){this.http=_}listTargets(){return this.http.get("api/iscsi/target")}getTarget(_){return this.http.get(`api/iscsi/target/${_}`)}updateTarget(_,t){return this.http.put(`api/iscsi/target/${_}`,t,{observe:"response"})}status(){return this.http.get("ui-api/iscsi/status")}settings(){return this.http.get("ui-api/iscsi/settings")}version(){return this.http.get("ui-api/iscsi/version")}portals(){return this.http.get("ui-api/iscsi/portals")}createTarget(_){return this.http.post("api/iscsi/target",_,{observe:"response"})}deleteTarget(_){return this.http.delete(`api/iscsi/target/${_}`,{observe:"response"})}getDiscovery(){return this.http.get("api/iscsi/discoveryauth")}updateDiscovery(_){return this.http.put("api/iscsi/discoveryauth",_)}overview(){return this.http.get("ui-api/iscsi/overview")}};k.\u0275fac=function(_){return new(_||k)(e.LFG(ie.eN))},k.\u0275prov=e.Yz7({token:k,factory:k.\u0275fac,providedIn:"root"}),k=(0,b.gn)([Y.o,(0,b.w6)("design:paramtypes",[ie.eN])],k);var Ne=p(88002),Z=p(19358),Ae=p(34089);let x=class{constructor(_,t){this.http=_,this.rbdConfigurationService=t}isRBDPool(_){return-1!==C().indexOf(_.application_metadata,"rbd")&&!_.pool_name.includes("/")}create(_){return this.http.post("api/block/image",_,{observe:"response"})}delete(_){return this.http.delete(`api/block/image/${_.toStringEncoded()}`,{observe:"response"})}update(_,t){return this.http.put(`api/block/image/${_.toStringEncoded()}`,t,{observe:"response"})}get(_){return this.http.get(`api/block/image/${_.toStringEncoded()}`)}list(){return this.http.get("api/block/image").pipe((0,Ne.U)(_=>_.map(t=>(t.value.map(o=>(o.configuration&&o.configuration.map(i=>Object.assign(i,this.rbdConfigurationService.getOptionByName(i.name))),o)),t))))}copy(_,t){return this.http.post(`api/block/image/${_.toStringEncoded()}/copy`,t,{observe:"response"})}flatten(_){return this.http.post(`api/block/image/${_.toStringEncoded()}/flatten`,null,{observe:"response"})}defaultFeatures(){return this.http.get("api/block/image/default_features")}cloneFormatVersion(){return this.http.get("api/block/image/clone_format_version")}createSnapshot(_,t){const o={snapshot_name:t};return this.http.post(`api/block/image/${_.toStringEncoded()}/snap`,o,{observe:"response"})}renameSnapshot(_,t,o){const i={new_snap_name:o};return this.http.put(`api/block/image/${_.toStringEncoded()}/snap/${t}`,i,{observe:"response"})}protectSnapshot(_,t,o){const i={is_protected:o};return this.http.put(`api/block/image/${_.toStringEncoded()}/snap/${t}`,i,{observe:"response"})}rollbackSnapshot(_,t){return this.http.post(`api/block/image/${_.toStringEncoded()}/snap/${t}/rollback`,null,{observe:"response"})}cloneSnapshot(_,t,o){return this.http.post(`api/block/image/${_.toStringEncoded()}/snap/${t}/clone`,o,{observe:"response"})}deleteSnapshot(_,t){return this.http.delete(`api/block/image/${_.toStringEncoded()}/snap/${t}`,{observe:"response"})}listTrash(){return this.http.get("api/block/image/trash/")}createNamespace(_,t){return this.http.post(`api/block/pool/${_}/namespace`,{namespace:t},{observe:"response"})}listNamespaces(_){return this.http.get(`api/block/pool/${_}/namespace/`)}deleteNamespace(_,t){return this.http.delete(`api/block/pool/${_}/namespace/${t}`,{observe:"response"})}moveTrash(_,t){return this.http.post(`api/block/image/${_.toStringEncoded()}/move_trash`,{delay:t},{observe:"response"})}purgeTrash(_){return this.http.post(`api/block/image/trash/purge/?pool_name=${_}`,null,{observe:"response"})}restoreTrash(_,t){return this.http.post(`api/block/image/trash/${_.toStringEncoded()}/restore`,{new_image_name:t},{observe:"response"})}removeTrash(_,t=!1){return this.http.delete(`api/block/image/trash/${_.toStringEncoded()}/?force=${t}`,{observe:"response"})}};x.\u0275fac=function(_){return new(_||x)(e.LFG(ie.eN),e.LFG(Ae.n))},x.\u0275prov=e.Yz7({token:x,factory:x.\u0275fac,providedIn:"root"}),(0,b.gn)([(0,b.fM)(1,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[Z.N,String]),(0,b.w6)("design:returntype",void 0)],x.prototype,"createSnapshot",null),(0,b.gn)([(0,b.fM)(2,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[Z.N,String,String]),(0,b.w6)("design:returntype",void 0)],x.prototype,"renameSnapshot",null),(0,b.gn)([(0,b.fM)(2,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[Z.N,String,Boolean]),(0,b.w6)("design:returntype",void 0)],x.prototype,"protectSnapshot",null),(0,b.gn)([(0,b.fM)(1,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[Z.N,String]),(0,b.w6)("design:returntype",void 0)],x.prototype,"restoreTrash",null),x=(0,b.gn)([Y.o,(0,b.w6)("design:paramtypes",[ie.eN,Ae.n])],x);var ae=p(7022),V=p(14745),T=p(65862),q=p(93614),M=p(95463),B=p(77205),F=p(76111),Q=p(32337),R=p(60312),v=p(41582),g=p(56310),f=p(87925),h=p(94276),O=p(30839);function H(n,_){if(1&n&&(e.TgZ(0,"option",6),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("ngValue",t),e.xp6(1),e.Oqu(t)}}function De(n,_){if(1&n&&(e.TgZ(0,"select",5),e._UZ(1,"option",6),e.YNc(2,H,2,2,"option",7),e.qZA()),2&n){const t=e.oxw();e.s9C("id",t.setting),e.s9C("name",t.setting),e.Q6J("formControlName",t.setting),e.xp6(1),e.Q6J("ngValue",null),e.xp6(1),e.Q6J("ngForOf",t.limits.values)}}function Jt(n,_){if(1&n&&e._UZ(0,"input",10),2&n){const t=e.oxw(2);e.Q6J("formControlName",t.setting)}}function Yt(n,_){if(1&n&&e._UZ(0,"input",11),2&n){const t=e.oxw(2);e.Q6J("formControlName",t.setting)}}function Vt(n,_){if(1&n&&(e.ynx(0),e._UZ(1,"br"),e.TgZ(2,"div",12),e._UZ(3,"input",13),e.TgZ(4,"label",14),e._uU(5,"Yes"),e.qZA(),e.qZA(),e.TgZ(6,"div",12),e._UZ(7,"input",13),e.TgZ(8,"label",14),e._uU(9,"No"),e.qZA(),e.qZA(),e.BQk()),2&n){const t=e.oxw(2);e.xp6(3),e.Q6J("id",t.setting+"True")("value",!0)("formControlName",t.setting),e.xp6(1),e.Q6J("for",t.setting+"True"),e.xp6(3),e.Q6J("id",t.setting+"False")("value",!1)("formControlName",t.setting),e.xp6(1),e.Q6J("for",t.setting+"False")}}function Ut(n,_){if(1&n&&(e.TgZ(0,"span"),e.YNc(1,Jt,1,1,"input",8),e.YNc(2,Yt,1,1,"input",9),e.YNc(3,Vt,10,8,"ng-container",3),e.qZA()),2&n){const t=e.oxw();e.xp6(1),e.Q6J("ngIf","int"===t.limits.type),e.xp6(1),e.Q6J("ngIf","str"===t.limits.type),e.xp6(1),e.Q6J("ngIf","bool"===t.limits.type)}}function jt(n,_){if(1&n&&(e.TgZ(0,"span",15),e.ynx(1),e.SDv(2,16),e.BQk(),e.qZA()),2&n){const t=e.oxw();e.xp6(2),e.pQV(t.limits.min),e.QtT(2)}}function Wt(n,_){if(1&n&&(e.TgZ(0,"span",15),e.ynx(1),e.SDv(2,17),e.BQk(),e.qZA()),2&n){const t=e.oxw();e.xp6(2),e.pQV(t.limits.max),e.QtT(2)}}let _t=(()=>{class n{ngOnInit(){const t=[];"min"in this.limits&&t.push(r.kI.min(this.limits.min)),"max"in this.limits&&t.push(r.kI.max(this.limits.max)),this.settingsForm.get(this.setting).setValidators(t)}}return n.\u0275fac=function(t){return new(t||n)},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-setting"]],inputs:{settingsForm:"settingsForm",formDir:"formDir",setting:"setting",limits:"limits"},decls:7,vars:7,consts:function(){let _,t;return _="Must be greater than or equal to " + "\ufffd0\ufffd" + ".",t="Must be less than or equal to " + "\ufffd0\ufffd" + ".",[[1,"form-group",3,"formGroup"],[1,"col-form-label",3,"for"],["class","form-control custom-select",3,"id","name","formControlName",4,"ngIf"],[4,"ngIf"],["class","invalid-feedback",4,"ngIf"],[1,"form-control","custom-select",3,"id","name","formControlName"],[3,"ngValue"],[3,"ngValue",4,"ngFor","ngForOf"],["type","number","class","form-control",3,"formControlName",4,"ngIf"],["type","text","class","form-control",3,"formControlName",4,"ngIf"],["type","number",1,"form-control",3,"formControlName"],["type","text",1,"form-control",3,"formControlName"],[1,"custom-control","custom-radio","custom-control-inline"],["type","radio",1,"custom-control-input",3,"id","value","formControlName"],[1,"custom-control-label",3,"for"],[1,"invalid-feedback"],_,t]},template:function(t,o){1&t&&(e.TgZ(0,"div",0),e.TgZ(1,"label",1),e._uU(2),e.qZA(),e.YNc(3,De,3,5,"select",2),e.YNc(4,Ut,4,3,"span",3),e.YNc(5,jt,3,1,"span",4),e.YNc(6,Wt,3,1,"span",4),e.qZA()),2&t&&(e.Q6J("formGroup",o.settingsForm),e.xp6(1),e.s9C("for",o.setting),e.xp6(1),e.Oqu(o.setting),e.xp6(1),e.Q6J("ngIf","enum"===o.limits.type),e.xp6(1),e.Q6J("ngIf","enum"!==o.limits.type),e.xp6(1),e.Q6J("ngIf",o.settingsForm.showError(o.setting,o.formDir,"min")),e.xp6(1),e.Q6J("ngIf",o.settingsForm.showError(o.setting,o.formDir,"max")))},directives:[g.P,r.JL,r.sg,v.V,l.O5,f.o,r.EJ,h.b,r.JJ,r.u,r.YN,r.Kr,l.sg,r.wV,r.Fj,r._],styles:[""]}),n})();var He=p(88820);function eo(n,_){1&n&&(e.TgZ(0,"span",29),e.SDv(1,30),e.qZA())}function to(n,_){if(1&n&&(e.TgZ(0,"span"),e.TgZ(1,"legend",10),e.SDv(2,21),e.qZA(),e.TgZ(3,"div",12),e.TgZ(4,"div",13),e.TgZ(5,"label",22),e.SDv(6,23),e.qZA(),e._UZ(7,"input",24),e.YNc(8,eo,2,0,"span",25),e.qZA(),e.qZA(),e.TgZ(9,"div",12),e.TgZ(10,"div",13),e.TgZ(11,"label",26),e.SDv(12,27),e.qZA(),e._UZ(13,"input",28),e.qZA(),e.qZA(),e.qZA()),2&n){const t=e.oxw(),o=e.MAs(9);e.xp6(8),e.Q6J("ngIf",t.settingsForm.showError("lun",o,"required"))}}function oo(n,_){if(1&n&&(e.TgZ(0,"option",31),e._uU(1),e.ALo(2,"iscsiBackstore"),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(e.lcZ(2,2,t))}}function no(n,_){if(1&n&&(e.TgZ(0,"div",12),e.TgZ(1,"div",13),e._UZ(2,"cd-iscsi-setting",33),e.qZA(),e.qZA()),2&n){const t=_.$implicit,o=e.oxw(2).$implicit,i=e.oxw(),s=e.MAs(9);e.xp6(2),e.Q6J("settingsForm",i.settingsForm)("formDir",s)("setting",t.key)("limits",i.getDiskControlLimits(o,t.key))}}function io(n,_){if(1&n&&(e.ynx(0),e.YNc(1,no,3,4,"div",32),e.ALo(2,"keyvalue"),e.BQk()),2&n){const t=e.oxw().$implicit,o=e.oxw();e.xp6(1),e.Q6J("ngForOf",e.lcZ(2,1,o.disk_default_controls[t]))}}function _o(n,_){if(1&n&&(e.ynx(0),e.YNc(1,io,3,3,"ng-container",9),e.BQk()),2&n){const t=_.$implicit,o=e.oxw();e.xp6(1),e.Q6J("ngIf",o.settingsForm.value.backstore===t)}}let so=(()=>{class n{constructor(t,o,i){this.activeModal=t,this.iscsiService=o,this.actionLabels=i}ngOnInit(){const t={backstore:new r.NI(this.imagesSettings[this.image].backstore),lun:new r.NI(this.imagesSettings[this.image].lun),wwn:new r.NI(this.imagesSettings[this.image].wwn)};C().forEach(this.backstores,o=>{const i=this.imagesSettings[this.image][o]||{};C().forIn(this.disk_default_controls[o],(s,a)=>{t[a]=new r.NI(i[a])})}),this.settingsForm=new M.d(t)}getDiskControlLimits(t,o){return this.disk_controls_limits?this.disk_controls_limits[t][o]:{type:"int"}}save(){const t=this.settingsForm.controls.backstore.value,o=this.settingsForm.controls.lun.value,i=this.settingsForm.controls.wwn.value,s={};C().forIn(this.settingsForm.controls,(a,d)=>{""!==a.value&&null!==a.value&&d in this.disk_default_controls[this.settingsForm.value.backstore]&&(s[d]=a.value,C().forEach(this.backstores,c=>{c!==t&&d in(this.imagesSettings[this.image][c]||{})&&(this.imagesSettings[this.image][c][d]=a.value)}))}),this.imagesSettings[this.image].backstore=t,this.imagesSettings[this.image].lun=o,this.imagesSettings[this.image].wwn=i,this.imagesSettings[this.image][t]=s,this.imagesSettings=Object.assign({},this.imagesSettings),this.control.updateValueAndValidity({emitEvent:!1}),this.activeModal.close()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(k),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-image-settings-modal"]],decls:25,vars:8,consts:function(){let _,t,o,i,s,a,d,c;return _="Configure",t="Changing these parameters from their default values is usually not necessary.",o="Settings",i="Backstore",s="Identifier",a="lun",d="wwn",c="This field is required.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","settingsForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"alert-warning"],t,[4,"ngIf"],[1,"cd-header"],o,[1,"form-group","row"],[1,"col-sm-12"],[1,"col-form-label"],i,["id","backstore","name","backstore","formControlName","backstore",1,"form-control","custom-select"],[3,"value",4,"ngFor","ngForOf"],[4,"ngFor","ngForOf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],s,["for","lun",1,"col-form-label","required"],a,["type","number","id","lun","name","lun","formControlName","lun",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","wwn",1,"col-form-label"],d,["type","text","id","wwn","name","wwn","formControlName","wwn",1,"form-control"],[1,"invalid-feedback"],c,[3,"value"],["class","form-group row",4,"ngFor","ngForOf"],[3,"settingsForm","formDir","setting","limits"]]},template:function(t,o){1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.ynx(2),e.SDv(3,2),e.BQk(),e._uU(4,"\xa0 "),e.TgZ(5,"small"),e._uU(6),e.qZA(),e.BQk(),e.ynx(7,3),e.TgZ(8,"form",4,5),e.TgZ(10,"div",6),e.TgZ(11,"p",7),e.SDv(12,8),e.qZA(),e.YNc(13,to,14,1,"span",9),e.TgZ(14,"legend",10),e.SDv(15,11),e.qZA(),e.TgZ(16,"div",12),e.TgZ(17,"div",13),e.TgZ(18,"label",14),e.SDv(19,15),e.qZA(),e.TgZ(20,"select",16),e.YNc(21,oo,3,4,"option",17),e.qZA(),e.qZA(),e.qZA(),e.YNc(22,_o,2,1,"ng-container",18),e.qZA(),e.TgZ(23,"div",19),e.TgZ(24,"cd-form-button-panel",20),e.NdJ("submitActionEvent",function(){return o.save()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t&&(e.Q6J("modalRef",o.activeModal),e.xp6(6),e.Oqu(o.image),e.xp6(2),e.Q6J("formGroup",o.settingsForm),e.xp6(5),e.Q6J("ngIf",o.api_version>=1),e.xp6(8),e.Q6J("ngForOf",o.backstores),e.xp6(1),e.Q6J("ngForOf",o.backstores),e.xp6(2),e.Q6J("form",o.settingsForm)("submitText",o.actionLabels.UPDATE))},directives:[R.z,r._Y,r.JL,r.sg,v.V,l.O5,g.P,f.o,r.EJ,h.b,r.JJ,r.u,l.sg,O.p,r.wV,r.Fj,r.YN,r.Kr,_t],pipes:[He.V,l.Nd],styles:[""]}),n})();function ao(n,_){if(1&n&&(e.TgZ(0,"div",12),e.TgZ(1,"div",13),e._UZ(2,"cd-iscsi-setting",14),e.qZA(),e.qZA()),2&n){const t=_.$implicit,o=e.oxw(),i=e.MAs(5);e.xp6(2),e.Q6J("settingsForm",o.settingsForm)("formDir",i)("setting",t.key)("limits",o.getTargetControlLimits(t.key))}}let ro=(()=>{class n{constructor(t,o,i){this.activeModal=t,this.iscsiService=o,this.actionLabels=i}ngOnInit(){const t={};C().forIn(this.target_default_controls,(o,i)=>{t[i]=new r.NI(this.target_controls.value[i])}),this.settingsForm=new M.d(t)}save(){const t={};C().forIn(this.settingsForm.controls,(o,i)=>{""===o.value||null===o.value||(t[i]=o.value)}),this.target_controls.setValue(t),this.activeModal.close()}getTargetControlLimits(t){return this.target_controls_limits?this.target_controls_limits[t]:["Yes","No"].includes(this.target_default_controls[t])?{type:"bool"}:{type:"int"}}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(k),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-iqn-settings-modal"]],decls:13,vars:7,consts:function(){let _,t;return _="Advanced Settings",t="Changing these parameters from their default values is usually not necessary.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","settingsForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"alert-warning"],t,["class","form-group row",4,"ngFor","ngForOf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"form-group","row"],[1,"col-sm-12"],[3,"settingsForm","formDir","setting","limits"]]},template:function(t,o){1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p",7),e.SDv(8,8),e.qZA(),e.YNc(9,ao,3,4,"div",9),e.ALo(10,"keyvalue"),e.qZA(),e.TgZ(11,"div",10),e.TgZ(12,"cd-form-button-panel",11),e.NdJ("submitActionEvent",function(){return o.save()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t&&(e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.settingsForm),e.xp6(5),e.Q6J("ngForOf",e.lcZ(10,5,o.settingsForm.controls)),e.xp6(3),e.Q6J("form",o.settingsForm)("submitText",o.actionLabels.UPDATE))},directives:[R.z,r._Y,r.JL,r.sg,v.V,l.sg,O.p,g.P,_t],pipes:[l.Nd],styles:[""]}),n})();var re=p(63285),st=p(63622);let lo=(()=>{class n{constructor(t){this.ngControl=t}onInput(t){this.setValue(t)}setValue(t){t=C().isString(t)?t.trim():t,this.ngControl.control.setValue(t)}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(r.a5))},n.\u0275dir=e.lG2({type:n,selectors:[["","cdTrim",""]],hostBindings:function(t,o){1&t&&e.NdJ("input",function(s){return o.onInput(s.target.value)})}}),n})();var co=p(39092),at=p(4416),Je=p(58039),Ye=p(10545);function po(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,42),e.qZA())}function go(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,43),e.qZA())}function uo(n,_){1&n&&(e.TgZ(0,"span",41),e.ynx(1),e.SDv(2,44),e.BQk(),e._UZ(3,"br"),e.ynx(4),e.SDv(5,45),e.BQk(),e._UZ(6,"br"),e.TgZ(7,"a",46),e.SDv(8,47),e.qZA(),e.qZA())}function mo(n,_){1&n&&(e.TgZ(0,"span",48),e.SDv(1,49),e.qZA())}const z=function(n){return[n]};function To(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.TgZ(4,"button",52),e.NdJ("click",function(){const i=e.CHM(t),s=i.index,a=i.$implicit;return e.oxw(2).removePortal(s,a)}),e._UZ(5,"i",16),e.qZA(),e.qZA(),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(2);e.xp6(2),e.Q6J("value",t),e.xp6(3),e.Q6J("ngClass",e.VKq(2,z,o.icons.destroy))}}function fo(n,_){if(1&n&&(e.TgZ(0,"span",41),e.SDv(1,53),e.qZA()),2&n){const t=e.oxw(2);e.xp6(1),e.pQV(t.minimum_gateways),e.QtT(1)}}function Co(n,_){if(1&n&&(e.TgZ(0,"div",56),e._uU(1),e.qZA()),2&n){const t=e.oxw().$implicit,o=e.oxw(2);e.xp6(1),e.hij("lun: ",o.imagesSettings[t].lun,"")}}function So(n,_){if(1&n&&(e.ynx(0),e.SDv(1,57),e.ALo(2,"iscsiBackstore"),e.BQk()),2&n){const t=e.oxw().$implicit,o=e.oxw(2);e.xp6(2),e.pQV(e.lcZ(2,1,o.imagesSettings[t].backstore)),e.QtT(1)}}function Eo(n,_){1&n&&(e.ynx(0),e.SDv(1,58),e.BQk())}function Ro(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.YNc(4,Co,2,1,"div",54),e.TgZ(5,"button",52),e.NdJ("click",function(){const s=e.CHM(t).$implicit;return e.oxw(2).imageSettingsModal(s)}),e._UZ(6,"i",16),e.qZA(),e.TgZ(7,"button",52),e.NdJ("click",function(){const i=e.CHM(t),s=i.index,a=i.$implicit;return e.oxw(2).removeImage(s,a)}),e._UZ(8,"i",16),e.qZA(),e.qZA(),e.qZA(),e.TgZ(9,"span",48),e.YNc(10,So,3,3,"ng-container",55),e.YNc(11,Eo,2,0,"ng-container",55),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(2);e.xp6(2),e.Q6J("value",t),e.xp6(2),e.Q6J("ngIf",o.api_version>=1),e.xp6(2),e.Q6J("ngClass",e.VKq(6,z,o.icons.deepCheck)),e.xp6(2),e.Q6J("ngClass",e.VKq(8,z,o.icons.destroy)),e.xp6(2),e.Q6J("ngIf",o.backstores.length>1),e.xp6(1),e.Q6J("ngIf",o.hasAdvancedSettings(o.imagesSettings[t][o.imagesSettings[t].backstore]))}}function Mo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,59),e.qZA())}function Oo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,60),e.qZA())}function Ao(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,78),e.qZA())}function ho(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,79),e.qZA())}function Po(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,80),e.qZA())}function Io(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,81),e.qZA())}function bo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,82),e.qZA())}function No(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,83),e.qZA())}function Do(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,84),e.qZA())}function vo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,85),e.qZA())}function Lo(n,_){if(1&n&&(e.TgZ(0,"div",61),e.TgZ(1,"div",8),e.TgZ(2,"label",62),e.ynx(3),e.SDv(4,63),e.BQk(),e.qZA(),e.TgZ(5,"div",11),e._UZ(6,"input",64),e.YNc(7,Ao,2,0,"span",17),e.YNc(8,ho,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(9,"div",8),e.TgZ(10,"label",65),e.ynx(11),e.SDv(12,66),e.BQk(),e.qZA(),e.TgZ(13,"div",11),e.TgZ(14,"div",12),e._UZ(15,"input",67),e.TgZ(16,"span",14),e._UZ(17,"button",68),e._UZ(18,"cd-copy-2-clipboard-button",69),e.qZA(),e.qZA(),e.YNc(19,Po,2,0,"span",17),e.YNc(20,Io,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(21,"div",8),e.TgZ(22,"label",70),e.ynx(23),e.SDv(24,71),e.BQk(),e.qZA(),e.TgZ(25,"div",11),e._UZ(26,"input",72),e.YNc(27,bo,2,0,"span",17),e.YNc(28,No,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(29,"div",8),e.TgZ(30,"label",73),e.ynx(31),e.SDv(32,74),e.BQk(),e.qZA(),e.TgZ(33,"div",11),e.TgZ(34,"div",12),e._UZ(35,"input",75),e.TgZ(36,"span",14),e._UZ(37,"button",76),e._UZ(38,"cd-copy-2-clipboard-button",77),e.qZA(),e.qZA(),e.YNc(39,Do,2,0,"span",17),e.YNc(40,vo,2,0,"span",17),e.qZA(),e.qZA(),e.qZA()),2&n){e.oxw();const t=e.MAs(2),o=e.oxw();e.xp6(7),e.Q6J("ngIf",o.targetForm.showError("user",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("user",t,"pattern")),e.xp6(11),e.Q6J("ngIf",o.targetForm.showError("password",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("password",t,"pattern")),e.xp6(7),e.Q6J("ngIf",o.targetForm.showError("mutual_user",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("mutual_user",t,"pattern")),e.xp6(11),e.Q6J("ngIf",o.targetForm.showError("mutual_password",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("mutual_password",t,"pattern"))}}function Fo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,115),e.qZA())}function $o(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,116),e.qZA())}function Zo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,117),e.qZA())}function Bo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,118),e.qZA())}function Go(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,119),e.qZA())}function yo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,120),e.qZA())}function xo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,121),e.qZA())}function wo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,122),e.qZA())}function qo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,123),e.qZA())}function Ho(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,124),e.qZA())}function Ko(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,125),e.qZA())}function ko(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.TgZ(4,"button",52),e.NdJ("click",function(){const i=e.CHM(t),s=i.index,a=i.$implicit,d=e.oxw(),c=d.$implicit,u=d.index;return e.oxw(3).removeInitiatorImage(c,s,u,a)}),e._UZ(5,"i",16),e.qZA(),e.qZA(),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(4);e.xp6(2),e.Q6J("value",t),e.xp6(3),e.Q6J("ngClass",e.VKq(2,z,o.icons.destroy))}}function Xo(n,_){1&n&&(e.TgZ(0,"span"),e.SDv(1,126),e.qZA())}function Qo(n,_){if(1&n&&(e.TgZ(0,"div",22),e.TgZ(1,"div",23),e.TgZ(2,"cd-select",127),e._UZ(3,"i",25),e.ynx(4),e.SDv(5,128),e.BQk(),e.qZA(),e.qZA(),e.qZA()),2&n){const t=e.oxw(),o=t.$implicit,i=t.index,s=e.oxw(3);e.xp6(2),e.Q6J("data",o.getValue("luns"))("options",s.imagesInitiatorSelections[i])("messages",s.messages.initiatorImage),e.xp6(1),e.Q6J("ngClass",e.VKq(4,z,s.icons.add))}}function zo(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",92),e.TgZ(1,"div",5),e.ynx(2),e.SDv(3,93),e.BQk(),e._uU(4),e.TgZ(5,"button",94),e.NdJ("click",function(){const s=e.CHM(t).index;return e.oxw(3).removeInitiator(s)}),e._UZ(6,"i",25),e.qZA(),e.qZA(),e.TgZ(7,"div",7),e.TgZ(8,"div",8),e.TgZ(9,"label",95),e.SDv(10,96),e.qZA(),e.TgZ(11,"div",11),e.TgZ(12,"input",97),e.NdJ("blur",function(){return e.CHM(t),e.oxw(3).updatedInitiatorSelector()}),e.qZA(),e.YNc(13,Fo,2,0,"span",17),e.YNc(14,$o,2,0,"span",17),e.YNc(15,Zo,2,0,"span",17),e.qZA(),e.qZA(),e.ynx(16,61),e.TgZ(17,"div",8),e.TgZ(18,"label",98),e.SDv(19,99),e.qZA(),e.TgZ(20,"div",11),e._UZ(21,"input",100),e.YNc(22,Bo,2,0,"span",17),e.YNc(23,Go,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(24,"div",8),e.TgZ(25,"label",101),e.SDv(26,102),e.qZA(),e.TgZ(27,"div",11),e.TgZ(28,"div",12),e._UZ(29,"input",103),e.TgZ(30,"span",14),e._UZ(31,"button",104),e._UZ(32,"cd-copy-2-clipboard-button",105),e.qZA(),e.qZA(),e.YNc(33,yo,2,0,"span",17),e.YNc(34,xo,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(35,"div",8),e.TgZ(36,"label",106),e.ynx(37),e.SDv(38,107),e.BQk(),e.qZA(),e.TgZ(39,"div",11),e._UZ(40,"input",108),e.YNc(41,wo,2,0,"span",17),e.YNc(42,qo,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(43,"div",8),e.TgZ(44,"label",109),e.SDv(45,110),e.qZA(),e.TgZ(46,"div",11),e.TgZ(47,"div",12),e._UZ(48,"input",111),e.TgZ(49,"span",14),e._UZ(50,"button",104),e._UZ(51,"cd-copy-2-clipboard-button",105),e.qZA(),e.qZA(),e.YNc(52,Ho,2,0,"span",17),e.YNc(53,Ko,2,0,"span",17),e.qZA(),e.qZA(),e.BQk(),e.TgZ(54,"div",8),e.TgZ(55,"label",112),e.SDv(56,113),e.qZA(),e.TgZ(57,"div",11),e.YNc(58,ko,6,4,"ng-container",21),e.YNc(59,Xo,2,0,"span",55),e.YNc(60,Qo,6,6,"div",114),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=_.$implicit,o=_.index;e.oxw(2);const i=e.MAs(2),s=e.oxw();e.Q6J("formGroup",t),e.xp6(4),e.hij(": ",t.getValue("client_iqn")," "),e.xp6(2),e.Q6J("ngClass",e.VKq(25,z,s.icons.destroy)),e.xp6(7),e.Q6J("ngIf",t.showError("client_iqn",i,"notUnique")),e.xp6(1),e.Q6J("ngIf",t.showError("client_iqn",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("client_iqn",i,"pattern")),e.xp6(6),e.Q6J("id","user"+o),e.xp6(1),e.Q6J("ngIf",t.showError("user",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("user",i,"pattern")),e.xp6(6),e.Q6J("id","password"+o),e.xp6(2),e.Q6J("cdPasswordButton","password"+o),e.xp6(1),e.Q6J("source","password"+o),e.xp6(1),e.Q6J("ngIf",t.showError("password",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("password",i,"pattern")),e.xp6(6),e.Q6J("id","mutual_user"+o),e.xp6(1),e.Q6J("ngIf",t.showError("mutual_user",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("mutual_user",i,"pattern")),e.xp6(6),e.Q6J("id","mutual_password"+o),e.xp6(2),e.Q6J("cdPasswordButton","mutual_password"+o),e.xp6(1),e.Q6J("source","mutual_password"+o),e.xp6(1),e.Q6J("ngIf",t.showError("mutual_password",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("mutual_password",i,"pattern")),e.xp6(5),e.Q6J("ngForOf",t.getValue("luns")),e.xp6(1),e.Q6J("ngIf",t.getValue("cdIsInGroup")),e.xp6(1),e.Q6J("ngIf",!t.getValue("cdIsInGroup"))}}function Jo(n,_){1&n&&(e.TgZ(0,"span",48),e.SDv(1,129),e.qZA())}function Yo(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",8),e.TgZ(1,"label",86),e.SDv(2,87),e.qZA(),e.TgZ(3,"div",88),e.YNc(4,zo,61,27,"div",89),e.TgZ(5,"div",22),e.TgZ(6,"div",23),e.YNc(7,Jo,2,0,"span",18),e.TgZ(8,"button",90),e.NdJ("click",function(){return e.CHM(t),e.oxw(2).addInitiator(),!1}),e._UZ(9,"i",25),e.ynx(10),e.SDv(11,91),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(12,"hr"),e.qZA(),e.qZA()}if(2&n){const t=e.oxw(2);e.xp6(4),e.Q6J("ngForOf",t.initiators.controls),e.xp6(3),e.Q6J("ngIf",0===t.initiators.controls.length),e.xp6(2),e.Q6J("ngClass",e.VKq(3,z,t.icons.add))}}function Vo(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.TgZ(4,"button",52),e.NdJ("click",function(){const s=e.CHM(t).index,a=e.oxw(),d=a.$implicit,c=a.index;return e.oxw(3).removeGroupInitiator(d,s,c)}),e._UZ(5,"i",16),e.qZA(),e.qZA(),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(4);e.xp6(2),e.Q6J("value",t),e.xp6(3),e.Q6J("ngClass",e.VKq(2,z,o.icons.destroy))}}function Uo(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.TgZ(4,"button",52),e.NdJ("click",function(){const s=e.CHM(t).index,a=e.oxw(),d=a.$implicit,c=a.index;return e.oxw(3).removeGroupDisk(d,s,c)}),e._UZ(5,"i",16),e.qZA(),e.qZA(),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(4);e.xp6(2),e.Q6J("value",t),e.xp6(3),e.Q6J("ngClass",e.VKq(2,z,o.icons.destroy))}}function jo(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",92),e.TgZ(1,"div",5),e.ynx(2),e.SDv(3,133),e.BQk(),e._uU(4),e.TgZ(5,"button",94),e.NdJ("click",function(){const s=e.CHM(t).index;return e.oxw(3).removeGroup(s)}),e._UZ(6,"i",25),e.qZA(),e.qZA(),e.TgZ(7,"div",7),e.TgZ(8,"div",8),e.TgZ(9,"label",134),e.SDv(10,135),e.qZA(),e.TgZ(11,"div",11),e._UZ(12,"input",136),e.qZA(),e.qZA(),e.TgZ(13,"div",8),e.TgZ(14,"label",137),e.ynx(15),e.SDv(16,138),e.BQk(),e.qZA(),e.TgZ(17,"div",11),e.YNc(18,Vo,6,4,"ng-container",21),e.TgZ(19,"div",22),e.TgZ(20,"div",23),e.TgZ(21,"cd-select",24),e.NdJ("selection",function(i){const a=e.CHM(t).index;return e.oxw(3).onGroupMemberSelection(i,a)}),e._UZ(22,"i",25),e.ynx(23),e.SDv(24,139),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(25,"hr"),e.qZA(),e.qZA(),e.TgZ(26,"div",8),e.TgZ(27,"label",28),e.ynx(28),e.SDv(29,140),e.BQk(),e.qZA(),e.TgZ(30,"div",11),e.YNc(31,Uo,6,4,"ng-container",21),e.TgZ(32,"div",22),e.TgZ(33,"div",23),e.TgZ(34,"cd-select",127),e._UZ(35,"i",25),e.ynx(36),e.SDv(37,141),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(38,"hr"),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=_.$implicit,o=_.index,i=e.oxw(3);e.Q6J("formGroup",t),e.xp6(4),e.hij(": ",t.getValue("group_id")," "),e.xp6(2),e.Q6J("ngClass",e.VKq(13,z,i.icons.destroy)),e.xp6(12),e.Q6J("ngForOf",t.getValue("members")),e.xp6(3),e.Q6J("data",t.getValue("members"))("options",i.groupMembersSelections[o])("messages",i.messages.groupInitiator),e.xp6(1),e.Q6J("ngClass",e.VKq(15,z,i.icons.add)),e.xp6(9),e.Q6J("ngForOf",t.getValue("disks")),e.xp6(3),e.Q6J("data",t.getValue("disks"))("options",i.groupDiskSelections[o])("messages",i.messages.initiatorImage),e.xp6(1),e.Q6J("ngClass",e.VKq(17,z,i.icons.add))}}function Wo(n,_){1&n&&(e.TgZ(0,"span",48),e.SDv(1,142),e.qZA())}function en(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",8),e.TgZ(1,"label",86),e.SDv(2,130),e.qZA(),e.TgZ(3,"div",131),e.YNc(4,jo,39,19,"div",89),e.TgZ(5,"div",22),e.TgZ(6,"div",23),e.YNc(7,Wo,2,0,"span",18),e.TgZ(8,"button",90),e.NdJ("click",function(){return e.CHM(t),e.oxw(2).addGroup(),!1}),e._UZ(9,"i",25),e.ynx(10),e.SDv(11,132),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=e.oxw(2);e.xp6(4),e.Q6J("ngForOf",t.groups.controls),e.xp6(3),e.Q6J("ngIf",0===t.groups.controls.length),e.xp6(2),e.Q6J("ngClass",e.VKq(3,z,t.icons.add))}}function tn(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.TgZ(9,"div",8),e.TgZ(10,"label",9),e.SDv(11,10),e.qZA(),e.TgZ(12,"div",11),e.TgZ(13,"div",12),e._UZ(14,"input",13),e.TgZ(15,"span",14),e.TgZ(16,"button",15),e.NdJ("click",function(){return e.CHM(t),e.oxw().targetSettingsModal()}),e._UZ(17,"i",16),e.qZA(),e.qZA(),e.qZA(),e.YNc(18,po,2,0,"span",17),e.YNc(19,go,2,0,"span",17),e.YNc(20,uo,9,0,"span",17),e.YNc(21,mo,2,0,"span",18),e._UZ(22,"hr"),e.qZA(),e.qZA(),e.TgZ(23,"div",8),e.TgZ(24,"label",19),e.SDv(25,20),e.qZA(),e.TgZ(26,"div",11),e.YNc(27,To,6,4,"ng-container",21),e.TgZ(28,"div",22),e.TgZ(29,"div",23),e.TgZ(30,"cd-select",24),e.NdJ("selection",function(i){return e.CHM(t),e.oxw().onPortalSelection(i)}),e._UZ(31,"i",25),e.ynx(32),e.SDv(33,26),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(34,"input",27),e.YNc(35,fo,2,1,"span",17),e._UZ(36,"hr"),e.qZA(),e.qZA(),e.TgZ(37,"div",8),e.TgZ(38,"label",28),e.SDv(39,29),e.qZA(),e.TgZ(40,"div",11),e.YNc(41,Ro,12,10,"ng-container",21),e._UZ(42,"input",30),e.YNc(43,Mo,2,0,"span",17),e.YNc(44,Oo,2,0,"span",17),e.TgZ(45,"div",22),e.TgZ(46,"div",23),e.TgZ(47,"cd-select",24),e.NdJ("selection",function(i){return e.CHM(t),e.oxw().onImageSelection(i)}),e._UZ(48,"i",25),e.ynx(49),e.SDv(50,31),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(51,"hr"),e.qZA(),e.qZA(),e.TgZ(52,"div",8),e.TgZ(53,"div",32),e.TgZ(54,"div",33),e._UZ(55,"input",34),e.TgZ(56,"label",35),e.SDv(57,36),e.qZA(),e.qZA(),e._UZ(58,"hr"),e.qZA(),e.qZA(),e.YNc(59,Lo,41,8,"div",37),e.YNc(60,Yo,13,5,"div",38),e.YNc(61,en,12,5,"div",38),e.qZA(),e.TgZ(62,"div",39),e.TgZ(63,"cd-form-button-panel",40),e.NdJ("submitActionEvent",function(){return e.CHM(t),e.oxw().submit()}),e.ALo(64,"titlecase"),e.ALo(65,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=e.MAs(2),o=e.oxw();e.xp6(1),e.Q6J("formGroup",o.targetForm),e.xp6(6),e.pQV(e.lcZ(6,26,o.action))(e.lcZ(7,28,o.resource)),e.QtT(5),e.xp6(10),e.Q6J("ngClass",e.VKq(34,z,o.icons.deepCheck)),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("target_iqn",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("target_iqn",t,"pattern")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("target_iqn",t,"iqn")),e.xp6(1),e.Q6J("ngIf",o.hasAdvancedSettings(o.targetForm.getValue("target_controls"))),e.xp6(6),e.Q6J("ngForOf",o.portals.value),e.xp6(3),e.Q6J("data",o.portals.value)("options",o.portalsSelections)("messages",o.messages.portals),e.xp6(1),e.Q6J("ngClass",e.VKq(36,z,o.icons.add)),e.xp6(4),e.Q6J("ngIf",o.targetForm.showError("portals",t,"minGateways")),e.xp6(6),e.Q6J("ngForOf",o.targetForm.getValue("disks")),e.xp6(2),e.Q6J("ngIf",o.targetForm.showError("disks",t,"dupLunId")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("disks",t,"dupWwn")),e.xp6(3),e.Q6J("data",o.disks.value)("options",o.imagesSelections)("messages",o.messages.images),e.xp6(1),e.Q6J("ngClass",e.VKq(38,z,o.icons.add)),e.xp6(11),e.Q6J("ngIf",o.cephIscsiConfigVersion>10&&!o.targetForm.getValue("acl_enabled")),e.xp6(1),e.Q6J("ngIf",o.targetForm.getValue("acl_enabled")),e.xp6(1),e.Q6J("ngIf",o.targetForm.getValue("acl_enabled")),e.xp6(2),e.Q6J("form",o.targetForm)("submitText",e.lcZ(64,30,o.action)+" "+e.lcZ(65,32,o.resource))}}let rt=(()=>{class n extends q.E{constructor(t,o,i,s,a,d,c){super(),this.iscsiService=t,this.modalService=o,this.rbdService=i,this.router=s,this.route=a,this.taskWrapper=d,this.actionLabels=c,this.api_version=0,this.minimum_gateways=1,this.icons=T.P,this.isEdit=!1,this.portalsSelections=[],this.imagesInitiatorSelections=[],this.groupDiskSelections=[],this.groupMembersSelections=[],this.imagesSettings={},this.messages={portals:new ae.a({noOptions:"There are no portals available."}),images:new ae.a({noOptions:"There are no images available."}),initiatorImage:new ae.a({noOptions:"There are no images available. Please make sure you add an image to the target."}),groupInitiator:new ae.a({noOptions:"There are no initiators available. Please make sure you add an initiator to the target."})},this.IQN_REGEX=/^iqn\.(19|20)\d\d-(0[1-9]|1[0-2])\.\D{2,3}(\.[A-Za-z0-9-]+)+(:[A-Za-z0-9-\.]+)*$/,this.USER_REGEX=/^[\w\.:@_-]{8,64}$/,this.PASSWORD_REGEX=/^[\w@\-_\/]{12,16}$/,this.resource="target"}ngOnInit(){const t=[this.iscsiService.listTargets(),this.rbdService.list(),this.iscsiService.portals(),this.iscsiService.settings(),this.iscsiService.version()];this.router.url.startsWith("/block/iscsi/targets/edit")&&(this.isEdit=!0,this.route.params.subscribe(o=>{this.target_iqn=decodeURIComponent(o.target_iqn),t.push(this.iscsiService.getTarget(this.target_iqn))})),this.action=this.isEdit?this.actionLabels.EDIT:this.actionLabels.CREATE,(0,W.D)(t).subscribe(o=>{const i=C()(o[0]).filter(a=>a.target_iqn!==this.target_iqn).flatMap(a=>a.disks).map(a=>`${a.pool}/${a.image}`).value();"api_version"in o[3]&&(this.api_version=o[3].api_version),this.minimum_gateways=o[3].config.minimum_gateways,this.target_default_controls=o[3].target_default_controls,this.target_controls_limits=o[3].target_controls_limits,this.disk_default_controls=o[3].disk_default_controls,this.disk_controls_limits=o[3].disk_controls_limits,this.backstores=o[3].backstores,this.default_backstore=o[3].default_backstore,this.unsupported_rbd_features=o[3].unsupported_rbd_features,this.required_rbd_features=o[3].required_rbd_features,this.imagesAll=C()(o[1]).flatMap(a=>a.value).filter(a=>!a.namespace&&!(-1!==i.indexOf(`${a.pool_name}/${a.name}`)||0===this.getValidBackstores(a).length)).value(),this.imagesSelections=this.imagesAll.map(a=>new V.$(!1,`${a.pool_name}/${a.name}`,""));const s=[];o[2].forEach(a=>{a.ip_addresses.forEach(d=>{s.push(new V.$(!1,a.name+":"+d,""))})}),this.portalsSelections=[...s],this.cephIscsiConfigVersion=o[4].ceph_iscsi_config_version,this.createForm(),o[5]&&this.resolveModel(o[5]),this.loadingReady()})}createForm(){if(this.targetForm=new M.d({target_iqn:new r.NI("iqn.2001-07.com.ceph:"+Date.now(),{validators:[r.kI.required,r.kI.pattern(this.IQN_REGEX)]}),target_controls:new r.NI({}),portals:new r.NI([],{validators:[B.h.custom("minGateways",t=>C().uniq(t.map(i=>i.split(":")[0])).length{const o=this.getLunIds(t);return o.length!==C().uniq(o).length}),B.h.custom("dupWwn",t=>{const o=this.getWwns(t);return o.length!==C().uniq(o).length})]}),initiators:new r.Oe([]),groups:new r.Oe([]),acl_enabled:new r.NI(!1)}),this.cephIscsiConfigVersion>10){const t=new M.d({user:new r.NI(""),password:new r.NI(""),mutual_user:new r.NI(""),mutual_password:new r.NI("")});this.setAuthValidator(t),this.targetForm.addControl("auth",t)}}resolveModel(t){this.targetForm.patchValue({target_iqn:t.target_iqn,target_controls:t.target_controls,acl_enabled:t.acl_enabled}),this.cephIscsiConfigVersion>10&&this.targetForm.patchValue({auth:t.auth});const o=[];C().forEach(t.portals,s=>{o.push(`${s.host}:${s.ip}`)}),this.targetForm.patchValue({portals:o});const i=[];C().forEach(t.disks,s=>{const a=`${s.pool}/${s.image}`;i.push(a),this.imagesSettings[a]={backstore:s.backstore},this.imagesSettings[a][s.backstore]=s.controls,"lun"in s&&(this.imagesSettings[a].lun=s.lun),"wwn"in s&&(this.imagesSettings[a].wwn=s.wwn),this.onImageSelection({option:{name:a,selected:!0}})}),this.targetForm.patchValue({disks:i}),C().forEach(t.clients,s=>{const a=this.addInitiator();s.luns=C().map(s.luns,d=>`${d.pool}/${d.image}`),a.patchValue(s)}),t.groups.forEach((s,a)=>{const d=this.addGroup();s.disks=C().map(s.disks,c=>`${c.pool}/${c.image}`),d.patchValue(s),C().forEach(s.members,c=>{this.onGroupMemberSelection({option:new V.$(!0,c,"")},a)})})}hasAdvancedSettings(t){return Object.values(t).length>0}get portals(){return this.targetForm.get("portals")}onPortalSelection(){this.portals.setValue(this.portals.value)}removePortal(t,o){return this.portalsSelections.forEach(i=>{i.name===o&&(i.selected=!1)}),this.portals.value.splice(t,1),this.portals.setValue(this.portals.value),!1}get disks(){return this.targetForm.get("disks")}removeImage(t,o){return this.imagesSelections.forEach(i=>{i.name===o&&(i.selected=!1)}),this.disks.value.splice(t,1),this.removeImageRefs(o),this.targetForm.get("disks").updateValueAndValidity({emitEvent:!1}),!1}removeImageRefs(t){this.initiators.controls.forEach(o=>{const i=o.value.luns.filter(s=>s!==t);o.get("luns").setValue(i)}),this.groups.controls.forEach(o=>{const i=o.value.disks.filter(s=>s!==t);o.get("disks").setValue(i)}),C().forEach(this.imagesInitiatorSelections,(o,i)=>{this.imagesInitiatorSelections[i]=o.filter(s=>s.name!==t)}),C().forEach(this.groupDiskSelections,(o,i)=>{this.groupDiskSelections[i]=o.filter(s=>s.name!==t)})}getDefaultBackstore(t){let o=this.default_backstore;const i=this.getImageById(t);return this.validFeatures(i,this.default_backstore)||this.backstores.forEach(s=>{s!==this.default_backstore&&this.validFeatures(i,s)&&(o=s)}),o}isLunIdInUse(t,o){const i=this.disks.value.filter(s=>s!==o);return this.getLunIds(i).includes(t)}getLunIds(t){return C().map(t,o=>this.imagesSettings[o].lun)}nextLunId(t){const o=this.disks.value.filter(a=>a!==t),i=this.getLunIds(o);let s=0;for(;i.includes(s);)s++;return s}getWwns(t){return C().map(t,i=>this.imagesSettings[i].wwn).filter(i=>C().isString(i)&&""!==i)}onImageSelection(t){const o=t.option;if(o.selected){if(this.imagesSettings[o.name])this.isLunIdInUse(this.imagesSettings[o.name].lun,o.name)&&(this.imagesSettings[o.name].lun=this.nextLunId(o.name));else{const i=this.getDefaultBackstore(o.name);this.imagesSettings[o.name]={backstore:i,lun:this.nextLunId(o.name)},this.imagesSettings[o.name][i]={}}C().forEach(this.imagesInitiatorSelections,(i,s)=>{i.push(new V.$(!1,o.name,"")),this.imagesInitiatorSelections[s]=[...i]}),C().forEach(this.groupDiskSelections,(i,s)=>{i.push(new V.$(!1,o.name,"")),this.groupDiskSelections[s]=[...i]})}else this.removeImageRefs(o.name);this.targetForm.get("disks").updateValueAndValidity({emitEvent:!1})}get initiators(){return this.targetForm.get("initiators")}addInitiator(){const t=new M.d({client_iqn:new r.NI("",{validators:[r.kI.required,B.h.custom("notUnique",i=>{const s=this.initiators.controls.reduce(function(a,d){return a.concat(d.value.client_iqn)},[]);return s.indexOf(i)!==s.lastIndexOf(i)}),r.kI.pattern(this.IQN_REGEX)]}),auth:new M.d({user:new r.NI(""),password:new r.NI(""),mutual_user:new r.NI(""),mutual_password:new r.NI("")}),luns:new r.NI([]),cdIsInGroup:new r.NI(!1)});this.setAuthValidator(t),this.initiators.push(t),C().forEach(this.groupMembersSelections,(i,s)=>{i.push(new V.$(!1,"","")),this.groupMembersSelections[s]=[...i]});const o=C().map(this.targetForm.getValue("disks"),i=>new V.$(!1,i,""));return this.imagesInitiatorSelections.push(o),t}setAuthValidator(t){B.h.validateIf(t.get("user"),()=>t.getValue("password")||t.getValue("mutual_user")||t.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.USER_REGEX)],[t.get("password"),t.get("mutual_user"),t.get("mutual_password")]),B.h.validateIf(t.get("password"),()=>t.getValue("user")||t.getValue("mutual_user")||t.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.PASSWORD_REGEX)],[t.get("user"),t.get("mutual_user"),t.get("mutual_password")]),B.h.validateIf(t.get("mutual_user"),()=>t.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.USER_REGEX)],[t.get("user"),t.get("password"),t.get("mutual_password")]),B.h.validateIf(t.get("mutual_password"),()=>t.getValue("mutual_user"),[r.kI.required],[r.kI.pattern(this.PASSWORD_REGEX)],[t.get("user"),t.get("password"),t.get("mutual_user")])}removeInitiator(t){const o=this.initiators.value[t];this.initiators.removeAt(t),C().forEach(this.groupMembersSelections,(i,s)=>{i.splice(t,1),this.groupMembersSelections[s]=[...i]}),this.groups.controls.forEach(i=>{const s=i.value.members.filter(a=>a!==o.client_iqn);i.get("members").setValue(s)}),this.imagesInitiatorSelections.splice(t,1)}updatedInitiatorSelector(){this.initiators.controls.forEach(t=>{t.get("client_iqn").updateValueAndValidity({emitEvent:!1})}),C().forEach(this.groupMembersSelections,(t,o)=>{C().forEach(t,(i,s)=>{const a=i.name;i.name=this.initiators.controls[s].value.client_iqn,this.groups.controls.forEach(d=>{const c=d.value.members,u=c.indexOf(a);-1!==u&&(c[u]=i.name),d.get("members").setValue(c)})}),this.groupMembersSelections[o]=[...this.groupMembersSelections[o]]})}removeInitiatorImage(t,o,i,s){const a=t.getValue("luns");return a.splice(o,1),t.patchValue({luns:a}),this.imagesInitiatorSelections[i].forEach(d=>{d.name===s&&(d.selected=!1)}),!1}get groups(){return this.targetForm.get("groups")}addGroup(){const t=new M.d({group_id:new r.NI("",{validators:[r.kI.required]}),members:new r.NI([]),disks:new r.NI([])});this.groups.push(t);const o=C().map(this.targetForm.getValue("disks"),s=>new V.$(!1,s,""));this.groupDiskSelections.push(o);const i=C().map(this.initiators.value,s=>new V.$(!1,s.client_iqn,"",!s.cdIsInGroup));return this.groupMembersSelections.push(i),t}removeGroup(t){this.groups.removeAt(t),this.groupMembersSelections[t].filter(i=>i.selected).forEach(i=>{i.selected=!1,this.onGroupMemberSelection({option:i},t)}),this.groupMembersSelections.splice(t,1),this.groupDiskSelections.splice(t,1)}onGroupMemberSelection(t,o){const i=t.option;let s=[];i.selected||(s=this.groupDiskSelections[o].filter(d=>d.selected).map(d=>d.name)),this.initiators.controls.forEach((a,d)=>{a.value.client_iqn===i.name&&(a.patchValue({luns:s}),a.get("cdIsInGroup").setValue(i.selected),C().forEach(this.groupMembersSelections,c=>{c[d].enabled=!i.selected}),this.imagesInitiatorSelections[d].forEach(c=>{c.selected=s.includes(c.name)}))})}removeGroupInitiator(t,o,i){const s=t.getValue("members")[o];t.getValue("members").splice(o,1),this.onGroupMemberSelection({option:new V.$(!1,s,"")},i)}removeGroupDisk(t,o,i){const s=t.getValue("disks")[o];t.getValue("disks").splice(o,1),this.groupDiskSelections[i].forEach(a=>{a.name===s&&(a.selected=!1)}),this.groupDiskSelections[i]=[...this.groupDiskSelections[i]]}submit(){const t=C().cloneDeep(this.targetForm.value),o={target_iqn:this.targetForm.getValue("target_iqn"),target_controls:this.targetForm.getValue("target_controls"),acl_enabled:this.targetForm.getValue("acl_enabled"),portals:[],disks:[],clients:[],groups:[]};if(this.cephIscsiConfigVersion>10){const s=this.targetForm.get("auth");s.getValue("user")||s.get("user").setValue(""),s.getValue("password")||s.get("password").setValue(""),s.getValue("mutual_user")||s.get("mutual_user").setValue(""),s.getValue("mutual_password")||s.get("mutual_password").setValue("");const a=this.targetForm.getValue("acl_enabled");o.auth={user:a?"":s.getValue("user"),password:a?"":s.getValue("password"),mutual_user:a?"":s.getValue("mutual_user"),mutual_password:a?"":s.getValue("mutual_password")}}let i;t.disks.forEach(s=>{const a=s.split("/"),d=this.imagesSettings[s].backstore;o.disks.push({pool:a[0],image:a[1],backstore:d,controls:this.imagesSettings[s][d],lun:this.imagesSettings[s].lun,wwn:this.imagesSettings[s].wwn})}),t.portals.forEach(s=>{const a=s.indexOf(":");o.portals.push({host:s.substring(0,a),ip:s.substring(a+1)})}),o.acl_enabled&&(t.initiators.forEach(s=>{s.auth.user||(s.auth.user=""),s.auth.password||(s.auth.password=""),s.auth.mutual_user||(s.auth.mutual_user=""),s.auth.mutual_password||(s.auth.mutual_password=""),delete s.cdIsInGroup;const a=[];s.luns.forEach(d=>{const c=d.split("/");a.push({pool:c[0],image:c[1]})}),s.luns=a}),o.clients=t.initiators),o.acl_enabled&&(t.groups.forEach(s=>{const a=[];s.disks.forEach(d=>{const c=d.split("/");a.push({pool:c[0],image:c[1]})}),s.disks=a}),o.groups=t.groups),this.isEdit?(o.new_target_iqn=o.target_iqn,o.target_iqn=this.target_iqn,i=this.taskWrapper.wrapTaskAroundCall({task:new F.R("iscsi/target/edit",{target_iqn:o.target_iqn}),call:this.iscsiService.updateTarget(this.target_iqn,o)})):i=this.taskWrapper.wrapTaskAroundCall({task:new F.R("iscsi/target/create",{target_iqn:o.target_iqn}),call:this.iscsiService.createTarget(o)}),i.subscribe({error:()=>{this.targetForm.setErrors({cdSubmitButton:!0})},complete:()=>this.router.navigate(["/block/iscsi/targets"])})}targetSettingsModal(){const t={target_controls:this.targetForm.get("target_controls"),target_default_controls:this.target_default_controls,target_controls_limits:this.target_controls_limits};this.modalRef=this.modalService.show(ro,t)}imageSettingsModal(t){const o={imagesSettings:this.imagesSettings,image:t,api_version:this.api_version,disk_default_controls:this.disk_default_controls,disk_controls_limits:this.disk_controls_limits,backstores:this.getValidBackstores(this.getImageById(t)),control:this.targetForm.get("disks")};this.modalRef=this.modalService.show(so,o)}validFeatures(t,o){const i=t.features,s=this.required_rbd_features[o];return(i&s)===s&&0==(i&this.unsupported_rbd_features[o])}getImageById(t){return this.imagesAll.find(o=>t===`${o.pool_name}/${o.name}`)}getValidBackstores(t){return this.backstores.filter(o=>this.validFeatures(t,o))}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(k),e.Y36(re.Z),e.Y36(x),e.Y36(m.F0),e.Y36(m.gz),e.Y36(Q.P),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-form"]],features:[e.qOj],decls:1,vars:1,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$,G,X,J,te,A,w,de,pe,ge,ue,me,Te,fe,Ce,Se,y,Ze,Be,Ge,ye,xe,we,qe,L,Ot,At,ht,Pt,It,bt,Nt,Dt,vt,Lt,Ft,$t,Zt,Bt,Gt,yt,xt,wt,qt,Ht,Kt,kt,Xt,Qt,zt;return _="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",t="Target IQN",o="Portals",i="Add portal",s="Images",a="Add image",d="ACL authentication",c="This field is required.",u="IQN has wrong pattern.",S="An IQN has the following notation 'iqn.$year-$month.$reversedAddress:$definedName'",N="For example: iqn.2016-06.org.dashboard:storage:disk.sn-a8675309",P="More information",$="This target has modified advanced settings.",G="At least " + "\ufffd0\ufffd" + " gateways are required.",X="Backstore: " + "\ufffd0\ufffd" + ".\xA0",J="This image has modified settings.",te="Duplicated LUN numbers.",A="Duplicated WWN.",w="User",de="Password",pe="Mutual User",ge="Mutual Password",ue="This field is required.",me="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",Te="This field is required.",fe="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",Ce="This field is required.",Se="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",y="This field is required.",Ze="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",Be="Initiators",Ge="Add initiator",ye="Initiator",xe="Client IQN",we="User",qe="Password",L="Mutual User",Ot="Mutual Password",At="Images",ht="Initiator IQN needs to be unique.",Pt="This field is required.",It="IQN has wrong pattern.",bt="This field is required.",Nt="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",Dt="This field is required.",vt="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",Lt="This field is required.",Ft="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",$t="This field is required.",Zt="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",Bt="Initiator belongs to a group. Images will be configure in the group.",Gt="Add image",yt="No items added.",xt="Groups",wt="Add group",qt="Group",Ht="Name",Kt="Initiators",kt="Add initiator",Xt="Images",Qt="Add image",zt="No items added.",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["name","targetForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"card"],[1,"card-header"],_,[1,"card-body"],[1,"form-group","row"],["for","target_iqn",1,"cd-col-form-label","required"],t,[1,"cd-col-form-input"],[1,"input-group"],["type","text","id","target_iqn","name","target_iqn","formControlName","target_iqn","cdTrim","",1,"form-control"],[1,"input-group-append"],["id","ecp-info-button","type","button",1,"btn","btn-light",3,"click"],["aria-hidden","true",3,"ngClass"],["class","invalid-feedback",4,"ngIf"],["class","form-text text-muted",4,"ngIf"],["for","portals",1,"cd-col-form-label","required"],o,[4,"ngFor","ngForOf"],[1,"row"],[1,"col-md-12"],["elemClass","btn btn-light float-right",3,"data","options","messages","selection"],[3,"ngClass"],i,["type","hidden","id","portals","name","portals","formControlName","portals",1,"form-control"],["for","disks",1,"cd-col-form-label"],s,["type","hidden","id","disks","name","disks","formControlName","disks",1,"form-control"],a,[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["type","checkbox","formControlName","acl_enabled","name","acl_enabled","id","acl_enabled",1,"custom-control-input"],["for","acl_enabled",1,"custom-control-label"],d,["formGroupName","auth",4,"ngIf"],["class","form-group row",4,"ngIf"],[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],c,u,S,N,["target","_blank","href","https://en.wikipedia.org/wiki/ISCSI#Addressing"],P,[1,"form-text","text-muted"],$,[1,"input-group","cd-mb"],["type","text","disabled","",1,"cd-form-control",3,"value"],["type","button",1,"btn","btn-light",3,"click"],G,["class","input-group-text",4,"ngIf"],[4,"ngIf"],[1,"input-group-text"],X,J,te,A,["formGroupName","auth"],["for","target_user",1,"cd-col-form-label"],w,["type","text","autocomplete","off","id","target_user","name","target_user","formControlName","user",1,"form-control"],["for","target_password",1,"cd-col-form-label"],de,["type","password","autocomplete","new-password","id","target_password","name","target_password","formControlName","password",1,"form-control"],["type","button","cdPasswordButton","target_password",1,"btn","btn-light"],["source","target_password"],["for","target_mutual_user",1,"cd-col-form-label"],pe,["type","text","autocomplete","off","id","target_mutual_user","name","target_mutual_user","formControlName","mutual_user",1,"form-control"],["for","target_mutual_password",1,"cd-col-form-label"],ge,["type","password","autocomplete","new-password","id","target_mutual_password","name","target_mutual_password","formControlName","mutual_password",1,"form-control"],["type","button","cdPasswordButton","target_mutual_password",1,"btn","btn-light"],["source","target_mutual_password"],ue,me,Te,fe,Ce,Se,y,Ze,["for","initiators",1,"cd-col-form-label"],Be,["formArrayName","initiators",1,"cd-col-form-input"],["class","card mb-2",3,"formGroup",4,"ngFor","ngForOf"],[1,"btn","btn-light","float-right",3,"click"],Ge,[1,"card","mb-2",3,"formGroup"],ye,["type","button",1,"close",3,"click"],["for","client_iqn",1,"cd-col-form-label","required"],xe,["type","text","formControlName","client_iqn","cdTrim","",1,"form-control",3,"blur"],["for","user",1,"cd-col-form-label"],we,["formControlName","user","autocomplete","off","type","text",1,"form-control",3,"id"],["for","password",1,"cd-col-form-label"],qe,["formControlName","password","autocomplete","new-password","type","password",1,"form-control",3,"id"],["type","button",1,"btn","btn-light",3,"cdPasswordButton"],[3,"source"],["for","mutual_user",1,"cd-col-form-label"],L,["formControlName","mutual_user","autocomplete","off","type","text",1,"form-control",3,"id"],["for","mutual_password",1,"cd-col-form-label"],Ot,["formControlName","mutual_password","autocomplete","new-password","type","password",1,"form-control",3,"id"],["for","luns",1,"cd-col-form-label"],At,["class","row",4,"ngIf"],ht,Pt,It,bt,Nt,Dt,vt,Lt,Ft,$t,Zt,Bt,["elemClass","btn btn-light float-right",3,"data","options","messages"],Gt,yt,xt,["formArrayName","groups",1,"cd-col-form-input"],wt,qt,["for","group_id",1,"cd-col-form-label","required"],Ht,["type","text","formControlName","group_id",1,"form-control"],["for","members",1,"cd-col-form-label"],Kt,kt,Xt,Qt,zt]},template:function(t,o){1&t&&e.YNc(0,tn,66,40,"div",0),2&t&&e.Q6J("cdFormLoading",o.loading)},directives:[st.y,r._Y,r.JL,r.sg,v.V,g.P,f.o,r.Fj,h.b,r.JJ,r.u,lo,l.mk,l.O5,l.sg,co.H,r.Wl,O.p,r.x0,at.C,Je.s,r.CE],pipes:[l.rS,Ye.m,He.V],styles:[".cd-mb[_ngcontent-%COMP%]{margin-bottom:10px}"]}),n})();var lt=p(68136),he=p(30982),ee=p(64337),ve=p(99466),Ee=p(68774),ct=p(55657),se=p(38047),Ve=p(18001),Le=p(97161),oe=p(74937);function on(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,31),e.qZA())}function nn(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,32),e.qZA())}function _n(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,33),e.qZA())}function sn(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,34),e.qZA())}function an(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,35),e.qZA())}function rn(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,36),e.qZA())}function ln(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,37),e.qZA())}function cn(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,38),e.qZA())}let dn=(()=>{class n{constructor(t,o,i,s,a){this.authStorageService=t,this.activeModal=o,this.actionLabels=i,this.iscsiService=s,this.notificationService=a,this.USER_REGEX=/^[\w\.:@_-]{8,64}$/,this.PASSWORD_REGEX=/^[\w@\-_\/]{12,16}$/,this.permission=this.authStorageService.getPermissions().iscsi}ngOnInit(){this.hasPermission=this.permission.update,this.createForm(),this.iscsiService.getDiscovery().subscribe(t=>{this.discoveryForm.patchValue(t)})}createForm(){this.discoveryForm=new M.d({user:new r.NI({value:"",disabled:!this.hasPermission}),password:new r.NI({value:"",disabled:!this.hasPermission}),mutual_user:new r.NI({value:"",disabled:!this.hasPermission}),mutual_password:new r.NI({value:"",disabled:!this.hasPermission})}),B.h.validateIf(this.discoveryForm.get("user"),()=>this.discoveryForm.getValue("password")||this.discoveryForm.getValue("mutual_user")||this.discoveryForm.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.USER_REGEX)],[this.discoveryForm.get("password"),this.discoveryForm.get("mutual_user"),this.discoveryForm.get("mutual_password")]),B.h.validateIf(this.discoveryForm.get("password"),()=>this.discoveryForm.getValue("user")||this.discoveryForm.getValue("mutual_user")||this.discoveryForm.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.PASSWORD_REGEX)],[this.discoveryForm.get("user"),this.discoveryForm.get("mutual_user"),this.discoveryForm.get("mutual_password")]),B.h.validateIf(this.discoveryForm.get("mutual_user"),()=>this.discoveryForm.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.USER_REGEX)],[this.discoveryForm.get("user"),this.discoveryForm.get("password"),this.discoveryForm.get("mutual_password")]),B.h.validateIf(this.discoveryForm.get("mutual_password"),()=>this.discoveryForm.getValue("mutual_user"),[r.kI.required],[r.kI.pattern(this.PASSWORD_REGEX)],[this.discoveryForm.get("user"),this.discoveryForm.get("password"),this.discoveryForm.get("mutual_user")])}submitAction(){this.iscsiService.updateDiscovery(this.discoveryForm.value).subscribe(()=>{this.notificationService.show(Ve.k.success,"Updated discovery authentication"),this.activeModal.close()},()=>{this.discoveryForm.setErrors({cdSubmitButton:!0})})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(I.Kz),e.Y36(D.p4),e.Y36(k),e.Y36(Le.g))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-discovery-modal"]],decls:46,vars:13,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$;return _="Discovery Authentication",t="User",o="Password",i="Mutual User",s="Mutual Password",a="This field is required.",d="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",c="This field is required.",u="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",S="This field is required.",N="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",P="This field is required.",$="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","discoveryForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","user",1,"cd-col-form-label"],t,[1,"cd-col-form-input"],["id","user","formControlName","user","type","text","autocomplete","off",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","password",1,"cd-col-form-label"],o,[1,"input-group"],["id","password","formControlName","password","type","password","autocomplete","new-password",1,"form-control"],[1,"input-group-append"],["type","button","cdPasswordButton","password",1,"btn","btn-light"],["source","password"],["for","mutual_user",1,"cd-col-form-label"],i,["id","mutual_user","formControlName","mutual_user","type","text","autocomplete","off",1,"form-control"],["for","mutual_password",1,"cd-col-form-label"],s,["id","mutual_password","formControlName","mutual_password","type","password","autocomplete","new-password",1,"form-control"],["type","button","cdPasswordButton","mutual_password",1,"btn","btn-light"],["source","mutual_password"],[1,"modal-footer"],[3,"form","showSubmit","submitText","submitActionEvent"],[1,"invalid-feedback"],a,d,c,u,S,N,P,$]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"div",7),e.TgZ(8,"label",8),e.SDv(9,9),e.qZA(),e.TgZ(10,"div",10),e._UZ(11,"input",11),e.YNc(12,on,2,0,"span",12),e.YNc(13,nn,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(14,"div",7),e.TgZ(15,"label",13),e.SDv(16,14),e.qZA(),e.TgZ(17,"div",10),e.TgZ(18,"div",15),e._UZ(19,"input",16),e.TgZ(20,"span",17),e._UZ(21,"button",18),e._UZ(22,"cd-copy-2-clipboard-button",19),e.qZA(),e.qZA(),e.YNc(23,_n,2,0,"span",12),e.YNc(24,sn,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(25,"div",7),e.TgZ(26,"label",20),e.ynx(27),e.SDv(28,21),e.BQk(),e.qZA(),e.TgZ(29,"div",10),e._UZ(30,"input",22),e.YNc(31,an,2,0,"span",12),e.YNc(32,rn,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(33,"div",7),e.TgZ(34,"label",23),e.SDv(35,24),e.qZA(),e.TgZ(36,"div",10),e.TgZ(37,"div",15),e._UZ(38,"input",25),e.TgZ(39,"span",17),e._UZ(40,"button",26),e._UZ(41,"cd-copy-2-clipboard-button",27),e.qZA(),e.qZA(),e.YNc(42,ln,2,0,"span",12),e.YNc(43,cn,2,0,"span",12),e.qZA(),e.qZA(),e.qZA(),e.TgZ(44,"div",28),e.TgZ(45,"cd-form-button-panel",29),e.NdJ("submitActionEvent",function(){return o.submitAction()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.discoveryForm),e.xp6(8),e.Q6J("ngIf",o.discoveryForm.showError("user",i,"required")),e.xp6(1),e.Q6J("ngIf",o.discoveryForm.showError("user",i,"pattern")),e.xp6(10),e.Q6J("ngIf",o.discoveryForm.showError("password",i,"required")),e.xp6(1),e.Q6J("ngIf",o.discoveryForm.showError("password",i,"pattern")),e.xp6(7),e.Q6J("ngIf",o.discoveryForm.showError("mutual_user",i,"required")),e.xp6(1),e.Q6J("ngIf",o.discoveryForm.showError("mutual_user",i,"pattern")),e.xp6(10),e.Q6J("ngIf",o.discoveryForm.showError("mutual_password",i,"required")),e.xp6(1),e.Q6J("ngIf",o.discoveryForm.showError("mutual_password",i,"pattern")),e.xp6(2),e.Q6J("form",o.discoveryForm)("showSubmit",o.hasPermission)("submitText",o.actionLabels.SUBMIT)}},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,f.o,r.Fj,h.b,r.JJ,r.u,l.O5,at.C,Je.s,O.p],styles:[""]}),n})();var pn=p(86969);let dt=(()=>{class n{constructor(t){this.router=t}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(m.F0))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-tabs"]],decls:8,vars:1,consts:function(){let _,t;return _="Overview",t="Targets",[["ngbNav","",1,"nav-tabs",3,"activeId","navChange"],["nav","ngbNav"],["ngbNavItem","/block/iscsi/overview"],["ngbNavLink",""],_,["ngbNavItem","/block/iscsi/targets"],t]},template:function(t,o){1&t&&(e.TgZ(0,"ul",0,1),e.NdJ("navChange",function(s){return o.router.navigate([s.nextId])}),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.qZA(),e.TgZ(5,"li",5),e.TgZ(6,"a",3),e.SDv(7,6),e.qZA(),e.qZA(),e.qZA()),2&t&&e.Q6J("activeId",o.router.url)},directives:[I.Pz,I.nv,I.Vx],styles:[""]}),n})();var pt=p(34501),gn=p(30490),Re=p(94928),un=p(68962);const mn=["highlightTpl"],Tn=["detailTable"],fn=["tree"],Cn=function(){return["logged_in"]},Sn=function(){return["logged_out"]},En=function(n,_){return{"badge-success":n,"badge-danger":_}};function Rn(n,_){if(1&n&&(e._UZ(0,"i"),e.TgZ(1,"span"),e._uU(2),e.qZA(),e._uU(3," \xa0 "),e.TgZ(4,"span",8),e._uU(5),e.qZA()),2&n){const t=_.$implicit;e.Tol(t.data.cdIcon),e.xp6(2),e.Oqu(t.data.name),e.xp6(2),e.Q6J("ngClass",e.WLB(7,En,e.DdM(5,Cn).includes(t.data.status),e.DdM(6,Sn).includes(t.data.status))),e.xp6(1),e.hij(" ",t.data.status," ")}}function Mn(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"legend"),e._uU(2),e.qZA(),e._UZ(3,"cd-table",10,11),e.qZA()),2&n){const t=e.oxw();e.xp6(2),e.Oqu(t.title),e.xp6(1),e.Q6J("data",t.data)("columns",t.columns)("limit",0)}}function On(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.Oqu(t)}}function An(n,_){if(1&n&&(e.TgZ(0,"strong"),e._uU(1),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.Oqu(t)}}function hn(n,_){if(1&n&&(e.YNc(0,On,2,1,"span",12),e.YNc(1,An,2,1,"strong",12)),2&n){const t=_.row;e.Q6J("ngIf",void 0===t.default||t.default===t.current),e.xp6(1),e.Q6J("ngIf",void 0!==t.default&&t.default!==t.current)}}let Pn=(()=>{class n{constructor(t,o){this.iscsiBackstorePipe=t,this.booleanTextPipe=o,this.icons=T.P,this.metadata={},this.nodes=[],this.treeOptions={useVirtualScroll:!0,actionMapping:{mouse:{click:this.onNodeSelected.bind(this)}}}}set content(t){this.detailTable=t,t&&t.updateColumns()}ngOnInit(){this.columns=[{prop:"displayName",name:"Name",flexGrow:1,cellTemplate:this.highlightTpl},{prop:"current",name:"Current",flexGrow:1,cellTemplate:this.highlightTpl},{prop:"default",name:"Default",flexGrow:1,cellTemplate:this.highlightTpl}]}ngOnChanges(){this.selection&&(this.selectedItem=this.selection,this.generateTree()),this.data=void 0}generateTree(){const t=C().cloneDeep(this.selectedItem.target_controls);this.cephIscsiConfigVersion>10&&C().extend(t,C().cloneDeep(this.selectedItem.auth)),this.metadata={root:t};const o={target:{expanded:C().join(this.selectedItem.cdExecuting?[T.P.large,T.P.spinner,T.P.spin]:[T.P.large,T.P.bullseye]," ")},initiators:{expanded:C().join([T.P.large,T.P.user]," "),leaf:C().join([T.P.user]," ")},groups:{expanded:C().join([T.P.large,T.P.users]," "),leaf:C().join([T.P.users]," ")},disks:{expanded:C().join([T.P.large,T.P.disk]," "),leaf:C().join([T.P.disk]," ")},portals:{expanded:C().join([T.P.large,T.P.server]," "),leaf:C().join([T.P.server]," ")}},i=[];C().forEach(this.selectedItem.disks,c=>{const u="disk_"+c.pool+"_"+c.image;this.metadata[u]={controls:c.controls,backstore:c.backstore},["wwn","lun"].forEach(S=>{S in c&&(this.metadata[u][S]=c[S])}),i.push({name:`${c.pool}/${c.image}`,cdId:u,cdIcon:o.disks.leaf})});const s=[];C().forEach(this.selectedItem.portals,c=>{s.push({name:`${c.host}:${c.ip}`,cdIcon:o.portals.leaf})});const a=[];C().forEach(this.selectedItem.clients,c=>{const u=C().cloneDeep(c.auth);c.info&&(C().extend(u,c.info),delete u.state,C().forEach(Object.keys(c.info.state),P=>{u[P.toLowerCase()]=c.info.state[P]})),this.metadata["client_"+c.client_iqn]=u;const S=[];c.luns.forEach(P=>{S.push({name:`${P.pool}/${P.image}`,cdId:"disk_"+P.pool+"_"+P.image,cdIcon:o.disks.leaf})});let N="";c.info&&(N=Object.keys(c.info.state).includes("LOGGED_IN")?"logged_in":"logged_out"),a.push({name:c.client_iqn,status:N,cdId:"client_"+c.client_iqn,children:S,cdIcon:o.initiators.leaf})});const d=[];C().forEach(this.selectedItem.groups,c=>{const u=[];c.disks.forEach(N=>{u.push({name:`${N.pool}/${N.image}`,cdId:"disk_"+N.pool+"_"+N.image,cdIcon:o.disks.leaf})});const S=[];c.members.forEach(N=>{S.push({name:N,cdId:"client_"+N})}),d.push({name:c.group_id,cdIcon:o.groups.leaf,children:[{name:"Disks",children:u,cdIcon:o.disks.expanded},{name:"Initiators",children:S,cdIcon:o.initiators.expanded}]})}),this.nodes=[{name:this.selectedItem.target_iqn,cdId:"root",isExpanded:!0,cdIcon:o.target.expanded,children:[{name:"Disks",isExpanded:!0,children:i,cdIcon:o.disks.expanded},{name:"Portals",isExpanded:!0,children:s,cdIcon:o.portals.expanded},{name:"Initiators",isExpanded:!0,children:a,cdIcon:o.initiators.expanded},{name:"Groups",isExpanded:!0,children:d,cdIcon:o.groups.expanded}]}]}format(t){return"boolean"==typeof t?this.booleanTextPipe.transform(t):t}onNodeSelected(t,o){var i,s,a,d;if(ne.iM.ACTIVATE(t,o,!0),o.data.cdId){this.title=o.data.name;const c=this.metadata[o.data.cdId]||{};"root"===o.data.cdId?(null===(i=this.detailTable)||void 0===i||i.toggleColumn({prop:"default",isHidden:!0}),this.data=C().map(this.settings.target_default_controls,(u,S)=>({displayName:S,default:u=this.format(u),current:C().isUndefined(c[S])?u:this.format(c[S])})),this.cephIscsiConfigVersion>10&&["user","password","mutual_user","mutual_password"].forEach(u=>{this.data.push({displayName:u,default:null,current:c[u]})})):o.data.cdId.toString().startsWith("disk_")?(null===(s=this.detailTable)||void 0===s||s.toggleColumn({prop:"default",isHidden:!0}),this.data=C().map(this.settings.disk_default_controls[c.backstore],(u,S)=>({displayName:S,default:u=this.format(u),current:C().isUndefined(c.controls[S])?u:this.format(c.controls[S])})),this.data.push({displayName:"backstore",default:this.iscsiBackstorePipe.transform(this.settings.default_backstore),current:this.iscsiBackstorePipe.transform(c.backstore)}),["wwn","lun"].forEach(u=>{u in c&&this.data.push({displayName:u,default:void 0,current:c[u]})})):(null===(a=this.detailTable)||void 0===a||a.toggleColumn({prop:"default",isHidden:!1}),this.data=C().map(c,(u,S)=>({displayName:S,default:void 0,current:this.format(u)})))}else this.data=void 0;null===(d=this.detailTable)||void 0===d||d.updateColumns()}onUpdateData(){this.tree.treeModel.expandAll()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(He.V),e.Y36(un.T))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-details"]],viewQuery:function(t,o){if(1&t&&(e.Gf(mn,7),e.Gf(Tn,5),e.Gf(fn,5)),2&t){let i;e.iGM(i=e.CRH())&&(o.highlightTpl=i.first),e.iGM(i=e.CRH())&&(o.content=i.first),e.iGM(i=e.CRH())&&(o.tree=i.first)}},inputs:{selection:"selection",settings:"settings",cephIscsiConfigVersion:"cephIscsiConfigVersion"},features:[e.TTD],decls:11,vars:3,consts:function(){let _;return _="iSCSI Topology",[[1,"row"],[1,"col-6"],_,[3,"nodes","options","updateData"],["tree",""],["treeNodeTemplate",""],["class","col-6 metadata",4,"ngIf"],["highlightTpl",""],[1,"badge",3,"ngClass"],[1,"col-6","metadata"],["columnMode","flex",3,"data","columns","limit"],["detailTable",""],[4,"ngIf"]]},template:function(t,o){1&t&&(e.TgZ(0,"div",0),e.TgZ(1,"div",1),e.TgZ(2,"legend"),e.SDv(3,2),e.qZA(),e.TgZ(4,"tree-root",3,4),e.NdJ("updateData",function(){return o.onUpdateData()}),e.YNc(6,Rn,6,10,"ng-template",null,5,e.W1O),e.qZA(),e.qZA(),e.YNc(8,Mn,5,4,"div",6),e.qZA(),e.YNc(9,hn,2,2,"ng-template",null,7,e.W1O)),2&t&&(e.xp6(4),e.Q6J("nodes",o.nodes)("options",o.treeOptions),e.xp6(4),e.Q6J("ngIf",o.data))},directives:[ne.qr,l.O5,l.mk,ee.a],styles:[""]}),n})();function In(n,_){if(1&n&&(e.ynx(0),e._UZ(1,"br"),e.TgZ(2,"span"),e.SDv(3,6),e.qZA(),e.TgZ(4,"pre"),e._uU(5),e.qZA(),e.BQk()),2&n){const t=e.oxw(2);e.xp6(5),e.Oqu(t.status)}}function bn(n,_){if(1&n&&(e.TgZ(0,"cd-alert-panel",2),e.ynx(1),e.tHW(2,3),e._UZ(3,"cd-doc",4),e.N_p(),e.BQk(),e.YNc(4,In,6,1,"ng-container",5),e.qZA()),2&n){const t=e.oxw();e.xp6(4),e.Q6J("ngIf",t.status)}}function Nn(n,_){if(1&n&&e._UZ(0,"cd-iscsi-target-details",15),2&n){const t=e.oxw(2);e.Q6J("cephIscsiConfigVersion",t.cephIscsiConfigVersion)("selection",t.expandedRow)("settings",t.settings)}}const Dn=function(n){return[n]};function vn(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"cd-table",7,8),e.NdJ("fetchData",function(){return e.CHM(t),e.oxw().getTargets()})("setExpandedRow",function(i){return e.CHM(t),e.oxw().setExpandedRow(i)})("updateSelection",function(i){return e.CHM(t),e.oxw().updateSelection(i)}),e.TgZ(2,"div",9),e._UZ(3,"cd-table-actions",10),e.TgZ(4,"button",11),e.NdJ("click",function(){return e.CHM(t),e.oxw().configureDiscoveryAuth()}),e._UZ(5,"i",12),e.ynx(6),e.SDv(7,13),e.BQk(),e.qZA(),e.qZA(),e.YNc(8,Nn,1,3,"cd-iscsi-target-details",14),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("data",t.targets)("columns",t.columns)("hasDetails",!0)("autoReload",!1)("status",t.tableStatus),e.xp6(3),e.Q6J("permission",t.permission)("selection",t.selection)("tableActions",t.tableActions),e.xp6(2),e.Q6J("ngClass",e.VKq(10,Dn,t.icons.key)),e.xp6(3),e.Q6J("ngIf",t.expandedRow)}}let Ln=(()=>{class n extends lt.o{constructor(t,o,i,s,a,d,c,u,S){super(S),this.authStorageService=t,this.iscsiService=o,this.joinPipe=i,this.taskListService=s,this.notAvailablePipe=a,this.modalService=d,this.taskWrapper=c,this.actionLabels=u,this.ngZone=S,this.available=void 0,this.selection=new Ee.r,this.targets=[],this.icons=T.P,this.builders={"iscsi/target/create":N=>({target_iqn:N.target_iqn})},this.permission=this.authStorageService.getPermissions().iscsi,this.tableActions=[{permission:"create",icon:T.P.add,routerLink:()=>"/block/iscsi/targets/create",name:this.actionLabels.CREATE},{permission:"update",icon:T.P.edit,routerLink:()=>`/block/iscsi/targets/edit/${this.selection.first().target_iqn}`,name:this.actionLabels.EDIT,disable:()=>this.getEditDisableDesc()},{permission:"delete",icon:T.P.destroy,click:()=>this.deleteIscsiTargetModal(),name:this.actionLabels.DELETE,disable:()=>this.getDeleteDisableDesc()}]}ngOnInit(){this.columns=[{name:"Target",prop:"target_iqn",flexGrow:2,cellTransformation:ve.e.executing},{name:"Portals",prop:"cdPortals",pipe:this.joinPipe,flexGrow:2},{name:"Images",prop:"cdImages",pipe:this.joinPipe,flexGrow:2},{name:"# Sessions",prop:"info.num_sessions",pipe:this.notAvailablePipe,flexGrow:1}],this.iscsiService.status().subscribe(t=>{this.available=t.available,t.available||(this.status=t.message)})}getTargets(){this.available&&(this.setTableRefreshTimeout(),this.iscsiService.version().subscribe(t=>{this.cephIscsiConfigVersion=t.ceph_iscsi_config_version}),this.taskListService.init(()=>this.iscsiService.listTargets(),t=>this.prepareResponse(t),t=>this.targets=t,()=>this.onFetchError(),this.taskFilter,this.itemFilter,this.builders),this.iscsiService.settings().subscribe(t=>{this.settings=t}))}ngOnDestroy(){this.summaryDataSubscription&&this.summaryDataSubscription.unsubscribe()}getEditDisableDesc(){const t=this.selection.first();return t&&(null==t?void 0:t.cdExecuting)?t.cdExecuting:t&&C().isUndefined(null==t?void 0:t.info)?"Unavailable gateway(s)":!t}getDeleteDisableDesc(){var t;const o=this.selection.first();return(null==o?void 0:o.cdExecuting)?o.cdExecuting:o&&C().isUndefined(null==o?void 0:o.info)?"Unavailable gateway(s)":o&&(null===(t=null==o?void 0:o.info)||void 0===t?void 0:t.num_sessions)?"Target has active sessions":!o}prepareResponse(t){return t.forEach(o=>{o.cdPortals=o.portals.map(i=>`${i.host}:${i.ip}`),o.cdImages=o.disks.map(i=>`${i.pool}/${i.image}`)}),t}onFetchError(){this.table.reset()}itemFilter(t,o){return t.target_iqn===o.metadata.target_iqn}taskFilter(t){return["iscsi/target/create","iscsi/target/edit","iscsi/target/delete"].includes(t.name)}updateSelection(t){this.selection=t}deleteIscsiTargetModal(){const t=this.selection.first().target_iqn;this.modalRef=this.modalService.show(he.M,{itemDescription:"iSCSI target",itemNames:[t],submitActionObservable:()=>this.taskWrapper.wrapTaskAroundCall({task:new F.R("iscsi/target/delete",{target_iqn:t}),call:this.iscsiService.deleteTarget(t)})})}configureDiscoveryAuth(){this.modalService.show(dn)}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(k),e.Y36(pn.A),e.Y36(se.j),e.Y36(ct.g),e.Y36(re.Z),e.Y36(Q.P),e.Y36(D.p4),e.Y36(e.R0b))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-list"]],viewQuery:function(t,o){if(1&t&&e.Gf(ee.a,5),2&t){let i;e.iGM(i=e.CRH())&&(o.table=i.first)}},features:[e._Bn([se.j]),e.qOj],decls:3,vars:2,consts:function(){let _,t,o,i;return _="iSCSI Targets not available",t="Please consult the " + "\ufffd#3\ufffd" + "" + "\ufffd/#3\ufffd" + " on how to configure and enable the iSCSI Targets management functionality.",o="Available information:",i="Discovery authentication",[["type","info","title",_,4,"ngIf"],["columnMode","flex","identifier","target_iqn","forceIdentifier","true","selectionType","single",3,"data","columns","hasDetails","autoReload","status","fetchData","setExpandedRow","updateSelection",4,"ngIf"],["type","info","title",_],t,["section","iscsi"],[4,"ngIf"],o,["columnMode","flex","identifier","target_iqn","forceIdentifier","true","selectionType","single",3,"data","columns","hasDetails","autoReload","status","fetchData","setExpandedRow","updateSelection"],["table",""],[1,"table-actions","btn-toolbar"],[1,"btn-group",3,"permission","selection","tableActions"],["type","button",1,"btn","btn-light",3,"click"],["aria-hidden","true",3,"ngClass"],i,["cdTableDetail","",3,"cephIscsiConfigVersion","selection","settings",4,"ngIf"],["cdTableDetail","",3,"cephIscsiConfigVersion","selection","settings"]]},template:function(t,o){1&t&&(e._UZ(0,"cd-iscsi-tabs"),e.YNc(1,bn,5,1,"cd-alert-panel",0),e.YNc(2,vn,9,12,"cd-table",1)),2&t&&(e.xp6(1),e.Q6J("ngIf",!1===o.available),e.xp6(1),e.Q6J("ngIf",!0===o.available))},directives:[dt,l.O5,pt.G,gn.K,ee.a,Re.K,f.o,l.mk,Pn],styles:[""]}),n})();var Ue=p(66369),Fn=p(76446),$n=p(90068);const Zn=["iscsiSparklineTpl"],Bn=["iscsiPerSecondTpl"],Gn=["iscsiRelativeDateTpl"];function yn(n,_){if(1&n&&(e.TgZ(0,"span"),e._UZ(1,"cd-sparkline",9),e.qZA()),2&n){const t=e.oxw(),o=t.value,i=t.row;e.xp6(1),e.Q6J("data",o)("isBinary",i.cdIsBinary)}}function xn(n,_){1&n&&(e.TgZ(0,"span",10),e._uU(1," n/a "),e.qZA())}function wn(n,_){if(1&n&&(e.YNc(0,yn,2,2,"span",7),e.YNc(1,xn,2,0,"span",8)),2&n){const t=_.row;e.Q6J("ngIf","user:rbd"===t.backstore),e.xp6(1),e.Q6J("ngIf","user:rbd"!==t.backstore)}}function qn(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.hij(" ",t," /s ")}}function Hn(n,_){1&n&&(e.TgZ(0,"span",10),e._uU(1," n/a "),e.qZA())}function Kn(n,_){if(1&n&&(e.YNc(0,qn,2,1,"span",7),e.YNc(1,Hn,2,0,"span",8)),2&n){const t=_.row;e.Q6J("ngIf","user:rbd"===t.backstore),e.xp6(1),e.Q6J("ngIf","user:rbd"!==t.backstore)}}function kn(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.ALo(2,"notAvailable"),e.ALo(3,"relativeDate"),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.hij(" ",e.lcZ(2,1,e.lcZ(3,3,t))," ")}}function Xn(n,_){1&n&&(e.TgZ(0,"span",10),e._uU(1," n/a "),e.qZA())}function Qn(n,_){if(1&n&&(e.YNc(0,kn,4,5,"span",7),e.YNc(1,Xn,2,0,"span",8)),2&n){const t=_.row;e.Q6J("ngIf","user:rbd"===t.backstore),e.xp6(1),e.Q6J("ngIf","user:rbd"!==t.backstore)}}let zn=(()=>{class n{constructor(t,o,i){this.iscsiService=t,this.dimlessPipe=o,this.iscsiBackstorePipe=i,this.gateways=[],this.images=[]}ngOnInit(){this.gatewaysColumns=[{name:"Name",prop:"name"},{name:"State",prop:"state",flexGrow:1,cellTransformation:ve.e.badge,customTemplateConfig:{map:{up:{class:"badge-success"},down:{class:"badge-danger"}}}},{name:"# Targets",prop:"num_targets"},{name:"# Sessions",prop:"num_sessions"}],this.imagesColumns=[{name:"Pool",prop:"pool"},{name:"Image",prop:"image"},{name:"Backstore",prop:"backstore",pipe:this.iscsiBackstorePipe},{name:"Read Bytes",prop:"stats_history.rd_bytes",cellTemplate:this.iscsiSparklineTpl},{name:"Write Bytes",prop:"stats_history.wr_bytes",cellTemplate:this.iscsiSparklineTpl},{name:"Read Ops",prop:"stats.rd",pipe:this.dimlessPipe,cellTemplate:this.iscsiPerSecondTpl},{name:"Write Ops",prop:"stats.wr",pipe:this.dimlessPipe,cellTemplate:this.iscsiPerSecondTpl},{name:"A/O Since",prop:"optimized_since",cellTemplate:this.iscsiRelativeDateTpl}]}refresh(){this.iscsiService.overview().subscribe(t=>{this.gateways=t.gateways,this.images=t.images,this.images.map(o=>(o.stats_history&&(o.stats_history.rd_bytes=o.stats_history.rd_bytes.map(i=>i[1]),o.stats_history.wr_bytes=o.stats_history.wr_bytes.map(i=>i[1])),o.cdIsBinary=!0,o))})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(k),e.Y36(Ue.n),e.Y36(He.V))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi"]],viewQuery:function(t,o){if(1&t&&(e.Gf(Zn,7),e.Gf(Bn,7),e.Gf(Gn,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.iscsiSparklineTpl=i.first),e.iGM(i=e.CRH())&&(o.iscsiPerSecondTpl=i.first),e.iGM(i=e.CRH())&&(o.iscsiRelativeDateTpl=i.first)}},decls:13,vars:4,consts:function(){let _,t;return _="Gateways",t="Images",[_,[3,"data","columns","fetchData"],t,[3,"data","columns"],["iscsiSparklineTpl",""],["iscsiPerSecondTpl",""],["iscsiRelativeDateTpl",""],[4,"ngIf"],["class","text-muted",4,"ngIf"],[3,"data","isBinary"],[1,"text-muted"]]},template:function(t,o){1&t&&(e._UZ(0,"cd-iscsi-tabs"),e.TgZ(1,"legend"),e.SDv(2,0),e.qZA(),e.TgZ(3,"cd-table",1),e.NdJ("fetchData",function(){return o.refresh()}),e.qZA(),e.TgZ(4,"legend"),e.SDv(5,2),e.qZA(),e._UZ(6,"cd-table",3),e.YNc(7,wn,2,2,"ng-template",null,4,e.W1O),e.YNc(9,Kn,2,2,"ng-template",null,5,e.W1O),e.YNc(11,Qn,2,2,"ng-template",null,6,e.W1O)),2&t&&(e.xp6(3),e.Q6J("data",o.gateways)("columns",o.gatewaysColumns),e.xp6(3),e.Q6J("data",o.images)("columns",o.imagesColumns))},directives:[dt,ee.a,l.O5,Fn.l],pipes:[ct.g,$n.h],styles:[""]}),n})(),Jn=(()=>{class n{}return n.\u0275fac=function(t){return new(t||n)},n.\u0275mod=e.oAB({type:n}),n.\u0275inj=e.cJS({imports:[[l.ez,_e.m,I.Oz,m.Bz,r.u5,r.UX,I.ZQ]]}),n})();var Yn=p(75319),Vn=p(26215),Un=p(45435),gt=p(55358);let K=class{constructor(_,t){this.http=_,this.timerService=t,this.REFRESH_INTERVAL=3e4,this.summaryDataSource=new Vn.X(null),this.summaryData$=this.summaryDataSource.asObservable()}startPolling(){return this.timerService.get(()=>this.retrieveSummaryObservable(),this.REFRESH_INTERVAL).subscribe(this.retrieveSummaryObserver())}refresh(){return this.retrieveSummaryObservable().subscribe(this.retrieveSummaryObserver())}retrieveSummaryObservable(){return this.http.get("api/block/mirroring/summary")}retrieveSummaryObserver(){return _=>{this.summaryDataSource.next(_)}}subscribeSummary(_,t){return this.summaryData$.pipe((0,Un.h)(o=>!!o)).subscribe(_,t)}getPool(_){return this.http.get(`api/block/mirroring/pool/${_}`)}updatePool(_,t){return this.http.put(`api/block/mirroring/pool/${_}`,t,{observe:"response"})}getSiteName(){return this.http.get("api/block/mirroring/site_name")}setSiteName(_){return this.http.put("api/block/mirroring/site_name",{site_name:_},{observe:"response"})}createBootstrapToken(_){return this.http.post(`api/block/mirroring/pool/${_}/bootstrap/token`,{})}importBootstrapToken(_,t,o){return this.http.post(`api/block/mirroring/pool/${_}/bootstrap/peer`,{direction:t,token:o},{observe:"response"})}getPeer(_,t){return this.http.get(`api/block/mirroring/pool/${_}/peer/${t}`)}addPeer(_,t){return this.http.post(`api/block/mirroring/pool/${_}/peer`,t,{observe:"response"})}updatePeer(_,t,o){return this.http.put(`api/block/mirroring/pool/${_}/peer/${t}`,o,{observe:"response"})}deletePeer(_,t){return this.http.delete(`api/block/mirroring/pool/${_}/peer/${t}`,{observe:"response"})}};K.\u0275fac=function(_){return new(_||K)(e.LFG(ie.eN),e.LFG(gt.f))},K.\u0275prov=e.Yz7({token:K,factory:K.\u0275fac,providedIn:"root"}),(0,b.gn)([(0,b.fM)(0,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[String]),(0,b.w6)("design:returntype",void 0)],K.prototype,"setSiteName",null),(0,b.gn)([(0,b.fM)(1,Y.G),(0,b.fM)(2,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[String,String,String]),(0,b.w6)("design:returntype",void 0)],K.prototype,"importBootstrapToken",null),K=(0,b.gn)([Y.o,(0,b.w6)("design:paramtypes",[ie.eN,gt.f])],K);var je=p(58071),jn=p(68307),ut=p(12627),le=p(82945),Wn=p(39749),ei=p(13472);function ti(n,_){1&n&&(e.TgZ(0,"span",25),e.SDv(1,26),e.qZA())}function oi(n,_){if(1&n&&(e.TgZ(0,"div",27),e._UZ(1,"input",28),e.TgZ(2,"label",29),e._uU(3),e.qZA(),e.qZA()),2&n){const t=_.$implicit;e.xp6(1),e.s9C("id",t.name),e.s9C("name",t.name),e.s9C("formControlName",t.name),e.xp6(1),e.s9C("for",t.name),e.xp6(1),e.Oqu(t.name)}}function ni(n,_){1&n&&(e.TgZ(0,"span",25),e.SDv(1,30),e.qZA())}let ii=(()=>{class n{constructor(t,o,i){this.activeModal=t,this.rbdMirroringService=o,this.taskWrapper=i,this.pools=[],this.createForm()}createForm(){this.createBootstrapForm=new M.d({siteName:new r.NI("",{validators:[r.kI.required]}),pools:new r.cw({},{validators:[this.validatePools()]}),token:new r.NI("",{})})}ngOnInit(){this.createBootstrapForm.get("siteName").setValue(this.siteName),this.rbdMirroringService.getSiteName().subscribe(t=>{this.createBootstrapForm.get("siteName").setValue(t.site_name)}),this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.pools=t.content_data.pools.reduce((s,a)=>(s.push({name:a.name,mirror_mode:a.mirror_mode}),s),[]);const i=this.createBootstrapForm.get("pools");C().each(this.pools,s=>{const a=s.name,d="disabled"===s.mirror_mode,c=i.controls[a];c?d&&c.disabled?c.enable():!d&&c.enabled&&(c.disable(),c.setValue(!0)):i.addControl(a,new r.NI({value:!d,disabled:!d}))})})}ngOnDestroy(){this.subs&&this.subs.unsubscribe()}validatePools(){return t=>{let o=0;return C().each(t.controls,i=>{!0===i.value&&++o}),o>0?null:{requirePool:!0}}}generate(){this.createBootstrapForm.get("token").setValue("");let t="";const o=[],i=this.createBootstrapForm.get("pools");C().each(i.controls,(u,S)=>{!0===u.value&&(t=S,u.disabled||o.push(S))});const s={mirror_mode:"image"},a=(0,je.z)(this.rbdMirroringService.setSiteName(this.createBootstrapForm.getValue("siteName")),(0,W.D)(o.map(u=>this.rbdMirroringService.updatePool(u,s))),this.rbdMirroringService.createBootstrapToken(t).pipe((0,jn.b)(u=>this.createBootstrapForm.get("token").setValue(u.token)))).pipe((0,ut.Z)()),d=()=>{this.rbdMirroringService.refresh(),this.createBootstrapForm.setErrors({cdSubmitButton:!0})};this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/bootstrap/create",{}),call:a}).subscribe({error:d,complete:d})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-bootstrap-create-modal"]],decls:32,vars:6,consts:function(){let _,t,o,i,s,a,d,c,u,S,N;return _="Create Bootstrap Token",t="To create a bootstrap token which can be imported by a peer site cluster, provide the local site's name, select which pools will have mirroring enabled, and click\xA0 " + "\ufffd#10\ufffd" + "Generate" + "\ufffd/#10\ufffd" + ".",o="Site Name",i="Name...",s="Pools",a="Generate",d="Token",c="Generated token...",u="Close",S="This field is required.",N="At least one pool is required.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","createBootstrapForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","siteName",1,"col-form-label","required"],o,["type","text","placeholder",i,"id","siteName","name","siteName","formControlName","siteName","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["formGroupName","pools",1,"form-group"],["for","pools",1,"col-form-label","required"],s,["class","custom-control custom-checkbox",4,"ngFor","ngForOf"],[1,"mb-4","float-right",3,"form","submitAction"],a,["for","token",1,"col-form-label"],d,["placeholder",c,"id","token","formControlName","token","readonly","",1,"form-control","resize-vertical"],["source","token",1,"float-right"],[1,"modal-footer"],["name",u,3,"backAction"],[1,"invalid-feedback"],S,[1,"custom-control","custom-checkbox"],["type","checkbox",1,"custom-control-input",3,"id","name","formControlName"],[1,"custom-control-label",3,"for"],N]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.ynx(8),e.tHW(9,7),e._UZ(10,"kbd"),e.N_p(),e.BQk(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e._UZ(14,"input",11),e.YNc(15,ti,2,0,"span",12),e.qZA(),e.TgZ(16,"div",13),e.TgZ(17,"label",14),e.SDv(18,15),e.qZA(),e.YNc(19,oi,4,5,"div",16),e.YNc(20,ni,2,0,"span",12),e.qZA(),e.TgZ(21,"cd-submit-button",17),e.NdJ("submitAction",function(){return o.generate()}),e.SDv(22,18),e.qZA(),e.TgZ(23,"div",8),e.TgZ(24,"label",19),e.TgZ(25,"span"),e.SDv(26,20),e.qZA(),e.qZA(),e.TgZ(27,"textarea",21),e._uU(28," "),e.qZA(),e.qZA(),e._UZ(29,"cd-copy-2-clipboard-button",22),e.qZA(),e.TgZ(30,"div",23),e.TgZ(31,"cd-back-button",24),e.NdJ("backAction",function(){return o.activeModal.close()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.createBootstrapForm),e.xp6(11),e.Q6J("ngIf",o.createBootstrapForm.showError("siteName",i,"required")),e.xp6(4),e.Q6J("ngForOf",o.pools),e.xp6(1),e.Q6J("ngIf",o.createBootstrapForm.showError("pools",i,"requirePool")),e.xp6(1),e.Q6J("form",o.createBootstrapForm)}},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,r.x0,l.sg,Wn.w,Je.s,ei.W,r.Wl],styles:[".form-group.ng-invalid[_ngcontent-%COMP%] .invalid-feedback[_ngcontent-%COMP%]{display:block}"]}),n})();function _i(n,_){1&n&&(e.TgZ(0,"span",26),e.SDv(1,27),e.qZA())}function si(n,_){if(1&n&&(e.TgZ(0,"option",28),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.key),e.xp6(1),e.Oqu(t.desc)}}function ai(n,_){if(1&n&&(e.TgZ(0,"div",29),e._UZ(1,"input",30),e.TgZ(2,"label",31),e._uU(3),e.qZA(),e.qZA()),2&n){const t=_.$implicit;e.xp6(1),e.s9C("id",t.name),e.s9C("name",t.name),e.s9C("formControlName",t.name),e.xp6(1),e.s9C("for",t.name),e.xp6(1),e.Oqu(t.name)}}function ri(n,_){1&n&&(e.TgZ(0,"span",26),e.SDv(1,32),e.qZA())}function li(n,_){1&n&&(e.TgZ(0,"span",26),e.SDv(1,33),e.qZA())}function ci(n,_){1&n&&(e.TgZ(0,"span",26),e.SDv(1,34),e.qZA())}let di=(()=>{class n{constructor(t,o,i,s){this.activeModal=t,this.actionLabels=o,this.rbdMirroringService=i,this.taskWrapper=s,this.pools=[],this.directions=[{key:"rx-tx",desc:"Bidirectional"},{key:"rx",desc:"Unidirectional (receive-only)"}],this.createForm()}createForm(){this.importBootstrapForm=new M.d({siteName:new r.NI("",{validators:[r.kI.required]}),direction:new r.NI("rx-tx",{}),pools:new r.cw({},{validators:[this.validatePools()]}),token:new r.NI("",{validators:[r.kI.required,this.validateToken()]})})}ngOnInit(){this.rbdMirroringService.getSiteName().subscribe(t=>{this.importBootstrapForm.get("siteName").setValue(t.site_name)}),this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.pools=t.content_data.pools.reduce((s,a)=>(s.push({name:a.name,mirror_mode:a.mirror_mode}),s),[]);const i=this.importBootstrapForm.get("pools");C().each(this.pools,s=>{const a=s.name,d="disabled"===s.mirror_mode,c=i.controls[a];c?d&&c.disabled?c.enable():!d&&c.enabled&&(c.disable(),c.setValue(!0)):i.addControl(a,new r.NI({value:!d,disabled:!d}))})})}ngOnDestroy(){this.subs&&this.subs.unsubscribe()}validatePools(){return t=>{let o=0;return C().each(t.controls,i=>{!0===i.value&&++o}),o>0?null:{requirePool:!0}}}validateToken(){return t=>{try{if(JSON.parse(atob(t.value)))return null}catch(o){}return{invalidToken:!0}}}import(){const t=[],o=[],i=this.importBootstrapForm.get("pools");C().each(i.controls,(u,S)=>{!0===u.value&&(t.push(S),u.disabled||o.push(S))});const s={mirror_mode:"image"};let a=(0,je.z)(this.rbdMirroringService.setSiteName(this.importBootstrapForm.getValue("siteName")),(0,W.D)(o.map(u=>this.rbdMirroringService.updatePool(u,s))));a=t.reduce((u,S)=>(0,je.z)(u,this.rbdMirroringService.importBootstrapToken(S,this.importBootstrapForm.getValue("direction"),this.importBootstrapForm.getValue("token"))),a).pipe((0,ut.Z)());const d=()=>{this.rbdMirroringService.refresh(),this.importBootstrapForm.setErrors({cdSubmitButton:!0})};this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/bootstrap/import",{}),call:a}).subscribe({error:d,complete:()=>{d(),this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-bootstrap-import-modal"]],decls:36,vars:10,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P;return _="Import Bootstrap Token",t="To import a bootstrap token which was created by a peer site cluster, provide the local site's name, select which pools will have mirroring enabled, provide the generated token, and click\xA0" + "\ufffd#10\ufffd" + "Import" + "\ufffd/#10\ufffd" + ".",o="Site Name",i="Name...",s="Direction",a="Pools",d="Token",c="Generated token...",u="This field is required.",S="At least one pool is required.",N="This field is required.",P="The token is invalid.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","importBootstrapForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","siteName",1,"col-form-label","required"],o,["type","text","placeholder",i,"id","siteName","name","siteName","formControlName","siteName","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","direction",1,"col-form-label"],s,["id","direction","name","direction","formControlName","direction",1,"form-control","custom-select"],[3,"value",4,"ngFor","ngForOf"],["formGroupName","pools",1,"form-group"],["for","pools",1,"col-form-label","required"],a,["class","custom-control custom-checkbox",4,"ngFor","ngForOf"],["for","token",1,"col-form-label","required"],d,["placeholder",c,"id","token","formControlName","token",1,"form-control","resize-vertical"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],u,[3,"value"],[1,"custom-control","custom-checkbox"],["type","checkbox",1,"custom-control-input",3,"id","name","formControlName"],[1,"custom-control-label",3,"for"],S,N,P]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.ynx(8),e.tHW(9,7),e._UZ(10,"kbd"),e.N_p(),e.BQk(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e._UZ(14,"input",11),e.YNc(15,_i,2,0,"span",12),e.qZA(),e.TgZ(16,"div",8),e.TgZ(17,"label",13),e.TgZ(18,"span"),e.SDv(19,14),e.qZA(),e.qZA(),e.TgZ(20,"select",15),e.YNc(21,si,2,2,"option",16),e.qZA(),e.qZA(),e.TgZ(22,"div",17),e.TgZ(23,"label",18),e.SDv(24,19),e.qZA(),e.YNc(25,ai,4,5,"div",20),e.YNc(26,ri,2,0,"span",12),e.qZA(),e.TgZ(27,"div",8),e.TgZ(28,"label",21),e.SDv(29,22),e.qZA(),e.TgZ(30,"textarea",23),e._uU(31," "),e.qZA(),e.YNc(32,li,2,0,"span",12),e.YNc(33,ci,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(34,"div",24),e.TgZ(35,"cd-form-button-panel",25),e.NdJ("submitActionEvent",function(){return o.import()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.importBootstrapForm),e.xp6(11),e.Q6J("ngIf",o.importBootstrapForm.showError("siteName",i,"required")),e.xp6(6),e.Q6J("ngForOf",o.directions),e.xp6(4),e.Q6J("ngForOf",o.pools),e.xp6(1),e.Q6J("ngIf",o.importBootstrapForm.showError("pools",i,"requirePool")),e.xp6(6),e.Q6J("ngIf",o.importBootstrapForm.showError("token",i,"required")),e.xp6(1),e.Q6J("ngIf",o.importBootstrapForm.showError("token",i,"invalidToken")),e.xp6(2),e.Q6J("form",o.importBootstrapForm)("submitText",o.actionLabels.SUBMIT)}},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,r.EJ,l.sg,r.x0,O.p,r.YN,r.Kr,r.Wl],styles:[""]}),n})(),pi=(()=>{class n{constructor(t,o,i,s){this.activeModal=t,this.actionLabels=o,this.rbdMirroringService=i,this.taskWrapper=s,this.createForm()}createForm(){this.editSiteNameForm=new M.d({siteName:new r.NI("",{})})}ngOnInit(){this.editSiteNameForm.get("siteName").setValue(this.siteName),this.rbdMirroringService.getSiteName().subscribe(t=>{this.editSiteNameForm.get("siteName").setValue(t.site_name)})}update(){this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/site_name/edit",{}),call:this.rbdMirroringService.setSiteName(this.editSiteNameForm.getValue("siteName"))}).subscribe({error:()=>this.editSiteNameForm.setErrors({cdSubmitButton:!0}),complete:()=>{this.rbdMirroringService.refresh(),this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-edit-site-mode-modal"]],decls:17,vars:4,consts:function(){let _,t,o,i;return _="Edit site name",t="Edit the site name and click\xA0 " + "\ufffd#10\ufffd" + "Update" + "\ufffd/#10\ufffd" + ".",o="Site Name",i="Name...",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","editSiteNameForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","siteName",1,"col-form-label","required"],o,["type","text","placeholder",i,"id","siteName","name","siteName","formControlName","siteName","autofocus","",1,"form-control"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"]]},template:function(t,o){1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.ynx(8),e.tHW(9,7),e._UZ(10,"kbd"),e.N_p(),e.BQk(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e._UZ(14,"input",11),e.qZA(),e.qZA(),e.TgZ(15,"div",12),e.TgZ(16,"cd-form-button-panel",13),e.NdJ("submitActionEvent",function(){return o.update()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t&&(e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.editSiteNameForm),e.xp6(12),e.Q6J("form",o.editSiteNameForm)("submitText",o.actionLabels.UPDATE))},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,O.p],styles:[""]}),n})();var U=p(69158),gi=p(58111);let We=(()=>{class n{transform(t){return"warning"===t?"badge badge-warning":"error"===t?"badge badge-danger":"success"===t?"badge badge-success":"badge badge-info"}}return n.\u0275fac=function(t){return new(t||n)},n.\u0275pipe=e.Yjl({name:"mirrorHealthColor",type:n,pure:!0}),n})();const ui=["healthTmpl"];function mi(n,_){if(1&n&&(e.TgZ(0,"span",2),e.ALo(1,"mirrorHealthColor"),e._uU(2),e.qZA()),2&n){const o=_.value;e.Q6J("ngClass",e.lcZ(1,2,_.row.health_color)),e.xp6(2),e.Oqu(o)}}let Ti=(()=>{class n{constructor(t,o){this.rbdMirroringService=t,this.cephShortVersionPipe=o,this.tableStatus=new U.E}ngOnInit(){this.columns=[{prop:"instance_id",name:"Instance",flexGrow:2},{prop:"id",name:"ID",flexGrow:2},{prop:"server_hostname",name:"Hostname",flexGrow:2},{prop:"version",name:"Version",pipe:this.cephShortVersionPipe,flexGrow:2},{prop:"health",name:"Health",cellTemplate:this.healthTmpl,flexGrow:1}],this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.data=t.content_data.daemons,this.tableStatus=new U.E(t.status)})}ngOnDestroy(){this.subs.unsubscribe()}refresh(){this.rbdMirroringService.refresh()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(K),e.Y36(gi.F))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-mirroring-daemons"]],viewQuery:function(t,o){if(1&t&&e.Gf(ui,7),2&t){let i;e.iGM(i=e.CRH())&&(o.healthTmpl=i.first)}},decls:3,vars:4,consts:[["columnMode","flex",3,"data","columns","autoReload","status","fetchData"],["healthTmpl",""],[3,"ngClass"]],template:function(t,o){1&t&&(e.TgZ(0,"cd-table",0),e.NdJ("fetchData",function(){return o.refresh()}),e.qZA(),e.YNc(1,mi,3,4,"ng-template",null,1,e.W1O)),2&t&&e.Q6J("data",o.data)("columns",o.columns)("autoReload",-1)("status",o.tableStatus)},directives:[ee.a,l.mk],pipes:[We],styles:[""]}),n})();var fi=p(18891);class Ci{}function Si(n,_){if(1&n&&(e.TgZ(0,"option",16),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.id),e.xp6(1),e.Oqu(t.name)}}function Ei(n,_){1&n&&(e.TgZ(0,"span",17),e.SDv(1,18),e.qZA())}let Ri=(()=>{class n{constructor(t,o,i,s){this.activeModal=t,this.actionLabels=o,this.rbdMirroringService=i,this.taskWrapper=s,this.bsConfig={containerClass:"theme-default"},this.peerExists=!1,this.mirrorModes=[{id:"disabled",name:"Disabled"},{id:"pool",name:"Pool"},{id:"image",name:"Image"}],this.createForm()}createForm(){this.editModeForm=new M.d({mirrorMode:new r.NI("",{validators:[r.kI.required,this.validateMode.bind(this)]})})}ngOnInit(){this.pattern=`${this.poolName}`,this.rbdMirroringService.getPool(this.poolName).subscribe(t=>{this.setResponse(t)}),this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.peerExists=!1;const i=t.content_data.pools.find(s=>this.poolName===s.name);this.peerExists=i&&i.peer_uuids.length})}ngOnDestroy(){this.subs.unsubscribe()}validateMode(t){return"disabled"===t.value&&this.peerExists?{cannotDisable:{value:t.value}}:null}setResponse(t){this.editModeForm.get("mirrorMode").setValue(t.mirror_mode)}update(){const t=new Ci;t.mirror_mode=this.editModeForm.getValue("mirrorMode"),this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/pool/edit",{pool_name:this.poolName}),call:this.rbdMirroringService.updatePool(this.poolName,t)}).subscribe({error:()=>this.editModeForm.setErrors({cdSubmitButton:!0}),complete:()=>{this.rbdMirroringService.refresh(),this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-pool-edit-mode-modal"]],decls:21,vars:7,consts:function(){let _,t,o,i;return _="Edit pool mirror mode",t="To edit the mirror mode for pool\xA0 " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "" + "\ufffd0\ufffd" + "" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + ", select a new mode from the list and click\xA0 " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "Update" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + ".",t=e.Zx4(t),o="Mode",i="Peer clusters must be removed prior to disabling mirror.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","editModeForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","mirrorMode",1,"col-form-label"],o,["id","mirrorMode","name","mirrorMode","formControlName","mirrorMode",1,"form-control","custom-select"],[3,"value",4,"ngFor","ngForOf"],["class","invalid-feedback",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[3,"value"],[1,"invalid-feedback"],i]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.ynx(8),e.tHW(9,7),e._UZ(10,"kbd"),e._UZ(11,"kbd"),e.N_p(),e.BQk(),e.qZA(),e.TgZ(12,"div",8),e.TgZ(13,"label",9),e.TgZ(14,"span"),e.SDv(15,10),e.qZA(),e.qZA(),e.TgZ(16,"select",11),e.YNc(17,Si,2,2,"option",12),e.qZA(),e.YNc(18,Ei,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(19,"div",14),e.TgZ(20,"cd-form-button-panel",15),e.NdJ("submitActionEvent",function(){return o.update()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.editModeForm),e.xp6(7),e.pQV(o.poolName),e.QtT(9),e.xp6(6),e.Q6J("ngForOf",o.mirrorModes),e.xp6(1),e.Q6J("ngIf",o.editModeForm.showError("mirrorMode",i,"cannotDisable")),e.xp6(2),e.Q6J("form",o.editModeForm)("submitText",o.actionLabels.UPDATE)}},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.EJ,h.b,r.JJ,r.u,l.sg,l.O5,O.p,r.YN,r.Kr],styles:[""]}),n})();class Mi{}function Oi(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,25),e.qZA())}function Ai(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,26),e.qZA())}function hi(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,27),e.qZA())}function Pi(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,28),e.qZA())}function Ii(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,29),e.qZA())}function bi(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,30),e.qZA())}let Ni=(()=>{class n{constructor(t,o,i,s){this.activeModal=t,this.actionLabels=o,this.rbdMirroringService=i,this.taskWrapper=s,this.bsConfig={containerClass:"theme-default"},this.createForm()}createForm(){this.editPeerForm=new M.d({clusterName:new r.NI("",{validators:[r.kI.required,this.validateClusterName]}),clientID:new r.NI("",{validators:[r.kI.required,this.validateClientID]}),monAddr:new r.NI("",{validators:[this.validateMonAddr]}),key:new r.NI("",{validators:[this.validateKey]})})}ngOnInit(){this.pattern=`${this.poolName}/${this.peerUUID}`,"edit"===this.mode&&this.rbdMirroringService.getPeer(this.poolName,this.peerUUID).subscribe(t=>{this.setResponse(t)})}validateClusterName(t){if(!t.value.match(/^[\w\-_]*$/))return{invalidClusterName:{value:t.value}}}validateClientID(t){if(!t.value.match(/^(?!client\.)[\w\-_.]*$/))return{invalidClientID:{value:t.value}}}validateMonAddr(t){if(!t.value.match(/^[,; ]*([\w.\-_\[\]]+(:[\d]+)?[,; ]*)*$/))return{invalidMonAddr:{value:t.value}}}validateKey(t){try{if(""===t.value||atob(t.value))return null}catch(o){}return{invalidKey:{value:t.value}}}setResponse(t){this.response=t,this.editPeerForm.get("clusterName").setValue(t.cluster_name),this.editPeerForm.get("clientID").setValue(t.client_id),this.editPeerForm.get("monAddr").setValue(t.mon_host),this.editPeerForm.get("key").setValue(t.key)}update(){const t=new Mi;let o;t.cluster_name=this.editPeerForm.getValue("clusterName"),t.client_id=this.editPeerForm.getValue("clientID"),t.mon_host=this.editPeerForm.getValue("monAddr"),t.key=this.editPeerForm.getValue("key"),o=this.taskWrapper.wrapTaskAroundCall("edit"===this.mode?{task:new F.R("rbd/mirroring/peer/edit",{pool_name:this.poolName}),call:this.rbdMirroringService.updatePeer(this.poolName,this.peerUUID,t)}:{task:new F.R("rbd/mirroring/peer/add",{pool_name:this.poolName}),call:this.rbdMirroringService.addPeer(this.poolName,t)}),o.subscribe({error:()=>this.editPeerForm.setErrors({cdSubmitButton:!0}),complete:()=>{this.rbdMirroringService.refresh(),this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-pool-edit-peer-modal"]],decls:38,vars:13,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$,G,X,J,te,A;return _="{VAR_SELECT, select, edit {Edit} other {Add}}",_=e.Zx4(_,{VAR_SELECT:"\ufffd0\ufffd"}),t="" + _ + " pool mirror peer",o="{VAR_SELECT, select, edit {Edit} other {Add}}",o=e.Zx4(o,{VAR_SELECT:"\ufffd0\ufffd"}),i="" + o + " the pool mirror peer attributes for pool " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "" + "\ufffd1\ufffd" + "" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + " and click " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "Submit" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + ".",i=e.Zx4(i),s="Cluster Name",a="Name...",d="CephX ID",c="CephX ID...",u="Monitor Addresses",S="Comma-delimited addresses...",N="CephX Key",P="Base64-encoded key...",$="This field is required.",G="The cluster name is not valid.",X="This field is required.",J="The CephX ID is not valid.",te="The monitory address is not valid.",A="CephX key must be base64 encoded.",[[3,"modalRef"],[1,"modal-title"],t,[1,"modal-content"],["name","editPeerForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],i,[1,"form-group"],["for","clusterName",1,"col-form-label","required"],s,["type","text","placeholder",a,"id","clusterName","name","clusterName","formControlName","clusterName","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","clientID",1,"col-form-label","required"],d,["type","text","placeholder",c,"id","clientID","name","clientID","formControlName","clientID",1,"form-control"],["for","monAddr",1,"col-form-label"],u,["type","text","placeholder",S,"id","monAddr","name","monAddr","formControlName","monAddr",1,"form-control"],["for","key",1,"col-form-label"],N,["type","text","placeholder",P,"id","key","name","key","formControlName","key",1,"form-control"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],$,G,X,J,te,A]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.TgZ(1,"span",1),e.SDv(2,2),e.qZA(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.TgZ(8,"span"),e.tHW(9,7),e._UZ(10,"kbd"),e._UZ(11,"kbd"),e.N_p(),e.qZA(),e.qZA(),e.TgZ(12,"div",8),e.TgZ(13,"label",9),e.SDv(14,10),e.qZA(),e._UZ(15,"input",11),e.YNc(16,Oi,2,0,"span",12),e.YNc(17,Ai,2,0,"span",12),e.qZA(),e.TgZ(18,"div",8),e.TgZ(19,"label",13),e.SDv(20,14),e.qZA(),e._UZ(21,"input",15),e.YNc(22,hi,2,0,"span",12),e.YNc(23,Pi,2,0,"span",12),e.qZA(),e.TgZ(24,"div",8),e.TgZ(25,"label",16),e.TgZ(26,"span"),e.SDv(27,17),e.qZA(),e.qZA(),e._UZ(28,"input",18),e.YNc(29,Ii,2,0,"span",12),e.qZA(),e.TgZ(30,"div",8),e.TgZ(31,"label",19),e.TgZ(32,"span"),e.SDv(33,20),e.qZA(),e.qZA(),e._UZ(34,"input",21),e.YNc(35,bi,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(36,"div",22),e.TgZ(37,"cd-form-button-panel",23),e.NdJ("submitActionEvent",function(){return o.update()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(2),e.pQV(o.mode),e.QtT(2),e.xp6(2),e.Q6J("formGroup",o.editPeerForm),e.xp6(7),e.pQV(o.mode)(o.poolName),e.QtT(9),e.xp6(5),e.Q6J("ngIf",o.editPeerForm.showError("clusterName",i,"required")),e.xp6(1),e.Q6J("ngIf",o.editPeerForm.showError("clusterName",i,"invalidClusterName")),e.xp6(5),e.Q6J("ngIf",o.editPeerForm.showError("clientID",i,"required")),e.xp6(1),e.Q6J("ngIf",o.editPeerForm.showError("clientID",i,"invalidClientID")),e.xp6(6),e.Q6J("ngIf",o.editPeerForm.showError("monAddr",i,"invalidMonAddr")),e.xp6(6),e.Q6J("ngIf",o.editPeerForm.showError("key",i,"invalidKey")),e.xp6(2),e.Q6J("form",o.editPeerForm)("submitText",o.actionLabels.SUBMIT)}},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,O.p],styles:[""]}),n})();const Di=["healthTmpl"];function vi(n,_){if(1&n&&(e.TgZ(0,"span",3),e.ALo(1,"mirrorHealthColor"),e._uU(2),e.qZA()),2&n){const o=_.value;e.Q6J("ngClass",e.lcZ(1,2,_.row.health_color)),e.xp6(2),e.Oqu(o)}}let Li=(()=>{class n{constructor(t,o,i,s){this.authStorageService=t,this.rbdMirroringService=o,this.modalService=i,this.taskWrapper=s,this.selection=new Ee.r,this.tableStatus=new U.E,this.data=[],this.permission=this.authStorageService.getPermissions().rbdMirroring;const a={permission:"update",icon:T.P.edit,click:()=>this.editModeModal(),name:"Edit Mode",canBePrimary:()=>!0},d={permission:"create",icon:T.P.add,name:"Add Peer",click:()=>this.editPeersModal("add"),disable:()=>!this.selection.first()||"disabled"===this.selection.first().mirror_mode,visible:()=>!this.getPeerUUID(),canBePrimary:()=>!1},c={permission:"update",icon:T.P.exchange,name:"Edit Peer",click:()=>this.editPeersModal("edit"),visible:()=>!!this.getPeerUUID()},u={permission:"delete",icon:T.P.destroy,name:"Delete Peer",click:()=>this.deletePeersModal(),visible:()=>!!this.getPeerUUID()};this.tableActions=[a,d,c,u]}ngOnInit(){this.columns=[{prop:"name",name:"Name",flexGrow:2},{prop:"mirror_mode",name:"Mode",flexGrow:2},{prop:"leader_id",name:"Leader",flexGrow:2},{prop:"image_local_count",name:"# Local",flexGrow:2},{prop:"image_remote_count",name:"# Remote",flexGrow:2},{prop:"health",name:"Health",cellTemplate:this.healthTmpl,flexGrow:1}],this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.data=t.content_data.pools,this.tableStatus=new U.E(t.status)})}ngOnDestroy(){this.subs.unsubscribe()}refresh(){this.rbdMirroringService.refresh()}editModeModal(){const t={poolName:this.selection.first().name};this.modalRef=this.modalService.show(Ri,t)}editPeersModal(t){const o={poolName:this.selection.first().name,mode:t};"edit"===t&&(o.peerUUID=this.getPeerUUID()),this.modalRef=this.modalService.show(Ni,o)}deletePeersModal(){const t=this.selection.first().name,o=this.getPeerUUID();this.modalRef=this.modalService.show(he.M,{itemDescription:"mirror peer",itemNames:[`${t} (${o})`],submitActionObservable:()=>new fi.y(i=>{this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/peer/delete",{pool_name:t}),call:this.rbdMirroringService.deletePeer(t,o)}).subscribe({error:s=>i.error(s),complete:()=>{this.rbdMirroringService.refresh(),i.complete()}})})})}getPeerUUID(){const t=this.selection.first(),o=this.data.find(i=>t&&t.name===i.name);if(o&&o.peer_uuids)return o.peer_uuids[0]}updateSelection(t){this.selection=t}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(K),e.Y36(re.Z),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-mirroring-pools"]],viewQuery:function(t,o){if(1&t&&e.Gf(Di,7),2&t){let i;e.iGM(i=e.CRH())&&(o.healthTmpl=i.first)}},decls:4,vars:7,consts:[["columnMode","flex","identifier","name","forceIdentifier","true","selectionType","single",3,"data","columns","autoReload","status","fetchData","updateSelection"],[1,"table-actions",3,"permission","selection","tableActions"],["healthTmpl",""],[3,"ngClass"]],template:function(t,o){1&t&&(e.TgZ(0,"cd-table",0),e.NdJ("fetchData",function(){return o.refresh()})("updateSelection",function(s){return o.updateSelection(s)}),e._UZ(1,"cd-table-actions",1),e.qZA(),e.YNc(2,vi,3,4,"ng-template",null,2,e.W1O)),2&t&&(e.Q6J("data",o.data)("columns",o.columns)("autoReload",-1)("status",o.tableStatus),e.xp6(1),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions))},directives:[ee.a,Re.K,l.mk],pipes:[We],styles:[""]}),n})();var mt=p(59376);const Fi=["stateTmpl"],$i=["syncTmpl"],Zi=["progressTmpl"];function Bi(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"cd-table",14),e.NdJ("fetchData",function(){return e.CHM(t),e.oxw().refresh()}),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("data",t.image_error.data)("columns",t.image_error.columns)("autoReload",-1)("status",t.tableStatus)}}function Gi(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"cd-table",14),e.NdJ("fetchData",function(){return e.CHM(t),e.oxw().refresh()}),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("data",t.image_syncing.data)("columns",t.image_syncing.columns)("autoReload",-1)("status",t.tableStatus)}}function yi(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"cd-table",14),e.NdJ("fetchData",function(){return e.CHM(t),e.oxw().refresh()}),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("data",t.image_ready.data)("columns",t.image_ready.columns)("autoReload",-1)("status",t.tableStatus)}}function xi(n,_){if(1&n&&(e.TgZ(0,"span",15),e.ALo(1,"mirrorHealthColor"),e._uU(2),e.qZA()),2&n){const o=_.value;e.Q6J("ngClass",e.lcZ(1,2,_.row.state_color)),e.xp6(2),e.Oqu(o)}}function wi(n,_){1&n&&(e.TgZ(0,"span",16),e.SDv(1,17),e.qZA())}function qi(n,_){1&n&&e._UZ(0,"ngb-progressbar",18),2&n&&e.Q6J("value",_.value)("showValue",!0)}let Hi=(()=>{class n{constructor(t){this.rbdMirroringService=t,this.image_error={data:[],columns:{}},this.image_syncing={data:[],columns:{}},this.image_ready={data:[],columns:{}},this.tableStatus=new U.E}ngOnInit(){this.image_error.columns=[{prop:"pool_name",name:"Pool",flexGrow:2},{prop:"name",name:"Image",flexGrow:2},{prop:"description",name:"Issue",flexGrow:4},{prop:"state",name:"State",cellTemplate:this.stateTmpl,flexGrow:1}],this.image_syncing.columns=[{prop:"pool_name",name:"Pool",flexGrow:2},{prop:"name",name:"Image",flexGrow:2},{prop:"progress",name:"Progress",cellTemplate:this.progressTmpl,flexGrow:2},{prop:"state",name:"State",cellTemplate:this.syncTmpl,flexGrow:1}],this.image_ready.columns=[{prop:"pool_name",name:"Pool",flexGrow:2},{prop:"name",name:"Image",flexGrow:2},{prop:"description",name:"Description",flexGrow:4},{prop:"state",name:"State",cellTemplate:this.stateTmpl,flexGrow:1}],this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.image_error.data=t.content_data.image_error,this.image_syncing.data=t.content_data.image_syncing,this.image_ready.data=t.content_data.image_ready,this.tableStatus=new U.E(t.status)})}ngOnDestroy(){this.subs.unsubscribe()}refresh(){this.rbdMirroringService.refresh()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(K))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-mirroring-images"]],viewQuery:function(t,o){if(1&t&&(e.Gf(Fi,7),e.Gf($i,7),e.Gf(Zi,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.stateTmpl=i.first),e.iGM(i=e.CRH())&&(o.syncTmpl=i.first),e.iGM(i=e.CRH())&&(o.progressTmpl=i.first)}},decls:21,vars:1,consts:function(){let _,t,o,i;return _="Issues",t="Syncing",o="Ready",i="Syncing",[["ngbNav","","cdStatefulTab","image-list",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","issues"],["ngbNavLink",""],_,["ngbNavContent",""],["ngbNavItem","syncing"],t,["ngbNavItem","ready"],o,[3,"ngbNavOutlet"],["stateTmpl",""],["syncTmpl",""],["progressTmpl",""],["columnMode","flex",3,"data","columns","autoReload","status","fetchData"],[3,"ngClass"],[1,"badge","badge-info"],i,["type","info",3,"value","showValue"]]},template:function(t,o){if(1&t&&(e.TgZ(0,"ul",0,1),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.YNc(5,Bi,1,4,"ng-template",5),e.qZA(),e.TgZ(6,"li",6),e.TgZ(7,"a",3),e.SDv(8,7),e.qZA(),e.YNc(9,Gi,1,4,"ng-template",5),e.qZA(),e.TgZ(10,"li",8),e.TgZ(11,"a",3),e.SDv(12,9),e.qZA(),e.YNc(13,yi,1,4,"ng-template",5),e.qZA(),e.qZA(),e._UZ(14,"div",10),e.YNc(15,xi,3,4,"ng-template",null,11,e.W1O),e.YNc(17,wi,2,0,"ng-template",null,12,e.W1O),e.YNc(19,qi,1,2,"ng-template",null,13,e.W1O)),2&t){const i=e.MAs(1);e.xp6(14),e.Q6J("ngbNavOutlet",i)}},directives:[I.Pz,mt.m,I.nv,I.Vx,I.uN,I.tO,ee.a,l.mk,I.Ly],pipes:[We],styles:[""]}),n})(),Ki=(()=>{class n{constructor(t,o,i){this.authStorageService=t,this.rbdMirroringService=o,this.modalService=i,this.selection=new Ee.r,this.peersExist=!0,this.subs=new Yn.w,this.permission=this.authStorageService.getPermissions().rbdMirroring;const s={permission:"update",icon:T.P.edit,click:()=>this.editSiteNameModal(),name:"Edit Site Name",canBePrimary:()=>!0,disable:()=>!1},a={permission:"update",icon:T.P.upload,click:()=>this.createBootstrapModal(),name:"Create Bootstrap Token",disable:()=>!1},d={permission:"update",icon:T.P.download,click:()=>this.importBootstrapModal(),name:"Import Bootstrap Token",disable:()=>this.peersExist};this.tableActions=[s,a,d]}ngOnInit(){this.subs.add(this.rbdMirroringService.startPolling()),this.subs.add(this.rbdMirroringService.subscribeSummary(t=>{this.status=t.content_data.status,this.siteName=t.site_name,this.peersExist=!!t.content_data.pools.find(o=>o.peer_uuids.length>0)}))}ngOnDestroy(){this.subs.unsubscribe()}editSiteNameModal(){this.modalRef=this.modalService.show(pi,{siteName:this.siteName})}createBootstrapModal(){this.modalRef=this.modalService.show(ii,{siteName:this.siteName})}importBootstrapModal(){this.modalRef=this.modalService.show(di,{siteName:this.siteName})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(K),e.Y36(re.Z))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-mirroring"]],decls:21,vars:4,consts:function(){let _,t,o,i;return _="Site Name:",t="Daemons",o="Pools",i="Images",[[1,"row"],[1,"col-md-12"],_,[1,"table-actions","float-right",3,"permission","selection","tableActions"],[1,"col-sm-6"],t,o,i]},template:function(t,o){1&t&&(e.TgZ(0,"div",0),e.TgZ(1,"div",1),e.TgZ(2,"span"),e.TgZ(3,"strong"),e.SDv(4,2),e.qZA(),e._uU(5),e.qZA(),e._UZ(6,"cd-table-actions",3),e.qZA(),e.qZA(),e.TgZ(7,"div",0),e.TgZ(8,"div",4),e.TgZ(9,"legend"),e.SDv(10,5),e.qZA(),e._UZ(11,"cd-mirroring-daemons"),e.qZA(),e.TgZ(12,"div",4),e.TgZ(13,"legend"),e.SDv(14,6),e.qZA(),e._UZ(15,"cd-mirroring-pools"),e.qZA(),e.qZA(),e.TgZ(16,"div",0),e.TgZ(17,"div",1),e.TgZ(18,"legend"),e.SDv(19,7),e.qZA(),e._UZ(20,"cd-mirroring-images"),e.qZA(),e.qZA()),2&t&&(e.xp6(5),e.hij(" ",o.siteName,""),e.xp6(1),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions))},directives:[Re.K,Ti,Li,Hi],styles:[""]}),n})();var Tt=p(80226),ki=p(28049),Xi=p(43190),Ke=p(80842),et=p(30633),Fe=p(47557),Qi=p(28211);class zi{}var Pe=(()=>{return(n=Pe||(Pe={}))[n.V1=1]="V1",n[n.V2=2]="V2",Pe;var n})();class Ji{constructor(){this.features=[]}}class Yi{constructor(){this.features=[]}}class Ui extends class{}{constructor(){super(...arguments),this.features=[]}}class ji{constructor(){this.features=[]}}var ke=(()=>{return(n=ke||(ke={})).editing="editing",n.cloning="cloning",n.copying="copying",ke;var n})(),Wi=p(17932),e_=p(54555),t_=p(18372);function o_(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"label",56),e.SDv(2,57),e.ALo(3,"titlecase"),e.qZA(),e.TgZ(4,"div",12),e._UZ(5,"input",58),e._UZ(6,"hr"),e.qZA(),e.qZA()),2&n){const t=e.oxw(2);e.xp6(3),e.pQV(e.lcZ(3,1,t.action)),e.QtT(2)}}function n_(n,_){1&n&&(e.TgZ(0,"span",59),e.ynx(1),e.SDv(2,60),e.BQk(),e.qZA())}function i_(n,_){1&n&&(e.TgZ(0,"span",59),e.ynx(1),e.SDv(2,61),e.BQk(),e.qZA())}function __(n,_){1&n&&e._UZ(0,"input",62)}function s_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,65),e.qZA()),2&n&&e.Q6J("ngValue",null)}function a_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,66),e.qZA()),2&n&&e.Q6J("ngValue",null)}function r_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,67),e.qZA()),2&n&&e.Q6J("ngValue",null)}function l_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.pool_name),e.xp6(1),e.Oqu(t.pool_name)}}function c_(n,_){if(1&n&&(e.TgZ(0,"select",63),e.YNc(1,s_,2,1,"option",64),e.YNc(2,a_,2,1,"option",64),e.YNc(3,r_,2,1,"option",64),e.YNc(4,l_,2,2,"option",44),e.qZA()),2&n){const t=e.oxw(2);e.xp6(1),e.Q6J("ngIf",null===t.pools),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&0===t.pools.length),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&t.pools.length>0),e.xp6(1),e.Q6J("ngForOf",t.pools)}}function d_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,69),e.qZA())}const p_=function(n,_){return[n,_]};function g_(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"div",20),e._UZ(2,"i",70),e.qZA(),e.qZA()),2&n){const t=e.oxw(2);e.xp6(2),e.Q6J("ngClass",e.WLB(1,p_,t.icons.spinner,t.icons.spin))}}function u_(n,_){1&n&&e._UZ(0,"input",74)}function m_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,76),e.qZA()),2&n&&e.Q6J("ngValue",null)}function T_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,77),e.qZA()),2&n&&e.Q6J("ngValue",null)}function f_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,78),e.qZA()),2&n&&e.Q6J("ngValue",null)}function C_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(t)}}function S_(n,_){if(1&n&&(e.TgZ(0,"select",75),e.YNc(1,m_,2,1,"option",64),e.YNc(2,T_,2,1,"option",64),e.YNc(3,f_,2,1,"option",64),e.YNc(4,C_,2,2,"option",44),e.qZA()),2&n){const t=e.oxw(3);e.xp6(1),e.Q6J("ngIf",null===t.pools),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&0===t.pools.length),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&t.pools.length>0),e.xp6(1),e.Q6J("ngForOf",t.namespaces)}}function E_(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"label",71),e._uU(2," Namespace "),e.qZA(),e.TgZ(3,"div",12),e.YNc(4,u_,1,0,"input",72),e.YNc(5,S_,5,4,"select",73),e.qZA(),e.qZA()),2&n){const t=e.oxw(2);e.xp6(4),e.Q6J("ngIf","editing"===t.mode||!t.poolPermission.read),e.xp6(1),e.Q6J("ngIf","editing"!==t.mode&&t.poolPermission.read)}}function R_(n,_){1&n&&(e.TgZ(0,"cd-helper"),e.TgZ(1,"span"),e.SDv(2,79),e.qZA(),e.qZA())}function M_(n,_){1&n&&e._UZ(0,"input",85)}function O_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,87),e.qZA()),2&n&&e.Q6J("ngValue",null)}function A_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,88),e.qZA()),2&n&&e.Q6J("ngValue",null)}function h_(n,_){1&n&&(e.TgZ(0,"option",48),e._uU(1,"-- Select a data pool -- "),e.qZA()),2&n&&e.Q6J("ngValue",null)}function P_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.pool_name),e.xp6(1),e.Oqu(t.pool_name)}}function I_(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"select",86),e.NdJ("change",function(i){return e.CHM(t),e.oxw(3).onDataPoolChange(i.target.value)}),e.YNc(1,O_,2,1,"option",64),e.YNc(2,A_,2,1,"option",64),e.YNc(3,h_,2,1,"option",64),e.YNc(4,P_,2,2,"option",44),e.qZA()}if(2&n){const t=e.oxw(3);e.xp6(1),e.Q6J("ngIf",null===t.dataPools),e.xp6(1),e.Q6J("ngIf",null!==t.dataPools&&0===t.dataPools.length),e.xp6(1),e.Q6J("ngIf",null!==t.dataPools&&t.dataPools.length>0),e.xp6(1),e.Q6J("ngForOf",t.dataPools)}}function b_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,89),e.qZA())}const Xe=function(n){return{required:n}};function N_(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"label",80),e.TgZ(2,"span",70),e.SDv(3,81),e.qZA(),e._UZ(4,"cd-helper",82),e.qZA(),e.TgZ(5,"div",12),e.YNc(6,M_,1,0,"input",83),e.YNc(7,I_,5,4,"select",84),e.YNc(8,b_,2,0,"span",14),e.qZA(),e.qZA()),2&n){e.oxw();const t=e.MAs(2),o=e.oxw();e.xp6(2),e.Q6J("ngClass",e.VKq(4,Xe,"editing"!==o.mode)),e.xp6(4),e.Q6J("ngIf","editing"===o.mode||!o.poolPermission.read),e.xp6(1),e.Q6J("ngIf","editing"!==o.mode&&o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("dataPool",t,"required"))}}function D_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,90),e.qZA())}function v_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,91),e.qZA())}function L_(n,_){if(1&n&&e._UZ(0,"cd-helper",95),2&n){const t=e.oxw().$implicit;e.s9C("html",t.helperHtml)}}function F_(n,_){if(1&n&&(e.TgZ(0,"div",21),e._UZ(1,"input",92),e.TgZ(2,"label",93),e._uU(3),e.qZA(),e.YNc(4,L_,1,1,"cd-helper",94),e.qZA()),2&n){const t=_.$implicit;e.xp6(1),e.s9C("id",t.key),e.s9C("name",t.key),e.s9C("formControlName",t.key),e.xp6(1),e.s9C("for",t.key),e.xp6(1),e.Oqu(t.desc),e.xp6(1),e.Q6J("ngIf",t.helperHtml)}}function $_(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"a",96),e.NdJ("click",function(){return e.CHM(t),e.oxw(2).advancedEnabled=!0,!1}),e.SDv(1,97),e.qZA()}}function Z_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(t)}}function B_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(t)}}function G_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,98),e.qZA())}function y_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,99),e.qZA())}function x_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,100),e.qZA())}function w_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,101),e.qZA())}function q_(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.YNc(9,o_,7,3,"div",8),e.TgZ(10,"div",9),e.TgZ(11,"label",10),e.SDv(12,11),e.qZA(),e.TgZ(13,"div",12),e._UZ(14,"input",13),e.YNc(15,n_,3,0,"span",14),e.YNc(16,i_,3,0,"span",14),e.qZA(),e.qZA(),e.TgZ(17,"div",15),e.NdJ("change",function(i){return e.CHM(t),e.oxw().onPoolChange(i.target.value)}),e.TgZ(18,"label",16),e.SDv(19,17),e.qZA(),e.TgZ(20,"div",12),e.YNc(21,__,1,0,"input",18),e.YNc(22,c_,5,4,"select",19),e.YNc(23,d_,2,0,"span",14),e.qZA(),e.qZA(),e.YNc(24,g_,3,4,"div",8),e.YNc(25,E_,6,2,"div",8),e.TgZ(26,"div",9),e.TgZ(27,"div",20),e.TgZ(28,"div",21),e.TgZ(29,"input",22),e.NdJ("change",function(){return e.CHM(t),e.oxw().onUseDataPoolChange()}),e.qZA(),e.TgZ(30,"label",23),e.SDv(31,24),e.qZA(),e.YNc(32,R_,3,0,"cd-helper",25),e.qZA(),e.qZA(),e.qZA(),e.YNc(33,N_,9,6,"div",8),e.TgZ(34,"div",9),e.TgZ(35,"label",26),e.SDv(36,27),e.qZA(),e.TgZ(37,"div",12),e._UZ(38,"input",28),e.YNc(39,D_,2,0,"span",14),e.YNc(40,v_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(41,"div",29),e.TgZ(42,"label",30),e.SDv(43,31),e.qZA(),e.TgZ(44,"div",12),e.YNc(45,F_,5,6,"div",32),e.qZA(),e.qZA(),e.TgZ(46,"div",33),e.TgZ(47,"div",34),e.YNc(48,$_,2,0,"a",35),e.qZA(),e.qZA(),e.TgZ(49,"div",36),e.TgZ(50,"legend",37),e.SDv(51,38),e.qZA(),e.TgZ(52,"div",39),e.TgZ(53,"h4",37),e.SDv(54,40),e.qZA(),e.TgZ(55,"div",9),e.TgZ(56,"label",41),e.SDv(57,42),e.qZA(),e.TgZ(58,"div",12),e.TgZ(59,"select",43),e.YNc(60,Z_,2,2,"option",44),e.qZA(),e.qZA(),e.qZA(),e.TgZ(61,"div",9),e.TgZ(62,"label",45),e.SDv(63,46),e.qZA(),e.TgZ(64,"div",12),e.TgZ(65,"select",47),e.TgZ(66,"option",48),e.SDv(67,49),e.qZA(),e.YNc(68,B_,2,2,"option",44),e.qZA(),e.YNc(69,G_,2,0,"span",14),e.YNc(70,y_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(71,"div",9),e.TgZ(72,"label",50),e.SDv(73,51),e.qZA(),e.TgZ(74,"div",12),e._UZ(75,"input",52),e.YNc(76,x_,2,0,"span",14),e.YNc(77,w_,2,0,"span",14),e.qZA(),e.qZA(),e.qZA(),e.TgZ(78,"cd-rbd-configuration-form",53),e.NdJ("changes",function(i){return e.CHM(t),e.oxw().getDirtyConfigurationValues=i}),e.qZA(),e.qZA(),e.qZA(),e.TgZ(79,"div",54),e.TgZ(80,"cd-form-button-panel",55),e.NdJ("submitActionEvent",function(){return e.CHM(t),e.oxw().submit()}),e.ALo(81,"titlecase"),e.ALo(82,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=e.MAs(2),o=e.oxw();e.xp6(1),e.Q6J("formGroup",o.rbdForm),e.xp6(6),e.pQV(e.lcZ(6,32,o.action))(e.lcZ(7,34,o.resource)),e.QtT(5),e.xp6(2),e.Q6J("ngIf",o.rbdForm.getValue("parent")),e.xp6(6),e.Q6J("ngIf",o.rbdForm.showError("name",t,"required")),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("name",t,"pattern")),e.xp6(2),e.Q6J("ngClass",e.VKq(40,Xe,"editing"!==o.mode)),e.xp6(3),e.Q6J("ngIf","editing"===o.mode||!o.poolPermission.read),e.xp6(1),e.Q6J("ngIf","editing"!==o.mode&&o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("pool",t,"required")),e.xp6(1),e.Q6J("ngIf","editing"!==o.mode&&o.rbdForm.getValue("pool")&&null===o.namespaces),e.xp6(1),e.Q6J("ngIf","editing"===o.mode&&o.rbdForm.getValue("namespace")||"editing"!==o.mode&&(o.namespaces&&o.namespaces.length>0||!o.poolPermission.read)),e.xp6(7),e.Q6J("ngIf",o.allDataPools.length<=1),e.xp6(1),e.Q6J("ngIf",o.rbdForm.getValue("useDataPool")),e.xp6(6),e.Q6J("ngIf",o.rbdForm.showError("size",t,"required")),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("size",t,"invalidSizeObject")),e.xp6(5),e.Q6J("ngForOf",o.featuresList),e.xp6(3),e.Q6J("ngIf",!o.advancedEnabled),e.xp6(1),e.Q6J("hidden",!o.advancedEnabled),e.xp6(11),e.Q6J("ngForOf",o.objectSizes),e.xp6(2),e.Q6J("ngClass",e.VKq(42,Xe,o.rbdForm.getValue("stripingCount"))),e.xp6(4),e.Q6J("ngValue",null),e.xp6(2),e.Q6J("ngForOf",o.objectSizes),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("stripingUnit",t,"required")),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("stripingUnit",t,"invalidStripingUnit")),e.xp6(2),e.Q6J("ngClass",e.VKq(44,Xe,o.rbdForm.getValue("stripingUnit"))),e.xp6(4),e.Q6J("ngIf",o.rbdForm.showError("stripingCount",t,"required")),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("stripingCount",t,"min")),e.xp6(1),e.Q6J("form",o.rbdForm)("initializeData",o.initializeConfigData),e.xp6(2),e.Q6J("form",t)("submitText",e.lcZ(81,36,o.action)+" "+e.lcZ(82,38,o.resource))}}let $e=(()=>{class n extends q.E{constructor(t,o,i,s,a,d,c,u,S){super(),this.authStorageService=t,this.route=o,this.poolService=i,this.rbdService=s,this.formatter=a,this.taskWrapper=d,this.dimlessBinaryPipe=c,this.actionLabels=u,this.router=S,this.namespaces=[],this.namespacesByPoolCache={},this.pools=null,this.allPools=null,this.dataPools=null,this.allDataPools=[],this.featuresList=[],this.initializeConfigData=new Tt.t(1),this.advancedEnabled=!1,this.rbdFormMode=ke,this.defaultObjectSize="4 MiB",this.objectSizes=["4 KiB","8 KiB","16 KiB","32 KiB","64 KiB","128 KiB","256 KiB","512 KiB","1 MiB","2 MiB","4 MiB","8 MiB","16 MiB","32 MiB"],this.rbdImage=new Tt.t(1),this.icons=T.P,this.routerUrl=this.router.url,this.poolPermission=this.authStorageService.getPermissions().pool,this.resource="RBD",this.features={"deep-flatten":{desc:"Deep flatten",requires:null,allowEnable:!1,allowDisable:!0},layering:{desc:"Layering",requires:null,allowEnable:!1,allowDisable:!1},"exclusive-lock":{desc:"Exclusive lock",requires:null,allowEnable:!0,allowDisable:!0},"object-map":{desc:"Object map (requires exclusive-lock)",requires:"exclusive-lock",allowEnable:!0,allowDisable:!0,initDisabled:!0},journaling:{desc:"Journaling (requires exclusive-lock)",requires:"exclusive-lock",allowEnable:!0,allowDisable:!0,initDisabled:!0},"fast-diff":{desc:"Fast diff (interlocked with object-map)",requires:"object-map",allowEnable:!0,allowDisable:!0,interlockedWith:"object-map",initDisabled:!0}},this.featuresList=this.objToArray(this.features),this.createForm()}objToArray(t){return C().map(t,(o,i)=>Object.assign(o,{key:i}))}createForm(){this.rbdForm=new M.d({parent:new r.NI(""),name:new r.NI("",{validators:[r.kI.required,r.kI.pattern(/^[^@/]+?$/)]}),pool:new r.NI(null,{validators:[r.kI.required]}),namespace:new r.NI(null),useDataPool:new r.NI(!1),dataPool:new r.NI(null),size:new r.NI(null,{updateOn:"blur"}),obj_size:new r.NI(this.defaultObjectSize),features:new M.d(this.featuresList.reduce((t,o)=>(t[o.key]=new r.NI({value:!1,disabled:!!o.initDisabled}),t),{})),stripingUnit:new r.NI(null),stripingCount:new r.NI(null,{updateOn:"blur"})},this.validateRbdForm(this.formatter))}disableForEdit(){this.rbdForm.get("parent").disable(),this.rbdForm.get("pool").disable(),this.rbdForm.get("namespace").disable(),this.rbdForm.get("useDataPool").disable(),this.rbdForm.get("dataPool").disable(),this.rbdForm.get("obj_size").disable(),this.rbdForm.get("stripingUnit").disable(),this.rbdForm.get("stripingCount").disable(),this.rbdImage.subscribe(t=>{t.image_format===Pe.V1&&(this.rbdForm.get("deep-flatten").disable(),this.rbdForm.get("layering").disable(),this.rbdForm.get("exclusive-lock").disable())})}disableForClone(){this.rbdForm.get("parent").disable(),this.rbdForm.get("size").disable()}disableForCopy(){this.rbdForm.get("parent").disable(),this.rbdForm.get("size").disable()}ngOnInit(){this.prepareFormForAction(),this.gatherNeededData().subscribe(this.handleExternalData.bind(this))}prepareFormForAction(){const t=this.routerUrl;t.startsWith("/block/rbd/edit")?(this.mode=this.rbdFormMode.editing,this.action=this.actionLabels.EDIT,this.disableForEdit()):t.startsWith("/block/rbd/clone")?(this.mode=this.rbdFormMode.cloning,this.disableForClone(),this.action=this.actionLabels.CLONE):t.startsWith("/block/rbd/copy")?(this.mode=this.rbdFormMode.copying,this.action=this.actionLabels.COPY,this.disableForCopy()):this.action=this.actionLabels.CREATE,C().each(this.features,o=>{this.rbdForm.get("features").get(o.key).valueChanges.subscribe(i=>this.featureFormUpdate(o.key,i))})}gatherNeededData(){const t={};return this.mode?this.route.params.subscribe(o=>{const i=Z.N.fromString(decodeURIComponent(o.image_spec));o.snap&&(this.snapName=decodeURIComponent(o.snap)),t.rbd=this.rbdService.get(i)}):t.defaultFeatures=this.rbdService.defaultFeatures(),this.mode!==this.rbdFormMode.editing&&this.poolPermission.read&&(t.pools=this.poolService.list(["pool_name","type","flags_names","application_metadata"])),(0,W.D)(t)}handleExternalData(t){if(this.handlePoolData(t.pools),t.defaultFeatures&&this.setFeatures(t.defaultFeatures),t.rbd){const o=t.rbd;this.setResponse(o,this.snapName),this.rbdImage.next(o)}this.loadingReady()}handlePoolData(t){if(!t)return;const o=[],i=[];for(const s of t)this.rbdService.isRBDPool(s)&&("replicated"===s.type?(o.push(s),i.push(s)):"erasure"===s.type&&-1!==s.flags_names.indexOf("ec_overwrites")&&i.push(s));if(this.pools=o,this.allPools=o,this.dataPools=i,this.allDataPools=i,1===this.pools.length){const s=this.pools[0].pool_name;this.rbdForm.get("pool").setValue(s),this.onPoolChange(s)}this.allDataPools.length<=1&&this.rbdForm.get("useDataPool").disable()}onPoolChange(t){const o=this.rbdForm.get("dataPool");o.value===t&&o.setValue(null),this.dataPools=this.allDataPools?this.allDataPools.filter(i=>i.pool_name!==t):[],this.namespaces=null,t in this.namespacesByPoolCache?this.namespaces=this.namespacesByPoolCache[t]:this.rbdService.listNamespaces(t).subscribe(i=>{i=i.map(s=>s.namespace),this.namespacesByPoolCache[t]=i,this.namespaces=i}),this.rbdForm.get("namespace").setValue(null)}onUseDataPoolChange(){this.rbdForm.getValue("useDataPool")||(this.rbdForm.get("dataPool").setValue(null),this.onDataPoolChange(null))}onDataPoolChange(t){const o=this.allPools.filter(i=>i.pool_name!==t);this.rbdForm.getValue("pool")===t&&this.rbdForm.get("pool").setValue(null),this.pools=o}validateRbdForm(t){return o=>{const i=o.get("useDataPool"),s=o.get("dataPool");let a=null;i.value&&null==s.value&&(a={required:!0}),s.setErrors(a);const d=o.get("size"),c=o.get("obj_size"),u=t.toBytes(null!=c.value?c.value:this.defaultObjectSize),S=o.get("stripingCount"),N=null!=S.value?S.value:1;let P=null;null===d.value?P={required:!0}:N*u>t.toBytes(d.value)&&(P={invalidSizeObject:!0}),d.setErrors(P);const $=o.get("stripingUnit");let G=null;null===$.value&&null!==S.value?G={required:!0}:null!==$.value&&t.toBytes($.value)>u&&(G={invalidStripingUnit:!0}),$.setErrors(G);let X=null;return null===S.value&&null!==$.value?X={required:!0}:N<1&&(X={min:!0}),S.setErrors(X),null}}deepBoxCheck(t,o){this.getDependentChildFeatures(t).forEach(s=>{const a=this.rbdForm.get(s.key);o?a.enable({emitEvent:!1}):(a.disable({emitEvent:!1}),a.setValue(!1,{emitEvent:!1}),this.deepBoxCheck(s.key,o));const d=this.rbdForm.get("features");this.mode===this.rbdFormMode.editing&&d.get(s.key).enabled&&(-1!==this.response.features_name.indexOf(s.key)&&!s.allowDisable||-1===this.response.features_name.indexOf(s.key)&&!s.allowEnable)&&d.get(s.key).disable()})}getDependentChildFeatures(t){return C().filter(this.features,o=>o.requires===t)||[]}interlockCheck(t,o){const i=this.featuresList.find(s=>s.key===t);if(this.response){const s=null!=i.interlockedWith,a=this.featuresList.find(c=>c.interlockedWith===i.key),d=!!this.response.features_name.find(c=>c===i.key);if(s){if(d!==!!this.response.features_name.find(u=>u===i.interlockedWith))return}else if(a&&!!this.response.features_name.find(u=>u===a.key)!==d)return}o?C().filter(this.features,s=>s.interlockedWith===t).forEach(s=>this.rbdForm.get(s.key).setValue(!0,{emitEvent:!1})):i.interlockedWith&&this.rbdForm.get("features").get(i.interlockedWith).setValue(!1)}featureFormUpdate(t,o){if(o){const i=this.features[t].requires;if(i&&!this.rbdForm.getValue(i))return void this.rbdForm.get(`features.${t}`).setValue(!1)}this.deepBoxCheck(t,o),this.interlockCheck(t,o)}setFeatures(t){const o=this.rbdForm.get("features");C().forIn(this.features,i=>{-1!==t.indexOf(i.key)&&o.get(i.key).setValue(!0),this.featureFormUpdate(i.key,o.get(i.key).value)})}setResponse(t,o){this.response=t;const i=new Z.N(t.pool_name,t.namespace,t.name).toString();if(this.mode===this.rbdFormMode.cloning)this.rbdForm.get("parent").setValue(`${i}@${o}`);else if(this.mode===this.rbdFormMode.copying)o?this.rbdForm.get("parent").setValue(`${i}@${o}`):this.rbdForm.get("parent").setValue(`${i}`);else if(t.parent){const s=t.parent;this.rbdForm.get("parent").setValue(`${s.pool_name}/${s.image_name}@${s.snap_name}`)}this.mode===this.rbdFormMode.editing&&this.rbdForm.get("name").setValue(t.name),this.rbdForm.get("pool").setValue(t.pool_name),this.onPoolChange(t.pool_name),this.rbdForm.get("namespace").setValue(t.namespace),t.data_pool&&(this.rbdForm.get("useDataPool").setValue(!0),this.rbdForm.get("dataPool").setValue(t.data_pool)),this.rbdForm.get("size").setValue(this.dimlessBinaryPipe.transform(t.size)),this.rbdForm.get("obj_size").setValue(this.dimlessBinaryPipe.transform(t.obj_size)),this.setFeatures(t.features_name),this.rbdForm.get("stripingUnit").setValue(this.dimlessBinaryPipe.transform(t.stripe_unit)),this.rbdForm.get("stripingCount").setValue(t.stripe_count),this.initializeConfigData.next({initialData:this.response.configuration,sourceType:et.h.image})}createRequest(){const t=new Ui;return t.pool_name=this.rbdForm.getValue("pool"),t.namespace=this.rbdForm.getValue("namespace"),t.name=this.rbdForm.getValue("name"),t.size=this.formatter.toBytes(this.rbdForm.getValue("size")),this.addObjectSizeAndStripingToRequest(t),t.configuration=this.getDirtyConfigurationValues(),t}addObjectSizeAndStripingToRequest(t){t.obj_size=this.formatter.toBytes(this.rbdForm.getValue("obj_size")),C().forIn(this.features,o=>{this.rbdForm.getValue(o.key)&&t.features.push(o.key)}),t.stripe_unit=this.formatter.toBytes(this.rbdForm.getValue("stripingUnit")),t.stripe_count=this.rbdForm.getValue("stripingCount"),t.data_pool=this.rbdForm.getValue("dataPool")}createAction(){const t=this.createRequest();return this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/create",{pool_name:t.pool_name,namespace:t.namespace,image_name:t.name}),call:this.rbdService.create(t)})}editRequest(){const t=new ji;return t.name=this.rbdForm.getValue("name"),t.size=this.formatter.toBytes(this.rbdForm.getValue("size")),C().forIn(this.features,o=>{this.rbdForm.getValue(o.key)&&t.features.push(o.key)}),t.configuration=this.getDirtyConfigurationValues(),t}cloneRequest(){const t=new Ji;return t.child_pool_name=this.rbdForm.getValue("pool"),t.child_namespace=this.rbdForm.getValue("namespace"),t.child_image_name=this.rbdForm.getValue("name"),this.addObjectSizeAndStripingToRequest(t),t.configuration=this.getDirtyConfigurationValues(!0,et.h.image),t}editAction(){const t=new Z.N(this.response.pool_name,this.response.namespace,this.response.name);return this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/edit",{image_spec:t.toString()}),call:this.rbdService.update(t,this.editRequest())})}cloneAction(){const t=this.cloneRequest(),o=new Z.N(this.response.pool_name,this.response.namespace,this.response.name);return this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/clone",{parent_image_spec:o.toString(),parent_snap_name:this.snapName,child_pool_name:t.child_pool_name,child_namespace:t.child_namespace,child_image_name:t.child_image_name}),call:this.rbdService.cloneSnapshot(o,this.snapName,t)})}copyRequest(){const t=new Yi;return this.snapName&&(t.snapshot_name=this.snapName),t.dest_pool_name=this.rbdForm.getValue("pool"),t.dest_namespace=this.rbdForm.getValue("namespace"),t.dest_image_name=this.rbdForm.getValue("name"),this.addObjectSizeAndStripingToRequest(t),t.configuration=this.getDirtyConfigurationValues(!0,et.h.image),t}copyAction(){const t=this.copyRequest(),o=new Z.N(this.response.pool_name,this.response.namespace,this.response.name);return this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/copy",{src_image_spec:o.toString(),dest_pool_name:t.dest_pool_name,dest_namespace:t.dest_namespace,dest_image_name:t.dest_image_name}),call:this.rbdService.copy(o,t)})}submit(){this.mode||this.rbdImage.next("create"),this.rbdImage.pipe((0,ki.P)(),(0,Xi.w)(()=>this.mode===this.rbdFormMode.editing?this.editAction():this.mode===this.rbdFormMode.cloning?this.cloneAction():this.mode===this.rbdFormMode.copying?this.copyAction():this.createAction())).subscribe(()=>{},()=>this.rbdForm.setErrors({cdSubmitButton:!0}),()=>this.router.navigate(["/block/rbd"]))}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(m.gz),e.Y36(Ke.q),e.Y36(x),e.Y36(Qi.H),e.Y36(Q.P),e.Y36(Fe.$),e.Y36(D.p4),e.Y36(m.F0))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-form"]],features:[e.qOj],decls:1,vars:1,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$,G,X,J,te,A,w,de,pe,ge,ue,me,Te,fe,Ce,Se,y,Ze,Be,Ge,ye,xe,we,qe;return _="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",t="Name",o="Pool",i="Use a dedicated data pool",s="Size",a="e.g., 10GiB",d="Features",c="Advanced",u="Striping",S="Object size",N="Stripe unit",P="-- Select stripe unit --",$="Stripe count",G="" + "\ufffd0\ufffd" + " from",X="This field is required.",J="'/' and '@' are not allowed.",te="Loading...",A="-- No rbd pools available --",w="-- Select a pool --",de="This field is required.",pe="Loading...",ge="-- No namespaces available --",ue="-- Select a namespace --",me="You need more than one pool with the rbd application label use to use a dedicated data pool.",Te="Data pool",fe="Dedicated pool that stores the object-data of the RBD.",Ce="Loading...",Se="-- No data pools available --",y="This field is required.",Ze="This field is required.",Be="You have to increase the size.",Ge="Advanced...",ye="This field is required because stripe count is defined!",xe="Stripe unit is greater than object size.",we="This field is required because stripe unit is defined!",qe="Stripe count must be greater than 0.",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["name","rbdForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"card"],[1,"card-header"],_,[1,"card-body"],["class","form-group row",4,"ngIf"],[1,"form-group","row"],["for","name",1,"cd-col-form-label","required"],t,[1,"cd-col-form-input"],["type","text","placeholder","Name...","id","name","name","name","formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],[1,"form-group","row",3,"change"],["for","pool",1,"cd-col-form-label",3,"ngClass"],o,["class","form-control","type","text","placeholder","Pool name...","id","pool","name","pool","formControlName","pool",4,"ngIf"],["id","pool","name","pool","class","form-control custom-select","formControlName","pool",4,"ngIf"],[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["type","checkbox","id","useDataPool","name","useDataPool","formControlName","useDataPool",1,"custom-control-input",3,"change"],["for","useDataPool",1,"custom-control-label"],i,[4,"ngIf"],["for","size",1,"cd-col-form-label","required"],s,["id","size","name","size","type","text","formControlName","size","placeholder",a,"defaultUnit","GiB","cdDimlessBinary","",1,"form-control"],["formGroupName","features",1,"form-group","row"],["for","features",1,"cd-col-form-label"],d,["class","custom-control custom-checkbox",4,"ngFor","ngForOf"],[1,"row"],[1,"col-sm-12"],["class","float-right margin-right-md","href","",3,"click",4,"ngIf"],[3,"hidden"],[1,"cd-header"],c,[1,"col-md-12"],u,["for","size",1,"cd-col-form-label"],S,["id","obj_size","name","obj_size","formControlName","obj_size",1,"form-control","custom-select"],[3,"value",4,"ngFor","ngForOf"],["for","stripingUnit",1,"cd-col-form-label",3,"ngClass"],N,["id","stripingUnit","name","stripingUnit","formControlName","stripingUnit",1,"form-control","custom-select"],[3,"ngValue"],P,["for","stripingCount",1,"cd-col-form-label",3,"ngClass"],$,["id","stripingCount","name","stripingCount","formControlName","stripingCount","type","number",1,"form-control"],[3,"form","initializeData","changes"],[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],["for","name",1,"cd-col-form-label"],G,["type","text","id","parent","name","parent","formControlName","parent",1,"form-control"],[1,"invalid-feedback"],X,J,["type","text","placeholder","Pool name...","id","pool","name","pool","formControlName","pool",1,"form-control"],["id","pool","name","pool","formControlName","pool",1,"form-control","custom-select"],[3,"ngValue",4,"ngIf"],te,A,w,[3,"value"],de,[3,"ngClass"],["for","pool",1,"cd-col-form-label"],["class","form-control","type","text","placeholder","Namespace...","id","namespace","name","namespace","formControlName","namespace",4,"ngIf"],["id","namespace","name","namespace","class","form-control custom-select","formControlName","namespace",4,"ngIf"],["type","text","placeholder","Namespace...","id","namespace","name","namespace","formControlName","namespace",1,"form-control"],["id","namespace","name","namespace","formControlName","namespace",1,"form-control","custom-select"],pe,ge,ue,me,["for","dataPool",1,"cd-col-form-label"],Te,["html",fe],["class","form-control","type","text","placeholder","Data pool name...","id","dataPool","name","dataPool","formControlName","dataPool",4,"ngIf"],["id","dataPool","name","dataPool","class","form-control custom-select","formControlName","dataPool",3,"change",4,"ngIf"],["type","text","placeholder","Data pool name...","id","dataPool","name","dataPool","formControlName","dataPool",1,"form-control"],["id","dataPool","name","dataPool","formControlName","dataPool",1,"form-control","custom-select",3,"change"],Ce,Se,y,Ze,Be,["type","checkbox",1,"custom-control-input",3,"id","name","formControlName"],[1,"custom-control-label",3,"for"],[3,"html",4,"ngIf"],[3,"html"],["href","",1,"float-right","margin-right-md",3,"click"],Ge,ye,xe,we,qe]},template:function(t,o){1&t&&e.YNc(0,q_,83,46,"div",0),2&t&&e.Q6J("cdFormLoading",o.loading)},directives:[st.y,r._Y,r.JL,r.sg,v.V,l.O5,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.mk,r.Wl,Wi.Q,r.x0,l.sg,r.EJ,r.YN,r.Kr,r.wV,e_.d,O.p,t_.S],pipes:[l.rS,Ye.m],styles:[""]}),n})();var ft=p(36169),ce=p(91801),tt=p(51847),H_=p(16738),Me=p.n(H_),ot=p(62862),K_=p(52266);function k_(n,_){1&n&&(e.TgZ(0,"div",18),e.TgZ(1,"span"),e.SDv(2,19),e.qZA(),e.qZA())}function X_(n,_){1&n&&(e.TgZ(0,"span",20),e.SDv(1,21),e.qZA())}function Q_(n,_){1&n&&(e.TgZ(0,"span",20),e.SDv(1,22),e.qZA())}function z_(n,_){if(1&n&&e._UZ(0,"cd-date-time-picker",23),2&n){const t=e.oxw();e.Q6J("control",t.moveForm.get("expiresAt"))}}let J_=(()=>{class n{constructor(t,o,i,s,a){this.rbdService=t,this.activeModal=o,this.actionLabels=i,this.fb=s,this.taskWrapper=a,this.createForm()}createForm(){this.moveForm=this.fb.group({expiresAt:["",[B.h.custom("format",t=>!(""===t||Me()(t,"YYYY-MM-DD HH:mm:ss").isValid())),B.h.custom("expired",t=>Me()().isAfter(t))]]})}ngOnInit(){this.imageSpec=new Z.N(this.poolName,this.namespace,this.imageName),this.imageSpecStr=this.imageSpec.toString(),this.pattern=`${this.poolName}/${this.imageName}`}moveImage(){let t=0;const o=this.moveForm.getValue("expiresAt");o&&(t=Me()(o,"YYYY-MM-DD HH:mm:ss").diff(Me()(),"seconds",!0)),t<0&&(t=0),this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/trash/move",{image_spec:this.imageSpecStr}),call:this.rbdService.moveTrash(this.imageSpec,t)}).subscribe({complete:()=>{this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(x),e.Y36(I.Kz),e.Y36(D.p4),e.Y36(ot.O),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-trash-move-modal"]],decls:23,vars:9,consts:function(){let _,t,o,i,s,a,d;return _="Move an image to trash",t="To move " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "" + "\ufffd0\ufffd" + "" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + " to trash, click " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "Move" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + ". Optionally, you can pick an expiration date.",t=e.Zx4(t),o="Protection expires at",i="NOT PROTECTED",s="This image contains snapshot(s), which will prevent it from being removed after moved to trash.",a="Wrong date format. Please use \"YYYY-MM-DD HH:mm:ss\".",d="Protection has already expired. Please pick a future date or leave it empty.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","moveForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],["class","alert alert-warning","role","alert",4,"ngIf"],t,[1,"form-group"],["for","expiresAt",1,"col-form-label"],o,["type","text","placeholder",i,"formControlName","expiresAt","triggers","manual",1,"form-control",3,"ngbPopover","click","keypress"],["p","ngbPopover"],["class","invalid-feedback",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],["popContent",""],["role","alert",1,"alert","alert-warning"],s,[1,"invalid-feedback"],a,d,[3,"control"]]},template:function(t,o){if(1&t){const i=e.EpF();e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.YNc(7,k_,3,0,"div",7),e.TgZ(8,"p"),e.tHW(9,8),e._UZ(10,"kbd"),e._UZ(11,"kbd"),e.N_p(),e.qZA(),e.TgZ(12,"div",9),e.TgZ(13,"label",10),e.SDv(14,11),e.qZA(),e.TgZ(15,"input",12,13),e.NdJ("click",function(){return e.CHM(i),e.MAs(16).open()})("keypress",function(){return e.CHM(i),e.MAs(16).close()}),e.qZA(),e.YNc(17,X_,2,0,"span",14),e.YNc(18,Q_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(19,"div",15),e.TgZ(20,"cd-form-button-panel",16),e.NdJ("submitActionEvent",function(){return o.moveImage()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA(),e.YNc(21,z_,1,1,"ng-template",null,17,e.W1O)}if(2&t){const i=e.MAs(5),s=e.MAs(22);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.moveForm),e.xp6(3),e.Q6J("ngIf",o.hasSnapshots),e.xp6(4),e.pQV(o.imageSpecStr),e.QtT(9),e.xp6(4),e.Q6J("ngbPopover",s),e.xp6(2),e.Q6J("ngIf",o.moveForm.showError("expiresAt",i,"format")),e.xp6(1),e.Q6J("ngIf",o.moveForm.showError("expiresAt",i,"expired")),e.xp6(2),e.Q6J("form",o.moveForm)("submitText",o.actionLabels.MOVE)}},directives:[R.z,r._Y,r.JL,r.sg,v.V,l.O5,g.P,f.o,r.Fj,h.b,r.JJ,r.u,I.o8,O.p,K_.J],styles:[""]}),n})();function Y_(n,_){1&n&&(e.TgZ(0,"li",10),e.TgZ(1,"a",3),e.SDv(2,11),e.qZA(),e.qZA())}let Qe=(()=>{class n{constructor(t,o){this.authStorageService=t,this.router=o,this.grafanaPermission=this.authStorageService.getPermissions().grafana}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(m.F0))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-tabs"]],decls:12,vars:2,consts:function(){let _,t,o,i;return _="Images",t="Namespaces",o="Trash",i="Overall Performance",[["ngbNav","",1,"nav-tabs",3,"activeId","navChange"],["nav","ngbNav"],["ngbNavItem","/block/rbd"],["ngbNavLink",""],_,["ngbNavItem","/block/rbd/namespaces"],t,["ngbNavItem","/block/rbd/trash"],o,["ngbNavItem","/block/rbd/performance",4,"ngIf"],["ngbNavItem","/block/rbd/performance"],i]},template:function(t,o){1&t&&(e.TgZ(0,"ul",0,1),e.NdJ("navChange",function(s){return o.router.navigate([s.nextId])}),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.qZA(),e.TgZ(5,"li",5),e.TgZ(6,"a",3),e.SDv(7,6),e.qZA(),e.qZA(),e.TgZ(8,"li",7),e.TgZ(9,"a",3),e.SDv(10,8),e.qZA(),e.qZA(),e.YNc(11,Y_,3,0,"li",9),e.qZA()),2&t&&(e.Q6J("activeId",o.router.url),e.xp6(11),e.Q6J("ngIf",o.grafanaPermission.read))},directives:[I.Pz,I.nv,I.Vx,l.O5],styles:[""]}),n})();var V_=p(25917),Ct=p(51295),nt=p(60737),U_=p(74255),St=p(71099),Et=p(79765);function j_(n,_){1&n&&(e.TgZ(0,"span",15),e.SDv(1,16),e.qZA())}let W_=(()=>{class n{constructor(t,o,i,s,a){this.activeModal=t,this.rbdService=o,this.taskManagerService=i,this.notificationService=s,this.actionLabels=a,this.editing=!1,this.onSubmit=new Et.xQ,this.action=this.actionLabels.CREATE,this.resource="RBD Snapshot",this.createForm()}createForm(){this.snapshotForm=new M.d({snapshotName:new r.NI("",{validators:[r.kI.required]})})}setSnapName(t){this.snapName=t,this.snapshotForm.get("snapshotName").setValue(t)}setEditing(t=!0){this.editing=t,this.action=this.editing?this.actionLabels.RENAME:this.actionLabels.CREATE}editAction(){const t=this.snapshotForm.getValue("snapshotName"),o=new Z.N(this.poolName,this.namespace,this.imageName),i=new F.R;i.name="rbd/snap/edit",i.metadata={image_spec:o.toString(),snapshot_name:t},this.rbdService.renameSnapshot(o,this.snapName,t).toPromise().then(()=>{this.taskManagerService.subscribe(i.name,i.metadata,s=>{this.notificationService.notifyTask(s)}),this.activeModal.close(),this.onSubmit.next(this.snapName)}).catch(()=>{this.snapshotForm.setErrors({cdSubmitButton:!0})})}createAction(){const t=this.snapshotForm.getValue("snapshotName"),o=new Z.N(this.poolName,this.namespace,this.imageName),i=new F.R;i.name="rbd/snap/create",i.metadata={image_spec:o.toString(),snapshot_name:t},this.rbdService.createSnapshot(o,t).toPromise().then(()=>{this.taskManagerService.subscribe(i.name,i.metadata,s=>{this.notificationService.notifyTask(s)}),this.activeModal.close(),this.onSubmit.next(t)}).catch(()=>{this.snapshotForm.setErrors({cdSubmitButton:!0})})}submit(){this.editing?this.editAction():this.createAction()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(x),e.Y36(St.k),e.Y36(Le.g),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-snapshot-form-modal"]],decls:19,vars:15,consts:function(){let _,t,o;return _="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",t="Name",o="This field is required.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","snapshotForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","snapshotName",1,"cd-col-form-label","required"],t,[1,"cd-col-form-input"],["type","text","placeholder","Snapshot name...","id","snapshotName","name","snapshotName","formControlName","snapshotName","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],o]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e._UZ(13,"input",11),e.YNc(14,j_,2,0,"span",12),e.qZA(),e.qZA(),e.qZA(),e.TgZ(15,"div",13),e.TgZ(16,"cd-form-button-panel",14),e.NdJ("submitActionEvent",function(){return o.submit()}),e.ALo(17,"titlecase"),e.ALo(18,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(7);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.pQV(e.lcZ(3,7,o.action))(e.lcZ(4,9,o.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",o.snapshotForm),e.xp6(8),e.Q6J("ngIf",o.snapshotForm.showError("snapshotName",i,"required")),e.xp6(2),e.Q6J("form",o.snapshotForm)("submitText",e.lcZ(17,11,o.action)+" "+e.lcZ(18,13,o.resource))}},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,O.p],pipes:[l.rS,Ye.m],styles:[""]}),n})();class es{constructor(_,t,o){this.featuresName=t,this.cloneFormatVersion=1,o.cloneFormatVersion().subscribe(i=>{this.cloneFormatVersion=i}),this.create={permission:"create",icon:T.P.add,name:_.CREATE},this.rename={permission:"update",icon:T.P.edit,name:_.RENAME},this.protect={permission:"update",icon:T.P.lock,visible:i=>i.hasSingleSelection&&!i.first().is_protected,name:_.PROTECT},this.unprotect={permission:"update",icon:T.P.unlock,visible:i=>i.hasSingleSelection&&i.first().is_protected,name:_.UNPROTECT},this.clone={permission:"create",canBePrimary:i=>i.hasSingleSelection,disable:i=>this.getCloneDisableDesc(i,this.featuresName),icon:T.P.clone,name:_.CLONE},this.copy={permission:"create",canBePrimary:i=>i.hasSingleSelection,disable:i=>!i.hasSingleSelection||i.first().cdExecuting,icon:T.P.copy,name:_.COPY},this.rollback={permission:"update",icon:T.P.undo,name:_.ROLLBACK},this.deleteSnap={permission:"delete",icon:T.P.destroy,disable:i=>{const s=i.first();return!i.hasSingleSelection||s.cdExecuting||s.is_protected},name:_.DELETE},this.ordering=[this.create,this.rename,this.protect,this.unprotect,this.clone,this.copy,this.rollback,this.deleteSnap]}getCloneDisableDesc(_,t){return!(_.hasSingleSelection&&!_.first().cdExecuting)||((null==t?void 0:t.includes("layering"))?1===this.cloneFormatVersion&&!_.first().is_protected&&"Snapshot must be protected in order to clone.":"Parent image must support Layering")}}class ts{}var ze=p(96102);const os=["nameTpl"],ns=["rollbackTpl"];function is(n,_){if(1&n&&(e.ynx(0),e.SDv(1,3),e.BQk(),e.TgZ(2,"strong"),e._uU(3),e.qZA(),e._uU(4,".\n")),2&n){const t=_.$implicit;e.xp6(3),e.hij(" ",t.snapName,"")}}let _s=(()=>{class n{constructor(t,o,i,s,a,d,c,u,S,N,P){this.authStorageService=t,this.modalService=o,this.dimlessBinaryPipe=i,this.cdDatePipe=s,this.rbdService=a,this.taskManagerService=d,this.notificationService=c,this.summaryService=u,this.taskListService=S,this.actionLabels=N,this.cdr=P,this.snapshots=[],this.selection=new Ee.r,this.builders={"rbd/snap/create":$=>{const G=new ts;return G.name=$.snapshot_name,G}},this.permission=this.authStorageService.getPermissions().rbdImage}ngOnInit(){this.columns=[{name:"Name",prop:"name",cellTransformation:ve.e.executing,flexGrow:2},{name:"Size",prop:"size",flexGrow:1,cellClass:"text-right",pipe:this.dimlessBinaryPipe},{name:"Provisioned",prop:"disk_usage",flexGrow:1,cellClass:"text-right",pipe:this.dimlessBinaryPipe},{name:"State",prop:"is_protected",flexGrow:1,cellTransformation:ve.e.badge,customTemplateConfig:{map:{true:{value:"PROTECTED",class:"badge-success"},false:{value:"UNPROTECTED",class:"badge-info"}}}},{name:"Created",prop:"timestamp",flexGrow:1,pipe:this.cdDatePipe}],this.imageSpec=new Z.N(this.poolName,this.namespace,this.rbdName),this.rbdTableActions=new es(this.actionLabels,this.featuresName,this.rbdService),this.rbdTableActions.create.click=()=>this.openCreateSnapshotModal(),this.rbdTableActions.rename.click=()=>this.openEditSnapshotModal(),this.rbdTableActions.protect.click=()=>this.toggleProtection(),this.rbdTableActions.unprotect.click=()=>this.toggleProtection();const t=()=>this.selection.first()&&`${this.imageSpec.toStringEncoded()}/${encodeURIComponent(this.selection.first().name)}`;this.rbdTableActions.clone.routerLink=()=>`/block/rbd/clone/${t()}`,this.rbdTableActions.copy.routerLink=()=>`/block/rbd/copy/${t()}`,this.rbdTableActions.rollback.click=()=>this.rollbackModal(),this.rbdTableActions.deleteSnap.click=()=>this.deleteSnapshotModal(),this.tableActions=this.rbdTableActions.ordering,this.taskListService.init(()=>(0,V_.of)(this.snapshots),null,s=>{Ct.T.updateChanged(this,{data:s})&&(this.cdr.detectChanges(),this.data=[...this.data])},()=>{Ct.T.updateChanged(this,{data:this.snapshots})&&(this.cdr.detectChanges(),this.data=[...this.data])},s=>["rbd/snap/create","rbd/snap/delete","rbd/snap/edit","rbd/snap/rollback"].includes(s.name)&&this.imageSpec.toString()===s.metadata.image_spec,(s,a)=>s.name===a.metadata.snapshot_name,this.builders)}ngOnChanges(){this.columns&&(this.imageSpec=new Z.N(this.poolName,this.namespace,this.rbdName),this.rbdTableActions&&(this.rbdTableActions.featuresName=this.featuresName),this.taskListService.fetch())}openSnapshotModal(t,o=null){this.modalRef=this.modalService.show(W_),this.modalRef.componentInstance.poolName=this.poolName,this.modalRef.componentInstance.imageName=this.rbdName,this.modalRef.componentInstance.namespace=this.namespace,o?this.modalRef.componentInstance.setEditing():o=`${this.rbdName}_${Me()().toISOString(!0)}`,this.modalRef.componentInstance.setSnapName(o),this.modalRef.componentInstance.onSubmit.subscribe(i=>{const s=new nt.o;s.name=t,s.metadata={image_spec:this.imageSpec.toString(),snapshot_name:i},this.summaryService.addRunningTask(s)})}openCreateSnapshotModal(){this.openSnapshotModal("rbd/snap/create")}openEditSnapshotModal(){this.openSnapshotModal("rbd/snap/edit",this.selection.first().name)}toggleProtection(){const t=this.selection.first().name,o=this.selection.first().is_protected,i=new F.R;i.name="rbd/snap/edit";const s=new Z.N(this.poolName,this.namespace,this.rbdName);i.metadata={image_spec:s.toString(),snapshot_name:t},this.rbdService.protectSnapshot(s,t,!o).toPromise().then(()=>{const a=new nt.o;a.name=i.name,a.metadata=i.metadata,this.summaryService.addRunningTask(a),this.taskManagerService.subscribe(i.name,i.metadata,d=>{this.notificationService.notifyTask(d)})})}_asyncTask(t,o,i){const s=new F.R;s.name=o,s.metadata={image_spec:new Z.N(this.poolName,this.namespace,this.rbdName).toString(),snapshot_name:i};const a=new Z.N(this.poolName,this.namespace,this.rbdName);this.rbdService[t](a,i).toPromise().then(()=>{const d=new nt.o;d.name=s.name,d.metadata=s.metadata,this.summaryService.addRunningTask(d),this.modalRef.close(),this.taskManagerService.subscribe(d.name,d.metadata,c=>{this.notificationService.notifyTask(c)})}).catch(()=>{this.modalRef.componentInstance.stopLoadingSpinner()})}rollbackModal(){const t=this.selection.selected[0].name,o=new Z.N(this.poolName,this.namespace,this.rbdName).toString(),i={titleText:"RBD snapshot rollback",buttonText:"Rollback",bodyTpl:this.rollbackTpl,bodyData:{snapName:`${o}@${t}`},onSubmit:()=>{this._asyncTask("rollbackSnapshot","rbd/snap/rollback",t)}};this.modalRef=this.modalService.show(ft.Y,i)}deleteSnapshotModal(){const t=this.selection.selected[0].name;this.modalRef=this.modalService.show(he.M,{itemDescription:"RBD snapshot",itemNames:[t],submitAction:()=>this._asyncTask("deleteSnapshot","rbd/snap/delete",t)})}updateSelection(t){this.selection=t}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(re.Z),e.Y36(Fe.$),e.Y36(ze.N),e.Y36(x),e.Y36(St.k),e.Y36(Le.g),e.Y36(U_.J),e.Y36(se.j),e.Y36(D.p4),e.Y36(e.sBO))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-snapshot-list"]],viewQuery:function(t,o){if(1&t&&(e.Gf(os,5),e.Gf(ns,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.nameTpl=i.first),e.iGM(i=e.CRH())&&(o.rollbackTpl=i.first)}},inputs:{snapshots:"snapshots",featuresName:"featuresName",poolName:"poolName",namespace:"namespace",rbdName:"rbdName"},features:[e._Bn([se.j]),e.TTD],decls:4,vars:5,consts:function(){let _;return _="You are about to rollback",[["columnMode","flex","selectionType","single",3,"data","columns","updateSelection"],[1,"table-actions",3,"permission","selection","tableActions"],["rollbackTpl",""],_]},template:function(t,o){1&t&&(e.TgZ(0,"cd-table",0),e.NdJ("updateSelection",function(s){return o.updateSelection(s)}),e._UZ(1,"cd-table-actions",1),e.qZA(),e.YNc(2,is,5,1,"ng-template",null,2,e.W1O)),2&t&&(e.Q6J("data",o.data)("columns",o.columns),e.xp6(1),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions))},directives:[ee.a,Re.K],styles:[""],changeDetection:0}),n})();var ss=p(71752),Rt=p(76317),as=p(41039);const rs=["poolConfigurationSourceTpl"];function ls(n,_){1&n&&(e.ynx(0),e.tHW(1,3),e._UZ(2,"strong"),e.N_p(),e.BQk())}function cs(n,_){if(1&n&&(e.TgZ(0,"span"),e.TgZ(1,"span",38),e._uU(2),e.qZA(),e.qZA()),2&n){const t=_.$implicit;e.xp6(2),e.Oqu(t)}}function ds(n,_){if(1&n&&(e.TgZ(0,"span"),e.TgZ(1,"span",39),e.SDv(2,40),e.qZA(),e.qZA()),2&n){e.oxw(3);const t=e.MAs(1);e.xp6(1),e.Q6J("ngbTooltip",t)}}function ps(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.ALo(2,"dimlessBinary"),e.qZA()),2&n){const t=e.oxw(3);e.xp6(1),e.hij(" ",e.lcZ(2,1,t.selection.disk_usage)," ")}}function gs(n,_){if(1&n&&(e.TgZ(0,"span"),e.TgZ(1,"span",39),e.SDv(2,41),e.qZA(),e.qZA()),2&n){e.oxw(3);const t=e.MAs(1);e.xp6(1),e.Q6J("ngbTooltip",t)}}function us(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.ALo(2,"dimlessBinary"),e.qZA()),2&n){const t=e.oxw(3);e.xp6(1),e.hij(" ",e.lcZ(2,1,t.selection.total_disk_usage)," ")}}function ms(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.qZA()),2&n){const t=e.oxw(4);e.xp6(1),e.hij("/",t.selection.parent.pool_namespace,"")}}function Ts(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.YNc(2,ms,2,1,"span",1),e._uU(3),e.qZA()),2&n){const t=e.oxw(3);e.xp6(1),e.Oqu(t.selection.parent.pool_name),e.xp6(1),e.Q6J("ngIf",t.selection.parent.pool_namespace),e.xp6(1),e.AsE("/",t.selection.parent.image_name,"@",t.selection.parent.snap_name,"")}}function fs(n,_){1&n&&(e.TgZ(0,"span"),e._uU(1,"-"),e.qZA())}function Cs(n,_){if(1&n&&(e.TgZ(0,"table",17),e.TgZ(1,"tbody"),e.TgZ(2,"tr"),e.TgZ(3,"td",18),e.SDv(4,19),e.qZA(),e.TgZ(5,"td",20),e._uU(6),e.qZA(),e.qZA(),e.TgZ(7,"tr"),e.TgZ(8,"td",21),e.SDv(9,22),e.qZA(),e.TgZ(10,"td"),e._uU(11),e.qZA(),e.qZA(),e.TgZ(12,"tr"),e.TgZ(13,"td",21),e.SDv(14,23),e.qZA(),e.TgZ(15,"td"),e._uU(16),e.ALo(17,"empty"),e.qZA(),e.qZA(),e.TgZ(18,"tr"),e.TgZ(19,"td",21),e.SDv(20,24),e.qZA(),e.TgZ(21,"td"),e._uU(22),e.ALo(23,"cdDate"),e.qZA(),e.qZA(),e.TgZ(24,"tr"),e.TgZ(25,"td",21),e.SDv(26,25),e.qZA(),e.TgZ(27,"td"),e._uU(28),e.ALo(29,"dimlessBinary"),e.qZA(),e.qZA(),e.TgZ(30,"tr"),e.TgZ(31,"td",21),e.SDv(32,26),e.qZA(),e.TgZ(33,"td"),e._uU(34),e.ALo(35,"dimless"),e.qZA(),e.qZA(),e.TgZ(36,"tr"),e.TgZ(37,"td",21),e.SDv(38,27),e.qZA(),e.TgZ(39,"td"),e._uU(40),e.ALo(41,"dimlessBinary"),e.qZA(),e.qZA(),e.TgZ(42,"tr"),e.TgZ(43,"td",21),e.SDv(44,28),e.qZA(),e.TgZ(45,"td"),e.YNc(46,cs,3,1,"span",29),e.qZA(),e.qZA(),e.TgZ(47,"tr"),e.TgZ(48,"td",21),e.SDv(49,30),e.qZA(),e.TgZ(50,"td"),e.YNc(51,ds,3,1,"span",1),e.YNc(52,ps,3,3,"span",1),e.qZA(),e.qZA(),e.TgZ(53,"tr"),e.TgZ(54,"td",21),e.SDv(55,31),e.qZA(),e.TgZ(56,"td"),e.YNc(57,gs,3,1,"span",1),e.YNc(58,us,3,3,"span",1),e.qZA(),e.qZA(),e.TgZ(59,"tr"),e.TgZ(60,"td",21),e.SDv(61,32),e.qZA(),e.TgZ(62,"td"),e._uU(63),e.ALo(64,"dimlessBinary"),e.qZA(),e.qZA(),e.TgZ(65,"tr"),e.TgZ(66,"td",21),e.SDv(67,33),e.qZA(),e.TgZ(68,"td"),e._uU(69),e.qZA(),e.qZA(),e.TgZ(70,"tr"),e.TgZ(71,"td",21),e.SDv(72,34),e.qZA(),e.TgZ(73,"td"),e.YNc(74,Ts,4,4,"span",1),e.YNc(75,fs,2,0,"span",1),e.qZA(),e.qZA(),e.TgZ(76,"tr"),e.TgZ(77,"td",21),e.SDv(78,35),e.qZA(),e.TgZ(79,"td"),e._uU(80),e.qZA(),e.qZA(),e.TgZ(81,"tr"),e.TgZ(82,"td",21),e.SDv(83,36),e.qZA(),e.TgZ(84,"td"),e._uU(85),e.qZA(),e.qZA(),e.TgZ(86,"tr"),e.TgZ(87,"td",21),e.SDv(88,37),e.qZA(),e.TgZ(89,"td"),e._uU(90),e.qZA(),e.qZA(),e.qZA(),e.qZA()),2&n){const t=e.oxw(2);e.xp6(6),e.Oqu(t.selection.name),e.xp6(5),e.Oqu(t.selection.pool_name),e.xp6(5),e.Oqu(e.lcZ(17,19,t.selection.data_pool)),e.xp6(6),e.Oqu(e.lcZ(23,21,t.selection.timestamp)),e.xp6(6),e.Oqu(e.lcZ(29,23,t.selection.size)),e.xp6(6),e.Oqu(e.lcZ(35,25,t.selection.num_objs)),e.xp6(6),e.Oqu(e.lcZ(41,27,t.selection.obj_size)),e.xp6(6),e.Q6J("ngForOf",t.selection.features_name),e.xp6(5),e.Q6J("ngIf",-1===(null==t.selection.features_name?null:t.selection.features_name.indexOf("fast-diff"))),e.xp6(1),e.Q6J("ngIf",-1!==(null==t.selection.features_name?null:t.selection.features_name.indexOf("fast-diff"))),e.xp6(5),e.Q6J("ngIf",-1===(null==t.selection.features_name?null:t.selection.features_name.indexOf("fast-diff"))),e.xp6(1),e.Q6J("ngIf",-1!==(null==t.selection.features_name?null:t.selection.features_name.indexOf("fast-diff"))),e.xp6(5),e.Oqu(e.lcZ(64,29,t.selection.stripe_unit)),e.xp6(6),e.Oqu(t.selection.stripe_count),e.xp6(5),e.Q6J("ngIf",t.selection.parent),e.xp6(1),e.Q6J("ngIf",!t.selection.parent),e.xp6(5),e.Oqu(t.selection.block_name_prefix),e.xp6(5),e.Oqu(t.selection.order),e.xp6(5),e.Oqu(t.selection.image_format)}}function Ss(n,_){if(1&n&&e._UZ(0,"cd-rbd-snapshot-list",42),2&n){const t=e.oxw(2);e.Q6J("snapshots",t.selection.snapshots)("featuresName",t.selection.features_name)("poolName",t.selection.pool_name)("namespace",t.selection.namespace)("rbdName",t.selection.name)}}function Es(n,_){if(1&n&&e._UZ(0,"cd-rbd-configuration-table",43),2&n){const t=e.oxw(2);e.Q6J("data",t.selection.configuration)}}function Rs(n,_){if(1&n&&e._UZ(0,"cd-grafana",44),2&n){const t=e.oxw(2);e.Q6J("grafanaPath",t.rbdDashboardUrl)}}function Ms(n,_){if(1&n&&(e.ynx(0),e.TgZ(1,"ul",4,5),e.TgZ(3,"li",6),e.TgZ(4,"a",7),e.SDv(5,8),e.qZA(),e.YNc(6,Cs,91,31,"ng-template",9),e.qZA(),e.TgZ(7,"li",10),e.TgZ(8,"a",7),e.SDv(9,11),e.qZA(),e.YNc(10,Ss,1,5,"ng-template",9),e.qZA(),e.TgZ(11,"li",12),e.TgZ(12,"a",7),e.SDv(13,13),e.qZA(),e.YNc(14,Es,1,1,"ng-template",9),e.qZA(),e.TgZ(15,"li",14),e.TgZ(16,"a",7),e.SDv(17,15),e.qZA(),e.YNc(18,Rs,1,1,"ng-template",9),e.qZA(),e.qZA(),e._UZ(19,"div",16),e.BQk()),2&n){const t=e.MAs(2);e.xp6(19),e.Q6J("ngbNavOutlet",t)}}function Os(n,_){1&n&&(e.ynx(0),e.TgZ(1,"cd-alert-panel",45),e.SDv(2,46),e.qZA(),e.BQk())}function As(n,_){1&n&&(e.ynx(0),e.TgZ(1,"strong",49),e.SDv(2,50),e.qZA(),e.BQk())}function hs(n,_){1&n&&(e.TgZ(0,"span",51),e.SDv(1,52),e.qZA())}function Ps(n,_){if(1&n&&(e.YNc(0,As,3,0,"ng-container",47),e.YNc(1,hs,2,0,"ng-template",null,48,e.W1O)),2&n){const t=_.value,o=e.MAs(2);e.Q6J("ngIf",+t)("ngIfElse",o)}}let Is=(()=>{class n{ngOnChanges(){this.selection&&(this.rbdDashboardUrl=`rbd-details?var-Pool=${this.selection.pool_name}&var-Image=${this.selection.name}`)}}return n.\u0275fac=function(t){return new(t||n)},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-details"]],viewQuery:function(t,o){if(1&t&&(e.Gf(rs,7),e.Gf(I.Pz,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.poolConfigurationSourceTpl=i.first),e.iGM(i=e.CRH())&&(o.nav=i.first)}},inputs:{selection:"selection",images:"images"},features:[e.TTD],decls:6,vars:2,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$,G,X,J,te,A,w,de,pe,ge,ue,me,Te,fe,Ce,Se;return _="Only available for RBD images with " + "\ufffd#2\ufffd" + "fast-diff" + "\ufffd/#2\ufffd" + " enabled",t="Details",o="Snapshots",i="Configuration",s="Performance",a="Name",d="Pool",c="Data Pool",u="Created",S="Size",N="Objects",P="Object size",$="Features",G="Provisioned",X="Total provisioned",J="Striping unit",te="Striping count",A="Parent",w="Block name prefix",de="Order",pe="Format Version",ge="N/A",ue="N/A",me="Information can not be displayed for RBD in status 'Removing'.",Te="This setting overrides the global value",fe="Image",Ce="This is the global value. No value for this option has been set for this image.",Se="Global",[["usageNotAvailableTooltipTpl",""],[4,"ngIf"],["poolConfigurationSourceTpl",""],_,["ngbNav","","cdStatefulTab","rbd-details",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","details"],["ngbNavLink",""],t,["ngbNavContent",""],["ngbNavItem","snapshots"],o,["ngbNavItem","configuration"],i,["ngbNavItem","performance"],s,[3,"ngbNavOutlet"],[1,"table","table-striped","table-bordered"],[1,"bold","w-25"],a,[1,"w-75"],[1,"bold"],d,c,u,S,N,P,$,[4,"ngFor","ngForOf"],G,X,J,te,A,w,de,pe,[1,"badge","badge-dark","mr-2"],["placement","top",1,"form-text","text-muted",3,"ngbTooltip"],ge,ue,[3,"snapshots","featuresName","poolName","namespace","rbdName"],[3,"data"],["uid","YhCYGcuZz","grafanaStyle","one",3,"grafanaPath"],["type","warning"],me,[4,"ngIf","ngIfElse"],["global",""],["ngbTooltip",Te],fe,["ngbTooltip",Ce],Se]},template:function(t,o){1&t&&(e.YNc(0,ls,3,0,"ng-template",null,0,e.W1O),e.YNc(2,Ms,20,1,"ng-container",1),e.YNc(3,Os,3,0,"ng-container",1),e.YNc(4,Ps,3,2,"ng-template",null,2,e.W1O)),2&t&&(e.xp6(2),e.Q6J("ngIf",o.selection&&"REMOVING"!==o.selection.source),e.xp6(1),e.Q6J("ngIf",o.selection&&"REMOVING"===o.selection.source))},directives:[l.O5,I.Pz,mt.m,I.nv,I.Vx,I.uN,I.tO,l.sg,I._L,_s,ss.P,Rt.F,pt.G],pipes:[as.W,ze.N,Fe.$,Ue.n],styles:[""]}),n})();const bs=["usageTpl"],Ns=["parentTpl"],Ds=["nameTpl"],vs=["flattenTpl"],Ls=["deleteTpl"],Fs=["removingStatTpl"],$s=["provisionedNotAvailableTooltipTpl"],Zs=["totalProvisionedNotAvailableTooltipTpl"];function Bs(n,_){1&n&&e._UZ(0,"div",11),2&n&&e.Q6J("innerHtml","Only available for RBD images with fast-diff enabled",e.oJD)}function Gs(n,_){if(1&n&&(e.TgZ(0,"span",14),e.SDv(1,15),e.qZA()),2&n){e.oxw(2);const t=e.MAs(6);e.Q6J("ngbTooltip",t)}}function ys(n,_){if(1&n&&(e.SDv(0,16),e.ALo(1,"dimlessBinary")),2&n){const t=e.oxw().row;e.xp6(1),e.pQV(e.lcZ(1,1,t.disk_usage)),e.QtT(0)}}function xs(n,_){if(1&n&&(e.YNc(0,Gs,2,1,"span",12),e.YNc(1,ys,2,3,"ng-template",null,13,e.W1O)),2&n){const t=_.row,o=e.MAs(2);e.Q6J("ngIf",null===t.disk_usage&&!t.features_name.includes("fast-diff"))("ngIfElse",o)}}function ws(n,_){if(1&n&&(e.TgZ(0,"span",14),e.SDv(1,18),e.qZA()),2&n){e.oxw(2);const t=e.MAs(6);e.Q6J("ngbTooltip",t)}}function qs(n,_){if(1&n&&(e.SDv(0,19),e.ALo(1,"dimlessBinary")),2&n){const t=e.oxw().row;e.xp6(1),e.pQV(e.lcZ(1,1,t.total_disk_usage)),e.QtT(0)}}function Hs(n,_){if(1&n&&(e.YNc(0,ws,2,1,"span",12),e.YNc(1,qs,2,3,"ng-template",null,17,e.W1O)),2&n){const t=_.row,o=e.MAs(2);e.Q6J("ngIf",null===t.total_disk_usage&&!t.features_name.includes("fast-diff"))("ngIfElse",o)}}function Ks(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.qZA()),2&n){const t=e.oxw(2).value;e.xp6(1),e.hij("/",t.pool_namespace,"")}}function ks(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.YNc(2,Ks,2,1,"span",20),e._uU(3),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.Oqu(t.pool_name),e.xp6(1),e.Q6J("ngIf",t.pool_namespace),e.xp6(1),e.AsE("/",t.image_name,"@",t.snap_name,"")}}function Xs(n,_){1&n&&(e.TgZ(0,"span"),e._uU(1,"-"),e.qZA())}function Qs(n,_){if(1&n&&(e.YNc(0,ks,4,4,"span",20),e.YNc(1,Xs,2,0,"span",20)),2&n){const t=_.value;e.Q6J("ngIf",t),e.xp6(1),e.Q6J("ngIf",!t)}}function zs(n,_){if(1&n&&(e._uU(0," You are about to flatten "),e.TgZ(1,"strong"),e._uU(2),e.qZA(),e._uU(3,". "),e._UZ(4,"br"),e._UZ(5,"br"),e._uU(6," All blocks will be copied from parent "),e.TgZ(7,"strong"),e._uU(8),e.qZA(),e._uU(9," to child "),e.TgZ(10,"strong"),e._uU(11),e.qZA(),e._uU(12,".\n")),2&n){const t=_.$implicit;e.xp6(2),e.Oqu(t.child),e.xp6(6),e.Oqu(t.parent),e.xp6(3),e.Oqu(t.child)}}function Js(n,_){if(1&n&&(e.TgZ(0,"li"),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.xp6(1),e.Oqu(t)}}function Ys(n,_){if(1&n&&(e.ynx(0),e.TgZ(1,"span"),e.SDv(2,24),e.qZA(),e.TgZ(3,"ul"),e.YNc(4,Js,2,1,"li",25),e.qZA(),e.BQk()),2&n){const t=e.oxw(2).snapshots;e.xp6(4),e.Q6J("ngForOf",t)}}function Vs(n,_){if(1&n&&(e.TgZ(0,"div",22),e.TgZ(1,"span"),e.SDv(2,23),e.qZA(),e._UZ(3,"br"),e.YNc(4,Ys,5,1,"ng-container",20),e.qZA()),2&n){const t=e.oxw().snapshots;e.xp6(4),e.Q6J("ngIf",t.length>0)}}function Us(n,_){1&n&&e.YNc(0,Vs,5,1,"div",21),2&n&&e.Q6J("ngIf",_.hasSnapshots)}const js=function(n,_){return[n,_]};function Ws(n,_){if(1&n&&e._UZ(0,"i",27),2&n){const t=e.oxw(2);e.Q6J("ngClass",e.WLB(1,js,t.icons.spinner,t.icons.spin))}}function ea(n,_){if(1&n&&(e.TgZ(0,"span",27),e._uU(1),e.qZA()),2&n){const t=e.oxw(),o=t.column,i=t.row;e.Q6J("ngClass",null!=o&&null!=o.customTemplateConfig&&o.customTemplateConfig.executingClass?o.customTemplateConfig.executingClass:"text-muted italic"),e.xp6(1),e.hij(" (",i.cdExecuting,") ")}}function ta(n,_){if(1&n&&e._UZ(0,"i",29),2&n){const t=e.oxw(2);e.Gre("",t.icons.warning," warn")}}function oa(n,_){if(1&n&&(e.YNc(0,Ws,1,4,"i",26),e.TgZ(1,"span",27),e._uU(2),e.qZA(),e.YNc(3,ea,2,2,"span",26),e.YNc(4,ta,1,3,"i",28)),2&n){const t=_.column,o=_.value,i=_.row;e.Q6J("ngIf",i.cdExecuting),e.xp6(1),e.Q6J("ngClass",null==t||null==t.customTemplateConfig?null:t.customTemplateConfig.valueClass),e.xp6(1),e.hij(" ",o," "),e.xp6(1),e.Q6J("ngIf",i.cdExecuting),e.xp6(1),e.Q6J("ngIf",i.source&&"REMOVING"===i.source)}}let ia=(()=>{class n extends lt.o{constructor(t,o,i,s,a,d,c,u,S){super(),this.authStorageService=t,this.rbdService=o,this.dimlessBinaryPipe=i,this.dimlessPipe=s,this.modalService=a,this.taskWrapper=d,this.taskListService=c,this.urlBuilder=u,this.actionLabels=S,this.tableStatus=new U.E,this.selection=new Ee.r,this.icons=T.P,this.builders={"rbd/create":A=>this.createRbdFromTask(A.pool_name,A.namespace,A.image_name),"rbd/delete":A=>this.createRbdFromTaskImageSpec(A.image_spec),"rbd/clone":A=>this.createRbdFromTask(A.child_pool_name,A.child_namespace,A.child_image_name),"rbd/copy":A=>this.createRbdFromTask(A.dest_pool_name,A.dest_namespace,A.dest_image_name)},this.permission=this.authStorageService.getPermissions().rbdImage;const N=()=>this.selection.first()&&new Z.N(this.selection.first().pool_name,this.selection.first().namespace,this.selection.first().name).toStringEncoded();this.tableActions=[{permission:"create",icon:T.P.add,routerLink:()=>this.urlBuilder.getCreate(),canBePrimary:A=>!A.hasSingleSelection,name:this.actionLabels.CREATE},{permission:"update",icon:T.P.edit,routerLink:()=>this.urlBuilder.getEdit(N()),name:this.actionLabels.EDIT,disable:A=>this.getRemovingStatusDesc(A)||this.getInvalidNameDisable(A)},{permission:"create",canBePrimary:A=>A.hasSingleSelection,disable:A=>this.getRemovingStatusDesc(A)||this.getInvalidNameDisable(A)||!!A.first().cdExecuting,icon:T.P.copy,routerLink:()=>`/block/rbd/copy/${N()}`,name:this.actionLabels.COPY},{permission:"update",disable:A=>this.getRemovingStatusDesc(A)||this.getInvalidNameDisable(A)||A.first().cdExecuting||!A.first().parent,icon:T.P.flatten,click:()=>this.flattenRbdModal(),name:this.actionLabels.FLATTEN},{permission:"delete",icon:T.P.destroy,click:()=>this.deleteRbdModal(),name:this.actionLabels.DELETE,disable:A=>this.getDeleteDisableDesc(A)},{permission:"delete",icon:T.P.trash,click:()=>this.trashRbdModal(),name:this.actionLabels.TRASH,disable:A=>this.getRemovingStatusDesc(A)||this.getInvalidNameDisable(A)||A.first().image_format===Pe.V1}]}createRbdFromTaskImageSpec(t){const o=Z.N.fromString(t);return this.createRbdFromTask(o.poolName,o.namespace,o.imageName)}createRbdFromTask(t,o,i){const s=new zi;return s.id="-1",s.unique_id="-1",s.name=i,s.namespace=o,s.pool_name=t,s.image_format=Pe.V2,s}ngOnInit(){this.columns=[{name:"Name",prop:"name",flexGrow:2,cellTemplate:this.removingStatTpl},{name:"Pool",prop:"pool_name",flexGrow:2},{name:"Namespace",prop:"namespace",flexGrow:2},{name:"Size",prop:"size",flexGrow:1,cellClass:"text-right",pipe:this.dimlessBinaryPipe},{name:"Objects",prop:"num_objs",flexGrow:1,cellClass:"text-right",pipe:this.dimlessPipe},{name:"Object size",prop:"obj_size",flexGrow:1,cellClass:"text-right",pipe:this.dimlessBinaryPipe},{name:"Provisioned",prop:"disk_usage",cellClass:"text-center",flexGrow:1,pipe:this.dimlessBinaryPipe,cellTemplate:this.provisionedNotAvailableTooltipTpl},{name:"Total provisioned",prop:"total_disk_usage",cellClass:"text-center",flexGrow:1,pipe:this.dimlessBinaryPipe,cellTemplate:this.totalProvisionedNotAvailableTooltipTpl},{name:"Parent",prop:"parent",flexGrow:2,cellTemplate:this.parentTpl}],this.taskListService.init(()=>this.rbdService.list(),i=>this.prepareResponse(i),i=>this.images=i,()=>this.onFetchError(),i=>["rbd/clone","rbd/copy","rbd/create","rbd/delete","rbd/edit","rbd/flatten","rbd/trash/move"].includes(i.name),(i,s)=>{let a;switch(s.name){case"rbd/copy":a=new Z.N(s.metadata.dest_pool_name,s.metadata.dest_namespace,s.metadata.dest_image_name).toString();break;case"rbd/clone":a=new Z.N(s.metadata.child_pool_name,s.metadata.child_namespace,s.metadata.child_image_name).toString();break;case"rbd/create":a=new Z.N(s.metadata.pool_name,s.metadata.namespace,s.metadata.image_name).toString();break;default:a=s.metadata.image_spec}return a===new Z.N(i.pool_name,i.namespace,i.name).toString()},this.builders)}onFetchError(){this.table.reset(),this.tableStatus=new U.E(ce.T.ValueException)}prepareResponse(t){let o=[];const i={};let s;if(t.forEach(a=>{C().isUndefined(i[a.status])&&(i[a.status]=[]),i[a.status].push(a.pool_name),o=o.concat(a.value)}),i[ce.T.ValueException]?s=ce.T.ValueException:i[ce.T.ValueStale]?s=ce.T.ValueStale:i[ce.T.ValueNone]&&(s=ce.T.ValueNone),s){const a=(i[s].length>1?"pools ":"pool ")+i[s].join();this.tableStatus=new U.E(s,a)}else this.tableStatus=new U.E;return o}updateSelection(t){this.selection=t}deleteRbdModal(){const t=this.selection.first().pool_name,o=this.selection.first().namespace,i=this.selection.first().name,s=new Z.N(t,o,i);this.modalRef=this.modalService.show(he.M,{itemDescription:"RBD",itemNames:[s],bodyTemplate:this.deleteTpl,bodyContext:{hasSnapshots:this.hasSnapshots(),snapshots:this.listProtectedSnapshots()},submitActionObservable:()=>this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/delete",{image_spec:s.toString()}),call:this.rbdService.delete(s)})})}trashRbdModal(){const t={poolName:this.selection.first().pool_name,namespace:this.selection.first().namespace,imageName:this.selection.first().name,hasSnapshots:this.hasSnapshots()};this.modalRef=this.modalService.show(J_,t)}flattenRbd(t){this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/flatten",{image_spec:t.toString()}),call:this.rbdService.flatten(t)}).subscribe({complete:()=>{this.modalRef.close()}})}flattenRbdModal(){const t=this.selection.first().pool_name,o=this.selection.first().namespace,i=this.selection.first().name,s=this.selection.first().parent,a=new Z.N(s.pool_name,s.pool_namespace,s.image_name),d=new Z.N(t,o,i),c={titleText:"RBD flatten",buttonText:"Flatten",bodyTpl:this.flattenTpl,bodyData:{parent:`${a}@${s.snap_name}`,child:d.toString()},onSubmit:()=>{this.flattenRbd(d)}};this.modalRef=this.modalService.show(ft.Y,c)}hasSnapshots(){return(this.selection.first().snapshots||[]).length>0}hasClonedSnapshots(t){return(t.snapshots||[]).some(i=>i.children&&i.children.length>0)}listProtectedSnapshots(){return this.selection.first().snapshots.reduce((i,s)=>(s.is_protected&&i.push(s.name),i),[])}getDeleteDisableDesc(t){const o=t.first();return o&&this.hasClonedSnapshots(o)?"This RBD has cloned snapshots. Please delete related RBDs before deleting this RBD.":this.getInvalidNameDisable(t)||this.hasClonedSnapshots(t.first())}getInvalidNameDisable(t){var o;const i=t.first();return(null===(o=null==i?void 0:i.name)||void 0===o?void 0:o.match(/[@/]/))?"This RBD image has an invalid name and can't be managed by ceph.":!t.first()||!t.hasSingleSelection}getRemovingStatusDesc(t){const o=t.first();return"REMOVING"===(null==o?void 0:o.source)&&"Action not possible for an RBD in status 'Removing'"}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(x),e.Y36(Fe.$),e.Y36(Ue.n),e.Y36(re.Z),e.Y36(Q.P),e.Y36(se.j),e.Y36(tt.F),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-list"]],viewQuery:function(t,o){if(1&t&&(e.Gf(ee.a,7),e.Gf(bs,5),e.Gf(Ns,7),e.Gf(Ds,5),e.Gf(vs,7),e.Gf(Ls,7),e.Gf(Fs,7),e.Gf($s,7),e.Gf(Zs,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.table=i.first),e.iGM(i=e.CRH())&&(o.usageTpl=i.first),e.iGM(i=e.CRH())&&(o.parentTpl=i.first),e.iGM(i=e.CRH())&&(o.nameTpl=i.first),e.iGM(i=e.CRH())&&(o.flattenTpl=i.first),e.iGM(i=e.CRH())&&(o.deleteTpl=i.first),e.iGM(i=e.CRH())&&(o.removingStatTpl=i.first),e.iGM(i=e.CRH())&&(o.provisionedNotAvailableTooltipTpl=i.first),e.iGM(i=e.CRH())&&(o.totalProvisionedNotAvailableTooltipTpl=i.first)}},features:[e._Bn([se.j,{provide:tt.F,useValue:new tt.F("block/rbd")}]),e.qOj],decls:19,vars:10,consts:function(){let _,t,o,i,s,a,d;return _="N/A",t="" + "\ufffd0\ufffd" + "",o="N/A",i="" + "\ufffd0\ufffd" + "",s="Deleting this image will also delete all its snapshots.",a="The following snapshots are currently protected and will be removed:",d="RBD in status 'Removing'",[["columnMode","flex","identifier","unique_id","forceIdentifier","true","selectionType","single",3,"data","columns","searchableObjects","hasDetails","status","autoReload","fetchData","setExpandedRow","updateSelection"],["table",""],[1,"table-actions",3,"permission","selection","tableActions"],["cdTableDetail","",3,"selection"],["usageNotAvailableTooltipTpl",""],["provisionedNotAvailableTooltipTpl",""],["totalProvisionedNotAvailableTooltipTpl",""],["parentTpl",""],["flattenTpl",""],["deleteTpl",""],["removingStatTpl",""],[3,"innerHtml"],["placement","top",3,"ngbTooltip",4,"ngIf","ngIfElse"],["provisioned",""],["placement","top",3,"ngbTooltip"],_,t,["totalProvisioned",""],o,i,[4,"ngIf"],["class","alert alert-warning","role","alert",4,"ngIf"],["role","alert",1,"alert","alert-warning"],s,a,[4,"ngFor","ngForOf"],[3,"ngClass",4,"ngIf"],[3,"ngClass"],["title",d,3,"class",4,"ngIf"],["title",d]]},template:function(t,o){1&t&&(e._UZ(0,"cd-rbd-tabs"),e.TgZ(1,"cd-table",0,1),e.NdJ("fetchData",function(){return o.taskListService.fetch()})("setExpandedRow",function(s){return o.setExpandedRow(s)})("updateSelection",function(s){return o.updateSelection(s)}),e._UZ(3,"cd-table-actions",2),e._UZ(4,"cd-rbd-details",3),e.qZA(),e.YNc(5,Bs,1,1,"ng-template",null,4,e.W1O),e.YNc(7,xs,3,2,"ng-template",null,5,e.W1O),e.YNc(9,Hs,3,2,"ng-template",null,6,e.W1O),e.YNc(11,Qs,2,2,"ng-template",null,7,e.W1O),e.YNc(13,zs,13,3,"ng-template",null,8,e.W1O),e.YNc(15,Us,1,1,"ng-template",null,9,e.W1O),e.YNc(17,oa,5,5,"ng-template",null,10,e.W1O)),2&t&&(e.xp6(1),e.Q6J("data",o.images)("columns",o.columns)("searchableObjects",!0)("hasDetails",!0)("status",o.tableStatus)("autoReload",-1),e.xp6(2),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions),e.xp6(1),e.Q6J("selection",o.expandedRow))},directives:[Qe,ee.a,Re.K,Is,l.O5,I._L,l.sg,l.mk],pipes:[Fe.$],styles:[".warn[_ngcontent-%COMP%]{color:#ffc200}"]}),n})();function _a(n,_){1&n&&e._UZ(0,"input",19)}function sa(n,_){1&n&&(e.TgZ(0,"option",23),e.SDv(1,24),e.qZA()),2&n&&e.Q6J("ngValue",null)}function aa(n,_){1&n&&(e.TgZ(0,"option",23),e.SDv(1,25),e.qZA()),2&n&&e.Q6J("ngValue",null)}function ra(n,_){1&n&&(e.TgZ(0,"option",23),e.SDv(1,26),e.qZA()),2&n&&e.Q6J("ngValue",null)}function la(n,_){if(1&n&&(e.TgZ(0,"option",27),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.pool_name),e.xp6(1),e.Oqu(t.pool_name)}}function ca(n,_){if(1&n&&(e.TgZ(0,"select",20),e.YNc(1,sa,2,1,"option",21),e.YNc(2,aa,2,1,"option",21),e.YNc(3,ra,2,1,"option",21),e.YNc(4,la,2,2,"option",22),e.qZA()),2&n){const t=e.oxw();e.xp6(1),e.Q6J("ngIf",null===t.pools),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&0===t.pools.length),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&t.pools.length>0),e.xp6(1),e.Q6J("ngForOf",t.pools)}}function da(n,_){1&n&&(e.TgZ(0,"span",28),e.SDv(1,29),e.qZA())}function pa(n,_){1&n&&(e.TgZ(0,"span",28),e.SDv(1,30),e.qZA())}function ga(n,_){1&n&&(e.TgZ(0,"span",28),e.SDv(1,31),e.qZA())}let ua=(()=>{class n{constructor(t,o,i,s,a,d){this.activeModal=t,this.actionLabels=o,this.authStorageService=i,this.notificationService=s,this.poolService=a,this.rbdService=d,this.pools=null,this.editing=!1,this.poolPermission=this.authStorageService.getPermissions().pool,this.createForm()}createForm(){this.namespaceForm=new M.d({pool:new r.NI(""),namespace:new r.NI("")},this.validator(),this.asyncValidator())}validator(){return t=>{const o=t.get("pool"),i=t.get("namespace");let s=null;o.value||(s={required:!0}),o.setErrors(s);let a=null;return i.value||(a={required:!0}),i.setErrors(a),null}}asyncValidator(){return t=>new Promise(o=>{const i=t.get("pool"),s=t.get("namespace");this.rbdService.listNamespaces(i.value).subscribe(a=>{if(a.some(d=>d.namespace===s.value)){const d={namespaceExists:!0};s.setErrors(d),o(d)}else o(null)})})}ngOnInit(){this.onSubmit=new Et.xQ,this.poolPermission.read&&this.poolService.list(["pool_name","type","application_metadata"]).then(t=>{const o=[];for(const i of t)this.rbdService.isRBDPool(i)&&"replicated"===i.type&&o.push(i);if(this.pools=o,1===this.pools.length){const i=this.pools[0].pool_name;this.namespaceForm.get("pool").setValue(i)}})}submit(){const t=this.namespaceForm.getValue("pool"),o=this.namespaceForm.getValue("namespace"),i=new F.R;i.name="rbd/namespace/create",i.metadata={pool:t,namespace:o},this.rbdService.createNamespace(t,o).toPromise().then(()=>{this.notificationService.show(Ve.k.success,"Created namespace '" + t + "/" + o + "'"),this.activeModal.close(),this.onSubmit.next()}).catch(()=>{this.namespaceForm.setErrors({cdSubmitButton:!0})})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(oe.j),e.Y36(Le.g),e.Y36(Ke.q),e.Y36(x))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-namespace-form-modal"]],decls:23,vars:9,consts:function(){let _,t,o,i,s,a,d,c,u;return _="Create Namespace",t="Pool",o="Name",i="Loading...",s="-- No rbd pools available --",a="-- Select a pool --",d="This field is required.",c="This field is required.",u="Namespace already exists.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","namespaceForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","pool",1,"cd-col-form-label","required"],t,[1,"cd-col-form-input"],["class","form-control","type","text","placeholder","Pool name...","id","pool","name","pool","formControlName","pool",4,"ngIf"],["id","pool","name","pool","class","form-control custom-select","formControlName","pool",4,"ngIf"],["class","invalid-feedback",4,"ngIf"],["for","namespace",1,"cd-col-form-label","required"],o,["type","text","placeholder","Namespace name...","id","namespace","name","namespace","formControlName","namespace","autofocus","",1,"form-control"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],["type","text","placeholder","Pool name...","id","pool","name","pool","formControlName","pool",1,"form-control"],["id","pool","name","pool","formControlName","pool",1,"form-control","custom-select"],[3,"ngValue",4,"ngIf"],[3,"value",4,"ngFor","ngForOf"],[3,"ngValue"],i,s,a,[3,"value"],[1,"invalid-feedback"],d,c,u]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"div",7),e.TgZ(8,"label",8),e.SDv(9,9),e.qZA(),e.TgZ(10,"div",10),e.YNc(11,_a,1,0,"input",11),e.YNc(12,ca,5,4,"select",12),e.YNc(13,da,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(14,"div",7),e.TgZ(15,"label",14),e.SDv(16,15),e.qZA(),e.TgZ(17,"div",10),e._UZ(18,"input",16),e.YNc(19,pa,2,0,"span",13),e.YNc(20,ga,2,0,"span",13),e.qZA(),e.qZA(),e.qZA(),e.TgZ(21,"div",17),e.TgZ(22,"cd-form-button-panel",18),e.NdJ("submitActionEvent",function(){return o.submit()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.namespaceForm),e.xp6(7),e.Q6J("ngIf",!o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.namespaceForm.showError("pool",i,"required")),e.xp6(6),e.Q6J("ngIf",o.namespaceForm.showError("namespace",i,"required")),e.xp6(1),e.Q6J("ngIf",o.namespaceForm.showError("namespace",i,"namespaceExists")),e.xp6(2),e.Q6J("form",o.namespaceForm)("submitText",o.actionLabels.CREATE)}},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,l.O5,f.o,r.Fj,h.b,r.JJ,r.u,le.U,O.p,r.EJ,l.sg,r.YN,r.Kr],styles:[""]}),n})(),ma=(()=>{class n{constructor(t,o,i,s,a,d){this.authStorageService=t,this.rbdService=o,this.poolService=i,this.modalService=s,this.notificationService=a,this.actionLabels=d,this.selection=new Ee.r,this.permission=this.authStorageService.getPermissions().rbdImage,this.tableActions=[{permission:"create",icon:T.P.add,click:()=>this.createModal(),name:this.actionLabels.CREATE},{permission:"delete",icon:T.P.destroy,click:()=>this.deleteModal(),name:this.actionLabels.DELETE,disable:()=>this.getDeleteDisableDesc()}]}ngOnInit(){this.columns=[{name:"Namespace",prop:"namespace",flexGrow:1},{name:"Pool",prop:"pool",flexGrow:1},{name:"Total images",prop:"num_images",flexGrow:1}],this.refresh()}refresh(){this.poolService.list(["pool_name","type","application_metadata"]).then(t=>{t=t.filter(i=>this.rbdService.isRBDPool(i)&&"replicated"===i.type);const o=[];t.forEach(i=>{o.push(this.rbdService.listNamespaces(i.pool_name))}),o.length>0?(0,W.D)(o).subscribe(i=>{const s=[];for(let a=0;a{s.push({id:`${c}/${u.namespace}`,pool:c,namespace:u.namespace,num_images:u.num_images})})}this.namespaces=s}):this.namespaces=[]})}updateSelection(t){this.selection=t}createModal(){this.modalRef=this.modalService.show(ua),this.modalRef.componentInstance.onSubmit.subscribe(()=>{this.refresh()})}deleteModal(){const t=this.selection.first().pool,o=this.selection.first().namespace;this.modalRef=this.modalService.show(he.M,{itemDescription:"Namespace",itemNames:[`${t}/${o}`],submitAction:()=>this.rbdService.deleteNamespace(t,o).subscribe(()=>{this.notificationService.show(Ve.k.success,"Deleted namespace '" + t + "/" + o + "'"),this.modalRef.close(),this.refresh()},()=>{this.modalRef.componentInstance.stopLoadingSpinner()})})}getDeleteDisableDesc(){var t;const o=this.selection.first();return(null==o?void 0:o.num_images)>0?"Namespace contains images":!(null===(t=this.selection)||void 0===t?void 0:t.first())}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(x),e.Y36(Ke.q),e.Y36(re.Z),e.Y36(Le.g),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-namespace-list"]],features:[e._Bn([se.j])],decls:4,vars:5,consts:[["columnMode","flex","identifier","id","forceIdentifier","true","selectionType","single",3,"data","columns","fetchData","updateSelection"],[1,"table-actions","btn-toolbar"],[1,"btn-group",3,"permission","selection","tableActions"]],template:function(t,o){1&t&&(e._UZ(0,"cd-rbd-tabs"),e.TgZ(1,"cd-table",0),e.NdJ("fetchData",function(){return o.refresh()})("updateSelection",function(s){return o.updateSelection(s)}),e.TgZ(2,"div",1),e._UZ(3,"cd-table-actions",2),e.qZA(),e.qZA()),2&t&&(e.xp6(1),e.Q6J("data",o.namespaces)("columns",o.columns),e.xp6(2),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions))},directives:[Qe,ee.a,Re.K],styles:[""]}),n})(),Ta=(()=>{class n{}return n.\u0275fac=function(t){return new(t||n)},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-performance"]],decls:2,vars:1,consts:[["uid","41FrpeUiz","grafanaStyle","two",3,"grafanaPath"]],template:function(t,o){1&t&&(e._UZ(0,"cd-rbd-tabs"),e._UZ(1,"cd-grafana",0)),2&t&&(e.xp6(1),e.Q6J("grafanaPath","rbd-overview?"))},directives:[Qe,Rt.F],styles:[""]}),n})();function fa(n,_){1&n&&e._UZ(0,"input",15)}function Ca(n,_){if(1&n&&(e.TgZ(0,"option",20),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(t)}}function Sa(n,_){if(1&n&&(e.TgZ(0,"select",16),e.TgZ(1,"option",17),e.SDv(2,18),e.qZA(),e.YNc(3,Ca,2,2,"option",19),e.qZA()),2&n){const t=e.oxw();e.xp6(3),e.Q6J("ngForOf",t.pools)}}let Ea=(()=>{class n{constructor(t,o,i,s,a,d,c){this.authStorageService=t,this.rbdService=o,this.activeModal=i,this.actionLabels=s,this.fb=a,this.poolService=d,this.taskWrapper=c,this.poolPermission=this.authStorageService.getPermissions().pool}createForm(){this.purgeForm=this.fb.group({poolName:""})}ngOnInit(){this.poolPermission.read&&this.poolService.list(["pool_name","application_metadata"]).then(t=>{this.pools=t.filter(o=>o.application_metadata.includes("rbd")).map(o=>o.pool_name)}),this.createForm()}purge(){const t=this.purgeForm.getValue("poolName")||"";this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/trash/purge",{pool_name:t}),call:this.rbdService.purgeTrash(t)}).subscribe({error:()=>{this.purgeForm.setErrors({cdSubmitButton:!0})},complete:()=>{this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(x),e.Y36(I.Kz),e.Y36(D.p4),e.Y36(ot.O),e.Y36(Ke.q),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-trash-purge-modal"]],decls:18,vars:6,consts:function(){let _,t,o,i,s;return _="Purge Trash",t="To purge, select\xA0 " + "[\ufffd#9\ufffd|\ufffd#10\ufffd]" + "All" + "[\ufffd/#9\ufffd|\ufffd/#10\ufffd]" + "\xA0 or one pool and click\xA0 " + "[\ufffd#9\ufffd|\ufffd#10\ufffd]" + "Purge" + "[\ufffd/#9\ufffd|\ufffd/#10\ufffd]" + ".\xA0",t=e.Zx4(t),o="Pool:",i="Pool name...",s="All",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","purgeForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],[1,"col-form-label","mx-auto"],o,["class","form-control","type","text","placeholder",i,"formControlName","poolName",4,"ngIf"],["id","poolName","name","poolName","class","form-control custom-select","formControlName","poolName",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],["type","text","placeholder",i,"formControlName","poolName",1,"form-control"],["id","poolName","name","poolName","formControlName","poolName",1,"form-control","custom-select"],["value",""],s,[3,"value",4,"ngFor","ngForOf"],[3,"value"]]},template:function(t,o){1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.tHW(8,7),e._UZ(9,"kbd"),e._UZ(10,"kbd"),e.N_p(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e.YNc(14,fa,1,0,"input",11),e.YNc(15,Sa,4,1,"select",12),e.qZA(),e.qZA(),e.TgZ(16,"div",13),e.TgZ(17,"cd-form-button-panel",14),e.NdJ("submitActionEvent",function(){return o.purge()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t&&(e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.purgeForm),e.xp6(10),e.Q6J("ngIf",!o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.poolPermission.read),e.xp6(2),e.Q6J("form",o.purgeForm)("submitText",o.actionLabels.PURGE))},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,l.O5,O.p,f.o,r.Fj,h.b,r.JJ,r.u,r.EJ,r.YN,r.Kr,l.sg],styles:[""]}),n})();function Ra(n,_){1&n&&(e.TgZ(0,"span",15),e.SDv(1,16),e.qZA())}let Ma=(()=>{class n{constructor(t,o,i,s,a){this.rbdService=t,this.activeModal=o,this.actionLabels=i,this.fb=s,this.taskWrapper=a}ngOnInit(){this.imageSpec=new Z.N(this.poolName,this.namespace,this.imageName).toString(),this.restoreForm=this.fb.group({name:this.imageName})}restore(){const t=this.restoreForm.getValue("name"),o=new Z.N(this.poolName,this.namespace,this.imageId);this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/trash/restore",{image_id_spec:o.toString(),new_image_name:t}),call:this.rbdService.restoreTrash(o,t)}).subscribe({error:()=>{this.restoreForm.setErrors({cdSubmitButton:!0})},complete:()=>{this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(x),e.Y36(I.Kz),e.Y36(D.p4),e.Y36(ot.O),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-trash-restore-modal"]],decls:18,vars:7,consts:function(){let _,t,o,i;return _="Restore Image",t="To restore\xA0 " + "[\ufffd#9\ufffd|\ufffd#10\ufffd]" + "" + "\ufffd0\ufffd" + "@" + "\ufffd1\ufffd" + "" + "[\ufffd/#9\ufffd|\ufffd/#10\ufffd]" + ",\xA0 type the image's new name and click\xA0 " + "[\ufffd#9\ufffd|\ufffd#10\ufffd]" + "Restore" + "[\ufffd/#9\ufffd|\ufffd/#10\ufffd]" + ".",t=e.Zx4(t),o="New Name",i="This field is required.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","restoreForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","name",1,"col-form-label"],o,["type","text","name","name","id","name","autocomplete","off","formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],i]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.tHW(8,7),e._UZ(9,"kbd"),e._UZ(10,"kbd"),e.N_p(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e._UZ(14,"input",11),e.YNc(15,Ra,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(16,"div",13),e.TgZ(17,"cd-form-button-panel",14),e.NdJ("submitActionEvent",function(){return o.restore()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.restoreForm),e.xp6(6),e.pQV(o.imageSpec)(o.imageId),e.QtT(8),e.xp6(5),e.Q6J("ngIf",o.restoreForm.showError("name",i,"required")),e.xp6(2),e.Q6J("form",o.restoreForm)("submitText",o.actionLabels.RESTORE)}},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,O.p],styles:[""]}),n})();const Oa=["expiresTpl"],Aa=["deleteTpl"],ha=function(n){return[n]};function Pa(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"button",6),e.NdJ("click",function(){return e.CHM(t),e.oxw().purgeModal()}),e._UZ(1,"i",7),e.ynx(2),e.SDv(3,8),e.BQk(),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("disabled",t.disablePurgeBtn),e.xp6(1),e.Q6J("ngClass",e.VKq(2,ha,t.icons.destroy))}}function Ia(n,_){1&n&&(e.ynx(0),e.SDv(1,10),e.BQk())}function ba(n,_){1&n&&(e.ynx(0),e.SDv(1,11),e.BQk())}function Na(n,_){if(1&n&&(e.YNc(0,Ia,2,0,"ng-container",9),e.YNc(1,ba,2,0,"ng-container",9),e._uU(2),e.ALo(3,"cdDate")),2&n){const t=_.row,o=_.value;e.Q6J("ngIf",t.cdIsExpired),e.xp6(1),e.Q6J("ngIf",!t.cdIsExpired),e.xp6(1),e.hij(" ",e.lcZ(3,3,o),"\n")}}function Da(n,_){if(1&n&&(e.TgZ(0,"p",13),e.TgZ(1,"strong"),e.ynx(2),e.SDv(3,14),e.ALo(4,"cdDate"),e.BQk(),e.qZA(),e.qZA()),2&n){const t=e.oxw().expiresAt;e.xp6(4),e.pQV(e.lcZ(4,1,t)),e.QtT(3)}}function va(n,_){1&n&&e.YNc(0,Da,5,3,"p",12),2&n&&e.Q6J("ngIf",!_.isExpired)}let La=(()=>{class n{constructor(t,o,i,s,a,d,c){this.authStorageService=t,this.rbdService=o,this.modalService=i,this.cdDatePipe=s,this.taskListService=a,this.taskWrapper=d,this.actionLabels=c,this.icons=T.P,this.executingTasks=[],this.selection=new Ee.r,this.tableStatus=new U.E,this.disablePurgeBtn=!0,this.permission=this.authStorageService.getPermissions().rbdImage,this.tableActions=[{permission:"update",icon:T.P.undo,click:()=>this.restoreModal(),name:this.actionLabels.RESTORE},{permission:"delete",icon:T.P.destroy,click:()=>this.deleteModal(),name:this.actionLabels.DELETE}]}ngOnInit(){this.columns=[{name:"ID",prop:"id",flexGrow:1,cellTransformation:ve.e.executing},{name:"Name",prop:"name",flexGrow:1},{name:"Pool",prop:"pool_name",flexGrow:1},{name:"Namespace",prop:"namespace",flexGrow:1},{name:"Status",prop:"deferment_end_time",flexGrow:1,cellTemplate:this.expiresTpl},{name:"Deleted At",prop:"deletion_time",flexGrow:1,pipe:this.cdDatePipe}],this.taskListService.init(()=>this.rbdService.listTrash(),i=>this.prepareResponse(i),i=>this.images=i,()=>this.onFetchError(),i=>["rbd/trash/remove","rbd/trash/restore"].includes(i.name),(i,s)=>new Z.N(i.pool_name,i.namespace,i.id).toString()===s.metadata.image_id_spec,void 0)}prepareResponse(t){let o=[];const i={};let s;if(t.forEach(a=>{C().isUndefined(i[a.status])&&(i[a.status]=[]),i[a.status].push(a.pool_name),o=o.concat(a.value),this.disablePurgeBtn=!o.length}),i[3]?s=3:i[1]?s=1:i[2]&&(s=2),s){const a=(i[s].length>1?"pools ":"pool ")+i[s].join();this.tableStatus=new U.E(s,a)}else this.tableStatus=new U.E;return o.forEach(a=>{a.cdIsExpired=Me()().isAfter(a.deferment_end_time)}),o}onFetchError(){this.table.reset(),this.tableStatus=new U.E(ce.T.ValueException)}updateSelection(t){this.selection=t}restoreModal(){const t={poolName:this.selection.first().pool_name,namespace:this.selection.first().namespace,imageName:this.selection.first().name,imageId:this.selection.first().id};this.modalRef=this.modalService.show(Ma,t)}deleteModal(){const t=this.selection.first().pool_name,o=this.selection.first().namespace,i=this.selection.first().id,s=this.selection.first().deferment_end_time,a=Me()().isAfter(s),d=new Z.N(t,o,i);this.modalRef=this.modalService.show(he.M,{itemDescription:"RBD",itemNames:[d],bodyTemplate:this.deleteTpl,bodyContext:{expiresAt:s,isExpired:a},submitActionObservable:()=>this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/trash/remove",{image_id_spec:d.toString()}),call:this.rbdService.removeTrash(d,!0)})})}purgeModal(){this.modalService.show(Ea)}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(x),e.Y36(re.Z),e.Y36(ze.N),e.Y36(se.j),e.Y36(Q.P),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-trash-list"]],viewQuery:function(t,o){if(1&t&&(e.Gf(ee.a,7),e.Gf(Oa,7),e.Gf(Aa,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.table=i.first),e.iGM(i=e.CRH())&&(o.expiresTpl=i.first),e.iGM(i=e.CRH())&&(o.deleteTpl=i.first)}},features:[e._Bn([se.j])],decls:9,vars:8,consts:function(){let _,t,o,i;return _="Purge Trash",t="Expired at",o="Protected until",i="This image is protected until " + "\ufffd0\ufffd" + ".",[["columnMode","flex","identifier","id","forceIdentifier","true","selectionType","single",3,"data","columns","status","autoReload","fetchData","updateSelection"],[1,"table-actions","btn-toolbar"],[1,"btn-group",3,"permission","selection","tableActions"],["class","btn btn-light","type","button",3,"disabled","click",4,"ngIf"],["expiresTpl",""],["deleteTpl",""],["type","button",1,"btn","btn-light",3,"disabled","click"],["aria-hidden","true",3,"ngClass"],_,[4,"ngIf"],t,o,["class","text-danger",4,"ngIf"],[1,"text-danger"],i]},template:function(t,o){1&t&&(e._UZ(0,"cd-rbd-tabs"),e.TgZ(1,"cd-table",0),e.NdJ("fetchData",function(){return o.taskListService.fetch()})("updateSelection",function(s){return o.updateSelection(s)}),e.TgZ(2,"div",1),e._UZ(3,"cd-table-actions",2),e.YNc(4,Pa,4,4,"button",3),e.qZA(),e.qZA(),e.YNc(5,Na,4,5,"ng-template",null,4,e.W1O),e.YNc(7,va,1,1,"ng-template",null,5,e.W1O)),2&t&&(e.xp6(1),e.Q6J("data",o.images)("columns",o.columns)("status",o.tableStatus)("autoReload",-1),e.xp6(2),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions),e.xp6(1),e.Q6J("ngIf",o.permission.delete))},directives:[Qe,ee.a,Re.K,l.O5,f.o,l.mk],pipes:[ze.N],styles:[""]}),n})(),Mt=(()=>{class n{}return n.\u0275fac=function(t){return new(t||n)},n.\u0275mod=e.oAB({type:n}),n.\u0275inj=e.cJS({imports:[[l.ez,Jn,r.u5,r.UX,I.Oz,I.dT,I.HK,Ie.b,_e.m,m.Bz,ne.xc]]}),n})();const Fa=[{path:"",redirectTo:"rbd",pathMatch:"full"},{path:"rbd",canActivate:[j.T],data:{breadcrumbs:"Images"},children:[{path:"",component:ia},{path:"namespaces",component:ma,data:{breadcrumbs:"Namespaces"}},{path:"trash",component:La,data:{breadcrumbs:"Trash"}},{path:"performance",component:Ta,data:{breadcrumbs:"Overall Performance"}},{path:D.MQ.CREATE,component:$e,data:{breadcrumbs:D.Qn.CREATE}},{path:`${D.MQ.EDIT}/:image_spec`,component:$e,data:{breadcrumbs:D.Qn.EDIT}},{path:`${D.MQ.CLONE}/:image_spec/:snap`,component:$e,data:{breadcrumbs:D.Qn.CLONE}},{path:`${D.MQ.COPY}/:image_spec`,component:$e,data:{breadcrumbs:D.Qn.COPY}},{path:`${D.MQ.COPY}/:image_spec/:snap`,component:$e,data:{breadcrumbs:D.Qn.COPY}}]},{path:"mirroring",component:Ki,canActivate:[j.T],data:{breadcrumbs:"Mirroring"}},{path:"iscsi",canActivate:[j.T],data:{breadcrumbs:"iSCSI"},children:[{path:"",redirectTo:"overview",pathMatch:"full"},{path:"overview",component:zn,data:{breadcrumbs:"Overview"}},{path:"targets",data:{breadcrumbs:"Targets"},children:[{path:"",component:Ln},{path:D.MQ.CREATE,component:rt,data:{breadcrumbs:D.Qn.CREATE}},{path:`${D.MQ.EDIT}/:target_iqn`,component:rt,data:{breadcrumbs:D.Qn.EDIT}}]}]}];let $a=(()=>{class n{}return n.\u0275fac=function(t){return new(t||n)},n.\u0275mod=e.oAB({type:n}),n.\u0275inj=e.cJS({imports:[[Mt,m.Bz.forChild(Fa)]]}),n})()},54555:(it,Oe,p)=>{p.d(Oe,{d:()=>Q});var l=p(74788),r=p(24751),m=p(23815),ne=p.n(m),I=p(80226),Ie=p(65862),D=p(95463),j=p(30633),_e=p(28211),be=p(34089),C=p(41582),W=p(12057),b=p(56310),ie=p(18372),Y=p(87925),e=p(94276);let k=(()=>{class R{constructor(g,f){this.control=g,this.formatter=f}setValue(g){const f=this.formatter.toMilliseconds(g);this.control.control.setValue(`${f} ms`)}ngOnInit(){this.setValue(this.control.value),this.ngDataReady&&this.ngDataReady.subscribe(()=>this.setValue(this.control.value))}onUpdate(g){this.setValue(g)}}return R.\u0275fac=function(g){return new(g||R)(l.Y36(r.a5),l.Y36(_e.H))},R.\u0275dir=l.lG2({type:R,selectors:[["","cdMilliseconds",""]],hostBindings:function(g,f){1&g&&l.NdJ("blur",function(O){return f.onUpdate(O.target.value)})},inputs:{ngDataReady:"ngDataReady"}}),R})();var Ne=p(20044);let Z=(()=>{class R{constructor(g,f,h,O){this.elementRef=g,this.control=f,this.dimlessBinaryPerSecondPipe=h,this.formatter=O,this.ngModelChange=new l.vpe,this.el=this.elementRef.nativeElement}ngOnInit(){this.setValue(this.el.value),this.ngDataReady&&this.ngDataReady.subscribe(()=>this.setValue(this.el.value))}setValue(g){/^[\d.]+$/.test(g)&&(g+=this.defaultUnit||"m");const f=this.formatter.toBytes(g,0),h=this.round(f);this.el.value=this.dimlessBinaryPerSecondPipe.transform(h),null!==f?(this.ngModelChange.emit(this.el.value),this.control.control.setValue(this.el.value)):(this.ngModelChange.emit(null),this.control.control.setValue(null))}round(g){if(null!==g&&0!==g){if(!ne().isUndefined(this.minBytes)&&gthis.maxBytes)return this.maxBytes;if(!ne().isUndefined(this.roundPower)){const f=Math.round(Math.log(g)/Math.log(this.roundPower));return Math.pow(this.roundPower,f)}}return g}onBlur(g){this.setValue(g)}}return R.\u0275fac=function(g){return new(g||R)(l.Y36(l.SBq),l.Y36(r.a5),l.Y36(Ne.O),l.Y36(_e.H))},R.\u0275dir=l.lG2({type:R,selectors:[["","cdDimlessBinaryPerSecond",""]],hostBindings:function(g,f){1&g&&l.NdJ("blur",function(O){return f.onBlur(O.target.value)})},inputs:{ngDataReady:"ngDataReady",minBytes:"minBytes",maxBytes:"maxBytes",roundPower:"roundPower",defaultUnit:"defaultUnit"},outputs:{ngModelChange:"ngModelChange"}}),R})(),Ae=(()=>{class R{constructor(g,f){this.formatter=g,this.ngControl=f}setValue(g){const f=this.formatter.toIops(g);this.ngControl.control.setValue(`${f} IOPS`)}ngOnInit(){this.setValue(this.ngControl.value),this.ngDataReady&&this.ngDataReady.subscribe(()=>this.setValue(this.ngControl.value))}onUpdate(g){this.setValue(g)}}return R.\u0275fac=function(g){return new(g||R)(l.Y36(_e.H),l.Y36(r.a5))},R.\u0275dir=l.lG2({type:R,selectors:[["","cdIops",""]],hostBindings:function(g,f){1&g&&l.NdJ("blur",function(O){return f.onUpdate(O.target.value)})},inputs:{ngDataReady:"ngDataReady"}}),R})();function x(R,v){if(1&R&&(l.ynx(0),l._UZ(1,"input",18),l.BQk()),2&R){const g=l.oxw().$implicit,f=l.oxw(2);l.xp6(1),l.Q6J("id",g.name)("name",g.name)("formControlName",g.name)("ngDataReady",f.ngDataReady)}}function ae(R,v){if(1&R&&(l.ynx(0),l._UZ(1,"input",19),l.BQk()),2&R){const g=l.oxw().$implicit,f=l.oxw(2);l.xp6(1),l.Q6J("id",g.name)("name",g.name)("formControlName",g.name)("ngDataReady",f.ngDataReady)}}function V(R,v){if(1&R&&(l.ynx(0),l._UZ(1,"input",20),l.BQk()),2&R){const g=l.oxw().$implicit,f=l.oxw(2);l.xp6(1),l.Q6J("id",g.name)("name",g.name)("formControlName",g.name)("ngDataReady",f.ngDataReady)}}function T(R,v){1&R&&(l.TgZ(0,"span",21),l.SDv(1,22),l.qZA())}const q=function(R){return{active:R}},M=function(R){return[R]};function B(R,v){if(1&R){const g=l.EpF();l.TgZ(0,"div",10),l.TgZ(1,"label",11),l._uU(2),l.TgZ(3,"cd-helper"),l._uU(4),l.qZA(),l.qZA(),l.TgZ(5,"div"),l.TgZ(6,"div",12),l.ynx(7,13),l.YNc(8,x,2,4,"ng-container",14),l.YNc(9,ae,2,4,"ng-container",14),l.YNc(10,V,2,4,"ng-container",14),l.BQk(),l.TgZ(11,"span",15),l.TgZ(12,"button",16),l.NdJ("click",function(){const O=l.CHM(g).$implicit;return l.oxw(2).reset(O.name)}),l._UZ(13,"i",7),l.qZA(),l.qZA(),l.qZA(),l.YNc(14,T,2,0,"span",17),l.qZA(),l.qZA()}if(2&R){const g=v.$implicit,f=l.oxw().$implicit,h=l.oxw(),O=l.MAs(1);l.xp6(1),l.Q6J("for",g.name),l.xp6(1),l.Oqu(g.displayName),l.xp6(2),l.Oqu(g.description),l.xp6(1),l.Gre("cd-col-form-input ",f.heading,""),l.xp6(2),l.Q6J("ngSwitch",g.type),l.xp6(1),l.Q6J("ngSwitchCase",h.configurationType.milliseconds),l.xp6(1),l.Q6J("ngSwitchCase",h.configurationType.bps),l.xp6(1),l.Q6J("ngSwitchCase",h.configurationType.iops),l.xp6(2),l.Q6J("ngClass",l.VKq(13,q,h.isDisabled(g.name))),l.xp6(1),l.Q6J("ngClass",l.VKq(15,M,h.icons.erase)),l.xp6(1),l.Q6J("ngIf",h.form.showError("configuration."+g.name,O,"min"))}}function F(R,v){if(1&R){const g=l.EpF();l.TgZ(0,"div",4),l.TgZ(1,"h4",5),l.TgZ(2,"span",6),l.NdJ("click",function(){const O=l.CHM(g).$implicit;return l.oxw().toggleSectionVisibility(O.class)}),l._uU(3),l._UZ(4,"i",7),l.qZA(),l.qZA(),l.TgZ(5,"div",8),l.YNc(6,B,15,17,"div",9),l.qZA(),l.qZA()}if(2&R){const g=v.$implicit,f=l.oxw();l.xp6(3),l.hij(" ",g.heading," "),l.xp6(1),l.Q6J("ngClass",f.sectionVisibility[g.class]?f.icons.minusCircle:f.icons.addCircle),l.xp6(1),l.Tol(g.class),l.Q6J("hidden",!f.sectionVisibility[g.class]),l.xp6(1),l.Q6J("ngForOf",g.options)}}let Q=(()=>{class R{constructor(g,f){this.formatterService=g,this.rbdConfigurationService=f,this.initializeData=new I.t(1),this.changes=new l.vpe,this.icons=Ie.P,this.ngDataReady=new l.vpe,this.configurationType=j.r,this.sectionVisibility={}}ngOnInit(){const g=this.createConfigurationFormGroup();this.form.addControl("configuration",g),g.valueChanges.subscribe(()=>{this.changes.emit(this.getDirtyValues.bind(this))}),this.initializeData&&this.initializeData.subscribe(f=>{this.initialData=f.initialData;const h=f.sourceType;this.rbdConfigurationService.getWritableOptionFields().forEach(O=>{const H=f.initialData.filter(De=>De.name===O.name).pop();H&&H.source===h&&this.form.get(`configuration.${O.name}`).setValue(H.value)}),this.ngDataReady.emit()}),this.rbdConfigurationService.getWritableSections().forEach(f=>this.sectionVisibility[f.class]=!1)}getDirtyValues(g=!1,f){if(g&&!f)throw new Error("ProgrammingError: If local values shall be included, a proper localFieldType argument has to be provided, too");const h={};return this.rbdConfigurationService.getWritableOptionFields().forEach(O=>{const H=this.form.get("configuration").get(O.name);this.initialData&&this.initialData[O.name]===H.value||(H.dirty||g&&H.source===f)&&(h[O.name]=null===H.value?H.value:O.type===j.r.bps?this.formatterService.toBytes(H.value):O.type===j.r.milliseconds?this.formatterService.toMilliseconds(H.value):O.type===j.r.iops?this.formatterService.toIops(H.value):H.value)}),h}createConfigurationFormGroup(){const g=new D.d({});return this.rbdConfigurationService.getWritableOptionFields().forEach(f=>{let h;if(f.type!==j.r.milliseconds&&f.type!==j.r.iops&&f.type!==j.r.bps)throw new Error(`Type ${f.type} is unknown, you may need to add it to RbdConfiguration class`);{let O=0;ne().forEach(this.initialData,H=>{H.name===f.name&&(O=H.value)}),h=new r.NI(O,r.kI.min(0))}g.addControl(f.name,h)}),g}reset(g){const f=this.form.get("configuration").get(g);f.disabled?(f.setValue(f.previousValue||0),f.enable(),f.previousValue||f.markAsPristine()):(f.previousValue=f.value,f.setValue(null),f.markAsDirty(),f.disable())}isDisabled(g){return this.form.get("configuration").get(g).disabled}toggleSectionVisibility(g){this.sectionVisibility[g]=!this.sectionVisibility[g]}}return R.\u0275fac=function(g){return new(g||R)(l.Y36(_e.H),l.Y36(be.n))},R.\u0275cmp=l.Xpm({type:R,selectors:[["cd-rbd-configuration-form"]],inputs:{form:"form",initializeData:"initializeData"},outputs:{changes:"changes"},decls:5,vars:2,consts:function(){let v,g,f;return v="RBD Configuration",g="Remove the local configuration value. The parent configuration value will be inherited and used instead.",f="The minimum value is 0",[[3,"formGroup"],["cfgFormGroup",""],v,["class","col-12",4,"ngFor","ngForOf"],[1,"col-12"],[1,"cd-header"],[1,"collapsible",3,"click"],["aria-hidden","true",3,"ngClass"],[3,"hidden"],["class","form-group row",4,"ngFor","ngForOf"],[1,"form-group","row"],[1,"cd-col-form-label",3,"for"],[1,"input-group"],[3,"ngSwitch"],[4,"ngSwitchCase"],[1,"input-group-append"],["type","button","data-toggle","button","title",g,1,"btn","btn-light",3,"ngClass","click"],["class","invalid-feedback",4,"ngIf"],["type","text","cdMilliseconds","",1,"form-control",3,"id","name","formControlName","ngDataReady"],["type","text","defaultUnit","b","cdDimlessBinaryPerSecond","",1,"form-control",3,"id","name","formControlName","ngDataReady"],["type","text","cdIops","",1,"form-control",3,"id","name","formControlName","ngDataReady"],[1,"invalid-feedback"],f]},template:function(g,f){1&g&&(l.TgZ(0,"fieldset",0,1),l.TgZ(2,"legend"),l.SDv(3,2),l.qZA(),l.YNc(4,F,7,7,"div",3),l.qZA()),2&g&&(l.Q6J("formGroup",f.form.get("configuration")),l.xp6(4),l.Q6J("ngForOf",f.rbdConfigurationService.sections))},directives:[r.JL,r.sg,C.V,W.sg,W.mk,b.P,ie.S,W.RF,W.n9,Y.o,W.O5,r.Fj,e.b,k,r.JJ,r.u,Z,Ae],styles:[".collapsible[_ngcontent-%COMP%]{cursor:pointer;user-select:none}"]}),R})()},71752:(it,Oe,p)=>{p.d(Oe,{P:()=>V});var l=p(64337),r=p(30633),m=p(74788);let ne=(()=>{class T{transform(M){return{0:"global",1:"pool",2:"image"}[M]}}return T.\u0275fac=function(M){return new(M||T)},T.\u0275pipe=m.Yjl({name:"rbdConfigurationSource",type:T,pure:!0}),T})();var I=p(28211),Ie=p(34089),D=p(12057),j=p(20044),_e=p(48537),be=p(21766);const C=["configurationSourceTpl"],W=["configurationValueTpl"],b=["poolConfTable"];function ie(T,q){1&T&&(m.TgZ(0,"span"),m.SDv(1,6),m.qZA())}function Y(T,q){1&T&&(m.TgZ(0,"strong"),m.SDv(1,7),m.qZA())}function e(T,q){1&T&&(m.TgZ(0,"strong"),m.SDv(1,8),m.qZA())}function k(T,q){1&T&&(m.TgZ(0,"div",4),m.YNc(1,ie,2,0,"span",5),m.YNc(2,Y,2,0,"strong",5),m.YNc(3,e,2,0,"strong",5),m.qZA()),2&T&&(m.Q6J("ngSwitch",q.value),m.xp6(1),m.Q6J("ngSwitchCase","global"),m.xp6(1),m.Q6J("ngSwitchCase","image"),m.xp6(1),m.Q6J("ngSwitchCase","pool"))}function Ne(T,q){if(1&T&&(m.TgZ(0,"span"),m._uU(1),m.ALo(2,"dimlessBinaryPerSecond"),m.qZA()),2&T){const M=m.oxw().value;m.xp6(1),m.Oqu(m.lcZ(2,1,M))}}function Z(T,q){if(1&T&&(m.TgZ(0,"span"),m._uU(1),m.ALo(2,"milliseconds"),m.qZA()),2&T){const M=m.oxw().value;m.xp6(1),m.Oqu(m.lcZ(2,1,M))}}function Ae(T,q){if(1&T&&(m.TgZ(0,"span"),m._uU(1),m.ALo(2,"iops"),m.qZA()),2&T){const M=m.oxw().value;m.xp6(1),m.Oqu(m.lcZ(2,1,M))}}function x(T,q){if(1&T&&(m.TgZ(0,"span"),m._uU(1),m.qZA()),2&T){const M=m.oxw().value;m.xp6(1),m.Oqu(M)}}function ae(T,q){if(1&T&&(m.TgZ(0,"div",4),m.YNc(1,Ne,3,3,"span",5),m.YNc(2,Z,3,3,"span",5),m.YNc(3,Ae,3,3,"span",5),m.YNc(4,x,2,1,"span",9),m.qZA()),2&T){const M=q.row,B=m.oxw();m.Q6J("ngSwitch",M.type),m.xp6(1),m.Q6J("ngSwitchCase",B.typeField.bps),m.xp6(1),m.Q6J("ngSwitchCase",B.typeField.milliseconds),m.xp6(1),m.Q6J("ngSwitchCase",B.typeField.iops)}}let V=(()=>{class T{constructor(M,B){this.formatterService=M,this.rbdConfigurationService=B,this.sourceField=r.h,this.typeField=r.r}ngOnInit(){this.poolConfigurationColumns=[{prop:"displayName",name:"Name"},{prop:"description",name:"Description"},{prop:"name",name:"Key"},{prop:"source",name:"Source",cellTemplate:this.configurationSourceTpl,pipe:new ne},{prop:"value",name:"Value",cellTemplate:this.configurationValueTpl}]}ngOnChanges(){!this.data||(this.data=this.data.filter(M=>this.rbdConfigurationService.getOptionFields().map(B=>B.name).includes(M.name)))}}return T.\u0275fac=function(M){return new(M||T)(m.Y36(I.H),m.Y36(Ie.n))},T.\u0275cmp=m.Xpm({type:T,selectors:[["cd-rbd-configuration-table"]],viewQuery:function(M,B){if(1&M&&(m.Gf(C,7),m.Gf(W,7),m.Gf(b,7)),2&M){let F;m.iGM(F=m.CRH())&&(B.configurationSourceTpl=F.first),m.iGM(F=m.CRH())&&(B.configurationValueTpl=F.first),m.iGM(F=m.CRH())&&(B.poolConfTable=F.first)}},inputs:{data:"data"},features:[m.TTD],decls:6,vars:2,consts:function(){let q,M,B;return q="Global",M="Image",B="Pool",[["identifier","name",3,"data","columns"],["poolConfTable",""],["configurationSourceTpl",""],["configurationValueTpl",""],[3,"ngSwitch"],[4,"ngSwitchCase"],q,M,B,[4,"ngSwitchDefault"]]},template:function(M,B){1&M&&(m._UZ(0,"cd-table",0,1),m.YNc(2,k,4,4,"ng-template",null,2,m.W1O),m.YNc(4,ae,5,4,"ng-template",null,3,m.W1O)),2&M&&m.Q6J("data",B.data)("columns",B.poolConfigurationColumns)},directives:[l.a,D.RF,D.n9,D.ED],pipes:[j.O,_e.J,be.A],styles:[""]}),T})()}}]); \ No newline at end of file diff --git a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/330.4192d10f1b1db19145cc.js b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/330.4192d10f1b1db19145cc.js new file mode 100644 index 000000000..e1d1907d2 --- /dev/null +++ b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/330.4192d10f1b1db19145cc.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkceph_dashboard=self.webpackChunkceph_dashboard||[]).push([[330],{91330:(it,Oe,p)=>{p.r(Oe),p.d(Oe,{BlockModule:()=>Mt,RoutedBlockModule:()=>$a});var l=p(12057),r=p(24751),m=p(6283),ne=p(19723),I=p(38549),Ie=p(37496),D=p(79512),j=p(4222),_e=p(44466),be=p(23815),C=p.n(be),W=p(35758),b=p(64762),ie=p(58497),Y=p(93523),e=p(74788);let k=class{constructor(_){this.http=_}listTargets(){return this.http.get("api/iscsi/target")}getTarget(_){return this.http.get(`api/iscsi/target/${_}`)}updateTarget(_,t){return this.http.put(`api/iscsi/target/${_}`,t,{observe:"response"})}status(){return this.http.get("ui-api/iscsi/status")}settings(){return this.http.get("ui-api/iscsi/settings")}version(){return this.http.get("ui-api/iscsi/version")}portals(){return this.http.get("ui-api/iscsi/portals")}createTarget(_){return this.http.post("api/iscsi/target",_,{observe:"response"})}deleteTarget(_){return this.http.delete(`api/iscsi/target/${_}`,{observe:"response"})}getDiscovery(){return this.http.get("api/iscsi/discoveryauth")}updateDiscovery(_){return this.http.put("api/iscsi/discoveryauth",_)}overview(){return this.http.get("ui-api/iscsi/overview")}};k.\u0275fac=function(_){return new(_||k)(e.LFG(ie.eN))},k.\u0275prov=e.Yz7({token:k,factory:k.\u0275fac,providedIn:"root"}),k=(0,b.gn)([Y.o,(0,b.w6)("design:paramtypes",[ie.eN])],k);var Ne=p(88002),Z=p(19358),Ae=p(34089);let x=class{constructor(_,t){this.http=_,this.rbdConfigurationService=t}isRBDPool(_){return-1!==C().indexOf(_.application_metadata,"rbd")&&!_.pool_name.includes("/")}create(_){return this.http.post("api/block/image",_,{observe:"response"})}delete(_){return this.http.delete(`api/block/image/${_.toStringEncoded()}`,{observe:"response"})}update(_,t){return this.http.put(`api/block/image/${_.toStringEncoded()}`,t,{observe:"response"})}get(_){return this.http.get(`api/block/image/${_.toStringEncoded()}`)}list(){return this.http.get("api/block/image").pipe((0,Ne.U)(_=>_.map(t=>(t.value.map(o=>(o.configuration&&o.configuration.map(i=>Object.assign(i,this.rbdConfigurationService.getOptionByName(i.name))),o)),t))))}copy(_,t){return this.http.post(`api/block/image/${_.toStringEncoded()}/copy`,t,{observe:"response"})}flatten(_){return this.http.post(`api/block/image/${_.toStringEncoded()}/flatten`,null,{observe:"response"})}defaultFeatures(){return this.http.get("api/block/image/default_features")}cloneFormatVersion(){return this.http.get("api/block/image/clone_format_version")}createSnapshot(_,t){const o={snapshot_name:t};return this.http.post(`api/block/image/${_.toStringEncoded()}/snap`,o,{observe:"response"})}renameSnapshot(_,t,o){const i={new_snap_name:o};return this.http.put(`api/block/image/${_.toStringEncoded()}/snap/${t}`,i,{observe:"response"})}protectSnapshot(_,t,o){const i={is_protected:o};return this.http.put(`api/block/image/${_.toStringEncoded()}/snap/${t}`,i,{observe:"response"})}rollbackSnapshot(_,t){return this.http.post(`api/block/image/${_.toStringEncoded()}/snap/${t}/rollback`,null,{observe:"response"})}cloneSnapshot(_,t,o){return this.http.post(`api/block/image/${_.toStringEncoded()}/snap/${t}/clone`,o,{observe:"response"})}deleteSnapshot(_,t){return this.http.delete(`api/block/image/${_.toStringEncoded()}/snap/${t}`,{observe:"response"})}listTrash(){return this.http.get("api/block/image/trash/")}createNamespace(_,t){return this.http.post(`api/block/pool/${_}/namespace`,{namespace:t},{observe:"response"})}listNamespaces(_){return this.http.get(`api/block/pool/${_}/namespace/`)}deleteNamespace(_,t){return this.http.delete(`api/block/pool/${_}/namespace/${t}`,{observe:"response"})}moveTrash(_,t){return this.http.post(`api/block/image/${_.toStringEncoded()}/move_trash`,{delay:t},{observe:"response"})}purgeTrash(_){return this.http.post(`api/block/image/trash/purge/?pool_name=${_}`,null,{observe:"response"})}restoreTrash(_,t){return this.http.post(`api/block/image/trash/${_.toStringEncoded()}/restore`,{new_image_name:t},{observe:"response"})}removeTrash(_,t=!1){return this.http.delete(`api/block/image/trash/${_.toStringEncoded()}/?force=${t}`,{observe:"response"})}};x.\u0275fac=function(_){return new(_||x)(e.LFG(ie.eN),e.LFG(Ae.n))},x.\u0275prov=e.Yz7({token:x,factory:x.\u0275fac,providedIn:"root"}),(0,b.gn)([(0,b.fM)(1,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[Z.N,String]),(0,b.w6)("design:returntype",void 0)],x.prototype,"createSnapshot",null),(0,b.gn)([(0,b.fM)(2,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[Z.N,String,String]),(0,b.w6)("design:returntype",void 0)],x.prototype,"renameSnapshot",null),(0,b.gn)([(0,b.fM)(2,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[Z.N,String,Boolean]),(0,b.w6)("design:returntype",void 0)],x.prototype,"protectSnapshot",null),(0,b.gn)([(0,b.fM)(1,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[Z.N,String]),(0,b.w6)("design:returntype",void 0)],x.prototype,"restoreTrash",null),x=(0,b.gn)([Y.o,(0,b.w6)("design:paramtypes",[ie.eN,Ae.n])],x);var ae=p(7022),V=p(14745),T=p(65862),q=p(93614),M=p(95463),B=p(77205),F=p(76111),Q=p(32337),R=p(60312),v=p(41582),g=p(56310),f=p(87925),h=p(94276),O=p(30839);function H(n,_){if(1&n&&(e.TgZ(0,"option",6),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("ngValue",t),e.xp6(1),e.Oqu(t)}}function De(n,_){if(1&n&&(e.TgZ(0,"select",5),e._UZ(1,"option",6),e.YNc(2,H,2,2,"option",7),e.qZA()),2&n){const t=e.oxw();e.s9C("id",t.setting),e.s9C("name",t.setting),e.Q6J("formControlName",t.setting),e.xp6(1),e.Q6J("ngValue",null),e.xp6(1),e.Q6J("ngForOf",t.limits.values)}}function Jt(n,_){if(1&n&&e._UZ(0,"input",10),2&n){const t=e.oxw(2);e.Q6J("formControlName",t.setting)}}function Yt(n,_){if(1&n&&e._UZ(0,"input",11),2&n){const t=e.oxw(2);e.Q6J("formControlName",t.setting)}}function Vt(n,_){if(1&n&&(e.ynx(0),e._UZ(1,"br"),e.TgZ(2,"div",12),e._UZ(3,"input",13),e.TgZ(4,"label",14),e._uU(5,"Yes"),e.qZA(),e.qZA(),e.TgZ(6,"div",12),e._UZ(7,"input",13),e.TgZ(8,"label",14),e._uU(9,"No"),e.qZA(),e.qZA(),e.BQk()),2&n){const t=e.oxw(2);e.xp6(3),e.Q6J("id",t.setting+"True")("value",!0)("formControlName",t.setting),e.xp6(1),e.Q6J("for",t.setting+"True"),e.xp6(3),e.Q6J("id",t.setting+"False")("value",!1)("formControlName",t.setting),e.xp6(1),e.Q6J("for",t.setting+"False")}}function Ut(n,_){if(1&n&&(e.TgZ(0,"span"),e.YNc(1,Jt,1,1,"input",8),e.YNc(2,Yt,1,1,"input",9),e.YNc(3,Vt,10,8,"ng-container",3),e.qZA()),2&n){const t=e.oxw();e.xp6(1),e.Q6J("ngIf","int"===t.limits.type),e.xp6(1),e.Q6J("ngIf","str"===t.limits.type),e.xp6(1),e.Q6J("ngIf","bool"===t.limits.type)}}function jt(n,_){if(1&n&&(e.TgZ(0,"span",15),e.ynx(1),e.SDv(2,16),e.BQk(),e.qZA()),2&n){const t=e.oxw();e.xp6(2),e.pQV(t.limits.min),e.QtT(2)}}function Wt(n,_){if(1&n&&(e.TgZ(0,"span",15),e.ynx(1),e.SDv(2,17),e.BQk(),e.qZA()),2&n){const t=e.oxw();e.xp6(2),e.pQV(t.limits.max),e.QtT(2)}}let _t=(()=>{class n{ngOnInit(){const t=[];"min"in this.limits&&t.push(r.kI.min(this.limits.min)),"max"in this.limits&&t.push(r.kI.max(this.limits.max)),this.settingsForm.get(this.setting).setValidators(t)}}return n.\u0275fac=function(t){return new(t||n)},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-setting"]],inputs:{settingsForm:"settingsForm",formDir:"formDir",setting:"setting",limits:"limits"},decls:7,vars:7,consts:function(){let _,t;return _="Must be greater than or equal to " + "\ufffd0\ufffd" + ".",t="Must be less than or equal to " + "\ufffd0\ufffd" + ".",[[1,"form-group",3,"formGroup"],[1,"col-form-label",3,"for"],["class","form-control",3,"id","name","formControlName",4,"ngIf"],[4,"ngIf"],["class","invalid-feedback",4,"ngIf"],[1,"form-control",3,"id","name","formControlName"],[3,"ngValue"],[3,"ngValue",4,"ngFor","ngForOf"],["type","number","class","form-control",3,"formControlName",4,"ngIf"],["type","text","class","form-control",3,"formControlName",4,"ngIf"],["type","number",1,"form-control",3,"formControlName"],["type","text",1,"form-control",3,"formControlName"],[1,"custom-control","custom-radio","custom-control-inline"],["type","radio",1,"custom-control-input",3,"id","value","formControlName"],[1,"custom-control-label",3,"for"],[1,"invalid-feedback"],_,t]},template:function(t,o){1&t&&(e.TgZ(0,"div",0),e.TgZ(1,"label",1),e._uU(2),e.qZA(),e.YNc(3,De,3,5,"select",2),e.YNc(4,Ut,4,3,"span",3),e.YNc(5,jt,3,1,"span",4),e.YNc(6,Wt,3,1,"span",4),e.qZA()),2&t&&(e.Q6J("formGroup",o.settingsForm),e.xp6(1),e.s9C("for",o.setting),e.xp6(1),e.Oqu(o.setting),e.xp6(1),e.Q6J("ngIf","enum"===o.limits.type),e.xp6(1),e.Q6J("ngIf","enum"!==o.limits.type),e.xp6(1),e.Q6J("ngIf",o.settingsForm.showError(o.setting,o.formDir,"min")),e.xp6(1),e.Q6J("ngIf",o.settingsForm.showError(o.setting,o.formDir,"max")))},directives:[g.P,r.JL,r.sg,v.V,l.O5,f.o,r.EJ,h.b,r.JJ,r.u,r.YN,r.Kr,l.sg,r.wV,r.Fj,r._],styles:[""]}),n})();var He=p(88820);function eo(n,_){1&n&&(e.TgZ(0,"span",29),e.SDv(1,30),e.qZA())}function to(n,_){if(1&n&&(e.TgZ(0,"span"),e.TgZ(1,"legend",10),e.SDv(2,21),e.qZA(),e.TgZ(3,"div",12),e.TgZ(4,"div",13),e.TgZ(5,"label",22),e.SDv(6,23),e.qZA(),e._UZ(7,"input",24),e.YNc(8,eo,2,0,"span",25),e.qZA(),e.qZA(),e.TgZ(9,"div",12),e.TgZ(10,"div",13),e.TgZ(11,"label",26),e.SDv(12,27),e.qZA(),e._UZ(13,"input",28),e.qZA(),e.qZA(),e.qZA()),2&n){const t=e.oxw(),o=e.MAs(9);e.xp6(8),e.Q6J("ngIf",t.settingsForm.showError("lun",o,"required"))}}function oo(n,_){if(1&n&&(e.TgZ(0,"option",31),e._uU(1),e.ALo(2,"iscsiBackstore"),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(e.lcZ(2,2,t))}}function no(n,_){if(1&n&&(e.TgZ(0,"div",12),e.TgZ(1,"div",13),e._UZ(2,"cd-iscsi-setting",33),e.qZA(),e.qZA()),2&n){const t=_.$implicit,o=e.oxw(2).$implicit,i=e.oxw(),s=e.MAs(9);e.xp6(2),e.Q6J("settingsForm",i.settingsForm)("formDir",s)("setting",t.key)("limits",i.getDiskControlLimits(o,t.key))}}function io(n,_){if(1&n&&(e.ynx(0),e.YNc(1,no,3,4,"div",32),e.ALo(2,"keyvalue"),e.BQk()),2&n){const t=e.oxw().$implicit,o=e.oxw();e.xp6(1),e.Q6J("ngForOf",e.lcZ(2,1,o.disk_default_controls[t]))}}function _o(n,_){if(1&n&&(e.ynx(0),e.YNc(1,io,3,3,"ng-container",9),e.BQk()),2&n){const t=_.$implicit,o=e.oxw();e.xp6(1),e.Q6J("ngIf",o.settingsForm.value.backstore===t)}}let so=(()=>{class n{constructor(t,o,i){this.activeModal=t,this.iscsiService=o,this.actionLabels=i}ngOnInit(){const t={backstore:new r.NI(this.imagesSettings[this.image].backstore),lun:new r.NI(this.imagesSettings[this.image].lun),wwn:new r.NI(this.imagesSettings[this.image].wwn)};C().forEach(this.backstores,o=>{const i=this.imagesSettings[this.image][o]||{};C().forIn(this.disk_default_controls[o],(s,a)=>{t[a]=new r.NI(i[a])})}),this.settingsForm=new M.d(t)}getDiskControlLimits(t,o){return this.disk_controls_limits?this.disk_controls_limits[t][o]:{type:"int"}}save(){const t=this.settingsForm.controls.backstore.value,o=this.settingsForm.controls.lun.value,i=this.settingsForm.controls.wwn.value,s={};C().forIn(this.settingsForm.controls,(a,d)=>{""!==a.value&&null!==a.value&&d in this.disk_default_controls[this.settingsForm.value.backstore]&&(s[d]=a.value,C().forEach(this.backstores,c=>{c!==t&&d in(this.imagesSettings[this.image][c]||{})&&(this.imagesSettings[this.image][c][d]=a.value)}))}),this.imagesSettings[this.image].backstore=t,this.imagesSettings[this.image].lun=o,this.imagesSettings[this.image].wwn=i,this.imagesSettings[this.image][t]=s,this.imagesSettings=Object.assign({},this.imagesSettings),this.control.updateValueAndValidity({emitEvent:!1}),this.activeModal.close()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(k),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-image-settings-modal"]],decls:25,vars:8,consts:function(){let _,t,o,i,s,a,d,c;return _="Configure",t="Changing these parameters from their default values is usually not necessary.",o="Settings",i="Backstore",s="Identifier",a="lun",d="wwn",c="This field is required.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","settingsForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"alert-warning"],t,[4,"ngIf"],[1,"cd-header"],o,[1,"form-group","row"],[1,"col-sm-12"],[1,"col-form-label"],i,["id","backstore","name","backstore","formControlName","backstore",1,"form-control"],[3,"value",4,"ngFor","ngForOf"],[4,"ngFor","ngForOf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],s,["for","lun",1,"col-form-label","required"],a,["type","number","id","lun","name","lun","formControlName","lun",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","wwn",1,"col-form-label"],d,["type","text","id","wwn","name","wwn","formControlName","wwn",1,"form-control"],[1,"invalid-feedback"],c,[3,"value"],["class","form-group row",4,"ngFor","ngForOf"],[3,"settingsForm","formDir","setting","limits"]]},template:function(t,o){1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.ynx(2),e.SDv(3,2),e.BQk(),e._uU(4,"\xa0 "),e.TgZ(5,"small"),e._uU(6),e.qZA(),e.BQk(),e.ynx(7,3),e.TgZ(8,"form",4,5),e.TgZ(10,"div",6),e.TgZ(11,"p",7),e.SDv(12,8),e.qZA(),e.YNc(13,to,14,1,"span",9),e.TgZ(14,"legend",10),e.SDv(15,11),e.qZA(),e.TgZ(16,"div",12),e.TgZ(17,"div",13),e.TgZ(18,"label",14),e.SDv(19,15),e.qZA(),e.TgZ(20,"select",16),e.YNc(21,oo,3,4,"option",17),e.qZA(),e.qZA(),e.qZA(),e.YNc(22,_o,2,1,"ng-container",18),e.qZA(),e.TgZ(23,"div",19),e.TgZ(24,"cd-form-button-panel",20),e.NdJ("submitActionEvent",function(){return o.save()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t&&(e.Q6J("modalRef",o.activeModal),e.xp6(6),e.Oqu(o.image),e.xp6(2),e.Q6J("formGroup",o.settingsForm),e.xp6(5),e.Q6J("ngIf",o.api_version>=1),e.xp6(8),e.Q6J("ngForOf",o.backstores),e.xp6(1),e.Q6J("ngForOf",o.backstores),e.xp6(2),e.Q6J("form",o.settingsForm)("submitText",o.actionLabels.UPDATE))},directives:[R.z,r._Y,r.JL,r.sg,v.V,l.O5,g.P,f.o,r.EJ,h.b,r.JJ,r.u,l.sg,O.p,r.wV,r.Fj,r.YN,r.Kr,_t],pipes:[He.V,l.Nd],styles:[""]}),n})();function ao(n,_){if(1&n&&(e.TgZ(0,"div",12),e.TgZ(1,"div",13),e._UZ(2,"cd-iscsi-setting",14),e.qZA(),e.qZA()),2&n){const t=_.$implicit,o=e.oxw(),i=e.MAs(5);e.xp6(2),e.Q6J("settingsForm",o.settingsForm)("formDir",i)("setting",t.key)("limits",o.getTargetControlLimits(t.key))}}let ro=(()=>{class n{constructor(t,o,i){this.activeModal=t,this.iscsiService=o,this.actionLabels=i}ngOnInit(){const t={};C().forIn(this.target_default_controls,(o,i)=>{t[i]=new r.NI(this.target_controls.value[i])}),this.settingsForm=new M.d(t)}save(){const t={};C().forIn(this.settingsForm.controls,(o,i)=>{""===o.value||null===o.value||(t[i]=o.value)}),this.target_controls.setValue(t),this.activeModal.close()}getTargetControlLimits(t){return this.target_controls_limits?this.target_controls_limits[t]:["Yes","No"].includes(this.target_default_controls[t])?{type:"bool"}:{type:"int"}}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(k),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-iqn-settings-modal"]],decls:13,vars:7,consts:function(){let _,t;return _="Advanced Settings",t="Changing these parameters from their default values is usually not necessary.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","settingsForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"alert-warning"],t,["class","form-group row",4,"ngFor","ngForOf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"form-group","row"],[1,"col-sm-12"],[3,"settingsForm","formDir","setting","limits"]]},template:function(t,o){1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p",7),e.SDv(8,8),e.qZA(),e.YNc(9,ao,3,4,"div",9),e.ALo(10,"keyvalue"),e.qZA(),e.TgZ(11,"div",10),e.TgZ(12,"cd-form-button-panel",11),e.NdJ("submitActionEvent",function(){return o.save()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t&&(e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.settingsForm),e.xp6(5),e.Q6J("ngForOf",e.lcZ(10,5,o.settingsForm.controls)),e.xp6(3),e.Q6J("form",o.settingsForm)("submitText",o.actionLabels.UPDATE))},directives:[R.z,r._Y,r.JL,r.sg,v.V,l.sg,O.p,g.P,_t],pipes:[l.Nd],styles:[""]}),n})();var re=p(63285),st=p(63622);let lo=(()=>{class n{constructor(t){this.ngControl=t}onInput(t){this.setValue(t)}setValue(t){t=C().isString(t)?t.trim():t,this.ngControl.control.setValue(t)}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(r.a5))},n.\u0275dir=e.lG2({type:n,selectors:[["","cdTrim",""]],hostBindings:function(t,o){1&t&&e.NdJ("input",function(s){return o.onInput(s.target.value)})}}),n})();var co=p(39092),at=p(4416),Je=p(58039),Ye=p(10545);function po(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,42),e.qZA())}function go(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,43),e.qZA())}function uo(n,_){1&n&&(e.TgZ(0,"span",41),e.ynx(1),e.SDv(2,44),e.BQk(),e._UZ(3,"br"),e.ynx(4),e.SDv(5,45),e.BQk(),e._UZ(6,"br"),e.TgZ(7,"a",46),e.SDv(8,47),e.qZA(),e.qZA())}function mo(n,_){1&n&&(e.TgZ(0,"span",48),e.SDv(1,49),e.qZA())}const z=function(n){return[n]};function To(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.TgZ(4,"button",52),e.NdJ("click",function(){const i=e.CHM(t),s=i.index,a=i.$implicit;return e.oxw(2).removePortal(s,a)}),e._UZ(5,"i",16),e.qZA(),e.qZA(),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(2);e.xp6(2),e.Q6J("value",t),e.xp6(3),e.Q6J("ngClass",e.VKq(2,z,o.icons.destroy))}}function fo(n,_){if(1&n&&(e.TgZ(0,"span",41),e.SDv(1,53),e.qZA()),2&n){const t=e.oxw(2);e.xp6(1),e.pQV(t.minimum_gateways),e.QtT(1)}}function Co(n,_){if(1&n&&(e.TgZ(0,"div",56),e._uU(1),e.qZA()),2&n){const t=e.oxw().$implicit,o=e.oxw(2);e.xp6(1),e.hij("lun: ",o.imagesSettings[t].lun,"")}}function So(n,_){if(1&n&&(e.ynx(0),e.SDv(1,57),e.ALo(2,"iscsiBackstore"),e.BQk()),2&n){const t=e.oxw().$implicit,o=e.oxw(2);e.xp6(2),e.pQV(e.lcZ(2,1,o.imagesSettings[t].backstore)),e.QtT(1)}}function Eo(n,_){1&n&&(e.ynx(0),e.SDv(1,58),e.BQk())}function Ro(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.YNc(4,Co,2,1,"div",54),e.TgZ(5,"button",52),e.NdJ("click",function(){const s=e.CHM(t).$implicit;return e.oxw(2).imageSettingsModal(s)}),e._UZ(6,"i",16),e.qZA(),e.TgZ(7,"button",52),e.NdJ("click",function(){const i=e.CHM(t),s=i.index,a=i.$implicit;return e.oxw(2).removeImage(s,a)}),e._UZ(8,"i",16),e.qZA(),e.qZA(),e.qZA(),e.TgZ(9,"span",48),e.YNc(10,So,3,3,"ng-container",55),e.YNc(11,Eo,2,0,"ng-container",55),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(2);e.xp6(2),e.Q6J("value",t),e.xp6(2),e.Q6J("ngIf",o.api_version>=1),e.xp6(2),e.Q6J("ngClass",e.VKq(6,z,o.icons.deepCheck)),e.xp6(2),e.Q6J("ngClass",e.VKq(8,z,o.icons.destroy)),e.xp6(2),e.Q6J("ngIf",o.backstores.length>1),e.xp6(1),e.Q6J("ngIf",o.hasAdvancedSettings(o.imagesSettings[t][o.imagesSettings[t].backstore]))}}function Mo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,59),e.qZA())}function Oo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,60),e.qZA())}function Ao(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,78),e.qZA())}function ho(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,79),e.qZA())}function Po(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,80),e.qZA())}function Io(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,81),e.qZA())}function bo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,82),e.qZA())}function No(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,83),e.qZA())}function Do(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,84),e.qZA())}function vo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,85),e.qZA())}function Lo(n,_){if(1&n&&(e.TgZ(0,"div",61),e.TgZ(1,"div",8),e.TgZ(2,"label",62),e.ynx(3),e.SDv(4,63),e.BQk(),e.qZA(),e.TgZ(5,"div",11),e._UZ(6,"input",64),e.YNc(7,Ao,2,0,"span",17),e.YNc(8,ho,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(9,"div",8),e.TgZ(10,"label",65),e.ynx(11),e.SDv(12,66),e.BQk(),e.qZA(),e.TgZ(13,"div",11),e.TgZ(14,"div",12),e._UZ(15,"input",67),e.TgZ(16,"span",14),e._UZ(17,"button",68),e._UZ(18,"cd-copy-2-clipboard-button",69),e.qZA(),e.qZA(),e.YNc(19,Po,2,0,"span",17),e.YNc(20,Io,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(21,"div",8),e.TgZ(22,"label",70),e.ynx(23),e.SDv(24,71),e.BQk(),e.qZA(),e.TgZ(25,"div",11),e._UZ(26,"input",72),e.YNc(27,bo,2,0,"span",17),e.YNc(28,No,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(29,"div",8),e.TgZ(30,"label",73),e.ynx(31),e.SDv(32,74),e.BQk(),e.qZA(),e.TgZ(33,"div",11),e.TgZ(34,"div",12),e._UZ(35,"input",75),e.TgZ(36,"span",14),e._UZ(37,"button",76),e._UZ(38,"cd-copy-2-clipboard-button",77),e.qZA(),e.qZA(),e.YNc(39,Do,2,0,"span",17),e.YNc(40,vo,2,0,"span",17),e.qZA(),e.qZA(),e.qZA()),2&n){e.oxw();const t=e.MAs(2),o=e.oxw();e.xp6(7),e.Q6J("ngIf",o.targetForm.showError("user",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("user",t,"pattern")),e.xp6(11),e.Q6J("ngIf",o.targetForm.showError("password",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("password",t,"pattern")),e.xp6(7),e.Q6J("ngIf",o.targetForm.showError("mutual_user",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("mutual_user",t,"pattern")),e.xp6(11),e.Q6J("ngIf",o.targetForm.showError("mutual_password",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("mutual_password",t,"pattern"))}}function Fo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,115),e.qZA())}function $o(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,116),e.qZA())}function Zo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,117),e.qZA())}function Bo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,118),e.qZA())}function Go(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,119),e.qZA())}function yo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,120),e.qZA())}function xo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,121),e.qZA())}function wo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,122),e.qZA())}function qo(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,123),e.qZA())}function Ho(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,124),e.qZA())}function Ko(n,_){1&n&&(e.TgZ(0,"span",41),e.SDv(1,125),e.qZA())}function ko(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.TgZ(4,"button",52),e.NdJ("click",function(){const i=e.CHM(t),s=i.index,a=i.$implicit,d=e.oxw(),c=d.$implicit,u=d.index;return e.oxw(3).removeInitiatorImage(c,s,u,a)}),e._UZ(5,"i",16),e.qZA(),e.qZA(),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(4);e.xp6(2),e.Q6J("value",t),e.xp6(3),e.Q6J("ngClass",e.VKq(2,z,o.icons.destroy))}}function Xo(n,_){1&n&&(e.TgZ(0,"span"),e.SDv(1,126),e.qZA())}function Qo(n,_){if(1&n&&(e.TgZ(0,"div",22),e.TgZ(1,"div",23),e.TgZ(2,"cd-select",127),e._UZ(3,"i",25),e.ynx(4),e.SDv(5,128),e.BQk(),e.qZA(),e.qZA(),e.qZA()),2&n){const t=e.oxw(),o=t.$implicit,i=t.index,s=e.oxw(3);e.xp6(2),e.Q6J("data",o.getValue("luns"))("options",s.imagesInitiatorSelections[i])("messages",s.messages.initiatorImage),e.xp6(1),e.Q6J("ngClass",e.VKq(4,z,s.icons.add))}}function zo(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",92),e.TgZ(1,"div",5),e.ynx(2),e.SDv(3,93),e.BQk(),e._uU(4),e.TgZ(5,"button",94),e.NdJ("click",function(){const s=e.CHM(t).index;return e.oxw(3).removeInitiator(s)}),e._UZ(6,"i",25),e.qZA(),e.qZA(),e.TgZ(7,"div",7),e.TgZ(8,"div",8),e.TgZ(9,"label",95),e.SDv(10,96),e.qZA(),e.TgZ(11,"div",11),e.TgZ(12,"input",97),e.NdJ("blur",function(){return e.CHM(t),e.oxw(3).updatedInitiatorSelector()}),e.qZA(),e.YNc(13,Fo,2,0,"span",17),e.YNc(14,$o,2,0,"span",17),e.YNc(15,Zo,2,0,"span",17),e.qZA(),e.qZA(),e.ynx(16,61),e.TgZ(17,"div",8),e.TgZ(18,"label",98),e.SDv(19,99),e.qZA(),e.TgZ(20,"div",11),e._UZ(21,"input",100),e.YNc(22,Bo,2,0,"span",17),e.YNc(23,Go,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(24,"div",8),e.TgZ(25,"label",101),e.SDv(26,102),e.qZA(),e.TgZ(27,"div",11),e.TgZ(28,"div",12),e._UZ(29,"input",103),e.TgZ(30,"span",14),e._UZ(31,"button",104),e._UZ(32,"cd-copy-2-clipboard-button",105),e.qZA(),e.qZA(),e.YNc(33,yo,2,0,"span",17),e.YNc(34,xo,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(35,"div",8),e.TgZ(36,"label",106),e.ynx(37),e.SDv(38,107),e.BQk(),e.qZA(),e.TgZ(39,"div",11),e._UZ(40,"input",108),e.YNc(41,wo,2,0,"span",17),e.YNc(42,qo,2,0,"span",17),e.qZA(),e.qZA(),e.TgZ(43,"div",8),e.TgZ(44,"label",109),e.SDv(45,110),e.qZA(),e.TgZ(46,"div",11),e.TgZ(47,"div",12),e._UZ(48,"input",111),e.TgZ(49,"span",14),e._UZ(50,"button",104),e._UZ(51,"cd-copy-2-clipboard-button",105),e.qZA(),e.qZA(),e.YNc(52,Ho,2,0,"span",17),e.YNc(53,Ko,2,0,"span",17),e.qZA(),e.qZA(),e.BQk(),e.TgZ(54,"div",8),e.TgZ(55,"label",112),e.SDv(56,113),e.qZA(),e.TgZ(57,"div",11),e.YNc(58,ko,6,4,"ng-container",21),e.YNc(59,Xo,2,0,"span",55),e.YNc(60,Qo,6,6,"div",114),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=_.$implicit,o=_.index;e.oxw(2);const i=e.MAs(2),s=e.oxw();e.Q6J("formGroup",t),e.xp6(4),e.hij(": ",t.getValue("client_iqn")," "),e.xp6(2),e.Q6J("ngClass",e.VKq(25,z,s.icons.destroy)),e.xp6(7),e.Q6J("ngIf",t.showError("client_iqn",i,"notUnique")),e.xp6(1),e.Q6J("ngIf",t.showError("client_iqn",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("client_iqn",i,"pattern")),e.xp6(6),e.Q6J("id","user"+o),e.xp6(1),e.Q6J("ngIf",t.showError("user",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("user",i,"pattern")),e.xp6(6),e.Q6J("id","password"+o),e.xp6(2),e.Q6J("cdPasswordButton","password"+o),e.xp6(1),e.Q6J("source","password"+o),e.xp6(1),e.Q6J("ngIf",t.showError("password",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("password",i,"pattern")),e.xp6(6),e.Q6J("id","mutual_user"+o),e.xp6(1),e.Q6J("ngIf",t.showError("mutual_user",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("mutual_user",i,"pattern")),e.xp6(6),e.Q6J("id","mutual_password"+o),e.xp6(2),e.Q6J("cdPasswordButton","mutual_password"+o),e.xp6(1),e.Q6J("source","mutual_password"+o),e.xp6(1),e.Q6J("ngIf",t.showError("mutual_password",i,"required")),e.xp6(1),e.Q6J("ngIf",t.showError("mutual_password",i,"pattern")),e.xp6(5),e.Q6J("ngForOf",t.getValue("luns")),e.xp6(1),e.Q6J("ngIf",t.getValue("cdIsInGroup")),e.xp6(1),e.Q6J("ngIf",!t.getValue("cdIsInGroup"))}}function Jo(n,_){1&n&&(e.TgZ(0,"span",48),e.SDv(1,129),e.qZA())}function Yo(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",8),e.TgZ(1,"label",86),e.SDv(2,87),e.qZA(),e.TgZ(3,"div",88),e.YNc(4,zo,61,27,"div",89),e.TgZ(5,"div",22),e.TgZ(6,"div",23),e.YNc(7,Jo,2,0,"span",18),e.TgZ(8,"button",90),e.NdJ("click",function(){return e.CHM(t),e.oxw(2).addInitiator(),!1}),e._UZ(9,"i",25),e.ynx(10),e.SDv(11,91),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(12,"hr"),e.qZA(),e.qZA()}if(2&n){const t=e.oxw(2);e.xp6(4),e.Q6J("ngForOf",t.initiators.controls),e.xp6(3),e.Q6J("ngIf",0===t.initiators.controls.length),e.xp6(2),e.Q6J("ngClass",e.VKq(3,z,t.icons.add))}}function Vo(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.TgZ(4,"button",52),e.NdJ("click",function(){const s=e.CHM(t).index,a=e.oxw(),d=a.$implicit,c=a.index;return e.oxw(3).removeGroupInitiator(d,s,c)}),e._UZ(5,"i",16),e.qZA(),e.qZA(),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(4);e.xp6(2),e.Q6J("value",t),e.xp6(3),e.Q6J("ngClass",e.VKq(2,z,o.icons.destroy))}}function Uo(n,_){if(1&n){const t=e.EpF();e.ynx(0),e.TgZ(1,"div",50),e._UZ(2,"input",51),e.TgZ(3,"span",14),e.TgZ(4,"button",52),e.NdJ("click",function(){const s=e.CHM(t).index,a=e.oxw(),d=a.$implicit,c=a.index;return e.oxw(3).removeGroupDisk(d,s,c)}),e._UZ(5,"i",16),e.qZA(),e.qZA(),e.qZA(),e.BQk()}if(2&n){const t=_.$implicit,o=e.oxw(4);e.xp6(2),e.Q6J("value",t),e.xp6(3),e.Q6J("ngClass",e.VKq(2,z,o.icons.destroy))}}function jo(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",92),e.TgZ(1,"div",5),e.ynx(2),e.SDv(3,133),e.BQk(),e._uU(4),e.TgZ(5,"button",94),e.NdJ("click",function(){const s=e.CHM(t).index;return e.oxw(3).removeGroup(s)}),e._UZ(6,"i",25),e.qZA(),e.qZA(),e.TgZ(7,"div",7),e.TgZ(8,"div",8),e.TgZ(9,"label",134),e.SDv(10,135),e.qZA(),e.TgZ(11,"div",11),e._UZ(12,"input",136),e.qZA(),e.qZA(),e.TgZ(13,"div",8),e.TgZ(14,"label",137),e.ynx(15),e.SDv(16,138),e.BQk(),e.qZA(),e.TgZ(17,"div",11),e.YNc(18,Vo,6,4,"ng-container",21),e.TgZ(19,"div",22),e.TgZ(20,"div",23),e.TgZ(21,"cd-select",24),e.NdJ("selection",function(i){const a=e.CHM(t).index;return e.oxw(3).onGroupMemberSelection(i,a)}),e._UZ(22,"i",25),e.ynx(23),e.SDv(24,139),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(25,"hr"),e.qZA(),e.qZA(),e.TgZ(26,"div",8),e.TgZ(27,"label",28),e.ynx(28),e.SDv(29,140),e.BQk(),e.qZA(),e.TgZ(30,"div",11),e.YNc(31,Uo,6,4,"ng-container",21),e.TgZ(32,"div",22),e.TgZ(33,"div",23),e.TgZ(34,"cd-select",127),e._UZ(35,"i",25),e.ynx(36),e.SDv(37,141),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(38,"hr"),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=_.$implicit,o=_.index,i=e.oxw(3);e.Q6J("formGroup",t),e.xp6(4),e.hij(": ",t.getValue("group_id")," "),e.xp6(2),e.Q6J("ngClass",e.VKq(13,z,i.icons.destroy)),e.xp6(12),e.Q6J("ngForOf",t.getValue("members")),e.xp6(3),e.Q6J("data",t.getValue("members"))("options",i.groupMembersSelections[o])("messages",i.messages.groupInitiator),e.xp6(1),e.Q6J("ngClass",e.VKq(15,z,i.icons.add)),e.xp6(9),e.Q6J("ngForOf",t.getValue("disks")),e.xp6(3),e.Q6J("data",t.getValue("disks"))("options",i.groupDiskSelections[o])("messages",i.messages.initiatorImage),e.xp6(1),e.Q6J("ngClass",e.VKq(17,z,i.icons.add))}}function Wo(n,_){1&n&&(e.TgZ(0,"span",48),e.SDv(1,142),e.qZA())}function en(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",8),e.TgZ(1,"label",86),e.SDv(2,130),e.qZA(),e.TgZ(3,"div",131),e.YNc(4,jo,39,19,"div",89),e.TgZ(5,"div",22),e.TgZ(6,"div",23),e.YNc(7,Wo,2,0,"span",18),e.TgZ(8,"button",90),e.NdJ("click",function(){return e.CHM(t),e.oxw(2).addGroup(),!1}),e._UZ(9,"i",25),e.ynx(10),e.SDv(11,132),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=e.oxw(2);e.xp6(4),e.Q6J("ngForOf",t.groups.controls),e.xp6(3),e.Q6J("ngIf",0===t.groups.controls.length),e.xp6(2),e.Q6J("ngClass",e.VKq(3,z,t.icons.add))}}function tn(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.TgZ(9,"div",8),e.TgZ(10,"label",9),e.SDv(11,10),e.qZA(),e.TgZ(12,"div",11),e.TgZ(13,"div",12),e._UZ(14,"input",13),e.TgZ(15,"span",14),e.TgZ(16,"button",15),e.NdJ("click",function(){return e.CHM(t),e.oxw().targetSettingsModal()}),e._UZ(17,"i",16),e.qZA(),e.qZA(),e.qZA(),e.YNc(18,po,2,0,"span",17),e.YNc(19,go,2,0,"span",17),e.YNc(20,uo,9,0,"span",17),e.YNc(21,mo,2,0,"span",18),e._UZ(22,"hr"),e.qZA(),e.qZA(),e.TgZ(23,"div",8),e.TgZ(24,"label",19),e.SDv(25,20),e.qZA(),e.TgZ(26,"div",11),e.YNc(27,To,6,4,"ng-container",21),e.TgZ(28,"div",22),e.TgZ(29,"div",23),e.TgZ(30,"cd-select",24),e.NdJ("selection",function(i){return e.CHM(t),e.oxw().onPortalSelection(i)}),e._UZ(31,"i",25),e.ynx(32),e.SDv(33,26),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(34,"input",27),e.YNc(35,fo,2,1,"span",17),e._UZ(36,"hr"),e.qZA(),e.qZA(),e.TgZ(37,"div",8),e.TgZ(38,"label",28),e.SDv(39,29),e.qZA(),e.TgZ(40,"div",11),e.YNc(41,Ro,12,10,"ng-container",21),e._UZ(42,"input",30),e.YNc(43,Mo,2,0,"span",17),e.YNc(44,Oo,2,0,"span",17),e.TgZ(45,"div",22),e.TgZ(46,"div",23),e.TgZ(47,"cd-select",24),e.NdJ("selection",function(i){return e.CHM(t),e.oxw().onImageSelection(i)}),e._UZ(48,"i",25),e.ynx(49),e.SDv(50,31),e.BQk(),e.qZA(),e.qZA(),e.qZA(),e._UZ(51,"hr"),e.qZA(),e.qZA(),e.TgZ(52,"div",8),e.TgZ(53,"div",32),e.TgZ(54,"div",33),e._UZ(55,"input",34),e.TgZ(56,"label",35),e.SDv(57,36),e.qZA(),e.qZA(),e._UZ(58,"hr"),e.qZA(),e.qZA(),e.YNc(59,Lo,41,8,"div",37),e.YNc(60,Yo,13,5,"div",38),e.YNc(61,en,12,5,"div",38),e.qZA(),e.TgZ(62,"div",39),e.TgZ(63,"cd-form-button-panel",40),e.NdJ("submitActionEvent",function(){return e.CHM(t),e.oxw().submit()}),e.ALo(64,"titlecase"),e.ALo(65,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=e.MAs(2),o=e.oxw();e.xp6(1),e.Q6J("formGroup",o.targetForm),e.xp6(6),e.pQV(e.lcZ(6,26,o.action))(e.lcZ(7,28,o.resource)),e.QtT(5),e.xp6(10),e.Q6J("ngClass",e.VKq(34,z,o.icons.deepCheck)),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("target_iqn",t,"required")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("target_iqn",t,"pattern")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("target_iqn",t,"iqn")),e.xp6(1),e.Q6J("ngIf",o.hasAdvancedSettings(o.targetForm.getValue("target_controls"))),e.xp6(6),e.Q6J("ngForOf",o.portals.value),e.xp6(3),e.Q6J("data",o.portals.value)("options",o.portalsSelections)("messages",o.messages.portals),e.xp6(1),e.Q6J("ngClass",e.VKq(36,z,o.icons.add)),e.xp6(4),e.Q6J("ngIf",o.targetForm.showError("portals",t,"minGateways")),e.xp6(6),e.Q6J("ngForOf",o.targetForm.getValue("disks")),e.xp6(2),e.Q6J("ngIf",o.targetForm.showError("disks",t,"dupLunId")),e.xp6(1),e.Q6J("ngIf",o.targetForm.showError("disks",t,"dupWwn")),e.xp6(3),e.Q6J("data",o.disks.value)("options",o.imagesSelections)("messages",o.messages.images),e.xp6(1),e.Q6J("ngClass",e.VKq(38,z,o.icons.add)),e.xp6(11),e.Q6J("ngIf",o.cephIscsiConfigVersion>10&&!o.targetForm.getValue("acl_enabled")),e.xp6(1),e.Q6J("ngIf",o.targetForm.getValue("acl_enabled")),e.xp6(1),e.Q6J("ngIf",o.targetForm.getValue("acl_enabled")),e.xp6(2),e.Q6J("form",o.targetForm)("submitText",e.lcZ(64,30,o.action)+" "+e.lcZ(65,32,o.resource))}}let rt=(()=>{class n extends q.E{constructor(t,o,i,s,a,d,c){super(),this.iscsiService=t,this.modalService=o,this.rbdService=i,this.router=s,this.route=a,this.taskWrapper=d,this.actionLabels=c,this.api_version=0,this.minimum_gateways=1,this.icons=T.P,this.isEdit=!1,this.portalsSelections=[],this.imagesInitiatorSelections=[],this.groupDiskSelections=[],this.groupMembersSelections=[],this.imagesSettings={},this.messages={portals:new ae.a({noOptions:"There are no portals available."}),images:new ae.a({noOptions:"There are no images available."}),initiatorImage:new ae.a({noOptions:"There are no images available. Please make sure you add an image to the target."}),groupInitiator:new ae.a({noOptions:"There are no initiators available. Please make sure you add an initiator to the target."})},this.IQN_REGEX=/^iqn\.(19|20)\d\d-(0[1-9]|1[0-2])\.\D{2,3}(\.[A-Za-z0-9-]+)+(:[A-Za-z0-9-\.]+)*$/,this.USER_REGEX=/^[\w\.:@_-]{8,64}$/,this.PASSWORD_REGEX=/^[\w@\-_\/]{12,16}$/,this.resource="target"}ngOnInit(){const t=[this.iscsiService.listTargets(),this.rbdService.list(),this.iscsiService.portals(),this.iscsiService.settings(),this.iscsiService.version()];this.router.url.startsWith("/block/iscsi/targets/edit")&&(this.isEdit=!0,this.route.params.subscribe(o=>{this.target_iqn=decodeURIComponent(o.target_iqn),t.push(this.iscsiService.getTarget(this.target_iqn))})),this.action=this.isEdit?this.actionLabels.EDIT:this.actionLabels.CREATE,(0,W.D)(t).subscribe(o=>{const i=C()(o[0]).filter(a=>a.target_iqn!==this.target_iqn).flatMap(a=>a.disks).map(a=>`${a.pool}/${a.image}`).value();"api_version"in o[3]&&(this.api_version=o[3].api_version),this.minimum_gateways=o[3].config.minimum_gateways,this.target_default_controls=o[3].target_default_controls,this.target_controls_limits=o[3].target_controls_limits,this.disk_default_controls=o[3].disk_default_controls,this.disk_controls_limits=o[3].disk_controls_limits,this.backstores=o[3].backstores,this.default_backstore=o[3].default_backstore,this.unsupported_rbd_features=o[3].unsupported_rbd_features,this.required_rbd_features=o[3].required_rbd_features,this.imagesAll=C()(o[1]).flatMap(a=>a.value).filter(a=>!a.namespace&&!(-1!==i.indexOf(`${a.pool_name}/${a.name}`)||0===this.getValidBackstores(a).length)).value(),this.imagesSelections=this.imagesAll.map(a=>new V.$(!1,`${a.pool_name}/${a.name}`,""));const s=[];o[2].forEach(a=>{a.ip_addresses.forEach(d=>{s.push(new V.$(!1,a.name+":"+d,""))})}),this.portalsSelections=[...s],this.cephIscsiConfigVersion=o[4].ceph_iscsi_config_version,this.createForm(),o[5]&&this.resolveModel(o[5]),this.loadingReady()})}createForm(){if(this.targetForm=new M.d({target_iqn:new r.NI("iqn.2001-07.com.ceph:"+Date.now(),{validators:[r.kI.required,r.kI.pattern(this.IQN_REGEX)]}),target_controls:new r.NI({}),portals:new r.NI([],{validators:[B.h.custom("minGateways",t=>C().uniq(t.map(i=>i.split(":")[0])).length{const o=this.getLunIds(t);return o.length!==C().uniq(o).length}),B.h.custom("dupWwn",t=>{const o=this.getWwns(t);return o.length!==C().uniq(o).length})]}),initiators:new r.Oe([]),groups:new r.Oe([]),acl_enabled:new r.NI(!1)}),this.cephIscsiConfigVersion>10){const t=new M.d({user:new r.NI(""),password:new r.NI(""),mutual_user:new r.NI(""),mutual_password:new r.NI("")});this.setAuthValidator(t),this.targetForm.addControl("auth",t)}}resolveModel(t){this.targetForm.patchValue({target_iqn:t.target_iqn,target_controls:t.target_controls,acl_enabled:t.acl_enabled}),this.cephIscsiConfigVersion>10&&this.targetForm.patchValue({auth:t.auth});const o=[];C().forEach(t.portals,s=>{o.push(`${s.host}:${s.ip}`)}),this.targetForm.patchValue({portals:o});const i=[];C().forEach(t.disks,s=>{const a=`${s.pool}/${s.image}`;i.push(a),this.imagesSettings[a]={backstore:s.backstore},this.imagesSettings[a][s.backstore]=s.controls,"lun"in s&&(this.imagesSettings[a].lun=s.lun),"wwn"in s&&(this.imagesSettings[a].wwn=s.wwn),this.onImageSelection({option:{name:a,selected:!0}})}),this.targetForm.patchValue({disks:i}),C().forEach(t.clients,s=>{const a=this.addInitiator();s.luns=C().map(s.luns,d=>`${d.pool}/${d.image}`),a.patchValue(s)}),t.groups.forEach((s,a)=>{const d=this.addGroup();s.disks=C().map(s.disks,c=>`${c.pool}/${c.image}`),d.patchValue(s),C().forEach(s.members,c=>{this.onGroupMemberSelection({option:new V.$(!0,c,"")},a)})})}hasAdvancedSettings(t){return Object.values(t).length>0}get portals(){return this.targetForm.get("portals")}onPortalSelection(){this.portals.setValue(this.portals.value)}removePortal(t,o){return this.portalsSelections.forEach(i=>{i.name===o&&(i.selected=!1)}),this.portals.value.splice(t,1),this.portals.setValue(this.portals.value),!1}get disks(){return this.targetForm.get("disks")}removeImage(t,o){return this.imagesSelections.forEach(i=>{i.name===o&&(i.selected=!1)}),this.disks.value.splice(t,1),this.removeImageRefs(o),this.targetForm.get("disks").updateValueAndValidity({emitEvent:!1}),!1}removeImageRefs(t){this.initiators.controls.forEach(o=>{const i=o.value.luns.filter(s=>s!==t);o.get("luns").setValue(i)}),this.groups.controls.forEach(o=>{const i=o.value.disks.filter(s=>s!==t);o.get("disks").setValue(i)}),C().forEach(this.imagesInitiatorSelections,(o,i)=>{this.imagesInitiatorSelections[i]=o.filter(s=>s.name!==t)}),C().forEach(this.groupDiskSelections,(o,i)=>{this.groupDiskSelections[i]=o.filter(s=>s.name!==t)})}getDefaultBackstore(t){let o=this.default_backstore;const i=this.getImageById(t);return this.validFeatures(i,this.default_backstore)||this.backstores.forEach(s=>{s!==this.default_backstore&&this.validFeatures(i,s)&&(o=s)}),o}isLunIdInUse(t,o){const i=this.disks.value.filter(s=>s!==o);return this.getLunIds(i).includes(t)}getLunIds(t){return C().map(t,o=>this.imagesSettings[o].lun)}nextLunId(t){const o=this.disks.value.filter(a=>a!==t),i=this.getLunIds(o);let s=0;for(;i.includes(s);)s++;return s}getWwns(t){return C().map(t,i=>this.imagesSettings[i].wwn).filter(i=>C().isString(i)&&""!==i)}onImageSelection(t){const o=t.option;if(o.selected){if(this.imagesSettings[o.name])this.isLunIdInUse(this.imagesSettings[o.name].lun,o.name)&&(this.imagesSettings[o.name].lun=this.nextLunId(o.name));else{const i=this.getDefaultBackstore(o.name);this.imagesSettings[o.name]={backstore:i,lun:this.nextLunId(o.name)},this.imagesSettings[o.name][i]={}}C().forEach(this.imagesInitiatorSelections,(i,s)=>{i.push(new V.$(!1,o.name,"")),this.imagesInitiatorSelections[s]=[...i]}),C().forEach(this.groupDiskSelections,(i,s)=>{i.push(new V.$(!1,o.name,"")),this.groupDiskSelections[s]=[...i]})}else this.removeImageRefs(o.name);this.targetForm.get("disks").updateValueAndValidity({emitEvent:!1})}get initiators(){return this.targetForm.get("initiators")}addInitiator(){const t=new M.d({client_iqn:new r.NI("",{validators:[r.kI.required,B.h.custom("notUnique",i=>{const s=this.initiators.controls.reduce(function(a,d){return a.concat(d.value.client_iqn)},[]);return s.indexOf(i)!==s.lastIndexOf(i)}),r.kI.pattern(this.IQN_REGEX)]}),auth:new M.d({user:new r.NI(""),password:new r.NI(""),mutual_user:new r.NI(""),mutual_password:new r.NI("")}),luns:new r.NI([]),cdIsInGroup:new r.NI(!1)});this.setAuthValidator(t),this.initiators.push(t),C().forEach(this.groupMembersSelections,(i,s)=>{i.push(new V.$(!1,"","")),this.groupMembersSelections[s]=[...i]});const o=C().map(this.targetForm.getValue("disks"),i=>new V.$(!1,i,""));return this.imagesInitiatorSelections.push(o),t}setAuthValidator(t){B.h.validateIf(t.get("user"),()=>t.getValue("password")||t.getValue("mutual_user")||t.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.USER_REGEX)],[t.get("password"),t.get("mutual_user"),t.get("mutual_password")]),B.h.validateIf(t.get("password"),()=>t.getValue("user")||t.getValue("mutual_user")||t.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.PASSWORD_REGEX)],[t.get("user"),t.get("mutual_user"),t.get("mutual_password")]),B.h.validateIf(t.get("mutual_user"),()=>t.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.USER_REGEX)],[t.get("user"),t.get("password"),t.get("mutual_password")]),B.h.validateIf(t.get("mutual_password"),()=>t.getValue("mutual_user"),[r.kI.required],[r.kI.pattern(this.PASSWORD_REGEX)],[t.get("user"),t.get("password"),t.get("mutual_user")])}removeInitiator(t){const o=this.initiators.value[t];this.initiators.removeAt(t),C().forEach(this.groupMembersSelections,(i,s)=>{i.splice(t,1),this.groupMembersSelections[s]=[...i]}),this.groups.controls.forEach(i=>{const s=i.value.members.filter(a=>a!==o.client_iqn);i.get("members").setValue(s)}),this.imagesInitiatorSelections.splice(t,1)}updatedInitiatorSelector(){this.initiators.controls.forEach(t=>{t.get("client_iqn").updateValueAndValidity({emitEvent:!1})}),C().forEach(this.groupMembersSelections,(t,o)=>{C().forEach(t,(i,s)=>{const a=i.name;i.name=this.initiators.controls[s].value.client_iqn,this.groups.controls.forEach(d=>{const c=d.value.members,u=c.indexOf(a);-1!==u&&(c[u]=i.name),d.get("members").setValue(c)})}),this.groupMembersSelections[o]=[...this.groupMembersSelections[o]]})}removeInitiatorImage(t,o,i,s){const a=t.getValue("luns");return a.splice(o,1),t.patchValue({luns:a}),this.imagesInitiatorSelections[i].forEach(d=>{d.name===s&&(d.selected=!1)}),!1}get groups(){return this.targetForm.get("groups")}addGroup(){const t=new M.d({group_id:new r.NI("",{validators:[r.kI.required]}),members:new r.NI([]),disks:new r.NI([])});this.groups.push(t);const o=C().map(this.targetForm.getValue("disks"),s=>new V.$(!1,s,""));this.groupDiskSelections.push(o);const i=C().map(this.initiators.value,s=>new V.$(!1,s.client_iqn,"",!s.cdIsInGroup));return this.groupMembersSelections.push(i),t}removeGroup(t){this.groups.removeAt(t),this.groupMembersSelections[t].filter(i=>i.selected).forEach(i=>{i.selected=!1,this.onGroupMemberSelection({option:i},t)}),this.groupMembersSelections.splice(t,1),this.groupDiskSelections.splice(t,1)}onGroupMemberSelection(t,o){const i=t.option;let s=[];i.selected||(s=this.groupDiskSelections[o].filter(d=>d.selected).map(d=>d.name)),this.initiators.controls.forEach((a,d)=>{a.value.client_iqn===i.name&&(a.patchValue({luns:s}),a.get("cdIsInGroup").setValue(i.selected),C().forEach(this.groupMembersSelections,c=>{c[d].enabled=!i.selected}),this.imagesInitiatorSelections[d].forEach(c=>{c.selected=s.includes(c.name)}))})}removeGroupInitiator(t,o,i){const s=t.getValue("members")[o];t.getValue("members").splice(o,1),this.onGroupMemberSelection({option:new V.$(!1,s,"")},i)}removeGroupDisk(t,o,i){const s=t.getValue("disks")[o];t.getValue("disks").splice(o,1),this.groupDiskSelections[i].forEach(a=>{a.name===s&&(a.selected=!1)}),this.groupDiskSelections[i]=[...this.groupDiskSelections[i]]}submit(){const t=C().cloneDeep(this.targetForm.value),o={target_iqn:this.targetForm.getValue("target_iqn"),target_controls:this.targetForm.getValue("target_controls"),acl_enabled:this.targetForm.getValue("acl_enabled"),portals:[],disks:[],clients:[],groups:[]};if(this.cephIscsiConfigVersion>10){const s=this.targetForm.get("auth");s.getValue("user")||s.get("user").setValue(""),s.getValue("password")||s.get("password").setValue(""),s.getValue("mutual_user")||s.get("mutual_user").setValue(""),s.getValue("mutual_password")||s.get("mutual_password").setValue("");const a=this.targetForm.getValue("acl_enabled");o.auth={user:a?"":s.getValue("user"),password:a?"":s.getValue("password"),mutual_user:a?"":s.getValue("mutual_user"),mutual_password:a?"":s.getValue("mutual_password")}}let i;t.disks.forEach(s=>{const a=s.split("/"),d=this.imagesSettings[s].backstore;o.disks.push({pool:a[0],image:a[1],backstore:d,controls:this.imagesSettings[s][d],lun:this.imagesSettings[s].lun,wwn:this.imagesSettings[s].wwn})}),t.portals.forEach(s=>{const a=s.indexOf(":");o.portals.push({host:s.substring(0,a),ip:s.substring(a+1)})}),o.acl_enabled&&(t.initiators.forEach(s=>{s.auth.user||(s.auth.user=""),s.auth.password||(s.auth.password=""),s.auth.mutual_user||(s.auth.mutual_user=""),s.auth.mutual_password||(s.auth.mutual_password=""),delete s.cdIsInGroup;const a=[];s.luns.forEach(d=>{const c=d.split("/");a.push({pool:c[0],image:c[1]})}),s.luns=a}),o.clients=t.initiators),o.acl_enabled&&(t.groups.forEach(s=>{const a=[];s.disks.forEach(d=>{const c=d.split("/");a.push({pool:c[0],image:c[1]})}),s.disks=a}),o.groups=t.groups),this.isEdit?(o.new_target_iqn=o.target_iqn,o.target_iqn=this.target_iqn,i=this.taskWrapper.wrapTaskAroundCall({task:new F.R("iscsi/target/edit",{target_iqn:o.target_iqn}),call:this.iscsiService.updateTarget(this.target_iqn,o)})):i=this.taskWrapper.wrapTaskAroundCall({task:new F.R("iscsi/target/create",{target_iqn:o.target_iqn}),call:this.iscsiService.createTarget(o)}),i.subscribe({error:()=>{this.targetForm.setErrors({cdSubmitButton:!0})},complete:()=>this.router.navigate(["/block/iscsi/targets"])})}targetSettingsModal(){const t={target_controls:this.targetForm.get("target_controls"),target_default_controls:this.target_default_controls,target_controls_limits:this.target_controls_limits};this.modalRef=this.modalService.show(ro,t)}imageSettingsModal(t){const o={imagesSettings:this.imagesSettings,image:t,api_version:this.api_version,disk_default_controls:this.disk_default_controls,disk_controls_limits:this.disk_controls_limits,backstores:this.getValidBackstores(this.getImageById(t)),control:this.targetForm.get("disks")};this.modalRef=this.modalService.show(so,o)}validFeatures(t,o){const i=t.features,s=this.required_rbd_features[o];return(i&s)===s&&0==(i&this.unsupported_rbd_features[o])}getImageById(t){return this.imagesAll.find(o=>t===`${o.pool_name}/${o.name}`)}getValidBackstores(t){return this.backstores.filter(o=>this.validFeatures(t,o))}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(k),e.Y36(re.Z),e.Y36(x),e.Y36(m.F0),e.Y36(m.gz),e.Y36(Q.P),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-form"]],features:[e.qOj],decls:1,vars:1,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$,G,X,J,te,A,w,de,pe,ge,ue,me,Te,fe,Ce,Se,y,Ze,Be,Ge,ye,xe,we,qe,L,Ot,At,ht,Pt,It,bt,Nt,Dt,vt,Lt,Ft,$t,Zt,Bt,Gt,yt,xt,wt,qt,Ht,Kt,kt,Xt,Qt,zt;return _="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",t="Target IQN",o="Portals",i="Add portal",s="Images",a="Add image",d="ACL authentication",c="This field is required.",u="IQN has wrong pattern.",S="An IQN has the following notation 'iqn.$year-$month.$reversedAddress:$definedName'",N="For example: iqn.2016-06.org.dashboard:storage:disk.sn-a8675309",P="More information",$="This target has modified advanced settings.",G="At least " + "\ufffd0\ufffd" + " gateways are required.",X="Backstore: " + "\ufffd0\ufffd" + ".\xA0",J="This image has modified settings.",te="Duplicated LUN numbers.",A="Duplicated WWN.",w="User",de="Password",pe="Mutual User",ge="Mutual Password",ue="This field is required.",me="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",Te="This field is required.",fe="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",Ce="This field is required.",Se="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",y="This field is required.",Ze="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",Be="Initiators",Ge="Add initiator",ye="Initiator",xe="Client IQN",we="User",qe="Password",L="Mutual User",Ot="Mutual Password",At="Images",ht="Initiator IQN needs to be unique.",Pt="This field is required.",It="IQN has wrong pattern.",bt="This field is required.",Nt="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",Dt="This field is required.",vt="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",Lt="This field is required.",Ft="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",$t="This field is required.",Zt="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",Bt="Initiator belongs to a group. Images will be configure in the group.",Gt="Add image",yt="No items added.",xt="Groups",wt="Add group",qt="Group",Ht="Name",Kt="Initiators",kt="Add initiator",Xt="Images",Qt="Add image",zt="No items added.",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["name","targetForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"card"],[1,"card-header"],_,[1,"card-body"],[1,"form-group","row"],["for","target_iqn",1,"cd-col-form-label","required"],t,[1,"cd-col-form-input"],[1,"input-group"],["type","text","id","target_iqn","name","target_iqn","formControlName","target_iqn","cdTrim","",1,"form-control"],[1,"input-group-append"],["id","ecp-info-button","type","button",1,"btn","btn-light",3,"click"],["aria-hidden","true",3,"ngClass"],["class","invalid-feedback",4,"ngIf"],["class","form-text text-muted",4,"ngIf"],["for","portals",1,"cd-col-form-label","required"],o,[4,"ngFor","ngForOf"],[1,"row"],[1,"col-md-12"],["elemClass","btn btn-light float-right",3,"data","options","messages","selection"],[3,"ngClass"],i,["type","hidden","id","portals","name","portals","formControlName","portals",1,"form-control"],["for","disks",1,"cd-col-form-label"],s,["type","hidden","id","disks","name","disks","formControlName","disks",1,"form-control"],a,[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["type","checkbox","formControlName","acl_enabled","name","acl_enabled","id","acl_enabled",1,"custom-control-input"],["for","acl_enabled",1,"custom-control-label"],d,["formGroupName","auth",4,"ngIf"],["class","form-group row",4,"ngIf"],[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],c,u,S,N,["target","_blank","href","https://en.wikipedia.org/wiki/ISCSI#Addressing"],P,[1,"form-text","text-muted"],$,[1,"input-group","cd-mb"],["type","text","disabled","",1,"cd-form-control",3,"value"],["type","button",1,"btn","btn-light",3,"click"],G,["class","input-group-text",4,"ngIf"],[4,"ngIf"],[1,"input-group-text"],X,J,te,A,["formGroupName","auth"],["for","target_user",1,"cd-col-form-label"],w,["type","text","autocomplete","off","id","target_user","name","target_user","formControlName","user",1,"form-control"],["for","target_password",1,"cd-col-form-label"],de,["type","password","autocomplete","new-password","id","target_password","name","target_password","formControlName","password",1,"form-control"],["type","button","cdPasswordButton","target_password",1,"btn","btn-light"],["source","target_password"],["for","target_mutual_user",1,"cd-col-form-label"],pe,["type","text","autocomplete","off","id","target_mutual_user","name","target_mutual_user","formControlName","mutual_user",1,"form-control"],["for","target_mutual_password",1,"cd-col-form-label"],ge,["type","password","autocomplete","new-password","id","target_mutual_password","name","target_mutual_password","formControlName","mutual_password",1,"form-control"],["type","button","cdPasswordButton","target_mutual_password",1,"btn","btn-light"],["source","target_mutual_password"],ue,me,Te,fe,Ce,Se,y,Ze,["for","initiators",1,"cd-col-form-label"],Be,["formArrayName","initiators",1,"cd-col-form-input"],["class","card mb-2",3,"formGroup",4,"ngFor","ngForOf"],[1,"btn","btn-light","float-right",3,"click"],Ge,[1,"card","mb-2",3,"formGroup"],ye,["type","button",1,"close",3,"click"],["for","client_iqn",1,"cd-col-form-label","required"],xe,["type","text","formControlName","client_iqn","cdTrim","",1,"form-control",3,"blur"],["for","user",1,"cd-col-form-label"],we,["formControlName","user","autocomplete","off","type","text",1,"form-control",3,"id"],["for","password",1,"cd-col-form-label"],qe,["formControlName","password","autocomplete","new-password","type","password",1,"form-control",3,"id"],["type","button",1,"btn","btn-light",3,"cdPasswordButton"],[3,"source"],["for","mutual_user",1,"cd-col-form-label"],L,["formControlName","mutual_user","autocomplete","off","type","text",1,"form-control",3,"id"],["for","mutual_password",1,"cd-col-form-label"],Ot,["formControlName","mutual_password","autocomplete","new-password","type","password",1,"form-control",3,"id"],["for","luns",1,"cd-col-form-label"],At,["class","row",4,"ngIf"],ht,Pt,It,bt,Nt,Dt,vt,Lt,Ft,$t,Zt,Bt,["elemClass","btn btn-light float-right",3,"data","options","messages"],Gt,yt,xt,["formArrayName","groups",1,"cd-col-form-input"],wt,qt,["for","group_id",1,"cd-col-form-label","required"],Ht,["type","text","formControlName","group_id",1,"form-control"],["for","members",1,"cd-col-form-label"],Kt,kt,Xt,Qt,zt]},template:function(t,o){1&t&&e.YNc(0,tn,66,40,"div",0),2&t&&e.Q6J("cdFormLoading",o.loading)},directives:[st.y,r._Y,r.JL,r.sg,v.V,g.P,f.o,r.Fj,h.b,r.JJ,r.u,lo,l.mk,l.O5,l.sg,co.H,r.Wl,O.p,r.x0,at.C,Je.s,r.CE],pipes:[l.rS,Ye.m,He.V],styles:[".cd-mb[_ngcontent-%COMP%]{margin-bottom:10px}"]}),n})();var lt=p(68136),he=p(30982),ee=p(64337),ve=p(99466),Ee=p(68774),ct=p(55657),se=p(38047),Ve=p(18001),Le=p(97161),oe=p(74937);function on(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,31),e.qZA())}function nn(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,32),e.qZA())}function _n(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,33),e.qZA())}function sn(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,34),e.qZA())}function an(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,35),e.qZA())}function rn(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,36),e.qZA())}function ln(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,37),e.qZA())}function cn(n,_){1&n&&(e.TgZ(0,"span",30),e.SDv(1,38),e.qZA())}let dn=(()=>{class n{constructor(t,o,i,s,a){this.authStorageService=t,this.activeModal=o,this.actionLabels=i,this.iscsiService=s,this.notificationService=a,this.USER_REGEX=/^[\w\.:@_-]{8,64}$/,this.PASSWORD_REGEX=/^[\w@\-_\/]{12,16}$/,this.permission=this.authStorageService.getPermissions().iscsi}ngOnInit(){this.hasPermission=this.permission.update,this.createForm(),this.iscsiService.getDiscovery().subscribe(t=>{this.discoveryForm.patchValue(t)})}createForm(){this.discoveryForm=new M.d({user:new r.NI({value:"",disabled:!this.hasPermission}),password:new r.NI({value:"",disabled:!this.hasPermission}),mutual_user:new r.NI({value:"",disabled:!this.hasPermission}),mutual_password:new r.NI({value:"",disabled:!this.hasPermission})}),B.h.validateIf(this.discoveryForm.get("user"),()=>this.discoveryForm.getValue("password")||this.discoveryForm.getValue("mutual_user")||this.discoveryForm.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.USER_REGEX)],[this.discoveryForm.get("password"),this.discoveryForm.get("mutual_user"),this.discoveryForm.get("mutual_password")]),B.h.validateIf(this.discoveryForm.get("password"),()=>this.discoveryForm.getValue("user")||this.discoveryForm.getValue("mutual_user")||this.discoveryForm.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.PASSWORD_REGEX)],[this.discoveryForm.get("user"),this.discoveryForm.get("mutual_user"),this.discoveryForm.get("mutual_password")]),B.h.validateIf(this.discoveryForm.get("mutual_user"),()=>this.discoveryForm.getValue("mutual_password"),[r.kI.required],[r.kI.pattern(this.USER_REGEX)],[this.discoveryForm.get("user"),this.discoveryForm.get("password"),this.discoveryForm.get("mutual_password")]),B.h.validateIf(this.discoveryForm.get("mutual_password"),()=>this.discoveryForm.getValue("mutual_user"),[r.kI.required],[r.kI.pattern(this.PASSWORD_REGEX)],[this.discoveryForm.get("user"),this.discoveryForm.get("password"),this.discoveryForm.get("mutual_user")])}submitAction(){this.iscsiService.updateDiscovery(this.discoveryForm.value).subscribe(()=>{this.notificationService.show(Ve.k.success,"Updated discovery authentication"),this.activeModal.close()},()=>{this.discoveryForm.setErrors({cdSubmitButton:!0})})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(I.Kz),e.Y36(D.p4),e.Y36(k),e.Y36(Le.g))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-discovery-modal"]],decls:46,vars:13,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$;return _="Discovery Authentication",t="User",o="Password",i="Mutual User",s="Mutual Password",a="This field is required.",d="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",c="This field is required.",u="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",S="This field is required.",N="User names must have a length of 8 to 64 characters and can contain alphanumeric characters, '.', '@', '-', '_' or ':'.",P="This field is required.",$="Passwords must have a length of 12 to 16 characters and can contain alphanumeric characters, '@', '-', '_' or '/'.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","discoveryForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","user",1,"cd-col-form-label"],t,[1,"cd-col-form-input"],["id","user","formControlName","user","type","text","autocomplete","off",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","password",1,"cd-col-form-label"],o,[1,"input-group"],["id","password","formControlName","password","type","password","autocomplete","new-password",1,"form-control"],[1,"input-group-append"],["type","button","cdPasswordButton","password",1,"btn","btn-light"],["source","password"],["for","mutual_user",1,"cd-col-form-label"],i,["id","mutual_user","formControlName","mutual_user","type","text","autocomplete","off",1,"form-control"],["for","mutual_password",1,"cd-col-form-label"],s,["id","mutual_password","formControlName","mutual_password","type","password","autocomplete","new-password",1,"form-control"],["type","button","cdPasswordButton","mutual_password",1,"btn","btn-light"],["source","mutual_password"],[1,"modal-footer"],[3,"form","showSubmit","submitText","submitActionEvent"],[1,"invalid-feedback"],a,d,c,u,S,N,P,$]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"div",7),e.TgZ(8,"label",8),e.SDv(9,9),e.qZA(),e.TgZ(10,"div",10),e._UZ(11,"input",11),e.YNc(12,on,2,0,"span",12),e.YNc(13,nn,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(14,"div",7),e.TgZ(15,"label",13),e.SDv(16,14),e.qZA(),e.TgZ(17,"div",10),e.TgZ(18,"div",15),e._UZ(19,"input",16),e.TgZ(20,"span",17),e._UZ(21,"button",18),e._UZ(22,"cd-copy-2-clipboard-button",19),e.qZA(),e.qZA(),e.YNc(23,_n,2,0,"span",12),e.YNc(24,sn,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(25,"div",7),e.TgZ(26,"label",20),e.ynx(27),e.SDv(28,21),e.BQk(),e.qZA(),e.TgZ(29,"div",10),e._UZ(30,"input",22),e.YNc(31,an,2,0,"span",12),e.YNc(32,rn,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(33,"div",7),e.TgZ(34,"label",23),e.SDv(35,24),e.qZA(),e.TgZ(36,"div",10),e.TgZ(37,"div",15),e._UZ(38,"input",25),e.TgZ(39,"span",17),e._UZ(40,"button",26),e._UZ(41,"cd-copy-2-clipboard-button",27),e.qZA(),e.qZA(),e.YNc(42,ln,2,0,"span",12),e.YNc(43,cn,2,0,"span",12),e.qZA(),e.qZA(),e.qZA(),e.TgZ(44,"div",28),e.TgZ(45,"cd-form-button-panel",29),e.NdJ("submitActionEvent",function(){return o.submitAction()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.discoveryForm),e.xp6(8),e.Q6J("ngIf",o.discoveryForm.showError("user",i,"required")),e.xp6(1),e.Q6J("ngIf",o.discoveryForm.showError("user",i,"pattern")),e.xp6(10),e.Q6J("ngIf",o.discoveryForm.showError("password",i,"required")),e.xp6(1),e.Q6J("ngIf",o.discoveryForm.showError("password",i,"pattern")),e.xp6(7),e.Q6J("ngIf",o.discoveryForm.showError("mutual_user",i,"required")),e.xp6(1),e.Q6J("ngIf",o.discoveryForm.showError("mutual_user",i,"pattern")),e.xp6(10),e.Q6J("ngIf",o.discoveryForm.showError("mutual_password",i,"required")),e.xp6(1),e.Q6J("ngIf",o.discoveryForm.showError("mutual_password",i,"pattern")),e.xp6(2),e.Q6J("form",o.discoveryForm)("showSubmit",o.hasPermission)("submitText",o.actionLabels.SUBMIT)}},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,f.o,r.Fj,h.b,r.JJ,r.u,l.O5,at.C,Je.s,O.p],styles:[""]}),n})();var pn=p(86969);let dt=(()=>{class n{constructor(t){this.router=t}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(m.F0))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-tabs"]],decls:8,vars:1,consts:function(){let _,t;return _="Overview",t="Targets",[["ngbNav","",1,"nav-tabs",3,"activeId","navChange"],["nav","ngbNav"],["ngbNavItem","/block/iscsi/overview"],["ngbNavLink",""],_,["ngbNavItem","/block/iscsi/targets"],t]},template:function(t,o){1&t&&(e.TgZ(0,"ul",0,1),e.NdJ("navChange",function(s){return o.router.navigate([s.nextId])}),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.qZA(),e.TgZ(5,"li",5),e.TgZ(6,"a",3),e.SDv(7,6),e.qZA(),e.qZA(),e.qZA()),2&t&&e.Q6J("activeId",o.router.url)},directives:[I.Pz,I.nv,I.Vx],styles:[""]}),n})();var pt=p(34501),gn=p(30490),Re=p(94928),un=p(68962);const mn=["highlightTpl"],Tn=["detailTable"],fn=["tree"],Cn=function(){return["logged_in"]},Sn=function(){return["logged_out"]},En=function(n,_){return{"badge-success":n,"badge-danger":_}};function Rn(n,_){if(1&n&&(e._UZ(0,"i"),e.TgZ(1,"span"),e._uU(2),e.qZA(),e._uU(3," \xa0 "),e.TgZ(4,"span",8),e._uU(5),e.qZA()),2&n){const t=_.$implicit;e.Tol(t.data.cdIcon),e.xp6(2),e.Oqu(t.data.name),e.xp6(2),e.Q6J("ngClass",e.WLB(7,En,e.DdM(5,Cn).includes(t.data.status),e.DdM(6,Sn).includes(t.data.status))),e.xp6(1),e.hij(" ",t.data.status," ")}}function Mn(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"legend"),e._uU(2),e.qZA(),e._UZ(3,"cd-table",10,11),e.qZA()),2&n){const t=e.oxw();e.xp6(2),e.Oqu(t.title),e.xp6(1),e.Q6J("data",t.data)("columns",t.columns)("limit",0)}}function On(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.Oqu(t)}}function An(n,_){if(1&n&&(e.TgZ(0,"strong"),e._uU(1),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.Oqu(t)}}function hn(n,_){if(1&n&&(e.YNc(0,On,2,1,"span",12),e.YNc(1,An,2,1,"strong",12)),2&n){const t=_.row;e.Q6J("ngIf",void 0===t.default||t.default===t.current),e.xp6(1),e.Q6J("ngIf",void 0!==t.default&&t.default!==t.current)}}let Pn=(()=>{class n{constructor(t,o){this.iscsiBackstorePipe=t,this.booleanTextPipe=o,this.icons=T.P,this.metadata={},this.nodes=[],this.treeOptions={useVirtualScroll:!0,actionMapping:{mouse:{click:this.onNodeSelected.bind(this)}}}}set content(t){this.detailTable=t,t&&t.updateColumns()}ngOnInit(){this.columns=[{prop:"displayName",name:"Name",flexGrow:1,cellTemplate:this.highlightTpl},{prop:"current",name:"Current",flexGrow:1,cellTemplate:this.highlightTpl},{prop:"default",name:"Default",flexGrow:1,cellTemplate:this.highlightTpl}]}ngOnChanges(){this.selection&&(this.selectedItem=this.selection,this.generateTree()),this.data=void 0}generateTree(){const t=C().cloneDeep(this.selectedItem.target_controls);this.cephIscsiConfigVersion>10&&C().extend(t,C().cloneDeep(this.selectedItem.auth)),this.metadata={root:t};const o={target:{expanded:C().join(this.selectedItem.cdExecuting?[T.P.large,T.P.spinner,T.P.spin]:[T.P.large,T.P.bullseye]," ")},initiators:{expanded:C().join([T.P.large,T.P.user]," "),leaf:C().join([T.P.user]," ")},groups:{expanded:C().join([T.P.large,T.P.users]," "),leaf:C().join([T.P.users]," ")},disks:{expanded:C().join([T.P.large,T.P.disk]," "),leaf:C().join([T.P.disk]," ")},portals:{expanded:C().join([T.P.large,T.P.server]," "),leaf:C().join([T.P.server]," ")}},i=[];C().forEach(this.selectedItem.disks,c=>{const u="disk_"+c.pool+"_"+c.image;this.metadata[u]={controls:c.controls,backstore:c.backstore},["wwn","lun"].forEach(S=>{S in c&&(this.metadata[u][S]=c[S])}),i.push({name:`${c.pool}/${c.image}`,cdId:u,cdIcon:o.disks.leaf})});const s=[];C().forEach(this.selectedItem.portals,c=>{s.push({name:`${c.host}:${c.ip}`,cdIcon:o.portals.leaf})});const a=[];C().forEach(this.selectedItem.clients,c=>{const u=C().cloneDeep(c.auth);c.info&&(C().extend(u,c.info),delete u.state,C().forEach(Object.keys(c.info.state),P=>{u[P.toLowerCase()]=c.info.state[P]})),this.metadata["client_"+c.client_iqn]=u;const S=[];c.luns.forEach(P=>{S.push({name:`${P.pool}/${P.image}`,cdId:"disk_"+P.pool+"_"+P.image,cdIcon:o.disks.leaf})});let N="";c.info&&(N=Object.keys(c.info.state).includes("LOGGED_IN")?"logged_in":"logged_out"),a.push({name:c.client_iqn,status:N,cdId:"client_"+c.client_iqn,children:S,cdIcon:o.initiators.leaf})});const d=[];C().forEach(this.selectedItem.groups,c=>{const u=[];c.disks.forEach(N=>{u.push({name:`${N.pool}/${N.image}`,cdId:"disk_"+N.pool+"_"+N.image,cdIcon:o.disks.leaf})});const S=[];c.members.forEach(N=>{S.push({name:N,cdId:"client_"+N})}),d.push({name:c.group_id,cdIcon:o.groups.leaf,children:[{name:"Disks",children:u,cdIcon:o.disks.expanded},{name:"Initiators",children:S,cdIcon:o.initiators.expanded}]})}),this.nodes=[{name:this.selectedItem.target_iqn,cdId:"root",isExpanded:!0,cdIcon:o.target.expanded,children:[{name:"Disks",isExpanded:!0,children:i,cdIcon:o.disks.expanded},{name:"Portals",isExpanded:!0,children:s,cdIcon:o.portals.expanded},{name:"Initiators",isExpanded:!0,children:a,cdIcon:o.initiators.expanded},{name:"Groups",isExpanded:!0,children:d,cdIcon:o.groups.expanded}]}]}format(t){return"boolean"==typeof t?this.booleanTextPipe.transform(t):t}onNodeSelected(t,o){var i,s,a,d;if(ne.iM.ACTIVATE(t,o,!0),o.data.cdId){this.title=o.data.name;const c=this.metadata[o.data.cdId]||{};"root"===o.data.cdId?(null===(i=this.detailTable)||void 0===i||i.toggleColumn({prop:"default",isHidden:!0}),this.data=C().map(this.settings.target_default_controls,(u,S)=>({displayName:S,default:u=this.format(u),current:C().isUndefined(c[S])?u:this.format(c[S])})),this.cephIscsiConfigVersion>10&&["user","password","mutual_user","mutual_password"].forEach(u=>{this.data.push({displayName:u,default:null,current:c[u]})})):o.data.cdId.toString().startsWith("disk_")?(null===(s=this.detailTable)||void 0===s||s.toggleColumn({prop:"default",isHidden:!0}),this.data=C().map(this.settings.disk_default_controls[c.backstore],(u,S)=>({displayName:S,default:u=this.format(u),current:C().isUndefined(c.controls[S])?u:this.format(c.controls[S])})),this.data.push({displayName:"backstore",default:this.iscsiBackstorePipe.transform(this.settings.default_backstore),current:this.iscsiBackstorePipe.transform(c.backstore)}),["wwn","lun"].forEach(u=>{u in c&&this.data.push({displayName:u,default:void 0,current:c[u]})})):(null===(a=this.detailTable)||void 0===a||a.toggleColumn({prop:"default",isHidden:!1}),this.data=C().map(c,(u,S)=>({displayName:S,default:void 0,current:this.format(u)})))}else this.data=void 0;null===(d=this.detailTable)||void 0===d||d.updateColumns()}onUpdateData(){this.tree.treeModel.expandAll()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(He.V),e.Y36(un.T))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-details"]],viewQuery:function(t,o){if(1&t&&(e.Gf(mn,7),e.Gf(Tn,5),e.Gf(fn,5)),2&t){let i;e.iGM(i=e.CRH())&&(o.highlightTpl=i.first),e.iGM(i=e.CRH())&&(o.content=i.first),e.iGM(i=e.CRH())&&(o.tree=i.first)}},inputs:{selection:"selection",settings:"settings",cephIscsiConfigVersion:"cephIscsiConfigVersion"},features:[e.TTD],decls:11,vars:3,consts:function(){let _;return _="iSCSI Topology",[[1,"row"],[1,"col-6"],_,[3,"nodes","options","updateData"],["tree",""],["treeNodeTemplate",""],["class","col-6 metadata",4,"ngIf"],["highlightTpl",""],[1,"badge",3,"ngClass"],[1,"col-6","metadata"],["columnMode","flex",3,"data","columns","limit"],["detailTable",""],[4,"ngIf"]]},template:function(t,o){1&t&&(e.TgZ(0,"div",0),e.TgZ(1,"div",1),e.TgZ(2,"legend"),e.SDv(3,2),e.qZA(),e.TgZ(4,"tree-root",3,4),e.NdJ("updateData",function(){return o.onUpdateData()}),e.YNc(6,Rn,6,10,"ng-template",null,5,e.W1O),e.qZA(),e.qZA(),e.YNc(8,Mn,5,4,"div",6),e.qZA(),e.YNc(9,hn,2,2,"ng-template",null,7,e.W1O)),2&t&&(e.xp6(4),e.Q6J("nodes",o.nodes)("options",o.treeOptions),e.xp6(4),e.Q6J("ngIf",o.data))},directives:[ne.qr,l.O5,l.mk,ee.a],styles:[""]}),n})();function In(n,_){if(1&n&&(e.ynx(0),e._UZ(1,"br"),e.TgZ(2,"span"),e.SDv(3,6),e.qZA(),e.TgZ(4,"pre"),e._uU(5),e.qZA(),e.BQk()),2&n){const t=e.oxw(2);e.xp6(5),e.Oqu(t.status)}}function bn(n,_){if(1&n&&(e.TgZ(0,"cd-alert-panel",2),e.ynx(1),e.tHW(2,3),e._UZ(3,"cd-doc",4),e.N_p(),e.BQk(),e.YNc(4,In,6,1,"ng-container",5),e.qZA()),2&n){const t=e.oxw();e.xp6(4),e.Q6J("ngIf",t.status)}}function Nn(n,_){if(1&n&&e._UZ(0,"cd-iscsi-target-details",15),2&n){const t=e.oxw(2);e.Q6J("cephIscsiConfigVersion",t.cephIscsiConfigVersion)("selection",t.expandedRow)("settings",t.settings)}}const Dn=function(n){return[n]};function vn(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"cd-table",7,8),e.NdJ("fetchData",function(){return e.CHM(t),e.oxw().getTargets()})("setExpandedRow",function(i){return e.CHM(t),e.oxw().setExpandedRow(i)})("updateSelection",function(i){return e.CHM(t),e.oxw().updateSelection(i)}),e.TgZ(2,"div",9),e._UZ(3,"cd-table-actions",10),e.TgZ(4,"button",11),e.NdJ("click",function(){return e.CHM(t),e.oxw().configureDiscoveryAuth()}),e._UZ(5,"i",12),e.ynx(6),e.SDv(7,13),e.BQk(),e.qZA(),e.qZA(),e.YNc(8,Nn,1,3,"cd-iscsi-target-details",14),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("data",t.targets)("columns",t.columns)("hasDetails",!0)("autoReload",!1)("status",t.tableStatus),e.xp6(3),e.Q6J("permission",t.permission)("selection",t.selection)("tableActions",t.tableActions),e.xp6(2),e.Q6J("ngClass",e.VKq(10,Dn,t.icons.key)),e.xp6(3),e.Q6J("ngIf",t.expandedRow)}}let Ln=(()=>{class n extends lt.o{constructor(t,o,i,s,a,d,c,u,S){super(S),this.authStorageService=t,this.iscsiService=o,this.joinPipe=i,this.taskListService=s,this.notAvailablePipe=a,this.modalService=d,this.taskWrapper=c,this.actionLabels=u,this.ngZone=S,this.available=void 0,this.selection=new Ee.r,this.targets=[],this.icons=T.P,this.builders={"iscsi/target/create":N=>({target_iqn:N.target_iqn})},this.permission=this.authStorageService.getPermissions().iscsi,this.tableActions=[{permission:"create",icon:T.P.add,routerLink:()=>"/block/iscsi/targets/create",name:this.actionLabels.CREATE},{permission:"update",icon:T.P.edit,routerLink:()=>`/block/iscsi/targets/edit/${this.selection.first().target_iqn}`,name:this.actionLabels.EDIT,disable:()=>this.getEditDisableDesc()},{permission:"delete",icon:T.P.destroy,click:()=>this.deleteIscsiTargetModal(),name:this.actionLabels.DELETE,disable:()=>this.getDeleteDisableDesc()}]}ngOnInit(){this.columns=[{name:"Target",prop:"target_iqn",flexGrow:2,cellTransformation:ve.e.executing},{name:"Portals",prop:"cdPortals",pipe:this.joinPipe,flexGrow:2},{name:"Images",prop:"cdImages",pipe:this.joinPipe,flexGrow:2},{name:"# Sessions",prop:"info.num_sessions",pipe:this.notAvailablePipe,flexGrow:1}],this.iscsiService.status().subscribe(t=>{this.available=t.available,t.available||(this.status=t.message)})}getTargets(){this.available&&(this.setTableRefreshTimeout(),this.iscsiService.version().subscribe(t=>{this.cephIscsiConfigVersion=t.ceph_iscsi_config_version}),this.taskListService.init(()=>this.iscsiService.listTargets(),t=>this.prepareResponse(t),t=>this.targets=t,()=>this.onFetchError(),this.taskFilter,this.itemFilter,this.builders),this.iscsiService.settings().subscribe(t=>{this.settings=t}))}ngOnDestroy(){this.summaryDataSubscription&&this.summaryDataSubscription.unsubscribe()}getEditDisableDesc(){const t=this.selection.first();return t&&(null==t?void 0:t.cdExecuting)?t.cdExecuting:t&&C().isUndefined(null==t?void 0:t.info)?"Unavailable gateway(s)":!t}getDeleteDisableDesc(){var t;const o=this.selection.first();return(null==o?void 0:o.cdExecuting)?o.cdExecuting:o&&C().isUndefined(null==o?void 0:o.info)?"Unavailable gateway(s)":o&&(null===(t=null==o?void 0:o.info)||void 0===t?void 0:t.num_sessions)?"Target has active sessions":!o}prepareResponse(t){return t.forEach(o=>{o.cdPortals=o.portals.map(i=>`${i.host}:${i.ip}`),o.cdImages=o.disks.map(i=>`${i.pool}/${i.image}`)}),t}onFetchError(){this.table.reset()}itemFilter(t,o){return t.target_iqn===o.metadata.target_iqn}taskFilter(t){return["iscsi/target/create","iscsi/target/edit","iscsi/target/delete"].includes(t.name)}updateSelection(t){this.selection=t}deleteIscsiTargetModal(){const t=this.selection.first().target_iqn;this.modalRef=this.modalService.show(he.M,{itemDescription:"iSCSI target",itemNames:[t],submitActionObservable:()=>this.taskWrapper.wrapTaskAroundCall({task:new F.R("iscsi/target/delete",{target_iqn:t}),call:this.iscsiService.deleteTarget(t)})})}configureDiscoveryAuth(){this.modalService.show(dn)}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(k),e.Y36(pn.A),e.Y36(se.j),e.Y36(ct.g),e.Y36(re.Z),e.Y36(Q.P),e.Y36(D.p4),e.Y36(e.R0b))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi-target-list"]],viewQuery:function(t,o){if(1&t&&e.Gf(ee.a,5),2&t){let i;e.iGM(i=e.CRH())&&(o.table=i.first)}},features:[e._Bn([se.j]),e.qOj],decls:3,vars:2,consts:function(){let _,t,o,i;return _="iSCSI Targets not available",t="Please consult the " + "\ufffd#3\ufffd" + "" + "\ufffd/#3\ufffd" + " on how to configure and enable the iSCSI Targets management functionality.",o="Available information:",i="Discovery authentication",[["type","info","title",_,4,"ngIf"],["columnMode","flex","identifier","target_iqn","forceIdentifier","true","selectionType","single",3,"data","columns","hasDetails","autoReload","status","fetchData","setExpandedRow","updateSelection",4,"ngIf"],["type","info","title",_],t,["section","iscsi"],[4,"ngIf"],o,["columnMode","flex","identifier","target_iqn","forceIdentifier","true","selectionType","single",3,"data","columns","hasDetails","autoReload","status","fetchData","setExpandedRow","updateSelection"],["table",""],[1,"table-actions","btn-toolbar"],[1,"btn-group",3,"permission","selection","tableActions"],["type","button",1,"btn","btn-light",3,"click"],["aria-hidden","true",3,"ngClass"],i,["cdTableDetail","",3,"cephIscsiConfigVersion","selection","settings",4,"ngIf"],["cdTableDetail","",3,"cephIscsiConfigVersion","selection","settings"]]},template:function(t,o){1&t&&(e._UZ(0,"cd-iscsi-tabs"),e.YNc(1,bn,5,1,"cd-alert-panel",0),e.YNc(2,vn,9,12,"cd-table",1)),2&t&&(e.xp6(1),e.Q6J("ngIf",!1===o.available),e.xp6(1),e.Q6J("ngIf",!0===o.available))},directives:[dt,l.O5,pt.G,gn.K,ee.a,Re.K,f.o,l.mk,Pn],styles:[""]}),n})();var Ue=p(66369),Fn=p(76446),$n=p(90068);const Zn=["iscsiSparklineTpl"],Bn=["iscsiPerSecondTpl"],Gn=["iscsiRelativeDateTpl"];function yn(n,_){if(1&n&&(e.TgZ(0,"span"),e._UZ(1,"cd-sparkline",9),e.qZA()),2&n){const t=e.oxw(),o=t.value,i=t.row;e.xp6(1),e.Q6J("data",o)("isBinary",i.cdIsBinary)}}function xn(n,_){1&n&&(e.TgZ(0,"span",10),e._uU(1," n/a "),e.qZA())}function wn(n,_){if(1&n&&(e.YNc(0,yn,2,2,"span",7),e.YNc(1,xn,2,0,"span",8)),2&n){const t=_.row;e.Q6J("ngIf","user:rbd"===t.backstore),e.xp6(1),e.Q6J("ngIf","user:rbd"!==t.backstore)}}function qn(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.hij(" ",t," /s ")}}function Hn(n,_){1&n&&(e.TgZ(0,"span",10),e._uU(1," n/a "),e.qZA())}function Kn(n,_){if(1&n&&(e.YNc(0,qn,2,1,"span",7),e.YNc(1,Hn,2,0,"span",8)),2&n){const t=_.row;e.Q6J("ngIf","user:rbd"===t.backstore),e.xp6(1),e.Q6J("ngIf","user:rbd"!==t.backstore)}}function kn(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.ALo(2,"notAvailable"),e.ALo(3,"relativeDate"),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.hij(" ",e.lcZ(2,1,e.lcZ(3,3,t))," ")}}function Xn(n,_){1&n&&(e.TgZ(0,"span",10),e._uU(1," n/a "),e.qZA())}function Qn(n,_){if(1&n&&(e.YNc(0,kn,4,5,"span",7),e.YNc(1,Xn,2,0,"span",8)),2&n){const t=_.row;e.Q6J("ngIf","user:rbd"===t.backstore),e.xp6(1),e.Q6J("ngIf","user:rbd"!==t.backstore)}}let zn=(()=>{class n{constructor(t,o,i){this.iscsiService=t,this.dimlessPipe=o,this.iscsiBackstorePipe=i,this.gateways=[],this.images=[]}ngOnInit(){this.gatewaysColumns=[{name:"Name",prop:"name"},{name:"State",prop:"state",flexGrow:1,cellTransformation:ve.e.badge,customTemplateConfig:{map:{up:{class:"badge-success"},down:{class:"badge-danger"}}}},{name:"# Targets",prop:"num_targets"},{name:"# Sessions",prop:"num_sessions"}],this.imagesColumns=[{name:"Pool",prop:"pool"},{name:"Image",prop:"image"},{name:"Backstore",prop:"backstore",pipe:this.iscsiBackstorePipe},{name:"Read Bytes",prop:"stats_history.rd_bytes",cellTemplate:this.iscsiSparklineTpl},{name:"Write Bytes",prop:"stats_history.wr_bytes",cellTemplate:this.iscsiSparklineTpl},{name:"Read Ops",prop:"stats.rd",pipe:this.dimlessPipe,cellTemplate:this.iscsiPerSecondTpl},{name:"Write Ops",prop:"stats.wr",pipe:this.dimlessPipe,cellTemplate:this.iscsiPerSecondTpl},{name:"A/O Since",prop:"optimized_since",cellTemplate:this.iscsiRelativeDateTpl}]}refresh(){this.iscsiService.overview().subscribe(t=>{this.gateways=t.gateways,this.images=t.images,this.images.map(o=>(o.stats_history&&(o.stats_history.rd_bytes=o.stats_history.rd_bytes.map(i=>i[1]),o.stats_history.wr_bytes=o.stats_history.wr_bytes.map(i=>i[1])),o.cdIsBinary=!0,o))})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(k),e.Y36(Ue.n),e.Y36(He.V))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-iscsi"]],viewQuery:function(t,o){if(1&t&&(e.Gf(Zn,7),e.Gf(Bn,7),e.Gf(Gn,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.iscsiSparklineTpl=i.first),e.iGM(i=e.CRH())&&(o.iscsiPerSecondTpl=i.first),e.iGM(i=e.CRH())&&(o.iscsiRelativeDateTpl=i.first)}},decls:13,vars:4,consts:function(){let _,t;return _="Gateways",t="Images",[_,[3,"data","columns","fetchData"],t,[3,"data","columns"],["iscsiSparklineTpl",""],["iscsiPerSecondTpl",""],["iscsiRelativeDateTpl",""],[4,"ngIf"],["class","text-muted",4,"ngIf"],[3,"data","isBinary"],[1,"text-muted"]]},template:function(t,o){1&t&&(e._UZ(0,"cd-iscsi-tabs"),e.TgZ(1,"legend"),e.SDv(2,0),e.qZA(),e.TgZ(3,"cd-table",1),e.NdJ("fetchData",function(){return o.refresh()}),e.qZA(),e.TgZ(4,"legend"),e.SDv(5,2),e.qZA(),e._UZ(6,"cd-table",3),e.YNc(7,wn,2,2,"ng-template",null,4,e.W1O),e.YNc(9,Kn,2,2,"ng-template",null,5,e.W1O),e.YNc(11,Qn,2,2,"ng-template",null,6,e.W1O)),2&t&&(e.xp6(3),e.Q6J("data",o.gateways)("columns",o.gatewaysColumns),e.xp6(3),e.Q6J("data",o.images)("columns",o.imagesColumns))},directives:[dt,ee.a,l.O5,Fn.l],pipes:[ct.g,$n.h],styles:[""]}),n})(),Jn=(()=>{class n{}return n.\u0275fac=function(t){return new(t||n)},n.\u0275mod=e.oAB({type:n}),n.\u0275inj=e.cJS({imports:[[l.ez,_e.m,I.Oz,m.Bz,r.u5,r.UX,I.ZQ]]}),n})();var Yn=p(75319),Vn=p(26215),Un=p(45435),gt=p(55358);let K=class{constructor(_,t){this.http=_,this.timerService=t,this.REFRESH_INTERVAL=3e4,this.summaryDataSource=new Vn.X(null),this.summaryData$=this.summaryDataSource.asObservable()}startPolling(){return this.timerService.get(()=>this.retrieveSummaryObservable(),this.REFRESH_INTERVAL).subscribe(this.retrieveSummaryObserver())}refresh(){return this.retrieveSummaryObservable().subscribe(this.retrieveSummaryObserver())}retrieveSummaryObservable(){return this.http.get("api/block/mirroring/summary")}retrieveSummaryObserver(){return _=>{this.summaryDataSource.next(_)}}subscribeSummary(_,t){return this.summaryData$.pipe((0,Un.h)(o=>!!o)).subscribe(_,t)}getPool(_){return this.http.get(`api/block/mirroring/pool/${_}`)}updatePool(_,t){return this.http.put(`api/block/mirroring/pool/${_}`,t,{observe:"response"})}getSiteName(){return this.http.get("api/block/mirroring/site_name")}setSiteName(_){return this.http.put("api/block/mirroring/site_name",{site_name:_},{observe:"response"})}createBootstrapToken(_){return this.http.post(`api/block/mirroring/pool/${_}/bootstrap/token`,{})}importBootstrapToken(_,t,o){return this.http.post(`api/block/mirroring/pool/${_}/bootstrap/peer`,{direction:t,token:o},{observe:"response"})}getPeer(_,t){return this.http.get(`api/block/mirroring/pool/${_}/peer/${t}`)}addPeer(_,t){return this.http.post(`api/block/mirroring/pool/${_}/peer`,t,{observe:"response"})}updatePeer(_,t,o){return this.http.put(`api/block/mirroring/pool/${_}/peer/${t}`,o,{observe:"response"})}deletePeer(_,t){return this.http.delete(`api/block/mirroring/pool/${_}/peer/${t}`,{observe:"response"})}};K.\u0275fac=function(_){return new(_||K)(e.LFG(ie.eN),e.LFG(gt.f))},K.\u0275prov=e.Yz7({token:K,factory:K.\u0275fac,providedIn:"root"}),(0,b.gn)([(0,b.fM)(0,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[String]),(0,b.w6)("design:returntype",void 0)],K.prototype,"setSiteName",null),(0,b.gn)([(0,b.fM)(1,Y.G),(0,b.fM)(2,Y.G),(0,b.w6)("design:type",Function),(0,b.w6)("design:paramtypes",[String,String,String]),(0,b.w6)("design:returntype",void 0)],K.prototype,"importBootstrapToken",null),K=(0,b.gn)([Y.o,(0,b.w6)("design:paramtypes",[ie.eN,gt.f])],K);var je=p(58071),jn=p(68307),ut=p(12627),le=p(82945),Wn=p(39749),ei=p(13472);function ti(n,_){1&n&&(e.TgZ(0,"span",25),e.SDv(1,26),e.qZA())}function oi(n,_){if(1&n&&(e.TgZ(0,"div",27),e._UZ(1,"input",28),e.TgZ(2,"label",29),e._uU(3),e.qZA(),e.qZA()),2&n){const t=_.$implicit;e.xp6(1),e.s9C("id",t.name),e.s9C("name",t.name),e.s9C("formControlName",t.name),e.xp6(1),e.s9C("for",t.name),e.xp6(1),e.Oqu(t.name)}}function ni(n,_){1&n&&(e.TgZ(0,"span",25),e.SDv(1,30),e.qZA())}let ii=(()=>{class n{constructor(t,o,i){this.activeModal=t,this.rbdMirroringService=o,this.taskWrapper=i,this.pools=[],this.createForm()}createForm(){this.createBootstrapForm=new M.d({siteName:new r.NI("",{validators:[r.kI.required]}),pools:new r.cw({},{validators:[this.validatePools()]}),token:new r.NI("",{})})}ngOnInit(){this.createBootstrapForm.get("siteName").setValue(this.siteName),this.rbdMirroringService.getSiteName().subscribe(t=>{this.createBootstrapForm.get("siteName").setValue(t.site_name)}),this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.pools=t.content_data.pools.reduce((s,a)=>(s.push({name:a.name,mirror_mode:a.mirror_mode}),s),[]);const i=this.createBootstrapForm.get("pools");C().each(this.pools,s=>{const a=s.name,d="disabled"===s.mirror_mode,c=i.controls[a];c?d&&c.disabled?c.enable():!d&&c.enabled&&(c.disable(),c.setValue(!0)):i.addControl(a,new r.NI({value:!d,disabled:!d}))})})}ngOnDestroy(){this.subs&&this.subs.unsubscribe()}validatePools(){return t=>{let o=0;return C().each(t.controls,i=>{!0===i.value&&++o}),o>0?null:{requirePool:!0}}}generate(){this.createBootstrapForm.get("token").setValue("");let t="";const o=[],i=this.createBootstrapForm.get("pools");C().each(i.controls,(u,S)=>{!0===u.value&&(t=S,u.disabled||o.push(S))});const s={mirror_mode:"image"},a=(0,je.z)(this.rbdMirroringService.setSiteName(this.createBootstrapForm.getValue("siteName")),(0,W.D)(o.map(u=>this.rbdMirroringService.updatePool(u,s))),this.rbdMirroringService.createBootstrapToken(t).pipe((0,jn.b)(u=>this.createBootstrapForm.get("token").setValue(u.token)))).pipe((0,ut.Z)()),d=()=>{this.rbdMirroringService.refresh(),this.createBootstrapForm.setErrors({cdSubmitButton:!0})};this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/bootstrap/create",{}),call:a}).subscribe({error:d,complete:d})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-bootstrap-create-modal"]],decls:32,vars:6,consts:function(){let _,t,o,i,s,a,d,c,u,S,N;return _="Create Bootstrap Token",t="To create a bootstrap token which can be imported by a peer site cluster, provide the local site's name, select which pools will have mirroring enabled, and click\xA0 " + "\ufffd#10\ufffd" + "Generate" + "\ufffd/#10\ufffd" + ".",o="Site Name",i="Name...",s="Pools",a="Generate",d="Token",c="Generated token...",u="Close",S="This field is required.",N="At least one pool is required.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","createBootstrapForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","siteName",1,"col-form-label","required"],o,["type","text","placeholder",i,"id","siteName","name","siteName","formControlName","siteName","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["formGroupName","pools",1,"form-group"],["for","pools",1,"col-form-label","required"],s,["class","custom-control custom-checkbox",4,"ngFor","ngForOf"],[1,"mb-4","float-right",3,"form","submitAction"],a,["for","token",1,"col-form-label"],d,["placeholder",c,"id","token","formControlName","token","readonly","",1,"form-control","resize-vertical"],["source","token",1,"float-right"],[1,"modal-footer"],["name",u,3,"backAction"],[1,"invalid-feedback"],S,[1,"custom-control","custom-checkbox"],["type","checkbox",1,"custom-control-input",3,"id","name","formControlName"],[1,"custom-control-label",3,"for"],N]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.ynx(8),e.tHW(9,7),e._UZ(10,"kbd"),e.N_p(),e.BQk(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e._UZ(14,"input",11),e.YNc(15,ti,2,0,"span",12),e.qZA(),e.TgZ(16,"div",13),e.TgZ(17,"label",14),e.SDv(18,15),e.qZA(),e.YNc(19,oi,4,5,"div",16),e.YNc(20,ni,2,0,"span",12),e.qZA(),e.TgZ(21,"cd-submit-button",17),e.NdJ("submitAction",function(){return o.generate()}),e.SDv(22,18),e.qZA(),e.TgZ(23,"div",8),e.TgZ(24,"label",19),e.TgZ(25,"span"),e.SDv(26,20),e.qZA(),e.qZA(),e.TgZ(27,"textarea",21),e._uU(28," "),e.qZA(),e.qZA(),e._UZ(29,"cd-copy-2-clipboard-button",22),e.qZA(),e.TgZ(30,"div",23),e.TgZ(31,"cd-back-button",24),e.NdJ("backAction",function(){return o.activeModal.close()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.createBootstrapForm),e.xp6(11),e.Q6J("ngIf",o.createBootstrapForm.showError("siteName",i,"required")),e.xp6(4),e.Q6J("ngForOf",o.pools),e.xp6(1),e.Q6J("ngIf",o.createBootstrapForm.showError("pools",i,"requirePool")),e.xp6(1),e.Q6J("form",o.createBootstrapForm)}},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,r.x0,l.sg,Wn.w,Je.s,ei.W,r.Wl],styles:[".form-group.ng-invalid[_ngcontent-%COMP%] .invalid-feedback[_ngcontent-%COMP%]{display:block}"]}),n})();function _i(n,_){1&n&&(e.TgZ(0,"span",26),e.SDv(1,27),e.qZA())}function si(n,_){if(1&n&&(e.TgZ(0,"option",28),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.key),e.xp6(1),e.Oqu(t.desc)}}function ai(n,_){if(1&n&&(e.TgZ(0,"div",29),e._UZ(1,"input",30),e.TgZ(2,"label",31),e._uU(3),e.qZA(),e.qZA()),2&n){const t=_.$implicit;e.xp6(1),e.s9C("id",t.name),e.s9C("name",t.name),e.s9C("formControlName",t.name),e.xp6(1),e.s9C("for",t.name),e.xp6(1),e.Oqu(t.name)}}function ri(n,_){1&n&&(e.TgZ(0,"span",26),e.SDv(1,32),e.qZA())}function li(n,_){1&n&&(e.TgZ(0,"span",26),e.SDv(1,33),e.qZA())}function ci(n,_){1&n&&(e.TgZ(0,"span",26),e.SDv(1,34),e.qZA())}let di=(()=>{class n{constructor(t,o,i,s){this.activeModal=t,this.actionLabels=o,this.rbdMirroringService=i,this.taskWrapper=s,this.pools=[],this.directions=[{key:"rx-tx",desc:"Bidirectional"},{key:"rx",desc:"Unidirectional (receive-only)"}],this.createForm()}createForm(){this.importBootstrapForm=new M.d({siteName:new r.NI("",{validators:[r.kI.required]}),direction:new r.NI("rx-tx",{}),pools:new r.cw({},{validators:[this.validatePools()]}),token:new r.NI("",{validators:[r.kI.required,this.validateToken()]})})}ngOnInit(){this.rbdMirroringService.getSiteName().subscribe(t=>{this.importBootstrapForm.get("siteName").setValue(t.site_name)}),this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.pools=t.content_data.pools.reduce((s,a)=>(s.push({name:a.name,mirror_mode:a.mirror_mode}),s),[]);const i=this.importBootstrapForm.get("pools");C().each(this.pools,s=>{const a=s.name,d="disabled"===s.mirror_mode,c=i.controls[a];c?d&&c.disabled?c.enable():!d&&c.enabled&&(c.disable(),c.setValue(!0)):i.addControl(a,new r.NI({value:!d,disabled:!d}))})})}ngOnDestroy(){this.subs&&this.subs.unsubscribe()}validatePools(){return t=>{let o=0;return C().each(t.controls,i=>{!0===i.value&&++o}),o>0?null:{requirePool:!0}}}validateToken(){return t=>{try{if(JSON.parse(atob(t.value)))return null}catch(o){}return{invalidToken:!0}}}import(){const t=[],o=[],i=this.importBootstrapForm.get("pools");C().each(i.controls,(u,S)=>{!0===u.value&&(t.push(S),u.disabled||o.push(S))});const s={mirror_mode:"image"};let a=(0,je.z)(this.rbdMirroringService.setSiteName(this.importBootstrapForm.getValue("siteName")),(0,W.D)(o.map(u=>this.rbdMirroringService.updatePool(u,s))));a=t.reduce((u,S)=>(0,je.z)(u,this.rbdMirroringService.importBootstrapToken(S,this.importBootstrapForm.getValue("direction"),this.importBootstrapForm.getValue("token"))),a).pipe((0,ut.Z)());const d=()=>{this.rbdMirroringService.refresh(),this.importBootstrapForm.setErrors({cdSubmitButton:!0})};this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/bootstrap/import",{}),call:a}).subscribe({error:d,complete:()=>{d(),this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-bootstrap-import-modal"]],decls:36,vars:10,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P;return _="Import Bootstrap Token",t="To import a bootstrap token which was created by a peer site cluster, provide the local site's name, select which pools will have mirroring enabled, provide the generated token, and click\xA0" + "\ufffd#10\ufffd" + "Import" + "\ufffd/#10\ufffd" + ".",o="Site Name",i="Name...",s="Direction",a="Pools",d="Token",c="Generated token...",u="This field is required.",S="At least one pool is required.",N="This field is required.",P="The token is invalid.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","importBootstrapForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","siteName",1,"col-form-label","required"],o,["type","text","placeholder",i,"id","siteName","name","siteName","formControlName","siteName","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","direction",1,"col-form-label"],s,["id","direction","name","direction","formControlName","direction",1,"form-control"],[3,"value",4,"ngFor","ngForOf"],["formGroupName","pools",1,"form-group"],["for","pools",1,"col-form-label","required"],a,["class","custom-control custom-checkbox",4,"ngFor","ngForOf"],["for","token",1,"col-form-label","required"],d,["placeholder",c,"id","token","formControlName","token",1,"form-control","resize-vertical"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],u,[3,"value"],[1,"custom-control","custom-checkbox"],["type","checkbox",1,"custom-control-input",3,"id","name","formControlName"],[1,"custom-control-label",3,"for"],S,N,P]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.ynx(8),e.tHW(9,7),e._UZ(10,"kbd"),e.N_p(),e.BQk(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e._UZ(14,"input",11),e.YNc(15,_i,2,0,"span",12),e.qZA(),e.TgZ(16,"div",8),e.TgZ(17,"label",13),e.TgZ(18,"span"),e.SDv(19,14),e.qZA(),e.qZA(),e.TgZ(20,"select",15),e.YNc(21,si,2,2,"option",16),e.qZA(),e.qZA(),e.TgZ(22,"div",17),e.TgZ(23,"label",18),e.SDv(24,19),e.qZA(),e.YNc(25,ai,4,5,"div",20),e.YNc(26,ri,2,0,"span",12),e.qZA(),e.TgZ(27,"div",8),e.TgZ(28,"label",21),e.SDv(29,22),e.qZA(),e.TgZ(30,"textarea",23),e._uU(31," "),e.qZA(),e.YNc(32,li,2,0,"span",12),e.YNc(33,ci,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(34,"div",24),e.TgZ(35,"cd-form-button-panel",25),e.NdJ("submitActionEvent",function(){return o.import()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.importBootstrapForm),e.xp6(11),e.Q6J("ngIf",o.importBootstrapForm.showError("siteName",i,"required")),e.xp6(6),e.Q6J("ngForOf",o.directions),e.xp6(4),e.Q6J("ngForOf",o.pools),e.xp6(1),e.Q6J("ngIf",o.importBootstrapForm.showError("pools",i,"requirePool")),e.xp6(6),e.Q6J("ngIf",o.importBootstrapForm.showError("token",i,"required")),e.xp6(1),e.Q6J("ngIf",o.importBootstrapForm.showError("token",i,"invalidToken")),e.xp6(2),e.Q6J("form",o.importBootstrapForm)("submitText",o.actionLabels.SUBMIT)}},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,r.EJ,l.sg,r.x0,O.p,r.YN,r.Kr,r.Wl],styles:[""]}),n})(),pi=(()=>{class n{constructor(t,o,i,s){this.activeModal=t,this.actionLabels=o,this.rbdMirroringService=i,this.taskWrapper=s,this.createForm()}createForm(){this.editSiteNameForm=new M.d({siteName:new r.NI("",{})})}ngOnInit(){this.editSiteNameForm.get("siteName").setValue(this.siteName),this.rbdMirroringService.getSiteName().subscribe(t=>{this.editSiteNameForm.get("siteName").setValue(t.site_name)})}update(){this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/site_name/edit",{}),call:this.rbdMirroringService.setSiteName(this.editSiteNameForm.getValue("siteName"))}).subscribe({error:()=>this.editSiteNameForm.setErrors({cdSubmitButton:!0}),complete:()=>{this.rbdMirroringService.refresh(),this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-edit-site-mode-modal"]],decls:17,vars:4,consts:function(){let _,t,o,i;return _="Edit site name",t="Edit the site name and click\xA0 " + "\ufffd#10\ufffd" + "Update" + "\ufffd/#10\ufffd" + ".",o="Site Name",i="Name...",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","editSiteNameForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","siteName",1,"col-form-label","required"],o,["type","text","placeholder",i,"id","siteName","name","siteName","formControlName","siteName","autofocus","",1,"form-control"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"]]},template:function(t,o){1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.ynx(8),e.tHW(9,7),e._UZ(10,"kbd"),e.N_p(),e.BQk(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e._UZ(14,"input",11),e.qZA(),e.qZA(),e.TgZ(15,"div",12),e.TgZ(16,"cd-form-button-panel",13),e.NdJ("submitActionEvent",function(){return o.update()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t&&(e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.editSiteNameForm),e.xp6(12),e.Q6J("form",o.editSiteNameForm)("submitText",o.actionLabels.UPDATE))},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,O.p],styles:[""]}),n})();var U=p(69158),gi=p(58111);let We=(()=>{class n{transform(t){return"warning"===t?"badge badge-warning":"error"===t?"badge badge-danger":"success"===t?"badge badge-success":"badge badge-info"}}return n.\u0275fac=function(t){return new(t||n)},n.\u0275pipe=e.Yjl({name:"mirrorHealthColor",type:n,pure:!0}),n})();const ui=["healthTmpl"];function mi(n,_){if(1&n&&(e.TgZ(0,"span",2),e.ALo(1,"mirrorHealthColor"),e._uU(2),e.qZA()),2&n){const o=_.value;e.Q6J("ngClass",e.lcZ(1,2,_.row.health_color)),e.xp6(2),e.Oqu(o)}}let Ti=(()=>{class n{constructor(t,o){this.rbdMirroringService=t,this.cephShortVersionPipe=o,this.tableStatus=new U.E}ngOnInit(){this.columns=[{prop:"instance_id",name:"Instance",flexGrow:2},{prop:"id",name:"ID",flexGrow:2},{prop:"server_hostname",name:"Hostname",flexGrow:2},{prop:"version",name:"Version",pipe:this.cephShortVersionPipe,flexGrow:2},{prop:"health",name:"Health",cellTemplate:this.healthTmpl,flexGrow:1}],this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.data=t.content_data.daemons,this.tableStatus=new U.E(t.status)})}ngOnDestroy(){this.subs.unsubscribe()}refresh(){this.rbdMirroringService.refresh()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(K),e.Y36(gi.F))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-mirroring-daemons"]],viewQuery:function(t,o){if(1&t&&e.Gf(ui,7),2&t){let i;e.iGM(i=e.CRH())&&(o.healthTmpl=i.first)}},decls:3,vars:4,consts:[["columnMode","flex",3,"data","columns","autoReload","status","fetchData"],["healthTmpl",""],[3,"ngClass"]],template:function(t,o){1&t&&(e.TgZ(0,"cd-table",0),e.NdJ("fetchData",function(){return o.refresh()}),e.qZA(),e.YNc(1,mi,3,4,"ng-template",null,1,e.W1O)),2&t&&e.Q6J("data",o.data)("columns",o.columns)("autoReload",-1)("status",o.tableStatus)},directives:[ee.a,l.mk],pipes:[We],styles:[""]}),n})();var fi=p(18891);class Ci{}function Si(n,_){if(1&n&&(e.TgZ(0,"option",16),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.id),e.xp6(1),e.Oqu(t.name)}}function Ei(n,_){1&n&&(e.TgZ(0,"span",17),e.SDv(1,18),e.qZA())}let Ri=(()=>{class n{constructor(t,o,i,s){this.activeModal=t,this.actionLabels=o,this.rbdMirroringService=i,this.taskWrapper=s,this.bsConfig={containerClass:"theme-default"},this.peerExists=!1,this.mirrorModes=[{id:"disabled",name:"Disabled"},{id:"pool",name:"Pool"},{id:"image",name:"Image"}],this.createForm()}createForm(){this.editModeForm=new M.d({mirrorMode:new r.NI("",{validators:[r.kI.required,this.validateMode.bind(this)]})})}ngOnInit(){this.pattern=`${this.poolName}`,this.rbdMirroringService.getPool(this.poolName).subscribe(t=>{this.setResponse(t)}),this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.peerExists=!1;const i=t.content_data.pools.find(s=>this.poolName===s.name);this.peerExists=i&&i.peer_uuids.length})}ngOnDestroy(){this.subs.unsubscribe()}validateMode(t){return"disabled"===t.value&&this.peerExists?{cannotDisable:{value:t.value}}:null}setResponse(t){this.editModeForm.get("mirrorMode").setValue(t.mirror_mode)}update(){const t=new Ci;t.mirror_mode=this.editModeForm.getValue("mirrorMode"),this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/pool/edit",{pool_name:this.poolName}),call:this.rbdMirroringService.updatePool(this.poolName,t)}).subscribe({error:()=>this.editModeForm.setErrors({cdSubmitButton:!0}),complete:()=>{this.rbdMirroringService.refresh(),this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-pool-edit-mode-modal"]],decls:21,vars:7,consts:function(){let _,t,o,i;return _="Edit pool mirror mode",t="To edit the mirror mode for pool\xA0 " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "" + "\ufffd0\ufffd" + "" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + ", select a new mode from the list and click\xA0 " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "Update" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + ".",t=e.Zx4(t),o="Mode",i="Peer clusters must be removed prior to disabling mirror.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","editModeForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","mirrorMode",1,"col-form-label"],o,["id","mirrorMode","name","mirrorMode","formControlName","mirrorMode",1,"form-control"],[3,"value",4,"ngFor","ngForOf"],["class","invalid-feedback",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[3,"value"],[1,"invalid-feedback"],i]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.ynx(8),e.tHW(9,7),e._UZ(10,"kbd"),e._UZ(11,"kbd"),e.N_p(),e.BQk(),e.qZA(),e.TgZ(12,"div",8),e.TgZ(13,"label",9),e.TgZ(14,"span"),e.SDv(15,10),e.qZA(),e.qZA(),e.TgZ(16,"select",11),e.YNc(17,Si,2,2,"option",12),e.qZA(),e.YNc(18,Ei,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(19,"div",14),e.TgZ(20,"cd-form-button-panel",15),e.NdJ("submitActionEvent",function(){return o.update()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.editModeForm),e.xp6(7),e.pQV(o.poolName),e.QtT(9),e.xp6(6),e.Q6J("ngForOf",o.mirrorModes),e.xp6(1),e.Q6J("ngIf",o.editModeForm.showError("mirrorMode",i,"cannotDisable")),e.xp6(2),e.Q6J("form",o.editModeForm)("submitText",o.actionLabels.UPDATE)}},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.EJ,h.b,r.JJ,r.u,l.sg,l.O5,O.p,r.YN,r.Kr],styles:[""]}),n})();class Mi{}function Oi(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,25),e.qZA())}function Ai(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,26),e.qZA())}function hi(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,27),e.qZA())}function Pi(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,28),e.qZA())}function Ii(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,29),e.qZA())}function bi(n,_){1&n&&(e.TgZ(0,"span",24),e.SDv(1,30),e.qZA())}let Ni=(()=>{class n{constructor(t,o,i,s){this.activeModal=t,this.actionLabels=o,this.rbdMirroringService=i,this.taskWrapper=s,this.bsConfig={containerClass:"theme-default"},this.createForm()}createForm(){this.editPeerForm=new M.d({clusterName:new r.NI("",{validators:[r.kI.required,this.validateClusterName]}),clientID:new r.NI("",{validators:[r.kI.required,this.validateClientID]}),monAddr:new r.NI("",{validators:[this.validateMonAddr]}),key:new r.NI("",{validators:[this.validateKey]})})}ngOnInit(){this.pattern=`${this.poolName}/${this.peerUUID}`,"edit"===this.mode&&this.rbdMirroringService.getPeer(this.poolName,this.peerUUID).subscribe(t=>{this.setResponse(t)})}validateClusterName(t){if(!t.value.match(/^[\w\-_]*$/))return{invalidClusterName:{value:t.value}}}validateClientID(t){if(!t.value.match(/^(?!client\.)[\w\-_.]*$/))return{invalidClientID:{value:t.value}}}validateMonAddr(t){if(!t.value.match(/^[,; ]*([\w.\-_\[\]]+(:[\d]+)?[,; ]*)*$/))return{invalidMonAddr:{value:t.value}}}validateKey(t){try{if(""===t.value||atob(t.value))return null}catch(o){}return{invalidKey:{value:t.value}}}setResponse(t){this.response=t,this.editPeerForm.get("clusterName").setValue(t.cluster_name),this.editPeerForm.get("clientID").setValue(t.client_id),this.editPeerForm.get("monAddr").setValue(t.mon_host),this.editPeerForm.get("key").setValue(t.key)}update(){const t=new Mi;let o;t.cluster_name=this.editPeerForm.getValue("clusterName"),t.client_id=this.editPeerForm.getValue("clientID"),t.mon_host=this.editPeerForm.getValue("monAddr"),t.key=this.editPeerForm.getValue("key"),o=this.taskWrapper.wrapTaskAroundCall("edit"===this.mode?{task:new F.R("rbd/mirroring/peer/edit",{pool_name:this.poolName}),call:this.rbdMirroringService.updatePeer(this.poolName,this.peerUUID,t)}:{task:new F.R("rbd/mirroring/peer/add",{pool_name:this.poolName}),call:this.rbdMirroringService.addPeer(this.poolName,t)}),o.subscribe({error:()=>this.editPeerForm.setErrors({cdSubmitButton:!0}),complete:()=>{this.rbdMirroringService.refresh(),this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(K),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-pool-edit-peer-modal"]],decls:38,vars:13,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$,G,X,J,te,A;return _="{VAR_SELECT, select, edit {Edit} other {Add}}",_=e.Zx4(_,{VAR_SELECT:"\ufffd0\ufffd"}),t="" + _ + " pool mirror peer",o="{VAR_SELECT, select, edit {Edit} other {Add}}",o=e.Zx4(o,{VAR_SELECT:"\ufffd0\ufffd"}),i="" + o + " the pool mirror peer attributes for pool " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "" + "\ufffd1\ufffd" + "" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + " and click " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "Submit" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + ".",i=e.Zx4(i),s="Cluster Name",a="Name...",d="CephX ID",c="CephX ID...",u="Monitor Addresses",S="Comma-delimited addresses...",N="CephX Key",P="Base64-encoded key...",$="This field is required.",G="The cluster name is not valid.",X="This field is required.",J="The CephX ID is not valid.",te="The monitory address is not valid.",A="CephX key must be base64 encoded.",[[3,"modalRef"],[1,"modal-title"],t,[1,"modal-content"],["name","editPeerForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],i,[1,"form-group"],["for","clusterName",1,"col-form-label","required"],s,["type","text","placeholder",a,"id","clusterName","name","clusterName","formControlName","clusterName","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","clientID",1,"col-form-label","required"],d,["type","text","placeholder",c,"id","clientID","name","clientID","formControlName","clientID",1,"form-control"],["for","monAddr",1,"col-form-label"],u,["type","text","placeholder",S,"id","monAddr","name","monAddr","formControlName","monAddr",1,"form-control"],["for","key",1,"col-form-label"],N,["type","text","placeholder",P,"id","key","name","key","formControlName","key",1,"form-control"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],$,G,X,J,te,A]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.TgZ(1,"span",1),e.SDv(2,2),e.qZA(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.TgZ(8,"span"),e.tHW(9,7),e._UZ(10,"kbd"),e._UZ(11,"kbd"),e.N_p(),e.qZA(),e.qZA(),e.TgZ(12,"div",8),e.TgZ(13,"label",9),e.SDv(14,10),e.qZA(),e._UZ(15,"input",11),e.YNc(16,Oi,2,0,"span",12),e.YNc(17,Ai,2,0,"span",12),e.qZA(),e.TgZ(18,"div",8),e.TgZ(19,"label",13),e.SDv(20,14),e.qZA(),e._UZ(21,"input",15),e.YNc(22,hi,2,0,"span",12),e.YNc(23,Pi,2,0,"span",12),e.qZA(),e.TgZ(24,"div",8),e.TgZ(25,"label",16),e.TgZ(26,"span"),e.SDv(27,17),e.qZA(),e.qZA(),e._UZ(28,"input",18),e.YNc(29,Ii,2,0,"span",12),e.qZA(),e.TgZ(30,"div",8),e.TgZ(31,"label",19),e.TgZ(32,"span"),e.SDv(33,20),e.qZA(),e.qZA(),e._UZ(34,"input",21),e.YNc(35,bi,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(36,"div",22),e.TgZ(37,"cd-form-button-panel",23),e.NdJ("submitActionEvent",function(){return o.update()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(2),e.pQV(o.mode),e.QtT(2),e.xp6(2),e.Q6J("formGroup",o.editPeerForm),e.xp6(7),e.pQV(o.mode)(o.poolName),e.QtT(9),e.xp6(5),e.Q6J("ngIf",o.editPeerForm.showError("clusterName",i,"required")),e.xp6(1),e.Q6J("ngIf",o.editPeerForm.showError("clusterName",i,"invalidClusterName")),e.xp6(5),e.Q6J("ngIf",o.editPeerForm.showError("clientID",i,"required")),e.xp6(1),e.Q6J("ngIf",o.editPeerForm.showError("clientID",i,"invalidClientID")),e.xp6(6),e.Q6J("ngIf",o.editPeerForm.showError("monAddr",i,"invalidMonAddr")),e.xp6(6),e.Q6J("ngIf",o.editPeerForm.showError("key",i,"invalidKey")),e.xp6(2),e.Q6J("form",o.editPeerForm)("submitText",o.actionLabels.SUBMIT)}},directives:[R.z,r._Y,r.JL,v.V,r.sg,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,O.p],styles:[""]}),n})();const Di=["healthTmpl"];function vi(n,_){if(1&n&&(e.TgZ(0,"span",3),e.ALo(1,"mirrorHealthColor"),e._uU(2),e.qZA()),2&n){const o=_.value;e.Q6J("ngClass",e.lcZ(1,2,_.row.health_color)),e.xp6(2),e.Oqu(o)}}let Li=(()=>{class n{constructor(t,o,i,s){this.authStorageService=t,this.rbdMirroringService=o,this.modalService=i,this.taskWrapper=s,this.selection=new Ee.r,this.tableStatus=new U.E,this.data=[],this.permission=this.authStorageService.getPermissions().rbdMirroring;const a={permission:"update",icon:T.P.edit,click:()=>this.editModeModal(),name:"Edit Mode",canBePrimary:()=>!0},d={permission:"create",icon:T.P.add,name:"Add Peer",click:()=>this.editPeersModal("add"),disable:()=>!this.selection.first()||"disabled"===this.selection.first().mirror_mode,visible:()=>!this.getPeerUUID(),canBePrimary:()=>!1},c={permission:"update",icon:T.P.exchange,name:"Edit Peer",click:()=>this.editPeersModal("edit"),visible:()=>!!this.getPeerUUID()},u={permission:"delete",icon:T.P.destroy,name:"Delete Peer",click:()=>this.deletePeersModal(),visible:()=>!!this.getPeerUUID()};this.tableActions=[a,d,c,u]}ngOnInit(){this.columns=[{prop:"name",name:"Name",flexGrow:2},{prop:"mirror_mode",name:"Mode",flexGrow:2},{prop:"leader_id",name:"Leader",flexGrow:2},{prop:"image_local_count",name:"# Local",flexGrow:2},{prop:"image_remote_count",name:"# Remote",flexGrow:2},{prop:"health",name:"Health",cellTemplate:this.healthTmpl,flexGrow:1}],this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.data=t.content_data.pools,this.tableStatus=new U.E(t.status)})}ngOnDestroy(){this.subs.unsubscribe()}refresh(){this.rbdMirroringService.refresh()}editModeModal(){const t={poolName:this.selection.first().name};this.modalRef=this.modalService.show(Ri,t)}editPeersModal(t){const o={poolName:this.selection.first().name,mode:t};"edit"===t&&(o.peerUUID=this.getPeerUUID()),this.modalRef=this.modalService.show(Ni,o)}deletePeersModal(){const t=this.selection.first().name,o=this.getPeerUUID();this.modalRef=this.modalService.show(he.M,{itemDescription:"mirror peer",itemNames:[`${t} (${o})`],submitActionObservable:()=>new fi.y(i=>{this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/mirroring/peer/delete",{pool_name:t}),call:this.rbdMirroringService.deletePeer(t,o)}).subscribe({error:s=>i.error(s),complete:()=>{this.rbdMirroringService.refresh(),i.complete()}})})})}getPeerUUID(){const t=this.selection.first(),o=this.data.find(i=>t&&t.name===i.name);if(o&&o.peer_uuids)return o.peer_uuids[0]}updateSelection(t){this.selection=t}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(K),e.Y36(re.Z),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-mirroring-pools"]],viewQuery:function(t,o){if(1&t&&e.Gf(Di,7),2&t){let i;e.iGM(i=e.CRH())&&(o.healthTmpl=i.first)}},decls:4,vars:7,consts:[["columnMode","flex","identifier","name","forceIdentifier","true","selectionType","single",3,"data","columns","autoReload","status","fetchData","updateSelection"],[1,"table-actions",3,"permission","selection","tableActions"],["healthTmpl",""],[3,"ngClass"]],template:function(t,o){1&t&&(e.TgZ(0,"cd-table",0),e.NdJ("fetchData",function(){return o.refresh()})("updateSelection",function(s){return o.updateSelection(s)}),e._UZ(1,"cd-table-actions",1),e.qZA(),e.YNc(2,vi,3,4,"ng-template",null,2,e.W1O)),2&t&&(e.Q6J("data",o.data)("columns",o.columns)("autoReload",-1)("status",o.tableStatus),e.xp6(1),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions))},directives:[ee.a,Re.K,l.mk],pipes:[We],styles:[""]}),n})();var mt=p(59376);const Fi=["stateTmpl"],$i=["syncTmpl"],Zi=["progressTmpl"];function Bi(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"cd-table",14),e.NdJ("fetchData",function(){return e.CHM(t),e.oxw().refresh()}),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("data",t.image_error.data)("columns",t.image_error.columns)("autoReload",-1)("status",t.tableStatus)}}function Gi(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"cd-table",14),e.NdJ("fetchData",function(){return e.CHM(t),e.oxw().refresh()}),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("data",t.image_syncing.data)("columns",t.image_syncing.columns)("autoReload",-1)("status",t.tableStatus)}}function yi(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"cd-table",14),e.NdJ("fetchData",function(){return e.CHM(t),e.oxw().refresh()}),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("data",t.image_ready.data)("columns",t.image_ready.columns)("autoReload",-1)("status",t.tableStatus)}}function xi(n,_){if(1&n&&(e.TgZ(0,"span",15),e.ALo(1,"mirrorHealthColor"),e._uU(2),e.qZA()),2&n){const o=_.value;e.Q6J("ngClass",e.lcZ(1,2,_.row.state_color)),e.xp6(2),e.Oqu(o)}}function wi(n,_){1&n&&(e.TgZ(0,"span",16),e.SDv(1,17),e.qZA())}function qi(n,_){1&n&&e._UZ(0,"ngb-progressbar",18),2&n&&e.Q6J("value",_.value)("showValue",!0)}let Hi=(()=>{class n{constructor(t){this.rbdMirroringService=t,this.image_error={data:[],columns:{}},this.image_syncing={data:[],columns:{}},this.image_ready={data:[],columns:{}},this.tableStatus=new U.E}ngOnInit(){this.image_error.columns=[{prop:"pool_name",name:"Pool",flexGrow:2},{prop:"name",name:"Image",flexGrow:2},{prop:"description",name:"Issue",flexGrow:4},{prop:"state",name:"State",cellTemplate:this.stateTmpl,flexGrow:1}],this.image_syncing.columns=[{prop:"pool_name",name:"Pool",flexGrow:2},{prop:"name",name:"Image",flexGrow:2},{prop:"progress",name:"Progress",cellTemplate:this.progressTmpl,flexGrow:2},{prop:"state",name:"State",cellTemplate:this.syncTmpl,flexGrow:1}],this.image_ready.columns=[{prop:"pool_name",name:"Pool",flexGrow:2},{prop:"name",name:"Image",flexGrow:2},{prop:"description",name:"Description",flexGrow:4},{prop:"state",name:"State",cellTemplate:this.stateTmpl,flexGrow:1}],this.subs=this.rbdMirroringService.subscribeSummary(t=>{this.image_error.data=t.content_data.image_error,this.image_syncing.data=t.content_data.image_syncing,this.image_ready.data=t.content_data.image_ready,this.tableStatus=new U.E(t.status)})}ngOnDestroy(){this.subs.unsubscribe()}refresh(){this.rbdMirroringService.refresh()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(K))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-mirroring-images"]],viewQuery:function(t,o){if(1&t&&(e.Gf(Fi,7),e.Gf($i,7),e.Gf(Zi,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.stateTmpl=i.first),e.iGM(i=e.CRH())&&(o.syncTmpl=i.first),e.iGM(i=e.CRH())&&(o.progressTmpl=i.first)}},decls:21,vars:1,consts:function(){let _,t,o,i;return _="Issues",t="Syncing",o="Ready",i="Syncing",[["ngbNav","","cdStatefulTab","image-list",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","issues"],["ngbNavLink",""],_,["ngbNavContent",""],["ngbNavItem","syncing"],t,["ngbNavItem","ready"],o,[3,"ngbNavOutlet"],["stateTmpl",""],["syncTmpl",""],["progressTmpl",""],["columnMode","flex",3,"data","columns","autoReload","status","fetchData"],[3,"ngClass"],[1,"badge","badge-info"],i,["type","info",3,"value","showValue"]]},template:function(t,o){if(1&t&&(e.TgZ(0,"ul",0,1),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.YNc(5,Bi,1,4,"ng-template",5),e.qZA(),e.TgZ(6,"li",6),e.TgZ(7,"a",3),e.SDv(8,7),e.qZA(),e.YNc(9,Gi,1,4,"ng-template",5),e.qZA(),e.TgZ(10,"li",8),e.TgZ(11,"a",3),e.SDv(12,9),e.qZA(),e.YNc(13,yi,1,4,"ng-template",5),e.qZA(),e.qZA(),e._UZ(14,"div",10),e.YNc(15,xi,3,4,"ng-template",null,11,e.W1O),e.YNc(17,wi,2,0,"ng-template",null,12,e.W1O),e.YNc(19,qi,1,2,"ng-template",null,13,e.W1O)),2&t){const i=e.MAs(1);e.xp6(14),e.Q6J("ngbNavOutlet",i)}},directives:[I.Pz,mt.m,I.nv,I.Vx,I.uN,I.tO,ee.a,l.mk,I.Ly],pipes:[We],styles:[""]}),n})(),Ki=(()=>{class n{constructor(t,o,i){this.authStorageService=t,this.rbdMirroringService=o,this.modalService=i,this.selection=new Ee.r,this.peersExist=!0,this.subs=new Yn.w,this.permission=this.authStorageService.getPermissions().rbdMirroring;const s={permission:"update",icon:T.P.edit,click:()=>this.editSiteNameModal(),name:"Edit Site Name",canBePrimary:()=>!0,disable:()=>!1},a={permission:"update",icon:T.P.upload,click:()=>this.createBootstrapModal(),name:"Create Bootstrap Token",disable:()=>!1},d={permission:"update",icon:T.P.download,click:()=>this.importBootstrapModal(),name:"Import Bootstrap Token",disable:()=>this.peersExist};this.tableActions=[s,a,d]}ngOnInit(){this.subs.add(this.rbdMirroringService.startPolling()),this.subs.add(this.rbdMirroringService.subscribeSummary(t=>{this.status=t.content_data.status,this.siteName=t.site_name,this.peersExist=!!t.content_data.pools.find(o=>o.peer_uuids.length>0)}))}ngOnDestroy(){this.subs.unsubscribe()}editSiteNameModal(){this.modalRef=this.modalService.show(pi,{siteName:this.siteName})}createBootstrapModal(){this.modalRef=this.modalService.show(ii,{siteName:this.siteName})}importBootstrapModal(){this.modalRef=this.modalService.show(di,{siteName:this.siteName})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(K),e.Y36(re.Z))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-mirroring"]],decls:21,vars:4,consts:function(){let _,t,o,i;return _="Site Name:",t="Daemons",o="Pools",i="Images",[[1,"row"],[1,"col-md-12"],_,[1,"table-actions","float-right",3,"permission","selection","tableActions"],[1,"col-sm-6"],t,o,i]},template:function(t,o){1&t&&(e.TgZ(0,"div",0),e.TgZ(1,"div",1),e.TgZ(2,"span"),e.TgZ(3,"strong"),e.SDv(4,2),e.qZA(),e._uU(5),e.qZA(),e._UZ(6,"cd-table-actions",3),e.qZA(),e.qZA(),e.TgZ(7,"div",0),e.TgZ(8,"div",4),e.TgZ(9,"legend"),e.SDv(10,5),e.qZA(),e._UZ(11,"cd-mirroring-daemons"),e.qZA(),e.TgZ(12,"div",4),e.TgZ(13,"legend"),e.SDv(14,6),e.qZA(),e._UZ(15,"cd-mirroring-pools"),e.qZA(),e.qZA(),e.TgZ(16,"div",0),e.TgZ(17,"div",1),e.TgZ(18,"legend"),e.SDv(19,7),e.qZA(),e._UZ(20,"cd-mirroring-images"),e.qZA(),e.qZA()),2&t&&(e.xp6(5),e.hij(" ",o.siteName,""),e.xp6(1),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions))},directives:[Re.K,Ti,Li,Hi],styles:[""]}),n})();var Tt=p(80226),ki=p(28049),Xi=p(43190),Ke=p(80842),et=p(30633),Fe=p(47557),Qi=p(28211);class zi{}var Pe=(()=>{return(n=Pe||(Pe={}))[n.V1=1]="V1",n[n.V2=2]="V2",Pe;var n})();class Ji{constructor(){this.features=[]}}class Yi{constructor(){this.features=[]}}class Ui extends class{}{constructor(){super(...arguments),this.features=[]}}class ji{constructor(){this.features=[]}}var ke=(()=>{return(n=ke||(ke={})).editing="editing",n.cloning="cloning",n.copying="copying",ke;var n})(),Wi=p(17932),e_=p(54555),t_=p(18372);function o_(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"label",56),e.SDv(2,57),e.ALo(3,"titlecase"),e.qZA(),e.TgZ(4,"div",12),e._UZ(5,"input",58),e._UZ(6,"hr"),e.qZA(),e.qZA()),2&n){const t=e.oxw(2);e.xp6(3),e.pQV(e.lcZ(3,1,t.action)),e.QtT(2)}}function n_(n,_){1&n&&(e.TgZ(0,"span",59),e.ynx(1),e.SDv(2,60),e.BQk(),e.qZA())}function i_(n,_){1&n&&(e.TgZ(0,"span",59),e.ynx(1),e.SDv(2,61),e.BQk(),e.qZA())}function __(n,_){1&n&&e._UZ(0,"input",62)}function s_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,65),e.qZA()),2&n&&e.Q6J("ngValue",null)}function a_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,66),e.qZA()),2&n&&e.Q6J("ngValue",null)}function r_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,67),e.qZA()),2&n&&e.Q6J("ngValue",null)}function l_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.pool_name),e.xp6(1),e.Oqu(t.pool_name)}}function c_(n,_){if(1&n&&(e.TgZ(0,"select",63),e.YNc(1,s_,2,1,"option",64),e.YNc(2,a_,2,1,"option",64),e.YNc(3,r_,2,1,"option",64),e.YNc(4,l_,2,2,"option",44),e.qZA()),2&n){const t=e.oxw(2);e.xp6(1),e.Q6J("ngIf",null===t.pools),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&0===t.pools.length),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&t.pools.length>0),e.xp6(1),e.Q6J("ngForOf",t.pools)}}function d_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,69),e.qZA())}const p_=function(n,_){return[n,_]};function g_(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"div",20),e._UZ(2,"i",70),e.qZA(),e.qZA()),2&n){const t=e.oxw(2);e.xp6(2),e.Q6J("ngClass",e.WLB(1,p_,t.icons.spinner,t.icons.spin))}}function u_(n,_){1&n&&e._UZ(0,"input",74)}function m_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,76),e.qZA()),2&n&&e.Q6J("ngValue",null)}function T_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,77),e.qZA()),2&n&&e.Q6J("ngValue",null)}function f_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,78),e.qZA()),2&n&&e.Q6J("ngValue",null)}function C_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(t)}}function S_(n,_){if(1&n&&(e.TgZ(0,"select",75),e.YNc(1,m_,2,1,"option",64),e.YNc(2,T_,2,1,"option",64),e.YNc(3,f_,2,1,"option",64),e.YNc(4,C_,2,2,"option",44),e.qZA()),2&n){const t=e.oxw(3);e.xp6(1),e.Q6J("ngIf",null===t.pools),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&0===t.pools.length),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&t.pools.length>0),e.xp6(1),e.Q6J("ngForOf",t.namespaces)}}function E_(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"label",71),e._uU(2," Namespace "),e.qZA(),e.TgZ(3,"div",12),e.YNc(4,u_,1,0,"input",72),e.YNc(5,S_,5,4,"select",73),e.qZA(),e.qZA()),2&n){const t=e.oxw(2);e.xp6(4),e.Q6J("ngIf","editing"===t.mode||!t.poolPermission.read),e.xp6(1),e.Q6J("ngIf","editing"!==t.mode&&t.poolPermission.read)}}function R_(n,_){1&n&&(e.TgZ(0,"cd-helper"),e.TgZ(1,"span"),e.SDv(2,79),e.qZA(),e.qZA())}function M_(n,_){1&n&&e._UZ(0,"input",85)}function O_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,87),e.qZA()),2&n&&e.Q6J("ngValue",null)}function A_(n,_){1&n&&(e.TgZ(0,"option",48),e.SDv(1,88),e.qZA()),2&n&&e.Q6J("ngValue",null)}function h_(n,_){1&n&&(e.TgZ(0,"option",48),e._uU(1,"-- Select a data pool -- "),e.qZA()),2&n&&e.Q6J("ngValue",null)}function P_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.pool_name),e.xp6(1),e.Oqu(t.pool_name)}}function I_(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"select",86),e.NdJ("change",function(i){return e.CHM(t),e.oxw(3).onDataPoolChange(i.target.value)}),e.YNc(1,O_,2,1,"option",64),e.YNc(2,A_,2,1,"option",64),e.YNc(3,h_,2,1,"option",64),e.YNc(4,P_,2,2,"option",44),e.qZA()}if(2&n){const t=e.oxw(3);e.xp6(1),e.Q6J("ngIf",null===t.dataPools),e.xp6(1),e.Q6J("ngIf",null!==t.dataPools&&0===t.dataPools.length),e.xp6(1),e.Q6J("ngIf",null!==t.dataPools&&t.dataPools.length>0),e.xp6(1),e.Q6J("ngForOf",t.dataPools)}}function b_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,89),e.qZA())}const Xe=function(n){return{required:n}};function N_(n,_){if(1&n&&(e.TgZ(0,"div",9),e.TgZ(1,"label",80),e.TgZ(2,"span",70),e.SDv(3,81),e.qZA(),e._UZ(4,"cd-helper",82),e.qZA(),e.TgZ(5,"div",12),e.YNc(6,M_,1,0,"input",83),e.YNc(7,I_,5,4,"select",84),e.YNc(8,b_,2,0,"span",14),e.qZA(),e.qZA()),2&n){e.oxw();const t=e.MAs(2),o=e.oxw();e.xp6(2),e.Q6J("ngClass",e.VKq(4,Xe,"editing"!==o.mode)),e.xp6(4),e.Q6J("ngIf","editing"===o.mode||!o.poolPermission.read),e.xp6(1),e.Q6J("ngIf","editing"!==o.mode&&o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("dataPool",t,"required"))}}function D_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,90),e.qZA())}function v_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,91),e.qZA())}function L_(n,_){if(1&n&&e._UZ(0,"cd-helper",95),2&n){const t=e.oxw().$implicit;e.s9C("html",t.helperHtml)}}function F_(n,_){if(1&n&&(e.TgZ(0,"div",21),e._UZ(1,"input",92),e.TgZ(2,"label",93),e._uU(3),e.qZA(),e.YNc(4,L_,1,1,"cd-helper",94),e.qZA()),2&n){const t=_.$implicit;e.xp6(1),e.s9C("id",t.key),e.s9C("name",t.key),e.s9C("formControlName",t.key),e.xp6(1),e.s9C("for",t.key),e.xp6(1),e.Oqu(t.desc),e.xp6(1),e.Q6J("ngIf",t.helperHtml)}}function $_(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"a",96),e.NdJ("click",function(){return e.CHM(t),e.oxw(2).advancedEnabled=!0,!1}),e.SDv(1,97),e.qZA()}}function Z_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(t)}}function B_(n,_){if(1&n&&(e.TgZ(0,"option",68),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(t)}}function G_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,98),e.qZA())}function y_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,99),e.qZA())}function x_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,100),e.qZA())}function w_(n,_){1&n&&(e.TgZ(0,"span",59),e.SDv(1,101),e.qZA())}function q_(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.YNc(9,o_,7,3,"div",8),e.TgZ(10,"div",9),e.TgZ(11,"label",10),e.SDv(12,11),e.qZA(),e.TgZ(13,"div",12),e._UZ(14,"input",13),e.YNc(15,n_,3,0,"span",14),e.YNc(16,i_,3,0,"span",14),e.qZA(),e.qZA(),e.TgZ(17,"div",15),e.NdJ("change",function(i){return e.CHM(t),e.oxw().onPoolChange(i.target.value)}),e.TgZ(18,"label",16),e.SDv(19,17),e.qZA(),e.TgZ(20,"div",12),e.YNc(21,__,1,0,"input",18),e.YNc(22,c_,5,4,"select",19),e.YNc(23,d_,2,0,"span",14),e.qZA(),e.qZA(),e.YNc(24,g_,3,4,"div",8),e.YNc(25,E_,6,2,"div",8),e.TgZ(26,"div",9),e.TgZ(27,"div",20),e.TgZ(28,"div",21),e.TgZ(29,"input",22),e.NdJ("change",function(){return e.CHM(t),e.oxw().onUseDataPoolChange()}),e.qZA(),e.TgZ(30,"label",23),e.SDv(31,24),e.qZA(),e.YNc(32,R_,3,0,"cd-helper",25),e.qZA(),e.qZA(),e.qZA(),e.YNc(33,N_,9,6,"div",8),e.TgZ(34,"div",9),e.TgZ(35,"label",26),e.SDv(36,27),e.qZA(),e.TgZ(37,"div",12),e._UZ(38,"input",28),e.YNc(39,D_,2,0,"span",14),e.YNc(40,v_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(41,"div",29),e.TgZ(42,"label",30),e.SDv(43,31),e.qZA(),e.TgZ(44,"div",12),e.YNc(45,F_,5,6,"div",32),e.qZA(),e.qZA(),e.TgZ(46,"div",33),e.TgZ(47,"div",34),e.YNc(48,$_,2,0,"a",35),e.qZA(),e.qZA(),e.TgZ(49,"div",36),e.TgZ(50,"legend",37),e.SDv(51,38),e.qZA(),e.TgZ(52,"div",39),e.TgZ(53,"h4",37),e.SDv(54,40),e.qZA(),e.TgZ(55,"div",9),e.TgZ(56,"label",41),e.SDv(57,42),e.qZA(),e.TgZ(58,"div",12),e.TgZ(59,"select",43),e.YNc(60,Z_,2,2,"option",44),e.qZA(),e.qZA(),e.qZA(),e.TgZ(61,"div",9),e.TgZ(62,"label",45),e.SDv(63,46),e.qZA(),e.TgZ(64,"div",12),e.TgZ(65,"select",47),e.TgZ(66,"option",48),e.SDv(67,49),e.qZA(),e.YNc(68,B_,2,2,"option",44),e.qZA(),e.YNc(69,G_,2,0,"span",14),e.YNc(70,y_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(71,"div",9),e.TgZ(72,"label",50),e.SDv(73,51),e.qZA(),e.TgZ(74,"div",12),e._UZ(75,"input",52),e.YNc(76,x_,2,0,"span",14),e.YNc(77,w_,2,0,"span",14),e.qZA(),e.qZA(),e.qZA(),e.TgZ(78,"cd-rbd-configuration-form",53),e.NdJ("changes",function(i){return e.CHM(t),e.oxw().getDirtyConfigurationValues=i}),e.qZA(),e.qZA(),e.qZA(),e.TgZ(79,"div",54),e.TgZ(80,"cd-form-button-panel",55),e.NdJ("submitActionEvent",function(){return e.CHM(t),e.oxw().submit()}),e.ALo(81,"titlecase"),e.ALo(82,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&n){const t=e.MAs(2),o=e.oxw();e.xp6(1),e.Q6J("formGroup",o.rbdForm),e.xp6(6),e.pQV(e.lcZ(6,32,o.action))(e.lcZ(7,34,o.resource)),e.QtT(5),e.xp6(2),e.Q6J("ngIf",o.rbdForm.getValue("parent")),e.xp6(6),e.Q6J("ngIf",o.rbdForm.showError("name",t,"required")),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("name",t,"pattern")),e.xp6(2),e.Q6J("ngClass",e.VKq(40,Xe,"editing"!==o.mode)),e.xp6(3),e.Q6J("ngIf","editing"===o.mode||!o.poolPermission.read),e.xp6(1),e.Q6J("ngIf","editing"!==o.mode&&o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("pool",t,"required")),e.xp6(1),e.Q6J("ngIf","editing"!==o.mode&&o.rbdForm.getValue("pool")&&null===o.namespaces),e.xp6(1),e.Q6J("ngIf","editing"===o.mode&&o.rbdForm.getValue("namespace")||"editing"!==o.mode&&(o.namespaces&&o.namespaces.length>0||!o.poolPermission.read)),e.xp6(7),e.Q6J("ngIf",o.allDataPools.length<=1),e.xp6(1),e.Q6J("ngIf",o.rbdForm.getValue("useDataPool")),e.xp6(6),e.Q6J("ngIf",o.rbdForm.showError("size",t,"required")),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("size",t,"invalidSizeObject")),e.xp6(5),e.Q6J("ngForOf",o.featuresList),e.xp6(3),e.Q6J("ngIf",!o.advancedEnabled),e.xp6(1),e.Q6J("hidden",!o.advancedEnabled),e.xp6(11),e.Q6J("ngForOf",o.objectSizes),e.xp6(2),e.Q6J("ngClass",e.VKq(42,Xe,o.rbdForm.getValue("stripingCount"))),e.xp6(4),e.Q6J("ngValue",null),e.xp6(2),e.Q6J("ngForOf",o.objectSizes),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("stripingUnit",t,"required")),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("stripingUnit",t,"invalidStripingUnit")),e.xp6(2),e.Q6J("ngClass",e.VKq(44,Xe,o.rbdForm.getValue("stripingUnit"))),e.xp6(4),e.Q6J("ngIf",o.rbdForm.showError("stripingCount",t,"required")),e.xp6(1),e.Q6J("ngIf",o.rbdForm.showError("stripingCount",t,"min")),e.xp6(1),e.Q6J("form",o.rbdForm)("initializeData",o.initializeConfigData),e.xp6(2),e.Q6J("form",t)("submitText",e.lcZ(81,36,o.action)+" "+e.lcZ(82,38,o.resource))}}let $e=(()=>{class n extends q.E{constructor(t,o,i,s,a,d,c,u,S){super(),this.authStorageService=t,this.route=o,this.poolService=i,this.rbdService=s,this.formatter=a,this.taskWrapper=d,this.dimlessBinaryPipe=c,this.actionLabels=u,this.router=S,this.namespaces=[],this.namespacesByPoolCache={},this.pools=null,this.allPools=null,this.dataPools=null,this.allDataPools=[],this.featuresList=[],this.initializeConfigData=new Tt.t(1),this.advancedEnabled=!1,this.rbdFormMode=ke,this.defaultObjectSize="4 MiB",this.objectSizes=["4 KiB","8 KiB","16 KiB","32 KiB","64 KiB","128 KiB","256 KiB","512 KiB","1 MiB","2 MiB","4 MiB","8 MiB","16 MiB","32 MiB"],this.rbdImage=new Tt.t(1),this.icons=T.P,this.routerUrl=this.router.url,this.poolPermission=this.authStorageService.getPermissions().pool,this.resource="RBD",this.features={"deep-flatten":{desc:"Deep flatten",requires:null,allowEnable:!1,allowDisable:!0},layering:{desc:"Layering",requires:null,allowEnable:!1,allowDisable:!1},"exclusive-lock":{desc:"Exclusive lock",requires:null,allowEnable:!0,allowDisable:!0},"object-map":{desc:"Object map (requires exclusive-lock)",requires:"exclusive-lock",allowEnable:!0,allowDisable:!0,initDisabled:!0},journaling:{desc:"Journaling (requires exclusive-lock)",requires:"exclusive-lock",allowEnable:!0,allowDisable:!0,initDisabled:!0},"fast-diff":{desc:"Fast diff (interlocked with object-map)",requires:"object-map",allowEnable:!0,allowDisable:!0,interlockedWith:"object-map",initDisabled:!0}},this.featuresList=this.objToArray(this.features),this.createForm()}objToArray(t){return C().map(t,(o,i)=>Object.assign(o,{key:i}))}createForm(){this.rbdForm=new M.d({parent:new r.NI(""),name:new r.NI("",{validators:[r.kI.required,r.kI.pattern(/^[^@/]+?$/)]}),pool:new r.NI(null,{validators:[r.kI.required]}),namespace:new r.NI(null),useDataPool:new r.NI(!1),dataPool:new r.NI(null),size:new r.NI(null,{updateOn:"blur"}),obj_size:new r.NI(this.defaultObjectSize),features:new M.d(this.featuresList.reduce((t,o)=>(t[o.key]=new r.NI({value:!1,disabled:!!o.initDisabled}),t),{})),stripingUnit:new r.NI(null),stripingCount:new r.NI(null,{updateOn:"blur"})},this.validateRbdForm(this.formatter))}disableForEdit(){this.rbdForm.get("parent").disable(),this.rbdForm.get("pool").disable(),this.rbdForm.get("namespace").disable(),this.rbdForm.get("useDataPool").disable(),this.rbdForm.get("dataPool").disable(),this.rbdForm.get("obj_size").disable(),this.rbdForm.get("stripingUnit").disable(),this.rbdForm.get("stripingCount").disable(),this.rbdImage.subscribe(t=>{t.image_format===Pe.V1&&(this.rbdForm.get("deep-flatten").disable(),this.rbdForm.get("layering").disable(),this.rbdForm.get("exclusive-lock").disable())})}disableForClone(){this.rbdForm.get("parent").disable(),this.rbdForm.get("size").disable()}disableForCopy(){this.rbdForm.get("parent").disable(),this.rbdForm.get("size").disable()}ngOnInit(){this.prepareFormForAction(),this.gatherNeededData().subscribe(this.handleExternalData.bind(this))}prepareFormForAction(){const t=this.routerUrl;t.startsWith("/block/rbd/edit")?(this.mode=this.rbdFormMode.editing,this.action=this.actionLabels.EDIT,this.disableForEdit()):t.startsWith("/block/rbd/clone")?(this.mode=this.rbdFormMode.cloning,this.disableForClone(),this.action=this.actionLabels.CLONE):t.startsWith("/block/rbd/copy")?(this.mode=this.rbdFormMode.copying,this.action=this.actionLabels.COPY,this.disableForCopy()):this.action=this.actionLabels.CREATE,C().each(this.features,o=>{this.rbdForm.get("features").get(o.key).valueChanges.subscribe(i=>this.featureFormUpdate(o.key,i))})}gatherNeededData(){const t={};return this.mode?this.route.params.subscribe(o=>{const i=Z.N.fromString(decodeURIComponent(o.image_spec));o.snap&&(this.snapName=decodeURIComponent(o.snap)),t.rbd=this.rbdService.get(i)}):t.defaultFeatures=this.rbdService.defaultFeatures(),this.mode!==this.rbdFormMode.editing&&this.poolPermission.read&&(t.pools=this.poolService.list(["pool_name","type","flags_names","application_metadata"])),(0,W.D)(t)}handleExternalData(t){if(this.handlePoolData(t.pools),t.defaultFeatures&&this.setFeatures(t.defaultFeatures),t.rbd){const o=t.rbd;this.setResponse(o,this.snapName),this.rbdImage.next(o)}this.loadingReady()}handlePoolData(t){if(!t)return;const o=[],i=[];for(const s of t)this.rbdService.isRBDPool(s)&&("replicated"===s.type?(o.push(s),i.push(s)):"erasure"===s.type&&-1!==s.flags_names.indexOf("ec_overwrites")&&i.push(s));if(this.pools=o,this.allPools=o,this.dataPools=i,this.allDataPools=i,1===this.pools.length){const s=this.pools[0].pool_name;this.rbdForm.get("pool").setValue(s),this.onPoolChange(s)}this.allDataPools.length<=1&&this.rbdForm.get("useDataPool").disable()}onPoolChange(t){const o=this.rbdForm.get("dataPool");o.value===t&&o.setValue(null),this.dataPools=this.allDataPools?this.allDataPools.filter(i=>i.pool_name!==t):[],this.namespaces=null,t in this.namespacesByPoolCache?this.namespaces=this.namespacesByPoolCache[t]:this.rbdService.listNamespaces(t).subscribe(i=>{i=i.map(s=>s.namespace),this.namespacesByPoolCache[t]=i,this.namespaces=i}),this.rbdForm.get("namespace").setValue(null)}onUseDataPoolChange(){this.rbdForm.getValue("useDataPool")||(this.rbdForm.get("dataPool").setValue(null),this.onDataPoolChange(null))}onDataPoolChange(t){const o=this.allPools.filter(i=>i.pool_name!==t);this.rbdForm.getValue("pool")===t&&this.rbdForm.get("pool").setValue(null),this.pools=o}validateRbdForm(t){return o=>{const i=o.get("useDataPool"),s=o.get("dataPool");let a=null;i.value&&null==s.value&&(a={required:!0}),s.setErrors(a);const d=o.get("size"),c=o.get("obj_size"),u=t.toBytes(null!=c.value?c.value:this.defaultObjectSize),S=o.get("stripingCount"),N=null!=S.value?S.value:1;let P=null;null===d.value?P={required:!0}:N*u>t.toBytes(d.value)&&(P={invalidSizeObject:!0}),d.setErrors(P);const $=o.get("stripingUnit");let G=null;null===$.value&&null!==S.value?G={required:!0}:null!==$.value&&t.toBytes($.value)>u&&(G={invalidStripingUnit:!0}),$.setErrors(G);let X=null;return null===S.value&&null!==$.value?X={required:!0}:N<1&&(X={min:!0}),S.setErrors(X),null}}deepBoxCheck(t,o){this.getDependentChildFeatures(t).forEach(s=>{const a=this.rbdForm.get(s.key);o?a.enable({emitEvent:!1}):(a.disable({emitEvent:!1}),a.setValue(!1,{emitEvent:!1}),this.deepBoxCheck(s.key,o));const d=this.rbdForm.get("features");this.mode===this.rbdFormMode.editing&&d.get(s.key).enabled&&(-1!==this.response.features_name.indexOf(s.key)&&!s.allowDisable||-1===this.response.features_name.indexOf(s.key)&&!s.allowEnable)&&d.get(s.key).disable()})}getDependentChildFeatures(t){return C().filter(this.features,o=>o.requires===t)||[]}interlockCheck(t,o){const i=this.featuresList.find(s=>s.key===t);if(this.response){const s=null!=i.interlockedWith,a=this.featuresList.find(c=>c.interlockedWith===i.key),d=!!this.response.features_name.find(c=>c===i.key);if(s){if(d!==!!this.response.features_name.find(u=>u===i.interlockedWith))return}else if(a&&!!this.response.features_name.find(u=>u===a.key)!==d)return}o?C().filter(this.features,s=>s.interlockedWith===t).forEach(s=>this.rbdForm.get(s.key).setValue(!0,{emitEvent:!1})):i.interlockedWith&&this.rbdForm.get("features").get(i.interlockedWith).setValue(!1)}featureFormUpdate(t,o){if(o){const i=this.features[t].requires;if(i&&!this.rbdForm.getValue(i))return void this.rbdForm.get(`features.${t}`).setValue(!1)}this.deepBoxCheck(t,o),this.interlockCheck(t,o)}setFeatures(t){const o=this.rbdForm.get("features");C().forIn(this.features,i=>{-1!==t.indexOf(i.key)&&o.get(i.key).setValue(!0),this.featureFormUpdate(i.key,o.get(i.key).value)})}setResponse(t,o){this.response=t;const i=new Z.N(t.pool_name,t.namespace,t.name).toString();if(this.mode===this.rbdFormMode.cloning)this.rbdForm.get("parent").setValue(`${i}@${o}`);else if(this.mode===this.rbdFormMode.copying)o?this.rbdForm.get("parent").setValue(`${i}@${o}`):this.rbdForm.get("parent").setValue(`${i}`);else if(t.parent){const s=t.parent;this.rbdForm.get("parent").setValue(`${s.pool_name}/${s.image_name}@${s.snap_name}`)}this.mode===this.rbdFormMode.editing&&this.rbdForm.get("name").setValue(t.name),this.rbdForm.get("pool").setValue(t.pool_name),this.onPoolChange(t.pool_name),this.rbdForm.get("namespace").setValue(t.namespace),t.data_pool&&(this.rbdForm.get("useDataPool").setValue(!0),this.rbdForm.get("dataPool").setValue(t.data_pool)),this.rbdForm.get("size").setValue(this.dimlessBinaryPipe.transform(t.size)),this.rbdForm.get("obj_size").setValue(this.dimlessBinaryPipe.transform(t.obj_size)),this.setFeatures(t.features_name),this.rbdForm.get("stripingUnit").setValue(this.dimlessBinaryPipe.transform(t.stripe_unit)),this.rbdForm.get("stripingCount").setValue(t.stripe_count),this.initializeConfigData.next({initialData:this.response.configuration,sourceType:et.h.image})}createRequest(){const t=new Ui;return t.pool_name=this.rbdForm.getValue("pool"),t.namespace=this.rbdForm.getValue("namespace"),t.name=this.rbdForm.getValue("name"),t.size=this.formatter.toBytes(this.rbdForm.getValue("size")),this.addObjectSizeAndStripingToRequest(t),t.configuration=this.getDirtyConfigurationValues(),t}addObjectSizeAndStripingToRequest(t){t.obj_size=this.formatter.toBytes(this.rbdForm.getValue("obj_size")),C().forIn(this.features,o=>{this.rbdForm.getValue(o.key)&&t.features.push(o.key)}),t.stripe_unit=this.formatter.toBytes(this.rbdForm.getValue("stripingUnit")),t.stripe_count=this.rbdForm.getValue("stripingCount"),t.data_pool=this.rbdForm.getValue("dataPool")}createAction(){const t=this.createRequest();return this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/create",{pool_name:t.pool_name,namespace:t.namespace,image_name:t.name}),call:this.rbdService.create(t)})}editRequest(){const t=new ji;return t.name=this.rbdForm.getValue("name"),t.size=this.formatter.toBytes(this.rbdForm.getValue("size")),C().forIn(this.features,o=>{this.rbdForm.getValue(o.key)&&t.features.push(o.key)}),t.configuration=this.getDirtyConfigurationValues(),t}cloneRequest(){const t=new Ji;return t.child_pool_name=this.rbdForm.getValue("pool"),t.child_namespace=this.rbdForm.getValue("namespace"),t.child_image_name=this.rbdForm.getValue("name"),this.addObjectSizeAndStripingToRequest(t),t.configuration=this.getDirtyConfigurationValues(!0,et.h.image),t}editAction(){const t=new Z.N(this.response.pool_name,this.response.namespace,this.response.name);return this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/edit",{image_spec:t.toString()}),call:this.rbdService.update(t,this.editRequest())})}cloneAction(){const t=this.cloneRequest(),o=new Z.N(this.response.pool_name,this.response.namespace,this.response.name);return this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/clone",{parent_image_spec:o.toString(),parent_snap_name:this.snapName,child_pool_name:t.child_pool_name,child_namespace:t.child_namespace,child_image_name:t.child_image_name}),call:this.rbdService.cloneSnapshot(o,this.snapName,t)})}copyRequest(){const t=new Yi;return this.snapName&&(t.snapshot_name=this.snapName),t.dest_pool_name=this.rbdForm.getValue("pool"),t.dest_namespace=this.rbdForm.getValue("namespace"),t.dest_image_name=this.rbdForm.getValue("name"),this.addObjectSizeAndStripingToRequest(t),t.configuration=this.getDirtyConfigurationValues(!0,et.h.image),t}copyAction(){const t=this.copyRequest(),o=new Z.N(this.response.pool_name,this.response.namespace,this.response.name);return this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/copy",{src_image_spec:o.toString(),dest_pool_name:t.dest_pool_name,dest_namespace:t.dest_namespace,dest_image_name:t.dest_image_name}),call:this.rbdService.copy(o,t)})}submit(){this.mode||this.rbdImage.next("create"),this.rbdImage.pipe((0,ki.P)(),(0,Xi.w)(()=>this.mode===this.rbdFormMode.editing?this.editAction():this.mode===this.rbdFormMode.cloning?this.cloneAction():this.mode===this.rbdFormMode.copying?this.copyAction():this.createAction())).subscribe(()=>{},()=>this.rbdForm.setErrors({cdSubmitButton:!0}),()=>this.router.navigate(["/block/rbd"]))}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(m.gz),e.Y36(Ke.q),e.Y36(x),e.Y36(Qi.H),e.Y36(Q.P),e.Y36(Fe.$),e.Y36(D.p4),e.Y36(m.F0))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-form"]],features:[e.qOj],decls:1,vars:1,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$,G,X,J,te,A,w,de,pe,ge,ue,me,Te,fe,Ce,Se,y,Ze,Be,Ge,ye,xe,we,qe;return _="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",t="Name",o="Pool",i="Use a dedicated data pool",s="Size",a="e.g., 10GiB",d="Features",c="Advanced",u="Striping",S="Object size",N="Stripe unit",P="-- Select stripe unit --",$="Stripe count",G="" + "\ufffd0\ufffd" + " from",X="This field is required.",J="'/' and '@' are not allowed.",te="Loading...",A="-- No rbd pools available --",w="-- Select a pool --",de="This field is required.",pe="Loading...",ge="-- No namespaces available --",ue="-- Select a namespace --",me="You need more than one pool with the rbd application label use to use a dedicated data pool.",Te="Data pool",fe="Dedicated pool that stores the object-data of the RBD.",Ce="Loading...",Se="-- No data pools available --",y="This field is required.",Ze="This field is required.",Be="You have to increase the size.",Ge="Advanced...",ye="This field is required because stripe count is defined!",xe="Stripe unit is greater than object size.",we="This field is required because stripe unit is defined!",qe="Stripe count must be greater than 0.",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["name","rbdForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"card"],[1,"card-header"],_,[1,"card-body"],["class","form-group row",4,"ngIf"],[1,"form-group","row"],["for","name",1,"cd-col-form-label","required"],t,[1,"cd-col-form-input"],["type","text","placeholder","Name...","id","name","name","name","formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],[1,"form-group","row",3,"change"],["for","pool",1,"cd-col-form-label",3,"ngClass"],o,["class","form-control","type","text","placeholder","Pool name...","id","pool","name","pool","formControlName","pool",4,"ngIf"],["id","pool","name","pool","class","form-control","formControlName","pool",4,"ngIf"],[1,"cd-col-form-offset"],[1,"custom-control","custom-checkbox"],["type","checkbox","id","useDataPool","name","useDataPool","formControlName","useDataPool",1,"custom-control-input",3,"change"],["for","useDataPool",1,"custom-control-label"],i,[4,"ngIf"],["for","size",1,"cd-col-form-label","required"],s,["id","size","name","size","type","text","formControlName","size","placeholder",a,"defaultUnit","GiB","cdDimlessBinary","",1,"form-control"],["formGroupName","features",1,"form-group","row"],["for","features",1,"cd-col-form-label"],d,["class","custom-control custom-checkbox",4,"ngFor","ngForOf"],[1,"row"],[1,"col-sm-12"],["class","float-right margin-right-md","href","",3,"click",4,"ngIf"],[3,"hidden"],[1,"cd-header"],c,[1,"col-md-12"],u,["for","size",1,"cd-col-form-label"],S,["id","obj_size","name","obj_size","formControlName","obj_size",1,"form-control"],[3,"value",4,"ngFor","ngForOf"],["for","stripingUnit",1,"cd-col-form-label",3,"ngClass"],N,["id","stripingUnit","name","stripingUnit","formControlName","stripingUnit",1,"form-control"],[3,"ngValue"],P,["for","stripingCount",1,"cd-col-form-label",3,"ngClass"],$,["id","stripingCount","name","stripingCount","formControlName","stripingCount","type","number",1,"form-control"],[3,"form","initializeData","changes"],[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],["for","name",1,"cd-col-form-label"],G,["type","text","id","parent","name","parent","formControlName","parent",1,"form-control"],[1,"invalid-feedback"],X,J,["type","text","placeholder","Pool name...","id","pool","name","pool","formControlName","pool",1,"form-control"],["id","pool","name","pool","formControlName","pool",1,"form-control"],[3,"ngValue",4,"ngIf"],te,A,w,[3,"value"],de,[3,"ngClass"],["for","pool",1,"cd-col-form-label"],["class","form-control","type","text","placeholder","Namespace...","id","namespace","name","namespace","formControlName","namespace",4,"ngIf"],["id","namespace","name","namespace","class","form-control","formControlName","namespace",4,"ngIf"],["type","text","placeholder","Namespace...","id","namespace","name","namespace","formControlName","namespace",1,"form-control"],["id","namespace","name","namespace","formControlName","namespace",1,"form-control"],pe,ge,ue,me,["for","dataPool",1,"cd-col-form-label"],Te,["html",fe],["class","form-control","type","text","placeholder","Data pool name...","id","dataPool","name","dataPool","formControlName","dataPool",4,"ngIf"],["id","dataPool","name","dataPool","class","form-control","formControlName","dataPool",3,"change",4,"ngIf"],["type","text","placeholder","Data pool name...","id","dataPool","name","dataPool","formControlName","dataPool",1,"form-control"],["id","dataPool","name","dataPool","formControlName","dataPool",1,"form-control",3,"change"],Ce,Se,y,Ze,Be,["type","checkbox",1,"custom-control-input",3,"id","name","formControlName"],[1,"custom-control-label",3,"for"],[3,"html",4,"ngIf"],[3,"html"],["href","",1,"float-right","margin-right-md",3,"click"],Ge,ye,xe,we,qe]},template:function(t,o){1&t&&e.YNc(0,q_,83,46,"div",0),2&t&&e.Q6J("cdFormLoading",o.loading)},directives:[st.y,r._Y,r.JL,r.sg,v.V,l.O5,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.mk,r.Wl,Wi.Q,r.x0,l.sg,r.EJ,r.YN,r.Kr,r.wV,e_.d,O.p,t_.S],pipes:[l.rS,Ye.m],styles:[""]}),n})();var ft=p(36169),ce=p(91801),tt=p(51847),H_=p(16738),Me=p.n(H_),ot=p(62862),K_=p(52266);function k_(n,_){1&n&&(e.TgZ(0,"div",18),e.TgZ(1,"span"),e.SDv(2,19),e.qZA(),e.qZA())}function X_(n,_){1&n&&(e.TgZ(0,"span",20),e.SDv(1,21),e.qZA())}function Q_(n,_){1&n&&(e.TgZ(0,"span",20),e.SDv(1,22),e.qZA())}function z_(n,_){if(1&n&&e._UZ(0,"cd-date-time-picker",23),2&n){const t=e.oxw();e.Q6J("control",t.moveForm.get("expiresAt"))}}let J_=(()=>{class n{constructor(t,o,i,s,a){this.rbdService=t,this.activeModal=o,this.actionLabels=i,this.fb=s,this.taskWrapper=a,this.createForm()}createForm(){this.moveForm=this.fb.group({expiresAt:["",[B.h.custom("format",t=>!(""===t||Me()(t,"YYYY-MM-DD HH:mm:ss").isValid())),B.h.custom("expired",t=>Me()().isAfter(t))]]})}ngOnInit(){this.imageSpec=new Z.N(this.poolName,this.namespace,this.imageName),this.imageSpecStr=this.imageSpec.toString(),this.pattern=`${this.poolName}/${this.imageName}`}moveImage(){let t=0;const o=this.moveForm.getValue("expiresAt");o&&(t=Me()(o,"YYYY-MM-DD HH:mm:ss").diff(Me()(),"seconds",!0)),t<0&&(t=0),this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/trash/move",{image_spec:this.imageSpecStr}),call:this.rbdService.moveTrash(this.imageSpec,t)}).subscribe({complete:()=>{this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(x),e.Y36(I.Kz),e.Y36(D.p4),e.Y36(ot.O),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-trash-move-modal"]],decls:23,vars:9,consts:function(){let _,t,o,i,s,a,d;return _="Move an image to trash",t="To move " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "" + "\ufffd0\ufffd" + "" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + " to trash, click " + "[\ufffd#10\ufffd|\ufffd#11\ufffd]" + "Move" + "[\ufffd/#10\ufffd|\ufffd/#11\ufffd]" + ". Optionally, you can pick an expiration date.",t=e.Zx4(t),o="Protection expires at",i="NOT PROTECTED",s="This image contains snapshot(s), which will prevent it from being removed after moved to trash.",a="Wrong date format. Please use \"YYYY-MM-DD HH:mm:ss\".",d="Protection has already expired. Please pick a future date or leave it empty.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","moveForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],["class","alert alert-warning","role","alert",4,"ngIf"],t,[1,"form-group"],["for","expiresAt",1,"col-form-label"],o,["type","text","placeholder",i,"formControlName","expiresAt","triggers","manual",1,"form-control",3,"ngbPopover","click","keypress"],["p","ngbPopover"],["class","invalid-feedback",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],["popContent",""],["role","alert",1,"alert","alert-warning"],s,[1,"invalid-feedback"],a,d,[3,"control"]]},template:function(t,o){if(1&t){const i=e.EpF();e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.YNc(7,k_,3,0,"div",7),e.TgZ(8,"p"),e.tHW(9,8),e._UZ(10,"kbd"),e._UZ(11,"kbd"),e.N_p(),e.qZA(),e.TgZ(12,"div",9),e.TgZ(13,"label",10),e.SDv(14,11),e.qZA(),e.TgZ(15,"input",12,13),e.NdJ("click",function(){return e.CHM(i),e.MAs(16).open()})("keypress",function(){return e.CHM(i),e.MAs(16).close()}),e.qZA(),e.YNc(17,X_,2,0,"span",14),e.YNc(18,Q_,2,0,"span",14),e.qZA(),e.qZA(),e.TgZ(19,"div",15),e.TgZ(20,"cd-form-button-panel",16),e.NdJ("submitActionEvent",function(){return o.moveImage()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA(),e.YNc(21,z_,1,1,"ng-template",null,17,e.W1O)}if(2&t){const i=e.MAs(5),s=e.MAs(22);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.moveForm),e.xp6(3),e.Q6J("ngIf",o.hasSnapshots),e.xp6(4),e.pQV(o.imageSpecStr),e.QtT(9),e.xp6(4),e.Q6J("ngbPopover",s),e.xp6(2),e.Q6J("ngIf",o.moveForm.showError("expiresAt",i,"format")),e.xp6(1),e.Q6J("ngIf",o.moveForm.showError("expiresAt",i,"expired")),e.xp6(2),e.Q6J("form",o.moveForm)("submitText",o.actionLabels.MOVE)}},directives:[R.z,r._Y,r.JL,r.sg,v.V,l.O5,g.P,f.o,r.Fj,h.b,r.JJ,r.u,I.o8,O.p,K_.J],styles:[""]}),n})();function Y_(n,_){1&n&&(e.TgZ(0,"li",10),e.TgZ(1,"a",3),e.SDv(2,11),e.qZA(),e.qZA())}let Qe=(()=>{class n{constructor(t,o){this.authStorageService=t,this.router=o,this.grafanaPermission=this.authStorageService.getPermissions().grafana}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(m.F0))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-tabs"]],decls:12,vars:2,consts:function(){let _,t,o,i;return _="Images",t="Namespaces",o="Trash",i="Overall Performance",[["ngbNav","",1,"nav-tabs",3,"activeId","navChange"],["nav","ngbNav"],["ngbNavItem","/block/rbd"],["ngbNavLink",""],_,["ngbNavItem","/block/rbd/namespaces"],t,["ngbNavItem","/block/rbd/trash"],o,["ngbNavItem","/block/rbd/performance",4,"ngIf"],["ngbNavItem","/block/rbd/performance"],i]},template:function(t,o){1&t&&(e.TgZ(0,"ul",0,1),e.NdJ("navChange",function(s){return o.router.navigate([s.nextId])}),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.qZA(),e.TgZ(5,"li",5),e.TgZ(6,"a",3),e.SDv(7,6),e.qZA(),e.qZA(),e.TgZ(8,"li",7),e.TgZ(9,"a",3),e.SDv(10,8),e.qZA(),e.qZA(),e.YNc(11,Y_,3,0,"li",9),e.qZA()),2&t&&(e.Q6J("activeId",o.router.url),e.xp6(11),e.Q6J("ngIf",o.grafanaPermission.read))},directives:[I.Pz,I.nv,I.Vx,l.O5],styles:[""]}),n})();var V_=p(25917),Ct=p(51295),nt=p(60737),U_=p(74255),St=p(71099),Et=p(79765);function j_(n,_){1&n&&(e.TgZ(0,"span",15),e.SDv(1,16),e.qZA())}let W_=(()=>{class n{constructor(t,o,i,s,a){this.activeModal=t,this.rbdService=o,this.taskManagerService=i,this.notificationService=s,this.actionLabels=a,this.editing=!1,this.onSubmit=new Et.xQ,this.action=this.actionLabels.CREATE,this.resource="RBD Snapshot",this.createForm()}createForm(){this.snapshotForm=new M.d({snapshotName:new r.NI("",{validators:[r.kI.required]})})}setSnapName(t){this.snapName=t,this.snapshotForm.get("snapshotName").setValue(t)}setEditing(t=!0){this.editing=t,this.action=this.editing?this.actionLabels.RENAME:this.actionLabels.CREATE}editAction(){const t=this.snapshotForm.getValue("snapshotName"),o=new Z.N(this.poolName,this.namespace,this.imageName),i=new F.R;i.name="rbd/snap/edit",i.metadata={image_spec:o.toString(),snapshot_name:t},this.rbdService.renameSnapshot(o,this.snapName,t).toPromise().then(()=>{this.taskManagerService.subscribe(i.name,i.metadata,s=>{this.notificationService.notifyTask(s)}),this.activeModal.close(),this.onSubmit.next(this.snapName)}).catch(()=>{this.snapshotForm.setErrors({cdSubmitButton:!0})})}createAction(){const t=this.snapshotForm.getValue("snapshotName"),o=new Z.N(this.poolName,this.namespace,this.imageName),i=new F.R;i.name="rbd/snap/create",i.metadata={image_spec:o.toString(),snapshot_name:t},this.rbdService.createSnapshot(o,t).toPromise().then(()=>{this.taskManagerService.subscribe(i.name,i.metadata,s=>{this.notificationService.notifyTask(s)}),this.activeModal.close(),this.onSubmit.next(t)}).catch(()=>{this.snapshotForm.setErrors({cdSubmitButton:!0})})}submit(){this.editing?this.editAction():this.createAction()}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(x),e.Y36(St.k),e.Y36(Le.g),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-snapshot-form-modal"]],decls:19,vars:15,consts:function(){let _,t,o;return _="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",t="Name",o="This field is required.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","snapshotForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","snapshotName",1,"cd-col-form-label","required"],t,[1,"cd-col-form-input"],["type","text","placeholder","Snapshot name...","id","snapshotName","name","snapshotName","formControlName","snapshotName","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],o]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e._UZ(13,"input",11),e.YNc(14,j_,2,0,"span",12),e.qZA(),e.qZA(),e.qZA(),e.TgZ(15,"div",13),e.TgZ(16,"cd-form-button-panel",14),e.NdJ("submitActionEvent",function(){return o.submit()}),e.ALo(17,"titlecase"),e.ALo(18,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(7);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.pQV(e.lcZ(3,7,o.action))(e.lcZ(4,9,o.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",o.snapshotForm),e.xp6(8),e.Q6J("ngIf",o.snapshotForm.showError("snapshotName",i,"required")),e.xp6(2),e.Q6J("form",o.snapshotForm)("submitText",e.lcZ(17,11,o.action)+" "+e.lcZ(18,13,o.resource))}},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,O.p],pipes:[l.rS,Ye.m],styles:[""]}),n})();class es{constructor(_,t,o){this.featuresName=t,this.cloneFormatVersion=1,o.cloneFormatVersion().subscribe(i=>{this.cloneFormatVersion=i}),this.create={permission:"create",icon:T.P.add,name:_.CREATE},this.rename={permission:"update",icon:T.P.edit,name:_.RENAME},this.protect={permission:"update",icon:T.P.lock,visible:i=>i.hasSingleSelection&&!i.first().is_protected,name:_.PROTECT},this.unprotect={permission:"update",icon:T.P.unlock,visible:i=>i.hasSingleSelection&&i.first().is_protected,name:_.UNPROTECT},this.clone={permission:"create",canBePrimary:i=>i.hasSingleSelection,disable:i=>this.getCloneDisableDesc(i,this.featuresName),icon:T.P.clone,name:_.CLONE},this.copy={permission:"create",canBePrimary:i=>i.hasSingleSelection,disable:i=>!i.hasSingleSelection||i.first().cdExecuting,icon:T.P.copy,name:_.COPY},this.rollback={permission:"update",icon:T.P.undo,name:_.ROLLBACK},this.deleteSnap={permission:"delete",icon:T.P.destroy,disable:i=>{const s=i.first();return!i.hasSingleSelection||s.cdExecuting||s.is_protected},name:_.DELETE},this.ordering=[this.create,this.rename,this.protect,this.unprotect,this.clone,this.copy,this.rollback,this.deleteSnap]}getCloneDisableDesc(_,t){return!(_.hasSingleSelection&&!_.first().cdExecuting)||((null==t?void 0:t.includes("layering"))?1===this.cloneFormatVersion&&!_.first().is_protected&&"Snapshot must be protected in order to clone.":"Parent image must support Layering")}}class ts{}var ze=p(96102);const os=["nameTpl"],ns=["rollbackTpl"];function is(n,_){if(1&n&&(e.ynx(0),e.SDv(1,3),e.BQk(),e.TgZ(2,"strong"),e._uU(3),e.qZA(),e._uU(4,".\n")),2&n){const t=_.$implicit;e.xp6(3),e.hij(" ",t.snapName,"")}}let _s=(()=>{class n{constructor(t,o,i,s,a,d,c,u,S,N,P){this.authStorageService=t,this.modalService=o,this.dimlessBinaryPipe=i,this.cdDatePipe=s,this.rbdService=a,this.taskManagerService=d,this.notificationService=c,this.summaryService=u,this.taskListService=S,this.actionLabels=N,this.cdr=P,this.snapshots=[],this.selection=new Ee.r,this.builders={"rbd/snap/create":$=>{const G=new ts;return G.name=$.snapshot_name,G}},this.permission=this.authStorageService.getPermissions().rbdImage}ngOnInit(){this.columns=[{name:"Name",prop:"name",cellTransformation:ve.e.executing,flexGrow:2},{name:"Size",prop:"size",flexGrow:1,cellClass:"text-right",pipe:this.dimlessBinaryPipe},{name:"Provisioned",prop:"disk_usage",flexGrow:1,cellClass:"text-right",pipe:this.dimlessBinaryPipe},{name:"State",prop:"is_protected",flexGrow:1,cellTransformation:ve.e.badge,customTemplateConfig:{map:{true:{value:"PROTECTED",class:"badge-success"},false:{value:"UNPROTECTED",class:"badge-info"}}}},{name:"Created",prop:"timestamp",flexGrow:1,pipe:this.cdDatePipe}],this.imageSpec=new Z.N(this.poolName,this.namespace,this.rbdName),this.rbdTableActions=new es(this.actionLabels,this.featuresName,this.rbdService),this.rbdTableActions.create.click=()=>this.openCreateSnapshotModal(),this.rbdTableActions.rename.click=()=>this.openEditSnapshotModal(),this.rbdTableActions.protect.click=()=>this.toggleProtection(),this.rbdTableActions.unprotect.click=()=>this.toggleProtection();const t=()=>this.selection.first()&&`${this.imageSpec.toStringEncoded()}/${encodeURIComponent(this.selection.first().name)}`;this.rbdTableActions.clone.routerLink=()=>`/block/rbd/clone/${t()}`,this.rbdTableActions.copy.routerLink=()=>`/block/rbd/copy/${t()}`,this.rbdTableActions.rollback.click=()=>this.rollbackModal(),this.rbdTableActions.deleteSnap.click=()=>this.deleteSnapshotModal(),this.tableActions=this.rbdTableActions.ordering,this.taskListService.init(()=>(0,V_.of)(this.snapshots),null,s=>{Ct.T.updateChanged(this,{data:s})&&(this.cdr.detectChanges(),this.data=[...this.data])},()=>{Ct.T.updateChanged(this,{data:this.snapshots})&&(this.cdr.detectChanges(),this.data=[...this.data])},s=>["rbd/snap/create","rbd/snap/delete","rbd/snap/edit","rbd/snap/rollback"].includes(s.name)&&this.imageSpec.toString()===s.metadata.image_spec,(s,a)=>s.name===a.metadata.snapshot_name,this.builders)}ngOnChanges(){this.columns&&(this.imageSpec=new Z.N(this.poolName,this.namespace,this.rbdName),this.rbdTableActions&&(this.rbdTableActions.featuresName=this.featuresName),this.taskListService.fetch())}openSnapshotModal(t,o=null){this.modalRef=this.modalService.show(W_),this.modalRef.componentInstance.poolName=this.poolName,this.modalRef.componentInstance.imageName=this.rbdName,this.modalRef.componentInstance.namespace=this.namespace,o?this.modalRef.componentInstance.setEditing():o=`${this.rbdName}_${Me()().toISOString(!0)}`,this.modalRef.componentInstance.setSnapName(o),this.modalRef.componentInstance.onSubmit.subscribe(i=>{const s=new nt.o;s.name=t,s.metadata={image_spec:this.imageSpec.toString(),snapshot_name:i},this.summaryService.addRunningTask(s)})}openCreateSnapshotModal(){this.openSnapshotModal("rbd/snap/create")}openEditSnapshotModal(){this.openSnapshotModal("rbd/snap/edit",this.selection.first().name)}toggleProtection(){const t=this.selection.first().name,o=this.selection.first().is_protected,i=new F.R;i.name="rbd/snap/edit";const s=new Z.N(this.poolName,this.namespace,this.rbdName);i.metadata={image_spec:s.toString(),snapshot_name:t},this.rbdService.protectSnapshot(s,t,!o).toPromise().then(()=>{const a=new nt.o;a.name=i.name,a.metadata=i.metadata,this.summaryService.addRunningTask(a),this.taskManagerService.subscribe(i.name,i.metadata,d=>{this.notificationService.notifyTask(d)})})}_asyncTask(t,o,i){const s=new F.R;s.name=o,s.metadata={image_spec:new Z.N(this.poolName,this.namespace,this.rbdName).toString(),snapshot_name:i};const a=new Z.N(this.poolName,this.namespace,this.rbdName);this.rbdService[t](a,i).toPromise().then(()=>{const d=new nt.o;d.name=s.name,d.metadata=s.metadata,this.summaryService.addRunningTask(d),this.modalRef.close(),this.taskManagerService.subscribe(d.name,d.metadata,c=>{this.notificationService.notifyTask(c)})}).catch(()=>{this.modalRef.componentInstance.stopLoadingSpinner()})}rollbackModal(){const t=this.selection.selected[0].name,o=new Z.N(this.poolName,this.namespace,this.rbdName).toString(),i={titleText:"RBD snapshot rollback",buttonText:"Rollback",bodyTpl:this.rollbackTpl,bodyData:{snapName:`${o}@${t}`},onSubmit:()=>{this._asyncTask("rollbackSnapshot","rbd/snap/rollback",t)}};this.modalRef=this.modalService.show(ft.Y,i)}deleteSnapshotModal(){const t=this.selection.selected[0].name;this.modalRef=this.modalService.show(he.M,{itemDescription:"RBD snapshot",itemNames:[t],submitAction:()=>this._asyncTask("deleteSnapshot","rbd/snap/delete",t)})}updateSelection(t){this.selection=t}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(re.Z),e.Y36(Fe.$),e.Y36(ze.N),e.Y36(x),e.Y36(St.k),e.Y36(Le.g),e.Y36(U_.J),e.Y36(se.j),e.Y36(D.p4),e.Y36(e.sBO))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-snapshot-list"]],viewQuery:function(t,o){if(1&t&&(e.Gf(os,5),e.Gf(ns,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.nameTpl=i.first),e.iGM(i=e.CRH())&&(o.rollbackTpl=i.first)}},inputs:{snapshots:"snapshots",featuresName:"featuresName",poolName:"poolName",namespace:"namespace",rbdName:"rbdName"},features:[e._Bn([se.j]),e.TTD],decls:4,vars:5,consts:function(){let _;return _="You are about to rollback",[["columnMode","flex","selectionType","single",3,"data","columns","updateSelection"],[1,"table-actions",3,"permission","selection","tableActions"],["rollbackTpl",""],_]},template:function(t,o){1&t&&(e.TgZ(0,"cd-table",0),e.NdJ("updateSelection",function(s){return o.updateSelection(s)}),e._UZ(1,"cd-table-actions",1),e.qZA(),e.YNc(2,is,5,1,"ng-template",null,2,e.W1O)),2&t&&(e.Q6J("data",o.data)("columns",o.columns),e.xp6(1),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions))},directives:[ee.a,Re.K],styles:[""],changeDetection:0}),n})();var ss=p(71752),Rt=p(76317),as=p(41039);const rs=["poolConfigurationSourceTpl"];function ls(n,_){1&n&&(e.ynx(0),e.tHW(1,3),e._UZ(2,"strong"),e.N_p(),e.BQk())}function cs(n,_){if(1&n&&(e.TgZ(0,"span"),e.TgZ(1,"span",38),e._uU(2),e.qZA(),e.qZA()),2&n){const t=_.$implicit;e.xp6(2),e.Oqu(t)}}function ds(n,_){if(1&n&&(e.TgZ(0,"span"),e.TgZ(1,"span",39),e.SDv(2,40),e.qZA(),e.qZA()),2&n){e.oxw(3);const t=e.MAs(1);e.xp6(1),e.Q6J("ngbTooltip",t)}}function ps(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.ALo(2,"dimlessBinary"),e.qZA()),2&n){const t=e.oxw(3);e.xp6(1),e.hij(" ",e.lcZ(2,1,t.selection.disk_usage)," ")}}function gs(n,_){if(1&n&&(e.TgZ(0,"span"),e.TgZ(1,"span",39),e.SDv(2,41),e.qZA(),e.qZA()),2&n){e.oxw(3);const t=e.MAs(1);e.xp6(1),e.Q6J("ngbTooltip",t)}}function us(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.ALo(2,"dimlessBinary"),e.qZA()),2&n){const t=e.oxw(3);e.xp6(1),e.hij(" ",e.lcZ(2,1,t.selection.total_disk_usage)," ")}}function ms(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.qZA()),2&n){const t=e.oxw(4);e.xp6(1),e.hij("/",t.selection.parent.pool_namespace,"")}}function Ts(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.YNc(2,ms,2,1,"span",1),e._uU(3),e.qZA()),2&n){const t=e.oxw(3);e.xp6(1),e.Oqu(t.selection.parent.pool_name),e.xp6(1),e.Q6J("ngIf",t.selection.parent.pool_namespace),e.xp6(1),e.AsE("/",t.selection.parent.image_name,"@",t.selection.parent.snap_name,"")}}function fs(n,_){1&n&&(e.TgZ(0,"span"),e._uU(1,"-"),e.qZA())}function Cs(n,_){if(1&n&&(e.TgZ(0,"table",17),e.TgZ(1,"tbody"),e.TgZ(2,"tr"),e.TgZ(3,"td",18),e.SDv(4,19),e.qZA(),e.TgZ(5,"td",20),e._uU(6),e.qZA(),e.qZA(),e.TgZ(7,"tr"),e.TgZ(8,"td",21),e.SDv(9,22),e.qZA(),e.TgZ(10,"td"),e._uU(11),e.qZA(),e.qZA(),e.TgZ(12,"tr"),e.TgZ(13,"td",21),e.SDv(14,23),e.qZA(),e.TgZ(15,"td"),e._uU(16),e.ALo(17,"empty"),e.qZA(),e.qZA(),e.TgZ(18,"tr"),e.TgZ(19,"td",21),e.SDv(20,24),e.qZA(),e.TgZ(21,"td"),e._uU(22),e.ALo(23,"cdDate"),e.qZA(),e.qZA(),e.TgZ(24,"tr"),e.TgZ(25,"td",21),e.SDv(26,25),e.qZA(),e.TgZ(27,"td"),e._uU(28),e.ALo(29,"dimlessBinary"),e.qZA(),e.qZA(),e.TgZ(30,"tr"),e.TgZ(31,"td",21),e.SDv(32,26),e.qZA(),e.TgZ(33,"td"),e._uU(34),e.ALo(35,"dimless"),e.qZA(),e.qZA(),e.TgZ(36,"tr"),e.TgZ(37,"td",21),e.SDv(38,27),e.qZA(),e.TgZ(39,"td"),e._uU(40),e.ALo(41,"dimlessBinary"),e.qZA(),e.qZA(),e.TgZ(42,"tr"),e.TgZ(43,"td",21),e.SDv(44,28),e.qZA(),e.TgZ(45,"td"),e.YNc(46,cs,3,1,"span",29),e.qZA(),e.qZA(),e.TgZ(47,"tr"),e.TgZ(48,"td",21),e.SDv(49,30),e.qZA(),e.TgZ(50,"td"),e.YNc(51,ds,3,1,"span",1),e.YNc(52,ps,3,3,"span",1),e.qZA(),e.qZA(),e.TgZ(53,"tr"),e.TgZ(54,"td",21),e.SDv(55,31),e.qZA(),e.TgZ(56,"td"),e.YNc(57,gs,3,1,"span",1),e.YNc(58,us,3,3,"span",1),e.qZA(),e.qZA(),e.TgZ(59,"tr"),e.TgZ(60,"td",21),e.SDv(61,32),e.qZA(),e.TgZ(62,"td"),e._uU(63),e.ALo(64,"dimlessBinary"),e.qZA(),e.qZA(),e.TgZ(65,"tr"),e.TgZ(66,"td",21),e.SDv(67,33),e.qZA(),e.TgZ(68,"td"),e._uU(69),e.qZA(),e.qZA(),e.TgZ(70,"tr"),e.TgZ(71,"td",21),e.SDv(72,34),e.qZA(),e.TgZ(73,"td"),e.YNc(74,Ts,4,4,"span",1),e.YNc(75,fs,2,0,"span",1),e.qZA(),e.qZA(),e.TgZ(76,"tr"),e.TgZ(77,"td",21),e.SDv(78,35),e.qZA(),e.TgZ(79,"td"),e._uU(80),e.qZA(),e.qZA(),e.TgZ(81,"tr"),e.TgZ(82,"td",21),e.SDv(83,36),e.qZA(),e.TgZ(84,"td"),e._uU(85),e.qZA(),e.qZA(),e.TgZ(86,"tr"),e.TgZ(87,"td",21),e.SDv(88,37),e.qZA(),e.TgZ(89,"td"),e._uU(90),e.qZA(),e.qZA(),e.qZA(),e.qZA()),2&n){const t=e.oxw(2);e.xp6(6),e.Oqu(t.selection.name),e.xp6(5),e.Oqu(t.selection.pool_name),e.xp6(5),e.Oqu(e.lcZ(17,19,t.selection.data_pool)),e.xp6(6),e.Oqu(e.lcZ(23,21,t.selection.timestamp)),e.xp6(6),e.Oqu(e.lcZ(29,23,t.selection.size)),e.xp6(6),e.Oqu(e.lcZ(35,25,t.selection.num_objs)),e.xp6(6),e.Oqu(e.lcZ(41,27,t.selection.obj_size)),e.xp6(6),e.Q6J("ngForOf",t.selection.features_name),e.xp6(5),e.Q6J("ngIf",-1===(null==t.selection.features_name?null:t.selection.features_name.indexOf("fast-diff"))),e.xp6(1),e.Q6J("ngIf",-1!==(null==t.selection.features_name?null:t.selection.features_name.indexOf("fast-diff"))),e.xp6(5),e.Q6J("ngIf",-1===(null==t.selection.features_name?null:t.selection.features_name.indexOf("fast-diff"))),e.xp6(1),e.Q6J("ngIf",-1!==(null==t.selection.features_name?null:t.selection.features_name.indexOf("fast-diff"))),e.xp6(5),e.Oqu(e.lcZ(64,29,t.selection.stripe_unit)),e.xp6(6),e.Oqu(t.selection.stripe_count),e.xp6(5),e.Q6J("ngIf",t.selection.parent),e.xp6(1),e.Q6J("ngIf",!t.selection.parent),e.xp6(5),e.Oqu(t.selection.block_name_prefix),e.xp6(5),e.Oqu(t.selection.order),e.xp6(5),e.Oqu(t.selection.image_format)}}function Ss(n,_){if(1&n&&e._UZ(0,"cd-rbd-snapshot-list",42),2&n){const t=e.oxw(2);e.Q6J("snapshots",t.selection.snapshots)("featuresName",t.selection.features_name)("poolName",t.selection.pool_name)("namespace",t.selection.namespace)("rbdName",t.selection.name)}}function Es(n,_){if(1&n&&e._UZ(0,"cd-rbd-configuration-table",43),2&n){const t=e.oxw(2);e.Q6J("data",t.selection.configuration)}}function Rs(n,_){if(1&n&&e._UZ(0,"cd-grafana",44),2&n){const t=e.oxw(2);e.Q6J("grafanaPath",t.rbdDashboardUrl)}}function Ms(n,_){if(1&n&&(e.ynx(0),e.TgZ(1,"ul",4,5),e.TgZ(3,"li",6),e.TgZ(4,"a",7),e.SDv(5,8),e.qZA(),e.YNc(6,Cs,91,31,"ng-template",9),e.qZA(),e.TgZ(7,"li",10),e.TgZ(8,"a",7),e.SDv(9,11),e.qZA(),e.YNc(10,Ss,1,5,"ng-template",9),e.qZA(),e.TgZ(11,"li",12),e.TgZ(12,"a",7),e.SDv(13,13),e.qZA(),e.YNc(14,Es,1,1,"ng-template",9),e.qZA(),e.TgZ(15,"li",14),e.TgZ(16,"a",7),e.SDv(17,15),e.qZA(),e.YNc(18,Rs,1,1,"ng-template",9),e.qZA(),e.qZA(),e._UZ(19,"div",16),e.BQk()),2&n){const t=e.MAs(2);e.xp6(19),e.Q6J("ngbNavOutlet",t)}}function Os(n,_){1&n&&(e.ynx(0),e.TgZ(1,"cd-alert-panel",45),e.SDv(2,46),e.qZA(),e.BQk())}function As(n,_){1&n&&(e.ynx(0),e.TgZ(1,"strong",49),e.SDv(2,50),e.qZA(),e.BQk())}function hs(n,_){1&n&&(e.TgZ(0,"span",51),e.SDv(1,52),e.qZA())}function Ps(n,_){if(1&n&&(e.YNc(0,As,3,0,"ng-container",47),e.YNc(1,hs,2,0,"ng-template",null,48,e.W1O)),2&n){const t=_.value,o=e.MAs(2);e.Q6J("ngIf",+t)("ngIfElse",o)}}let Is=(()=>{class n{ngOnChanges(){this.selection&&(this.rbdDashboardUrl=`rbd-details?var-Pool=${this.selection.pool_name}&var-Image=${this.selection.name}`)}}return n.\u0275fac=function(t){return new(t||n)},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-details"]],viewQuery:function(t,o){if(1&t&&(e.Gf(rs,7),e.Gf(I.Pz,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.poolConfigurationSourceTpl=i.first),e.iGM(i=e.CRH())&&(o.nav=i.first)}},inputs:{selection:"selection",images:"images"},features:[e.TTD],decls:6,vars:2,consts:function(){let _,t,o,i,s,a,d,c,u,S,N,P,$,G,X,J,te,A,w,de,pe,ge,ue,me,Te,fe,Ce,Se;return _="Only available for RBD images with " + "\ufffd#2\ufffd" + "fast-diff" + "\ufffd/#2\ufffd" + " enabled",t="Details",o="Snapshots",i="Configuration",s="Performance",a="Name",d="Pool",c="Data Pool",u="Created",S="Size",N="Objects",P="Object size",$="Features",G="Provisioned",X="Total provisioned",J="Striping unit",te="Striping count",A="Parent",w="Block name prefix",de="Order",pe="Format Version",ge="N/A",ue="N/A",me="Information can not be displayed for RBD in status 'Removing'.",Te="This setting overrides the global value",fe="Image",Ce="This is the global value. No value for this option has been set for this image.",Se="Global",[["usageNotAvailableTooltipTpl",""],[4,"ngIf"],["poolConfigurationSourceTpl",""],_,["ngbNav","","cdStatefulTab","rbd-details",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","details"],["ngbNavLink",""],t,["ngbNavContent",""],["ngbNavItem","snapshots"],o,["ngbNavItem","configuration"],i,["ngbNavItem","performance"],s,[3,"ngbNavOutlet"],[1,"table","table-striped","table-bordered"],[1,"bold","w-25"],a,[1,"w-75"],[1,"bold"],d,c,u,S,N,P,$,[4,"ngFor","ngForOf"],G,X,J,te,A,w,de,pe,[1,"badge","badge-dark","mr-2"],["placement","top",1,"form-text","text-muted",3,"ngbTooltip"],ge,ue,[3,"snapshots","featuresName","poolName","namespace","rbdName"],[3,"data"],["uid","YhCYGcuZz","grafanaStyle","one",3,"grafanaPath"],["type","warning"],me,[4,"ngIf","ngIfElse"],["global",""],["ngbTooltip",Te],fe,["ngbTooltip",Ce],Se]},template:function(t,o){1&t&&(e.YNc(0,ls,3,0,"ng-template",null,0,e.W1O),e.YNc(2,Ms,20,1,"ng-container",1),e.YNc(3,Os,3,0,"ng-container",1),e.YNc(4,Ps,3,2,"ng-template",null,2,e.W1O)),2&t&&(e.xp6(2),e.Q6J("ngIf",o.selection&&"REMOVING"!==o.selection.source),e.xp6(1),e.Q6J("ngIf",o.selection&&"REMOVING"===o.selection.source))},directives:[l.O5,I.Pz,mt.m,I.nv,I.Vx,I.uN,I.tO,l.sg,I._L,_s,ss.P,Rt.F,pt.G],pipes:[as.W,ze.N,Fe.$,Ue.n],styles:[""]}),n})();const bs=["usageTpl"],Ns=["parentTpl"],Ds=["nameTpl"],vs=["flattenTpl"],Ls=["deleteTpl"],Fs=["removingStatTpl"],$s=["provisionedNotAvailableTooltipTpl"],Zs=["totalProvisionedNotAvailableTooltipTpl"];function Bs(n,_){1&n&&e._UZ(0,"div",11),2&n&&e.Q6J("innerHtml","Only available for RBD images with fast-diff enabled",e.oJD)}function Gs(n,_){if(1&n&&(e.TgZ(0,"span",14),e.SDv(1,15),e.qZA()),2&n){e.oxw(2);const t=e.MAs(6);e.Q6J("ngbTooltip",t)}}function ys(n,_){if(1&n&&(e.SDv(0,16),e.ALo(1,"dimlessBinary")),2&n){const t=e.oxw().row;e.xp6(1),e.pQV(e.lcZ(1,1,t.disk_usage)),e.QtT(0)}}function xs(n,_){if(1&n&&(e.YNc(0,Gs,2,1,"span",12),e.YNc(1,ys,2,3,"ng-template",null,13,e.W1O)),2&n){const t=_.row,o=e.MAs(2);e.Q6J("ngIf",null===t.disk_usage&&!t.features_name.includes("fast-diff"))("ngIfElse",o)}}function ws(n,_){if(1&n&&(e.TgZ(0,"span",14),e.SDv(1,18),e.qZA()),2&n){e.oxw(2);const t=e.MAs(6);e.Q6J("ngbTooltip",t)}}function qs(n,_){if(1&n&&(e.SDv(0,19),e.ALo(1,"dimlessBinary")),2&n){const t=e.oxw().row;e.xp6(1),e.pQV(e.lcZ(1,1,t.total_disk_usage)),e.QtT(0)}}function Hs(n,_){if(1&n&&(e.YNc(0,ws,2,1,"span",12),e.YNc(1,qs,2,3,"ng-template",null,17,e.W1O)),2&n){const t=_.row,o=e.MAs(2);e.Q6J("ngIf",null===t.total_disk_usage&&!t.features_name.includes("fast-diff"))("ngIfElse",o)}}function Ks(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.qZA()),2&n){const t=e.oxw(2).value;e.xp6(1),e.hij("/",t.pool_namespace,"")}}function ks(n,_){if(1&n&&(e.TgZ(0,"span"),e._uU(1),e.YNc(2,Ks,2,1,"span",20),e._uU(3),e.qZA()),2&n){const t=e.oxw().value;e.xp6(1),e.Oqu(t.pool_name),e.xp6(1),e.Q6J("ngIf",t.pool_namespace),e.xp6(1),e.AsE("/",t.image_name,"@",t.snap_name,"")}}function Xs(n,_){1&n&&(e.TgZ(0,"span"),e._uU(1,"-"),e.qZA())}function Qs(n,_){if(1&n&&(e.YNc(0,ks,4,4,"span",20),e.YNc(1,Xs,2,0,"span",20)),2&n){const t=_.value;e.Q6J("ngIf",t),e.xp6(1),e.Q6J("ngIf",!t)}}function zs(n,_){if(1&n&&(e._uU(0," You are about to flatten "),e.TgZ(1,"strong"),e._uU(2),e.qZA(),e._uU(3,". "),e._UZ(4,"br"),e._UZ(5,"br"),e._uU(6," All blocks will be copied from parent "),e.TgZ(7,"strong"),e._uU(8),e.qZA(),e._uU(9," to child "),e.TgZ(10,"strong"),e._uU(11),e.qZA(),e._uU(12,".\n")),2&n){const t=_.$implicit;e.xp6(2),e.Oqu(t.child),e.xp6(6),e.Oqu(t.parent),e.xp6(3),e.Oqu(t.child)}}function Js(n,_){if(1&n&&(e.TgZ(0,"li"),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.xp6(1),e.Oqu(t)}}function Ys(n,_){if(1&n&&(e.ynx(0),e.TgZ(1,"span"),e.SDv(2,24),e.qZA(),e.TgZ(3,"ul"),e.YNc(4,Js,2,1,"li",25),e.qZA(),e.BQk()),2&n){const t=e.oxw(2).snapshots;e.xp6(4),e.Q6J("ngForOf",t)}}function Vs(n,_){if(1&n&&(e.TgZ(0,"div",22),e.TgZ(1,"span"),e.SDv(2,23),e.qZA(),e._UZ(3,"br"),e.YNc(4,Ys,5,1,"ng-container",20),e.qZA()),2&n){const t=e.oxw().snapshots;e.xp6(4),e.Q6J("ngIf",t.length>0)}}function Us(n,_){1&n&&e.YNc(0,Vs,5,1,"div",21),2&n&&e.Q6J("ngIf",_.hasSnapshots)}const js=function(n,_){return[n,_]};function Ws(n,_){if(1&n&&e._UZ(0,"i",27),2&n){const t=e.oxw(2);e.Q6J("ngClass",e.WLB(1,js,t.icons.spinner,t.icons.spin))}}function ea(n,_){if(1&n&&(e.TgZ(0,"span",27),e._uU(1),e.qZA()),2&n){const t=e.oxw(),o=t.column,i=t.row;e.Q6J("ngClass",null!=o&&null!=o.customTemplateConfig&&o.customTemplateConfig.executingClass?o.customTemplateConfig.executingClass:"text-muted italic"),e.xp6(1),e.hij(" (",i.cdExecuting,") ")}}function ta(n,_){if(1&n&&e._UZ(0,"i",29),2&n){const t=e.oxw(2);e.Gre("",t.icons.warning," warn")}}function oa(n,_){if(1&n&&(e.YNc(0,Ws,1,4,"i",26),e.TgZ(1,"span",27),e._uU(2),e.qZA(),e.YNc(3,ea,2,2,"span",26),e.YNc(4,ta,1,3,"i",28)),2&n){const t=_.column,o=_.value,i=_.row;e.Q6J("ngIf",i.cdExecuting),e.xp6(1),e.Q6J("ngClass",null==t||null==t.customTemplateConfig?null:t.customTemplateConfig.valueClass),e.xp6(1),e.hij(" ",o," "),e.xp6(1),e.Q6J("ngIf",i.cdExecuting),e.xp6(1),e.Q6J("ngIf",i.source&&"REMOVING"===i.source)}}let ia=(()=>{class n extends lt.o{constructor(t,o,i,s,a,d,c,u,S){super(),this.authStorageService=t,this.rbdService=o,this.dimlessBinaryPipe=i,this.dimlessPipe=s,this.modalService=a,this.taskWrapper=d,this.taskListService=c,this.urlBuilder=u,this.actionLabels=S,this.tableStatus=new U.E,this.selection=new Ee.r,this.icons=T.P,this.builders={"rbd/create":A=>this.createRbdFromTask(A.pool_name,A.namespace,A.image_name),"rbd/delete":A=>this.createRbdFromTaskImageSpec(A.image_spec),"rbd/clone":A=>this.createRbdFromTask(A.child_pool_name,A.child_namespace,A.child_image_name),"rbd/copy":A=>this.createRbdFromTask(A.dest_pool_name,A.dest_namespace,A.dest_image_name)},this.permission=this.authStorageService.getPermissions().rbdImage;const N=()=>this.selection.first()&&new Z.N(this.selection.first().pool_name,this.selection.first().namespace,this.selection.first().name).toStringEncoded();this.tableActions=[{permission:"create",icon:T.P.add,routerLink:()=>this.urlBuilder.getCreate(),canBePrimary:A=>!A.hasSingleSelection,name:this.actionLabels.CREATE},{permission:"update",icon:T.P.edit,routerLink:()=>this.urlBuilder.getEdit(N()),name:this.actionLabels.EDIT,disable:A=>this.getRemovingStatusDesc(A)||this.getInvalidNameDisable(A)},{permission:"create",canBePrimary:A=>A.hasSingleSelection,disable:A=>this.getRemovingStatusDesc(A)||this.getInvalidNameDisable(A)||!!A.first().cdExecuting,icon:T.P.copy,routerLink:()=>`/block/rbd/copy/${N()}`,name:this.actionLabels.COPY},{permission:"update",disable:A=>this.getRemovingStatusDesc(A)||this.getInvalidNameDisable(A)||A.first().cdExecuting||!A.first().parent,icon:T.P.flatten,click:()=>this.flattenRbdModal(),name:this.actionLabels.FLATTEN},{permission:"delete",icon:T.P.destroy,click:()=>this.deleteRbdModal(),name:this.actionLabels.DELETE,disable:A=>this.getDeleteDisableDesc(A)},{permission:"delete",icon:T.P.trash,click:()=>this.trashRbdModal(),name:this.actionLabels.TRASH,disable:A=>this.getRemovingStatusDesc(A)||this.getInvalidNameDisable(A)||A.first().image_format===Pe.V1}]}createRbdFromTaskImageSpec(t){const o=Z.N.fromString(t);return this.createRbdFromTask(o.poolName,o.namespace,o.imageName)}createRbdFromTask(t,o,i){const s=new zi;return s.id="-1",s.unique_id="-1",s.name=i,s.namespace=o,s.pool_name=t,s.image_format=Pe.V2,s}ngOnInit(){this.columns=[{name:"Name",prop:"name",flexGrow:2,cellTemplate:this.removingStatTpl},{name:"Pool",prop:"pool_name",flexGrow:2},{name:"Namespace",prop:"namespace",flexGrow:2},{name:"Size",prop:"size",flexGrow:1,cellClass:"text-right",pipe:this.dimlessBinaryPipe},{name:"Objects",prop:"num_objs",flexGrow:1,cellClass:"text-right",pipe:this.dimlessPipe},{name:"Object size",prop:"obj_size",flexGrow:1,cellClass:"text-right",pipe:this.dimlessBinaryPipe},{name:"Provisioned",prop:"disk_usage",cellClass:"text-center",flexGrow:1,pipe:this.dimlessBinaryPipe,cellTemplate:this.provisionedNotAvailableTooltipTpl},{name:"Total provisioned",prop:"total_disk_usage",cellClass:"text-center",flexGrow:1,pipe:this.dimlessBinaryPipe,cellTemplate:this.totalProvisionedNotAvailableTooltipTpl},{name:"Parent",prop:"parent",flexGrow:2,cellTemplate:this.parentTpl}],this.taskListService.init(()=>this.rbdService.list(),i=>this.prepareResponse(i),i=>this.images=i,()=>this.onFetchError(),i=>["rbd/clone","rbd/copy","rbd/create","rbd/delete","rbd/edit","rbd/flatten","rbd/trash/move"].includes(i.name),(i,s)=>{let a;switch(s.name){case"rbd/copy":a=new Z.N(s.metadata.dest_pool_name,s.metadata.dest_namespace,s.metadata.dest_image_name).toString();break;case"rbd/clone":a=new Z.N(s.metadata.child_pool_name,s.metadata.child_namespace,s.metadata.child_image_name).toString();break;case"rbd/create":a=new Z.N(s.metadata.pool_name,s.metadata.namespace,s.metadata.image_name).toString();break;default:a=s.metadata.image_spec}return a===new Z.N(i.pool_name,i.namespace,i.name).toString()},this.builders)}onFetchError(){this.table.reset(),this.tableStatus=new U.E(ce.T.ValueException)}prepareResponse(t){let o=[];const i={};let s;if(t.forEach(a=>{C().isUndefined(i[a.status])&&(i[a.status]=[]),i[a.status].push(a.pool_name),o=o.concat(a.value)}),i[ce.T.ValueException]?s=ce.T.ValueException:i[ce.T.ValueStale]?s=ce.T.ValueStale:i[ce.T.ValueNone]&&(s=ce.T.ValueNone),s){const a=(i[s].length>1?"pools ":"pool ")+i[s].join();this.tableStatus=new U.E(s,a)}else this.tableStatus=new U.E;return o}updateSelection(t){this.selection=t}deleteRbdModal(){const t=this.selection.first().pool_name,o=this.selection.first().namespace,i=this.selection.first().name,s=new Z.N(t,o,i);this.modalRef=this.modalService.show(he.M,{itemDescription:"RBD",itemNames:[s],bodyTemplate:this.deleteTpl,bodyContext:{hasSnapshots:this.hasSnapshots(),snapshots:this.listProtectedSnapshots()},submitActionObservable:()=>this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/delete",{image_spec:s.toString()}),call:this.rbdService.delete(s)})})}trashRbdModal(){const t={poolName:this.selection.first().pool_name,namespace:this.selection.first().namespace,imageName:this.selection.first().name,hasSnapshots:this.hasSnapshots()};this.modalRef=this.modalService.show(J_,t)}flattenRbd(t){this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/flatten",{image_spec:t.toString()}),call:this.rbdService.flatten(t)}).subscribe({complete:()=>{this.modalRef.close()}})}flattenRbdModal(){const t=this.selection.first().pool_name,o=this.selection.first().namespace,i=this.selection.first().name,s=this.selection.first().parent,a=new Z.N(s.pool_name,s.pool_namespace,s.image_name),d=new Z.N(t,o,i),c={titleText:"RBD flatten",buttonText:"Flatten",bodyTpl:this.flattenTpl,bodyData:{parent:`${a}@${s.snap_name}`,child:d.toString()},onSubmit:()=>{this.flattenRbd(d)}};this.modalRef=this.modalService.show(ft.Y,c)}hasSnapshots(){return(this.selection.first().snapshots||[]).length>0}hasClonedSnapshots(t){return(t.snapshots||[]).some(i=>i.children&&i.children.length>0)}listProtectedSnapshots(){return this.selection.first().snapshots.reduce((i,s)=>(s.is_protected&&i.push(s.name),i),[])}getDeleteDisableDesc(t){const o=t.first();return o&&this.hasClonedSnapshots(o)?"This RBD has cloned snapshots. Please delete related RBDs before deleting this RBD.":this.getInvalidNameDisable(t)||this.hasClonedSnapshots(t.first())}getInvalidNameDisable(t){var o;const i=t.first();return(null===(o=null==i?void 0:i.name)||void 0===o?void 0:o.match(/[@/]/))?"This RBD image has an invalid name and can't be managed by ceph.":!t.first()||!t.hasSingleSelection}getRemovingStatusDesc(t){const o=t.first();return"REMOVING"===(null==o?void 0:o.source)&&"Action not possible for an RBD in status 'Removing'"}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(x),e.Y36(Fe.$),e.Y36(Ue.n),e.Y36(re.Z),e.Y36(Q.P),e.Y36(se.j),e.Y36(tt.F),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-list"]],viewQuery:function(t,o){if(1&t&&(e.Gf(ee.a,7),e.Gf(bs,5),e.Gf(Ns,7),e.Gf(Ds,5),e.Gf(vs,7),e.Gf(Ls,7),e.Gf(Fs,7),e.Gf($s,7),e.Gf(Zs,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.table=i.first),e.iGM(i=e.CRH())&&(o.usageTpl=i.first),e.iGM(i=e.CRH())&&(o.parentTpl=i.first),e.iGM(i=e.CRH())&&(o.nameTpl=i.first),e.iGM(i=e.CRH())&&(o.flattenTpl=i.first),e.iGM(i=e.CRH())&&(o.deleteTpl=i.first),e.iGM(i=e.CRH())&&(o.removingStatTpl=i.first),e.iGM(i=e.CRH())&&(o.provisionedNotAvailableTooltipTpl=i.first),e.iGM(i=e.CRH())&&(o.totalProvisionedNotAvailableTooltipTpl=i.first)}},features:[e._Bn([se.j,{provide:tt.F,useValue:new tt.F("block/rbd")}]),e.qOj],decls:19,vars:10,consts:function(){let _,t,o,i,s,a,d;return _="N/A",t="" + "\ufffd0\ufffd" + "",o="N/A",i="" + "\ufffd0\ufffd" + "",s="Deleting this image will also delete all its snapshots.",a="The following snapshots are currently protected and will be removed:",d="RBD in status 'Removing'",[["columnMode","flex","identifier","unique_id","forceIdentifier","true","selectionType","single",3,"data","columns","searchableObjects","hasDetails","status","autoReload","fetchData","setExpandedRow","updateSelection"],["table",""],[1,"table-actions",3,"permission","selection","tableActions"],["cdTableDetail","",3,"selection"],["usageNotAvailableTooltipTpl",""],["provisionedNotAvailableTooltipTpl",""],["totalProvisionedNotAvailableTooltipTpl",""],["parentTpl",""],["flattenTpl",""],["deleteTpl",""],["removingStatTpl",""],[3,"innerHtml"],["placement","top",3,"ngbTooltip",4,"ngIf","ngIfElse"],["provisioned",""],["placement","top",3,"ngbTooltip"],_,t,["totalProvisioned",""],o,i,[4,"ngIf"],["class","alert alert-warning","role","alert",4,"ngIf"],["role","alert",1,"alert","alert-warning"],s,a,[4,"ngFor","ngForOf"],[3,"ngClass",4,"ngIf"],[3,"ngClass"],["title",d,3,"class",4,"ngIf"],["title",d]]},template:function(t,o){1&t&&(e._UZ(0,"cd-rbd-tabs"),e.TgZ(1,"cd-table",0,1),e.NdJ("fetchData",function(){return o.taskListService.fetch()})("setExpandedRow",function(s){return o.setExpandedRow(s)})("updateSelection",function(s){return o.updateSelection(s)}),e._UZ(3,"cd-table-actions",2),e._UZ(4,"cd-rbd-details",3),e.qZA(),e.YNc(5,Bs,1,1,"ng-template",null,4,e.W1O),e.YNc(7,xs,3,2,"ng-template",null,5,e.W1O),e.YNc(9,Hs,3,2,"ng-template",null,6,e.W1O),e.YNc(11,Qs,2,2,"ng-template",null,7,e.W1O),e.YNc(13,zs,13,3,"ng-template",null,8,e.W1O),e.YNc(15,Us,1,1,"ng-template",null,9,e.W1O),e.YNc(17,oa,5,5,"ng-template",null,10,e.W1O)),2&t&&(e.xp6(1),e.Q6J("data",o.images)("columns",o.columns)("searchableObjects",!0)("hasDetails",!0)("status",o.tableStatus)("autoReload",-1),e.xp6(2),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions),e.xp6(1),e.Q6J("selection",o.expandedRow))},directives:[Qe,ee.a,Re.K,Is,l.O5,I._L,l.sg,l.mk],pipes:[Fe.$],styles:[".warn[_ngcontent-%COMP%]{color:#ffc200}"]}),n})();function _a(n,_){1&n&&e._UZ(0,"input",19)}function sa(n,_){1&n&&(e.TgZ(0,"option",23),e.SDv(1,24),e.qZA()),2&n&&e.Q6J("ngValue",null)}function aa(n,_){1&n&&(e.TgZ(0,"option",23),e.SDv(1,25),e.qZA()),2&n&&e.Q6J("ngValue",null)}function ra(n,_){1&n&&(e.TgZ(0,"option",23),e.SDv(1,26),e.qZA()),2&n&&e.Q6J("ngValue",null)}function la(n,_){if(1&n&&(e.TgZ(0,"option",27),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t.pool_name),e.xp6(1),e.Oqu(t.pool_name)}}function ca(n,_){if(1&n&&(e.TgZ(0,"select",20),e.YNc(1,sa,2,1,"option",21),e.YNc(2,aa,2,1,"option",21),e.YNc(3,ra,2,1,"option",21),e.YNc(4,la,2,2,"option",22),e.qZA()),2&n){const t=e.oxw();e.xp6(1),e.Q6J("ngIf",null===t.pools),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&0===t.pools.length),e.xp6(1),e.Q6J("ngIf",null!==t.pools&&t.pools.length>0),e.xp6(1),e.Q6J("ngForOf",t.pools)}}function da(n,_){1&n&&(e.TgZ(0,"span",28),e.SDv(1,29),e.qZA())}function pa(n,_){1&n&&(e.TgZ(0,"span",28),e.SDv(1,30),e.qZA())}function ga(n,_){1&n&&(e.TgZ(0,"span",28),e.SDv(1,31),e.qZA())}let ua=(()=>{class n{constructor(t,o,i,s,a,d){this.activeModal=t,this.actionLabels=o,this.authStorageService=i,this.notificationService=s,this.poolService=a,this.rbdService=d,this.pools=null,this.editing=!1,this.poolPermission=this.authStorageService.getPermissions().pool,this.createForm()}createForm(){this.namespaceForm=new M.d({pool:new r.NI(""),namespace:new r.NI("")},this.validator(),this.asyncValidator())}validator(){return t=>{const o=t.get("pool"),i=t.get("namespace");let s=null;o.value||(s={required:!0}),o.setErrors(s);let a=null;return i.value||(a={required:!0}),i.setErrors(a),null}}asyncValidator(){return t=>new Promise(o=>{const i=t.get("pool"),s=t.get("namespace");this.rbdService.listNamespaces(i.value).subscribe(a=>{if(a.some(d=>d.namespace===s.value)){const d={namespaceExists:!0};s.setErrors(d),o(d)}else o(null)})})}ngOnInit(){this.onSubmit=new Et.xQ,this.poolPermission.read&&this.poolService.list(["pool_name","type","application_metadata"]).then(t=>{const o=[];for(const i of t)this.rbdService.isRBDPool(i)&&"replicated"===i.type&&o.push(i);if(this.pools=o,1===this.pools.length){const i=this.pools[0].pool_name;this.namespaceForm.get("pool").setValue(i)}})}submit(){const t=this.namespaceForm.getValue("pool"),o=this.namespaceForm.getValue("namespace"),i=new F.R;i.name="rbd/namespace/create",i.metadata={pool:t,namespace:o},this.rbdService.createNamespace(t,o).toPromise().then(()=>{this.notificationService.show(Ve.k.success,"Created namespace '" + t + "/" + o + "'"),this.activeModal.close(),this.onSubmit.next()}).catch(()=>{this.namespaceForm.setErrors({cdSubmitButton:!0})})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(I.Kz),e.Y36(D.p4),e.Y36(oe.j),e.Y36(Le.g),e.Y36(Ke.q),e.Y36(x))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-namespace-form-modal"]],decls:23,vars:9,consts:function(){let _,t,o,i,s,a,d,c,u;return _="Create Namespace",t="Pool",o="Name",i="Loading...",s="-- No rbd pools available --",a="-- Select a pool --",d="This field is required.",c="This field is required.",u="Namespace already exists.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","namespaceForm","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","pool",1,"cd-col-form-label","required"],t,[1,"cd-col-form-input"],["class","form-control","type","text","placeholder","Pool name...","id","pool","name","pool","formControlName","pool",4,"ngIf"],["id","pool","name","pool","class","form-control","formControlName","pool",4,"ngIf"],["class","invalid-feedback",4,"ngIf"],["for","namespace",1,"cd-col-form-label","required"],o,["type","text","placeholder","Namespace name...","id","namespace","name","namespace","formControlName","namespace","autofocus","",1,"form-control"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],["type","text","placeholder","Pool name...","id","pool","name","pool","formControlName","pool",1,"form-control"],["id","pool","name","pool","formControlName","pool",1,"form-control"],[3,"ngValue",4,"ngIf"],[3,"value",4,"ngFor","ngForOf"],[3,"ngValue"],i,s,a,[3,"value"],[1,"invalid-feedback"],d,c,u]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"div",7),e.TgZ(8,"label",8),e.SDv(9,9),e.qZA(),e.TgZ(10,"div",10),e.YNc(11,_a,1,0,"input",11),e.YNc(12,ca,5,4,"select",12),e.YNc(13,da,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(14,"div",7),e.TgZ(15,"label",14),e.SDv(16,15),e.qZA(),e.TgZ(17,"div",10),e._UZ(18,"input",16),e.YNc(19,pa,2,0,"span",13),e.YNc(20,ga,2,0,"span",13),e.qZA(),e.qZA(),e.qZA(),e.TgZ(21,"div",17),e.TgZ(22,"cd-form-button-panel",18),e.NdJ("submitActionEvent",function(){return o.submit()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.namespaceForm),e.xp6(7),e.Q6J("ngIf",!o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.namespaceForm.showError("pool",i,"required")),e.xp6(6),e.Q6J("ngIf",o.namespaceForm.showError("namespace",i,"required")),e.xp6(1),e.Q6J("ngIf",o.namespaceForm.showError("namespace",i,"namespaceExists")),e.xp6(2),e.Q6J("form",o.namespaceForm)("submitText",o.actionLabels.CREATE)}},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,l.O5,f.o,r.Fj,h.b,r.JJ,r.u,le.U,O.p,r.EJ,l.sg,r.YN,r.Kr],styles:[""]}),n})(),ma=(()=>{class n{constructor(t,o,i,s,a,d){this.authStorageService=t,this.rbdService=o,this.poolService=i,this.modalService=s,this.notificationService=a,this.actionLabels=d,this.selection=new Ee.r,this.permission=this.authStorageService.getPermissions().rbdImage,this.tableActions=[{permission:"create",icon:T.P.add,click:()=>this.createModal(),name:this.actionLabels.CREATE},{permission:"delete",icon:T.P.destroy,click:()=>this.deleteModal(),name:this.actionLabels.DELETE,disable:()=>this.getDeleteDisableDesc()}]}ngOnInit(){this.columns=[{name:"Namespace",prop:"namespace",flexGrow:1},{name:"Pool",prop:"pool",flexGrow:1},{name:"Total images",prop:"num_images",flexGrow:1}],this.refresh()}refresh(){this.poolService.list(["pool_name","type","application_metadata"]).then(t=>{t=t.filter(i=>this.rbdService.isRBDPool(i)&&"replicated"===i.type);const o=[];t.forEach(i=>{o.push(this.rbdService.listNamespaces(i.pool_name))}),o.length>0?(0,W.D)(o).subscribe(i=>{const s=[];for(let a=0;a{s.push({id:`${c}/${u.namespace}`,pool:c,namespace:u.namespace,num_images:u.num_images})})}this.namespaces=s}):this.namespaces=[]})}updateSelection(t){this.selection=t}createModal(){this.modalRef=this.modalService.show(ua),this.modalRef.componentInstance.onSubmit.subscribe(()=>{this.refresh()})}deleteModal(){const t=this.selection.first().pool,o=this.selection.first().namespace;this.modalRef=this.modalService.show(he.M,{itemDescription:"Namespace",itemNames:[`${t}/${o}`],submitAction:()=>this.rbdService.deleteNamespace(t,o).subscribe(()=>{this.notificationService.show(Ve.k.success,"Deleted namespace '" + t + "/" + o + "'"),this.modalRef.close(),this.refresh()},()=>{this.modalRef.componentInstance.stopLoadingSpinner()})})}getDeleteDisableDesc(){var t;const o=this.selection.first();return(null==o?void 0:o.num_images)>0?"Namespace contains images":!(null===(t=this.selection)||void 0===t?void 0:t.first())}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(x),e.Y36(Ke.q),e.Y36(re.Z),e.Y36(Le.g),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-namespace-list"]],features:[e._Bn([se.j])],decls:4,vars:5,consts:[["columnMode","flex","identifier","id","forceIdentifier","true","selectionType","single",3,"data","columns","fetchData","updateSelection"],[1,"table-actions","btn-toolbar"],[1,"btn-group",3,"permission","selection","tableActions"]],template:function(t,o){1&t&&(e._UZ(0,"cd-rbd-tabs"),e.TgZ(1,"cd-table",0),e.NdJ("fetchData",function(){return o.refresh()})("updateSelection",function(s){return o.updateSelection(s)}),e.TgZ(2,"div",1),e._UZ(3,"cd-table-actions",2),e.qZA(),e.qZA()),2&t&&(e.xp6(1),e.Q6J("data",o.namespaces)("columns",o.columns),e.xp6(2),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions))},directives:[Qe,ee.a,Re.K],styles:[""]}),n})(),Ta=(()=>{class n{}return n.\u0275fac=function(t){return new(t||n)},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-performance"]],decls:2,vars:1,consts:[["uid","41FrpeUiz","grafanaStyle","two",3,"grafanaPath"]],template:function(t,o){1&t&&(e._UZ(0,"cd-rbd-tabs"),e._UZ(1,"cd-grafana",0)),2&t&&(e.xp6(1),e.Q6J("grafanaPath","rbd-overview?"))},directives:[Qe,Rt.F],styles:[""]}),n})();function fa(n,_){1&n&&e._UZ(0,"input",15)}function Ca(n,_){if(1&n&&(e.TgZ(0,"option",20),e._uU(1),e.qZA()),2&n){const t=_.$implicit;e.Q6J("value",t),e.xp6(1),e.Oqu(t)}}function Sa(n,_){if(1&n&&(e.TgZ(0,"select",16),e.TgZ(1,"option",17),e.SDv(2,18),e.qZA(),e.YNc(3,Ca,2,2,"option",19),e.qZA()),2&n){const t=e.oxw();e.xp6(3),e.Q6J("ngForOf",t.pools)}}let Ea=(()=>{class n{constructor(t,o,i,s,a,d,c){this.authStorageService=t,this.rbdService=o,this.activeModal=i,this.actionLabels=s,this.fb=a,this.poolService=d,this.taskWrapper=c,this.poolPermission=this.authStorageService.getPermissions().pool}createForm(){this.purgeForm=this.fb.group({poolName:""})}ngOnInit(){this.poolPermission.read&&this.poolService.list(["pool_name","application_metadata"]).then(t=>{this.pools=t.filter(o=>o.application_metadata.includes("rbd")).map(o=>o.pool_name)}),this.createForm()}purge(){const t=this.purgeForm.getValue("poolName")||"";this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/trash/purge",{pool_name:t}),call:this.rbdService.purgeTrash(t)}).subscribe({error:()=>{this.purgeForm.setErrors({cdSubmitButton:!0})},complete:()=>{this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(x),e.Y36(I.Kz),e.Y36(D.p4),e.Y36(ot.O),e.Y36(Ke.q),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-trash-purge-modal"]],decls:18,vars:6,consts:function(){let _,t,o,i,s;return _="Purge Trash",t="To purge, select\xA0 " + "[\ufffd#9\ufffd|\ufffd#10\ufffd]" + "All" + "[\ufffd/#9\ufffd|\ufffd/#10\ufffd]" + "\xA0 or one pool and click\xA0 " + "[\ufffd#9\ufffd|\ufffd#10\ufffd]" + "Purge" + "[\ufffd/#9\ufffd|\ufffd/#10\ufffd]" + ".\xA0",t=e.Zx4(t),o="Pool:",i="Pool name...",s="All",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","purgeForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],[1,"col-form-label","mx-auto"],o,["class","form-control","type","text","placeholder",i,"formControlName","poolName",4,"ngIf"],["id","poolName","name","poolName","class","form-control","formControlName","poolName",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],["type","text","placeholder",i,"formControlName","poolName",1,"form-control"],["id","poolName","name","poolName","formControlName","poolName",1,"form-control"],["value",""],s,[3,"value",4,"ngFor","ngForOf"],[3,"value"]]},template:function(t,o){1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.tHW(8,7),e._UZ(9,"kbd"),e._UZ(10,"kbd"),e.N_p(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e.YNc(14,fa,1,0,"input",11),e.YNc(15,Sa,4,1,"select",12),e.qZA(),e.qZA(),e.TgZ(16,"div",13),e.TgZ(17,"cd-form-button-panel",14),e.NdJ("submitActionEvent",function(){return o.purge()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t&&(e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.purgeForm),e.xp6(10),e.Q6J("ngIf",!o.poolPermission.read),e.xp6(1),e.Q6J("ngIf",o.poolPermission.read),e.xp6(2),e.Q6J("form",o.purgeForm)("submitText",o.actionLabels.PURGE))},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,l.O5,O.p,f.o,r.Fj,h.b,r.JJ,r.u,r.EJ,r.YN,r.Kr,l.sg],styles:[""]}),n})();function Ra(n,_){1&n&&(e.TgZ(0,"span",15),e.SDv(1,16),e.qZA())}let Ma=(()=>{class n{constructor(t,o,i,s,a){this.rbdService=t,this.activeModal=o,this.actionLabels=i,this.fb=s,this.taskWrapper=a}ngOnInit(){this.imageSpec=new Z.N(this.poolName,this.namespace,this.imageName).toString(),this.restoreForm=this.fb.group({name:this.imageName})}restore(){const t=this.restoreForm.getValue("name"),o=new Z.N(this.poolName,this.namespace,this.imageId);this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/trash/restore",{image_id_spec:o.toString(),new_image_name:t}),call:this.rbdService.restoreTrash(o,t)}).subscribe({error:()=>{this.restoreForm.setErrors({cdSubmitButton:!0})},complete:()=>{this.activeModal.close()}})}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(x),e.Y36(I.Kz),e.Y36(D.p4),e.Y36(ot.O),e.Y36(Q.P))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-trash-restore-modal"]],decls:18,vars:7,consts:function(){let _,t,o,i;return _="Restore Image",t="To restore\xA0 " + "[\ufffd#9\ufffd|\ufffd#10\ufffd]" + "" + "\ufffd0\ufffd" + "@" + "\ufffd1\ufffd" + "" + "[\ufffd/#9\ufffd|\ufffd/#10\ufffd]" + ",\xA0 type the image's new name and click\xA0 " + "[\ufffd#9\ufffd|\ufffd#10\ufffd]" + "Restore" + "[\ufffd/#9\ufffd|\ufffd/#10\ufffd]" + ".",t=e.Zx4(t),o="New Name",i="This field is required.",[[3,"modalRef"],[1,"modal-title"],_,[1,"modal-content"],["name","restoreForm","novalidate","",1,"form",3,"formGroup"],["formDir","ngForm"],[1,"modal-body"],t,[1,"form-group"],["for","name",1,"col-form-label"],o,["type","text","name","name","id","name","autocomplete","off","formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],i]},template:function(t,o){if(1&t&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.BQk(),e.ynx(3,3),e.TgZ(4,"form",4,5),e.TgZ(6,"div",6),e.TgZ(7,"p"),e.tHW(8,7),e._UZ(9,"kbd"),e._UZ(10,"kbd"),e.N_p(),e.qZA(),e.TgZ(11,"div",8),e.TgZ(12,"label",9),e.SDv(13,10),e.qZA(),e._UZ(14,"input",11),e.YNc(15,Ra,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(16,"div",13),e.TgZ(17,"cd-form-button-panel",14),e.NdJ("submitActionEvent",function(){return o.restore()}),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&t){const i=e.MAs(5);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.Q6J("formGroup",o.restoreForm),e.xp6(6),e.pQV(o.imageSpec)(o.imageId),e.QtT(8),e.xp6(5),e.Q6J("ngIf",o.restoreForm.showError("name",i,"required")),e.xp6(2),e.Q6J("form",o.restoreForm)("submitText",o.actionLabels.RESTORE)}},directives:[R.z,r._Y,r.JL,r.sg,v.V,g.P,f.o,r.Fj,h.b,r.JJ,r.u,le.U,l.O5,O.p],styles:[""]}),n})();const Oa=["expiresTpl"],Aa=["deleteTpl"],ha=function(n){return[n]};function Pa(n,_){if(1&n){const t=e.EpF();e.TgZ(0,"button",6),e.NdJ("click",function(){return e.CHM(t),e.oxw().purgeModal()}),e._UZ(1,"i",7),e.ynx(2),e.SDv(3,8),e.BQk(),e.qZA()}if(2&n){const t=e.oxw();e.Q6J("disabled",t.disablePurgeBtn),e.xp6(1),e.Q6J("ngClass",e.VKq(2,ha,t.icons.destroy))}}function Ia(n,_){1&n&&(e.ynx(0),e.SDv(1,10),e.BQk())}function ba(n,_){1&n&&(e.ynx(0),e.SDv(1,11),e.BQk())}function Na(n,_){if(1&n&&(e.YNc(0,Ia,2,0,"ng-container",9),e.YNc(1,ba,2,0,"ng-container",9),e._uU(2),e.ALo(3,"cdDate")),2&n){const t=_.row,o=_.value;e.Q6J("ngIf",t.cdIsExpired),e.xp6(1),e.Q6J("ngIf",!t.cdIsExpired),e.xp6(1),e.hij(" ",e.lcZ(3,3,o),"\n")}}function Da(n,_){if(1&n&&(e.TgZ(0,"p",13),e.TgZ(1,"strong"),e.ynx(2),e.SDv(3,14),e.ALo(4,"cdDate"),e.BQk(),e.qZA(),e.qZA()),2&n){const t=e.oxw().expiresAt;e.xp6(4),e.pQV(e.lcZ(4,1,t)),e.QtT(3)}}function va(n,_){1&n&&e.YNc(0,Da,5,3,"p",12),2&n&&e.Q6J("ngIf",!_.isExpired)}let La=(()=>{class n{constructor(t,o,i,s,a,d,c){this.authStorageService=t,this.rbdService=o,this.modalService=i,this.cdDatePipe=s,this.taskListService=a,this.taskWrapper=d,this.actionLabels=c,this.icons=T.P,this.executingTasks=[],this.selection=new Ee.r,this.tableStatus=new U.E,this.disablePurgeBtn=!0,this.permission=this.authStorageService.getPermissions().rbdImage,this.tableActions=[{permission:"update",icon:T.P.undo,click:()=>this.restoreModal(),name:this.actionLabels.RESTORE},{permission:"delete",icon:T.P.destroy,click:()=>this.deleteModal(),name:this.actionLabels.DELETE}]}ngOnInit(){this.columns=[{name:"ID",prop:"id",flexGrow:1,cellTransformation:ve.e.executing},{name:"Name",prop:"name",flexGrow:1},{name:"Pool",prop:"pool_name",flexGrow:1},{name:"Namespace",prop:"namespace",flexGrow:1},{name:"Status",prop:"deferment_end_time",flexGrow:1,cellTemplate:this.expiresTpl},{name:"Deleted At",prop:"deletion_time",flexGrow:1,pipe:this.cdDatePipe}],this.taskListService.init(()=>this.rbdService.listTrash(),i=>this.prepareResponse(i),i=>this.images=i,()=>this.onFetchError(),i=>["rbd/trash/remove","rbd/trash/restore"].includes(i.name),(i,s)=>new Z.N(i.pool_name,i.namespace,i.id).toString()===s.metadata.image_id_spec,void 0)}prepareResponse(t){let o=[];const i={};let s;if(t.forEach(a=>{C().isUndefined(i[a.status])&&(i[a.status]=[]),i[a.status].push(a.pool_name),o=o.concat(a.value),this.disablePurgeBtn=!o.length}),i[3]?s=3:i[1]?s=1:i[2]&&(s=2),s){const a=(i[s].length>1?"pools ":"pool ")+i[s].join();this.tableStatus=new U.E(s,a)}else this.tableStatus=new U.E;return o.forEach(a=>{a.cdIsExpired=Me()().isAfter(a.deferment_end_time)}),o}onFetchError(){this.table.reset(),this.tableStatus=new U.E(ce.T.ValueException)}updateSelection(t){this.selection=t}restoreModal(){const t={poolName:this.selection.first().pool_name,namespace:this.selection.first().namespace,imageName:this.selection.first().name,imageId:this.selection.first().id};this.modalRef=this.modalService.show(Ma,t)}deleteModal(){const t=this.selection.first().pool_name,o=this.selection.first().namespace,i=this.selection.first().id,s=this.selection.first().deferment_end_time,a=Me()().isAfter(s),d=new Z.N(t,o,i);this.modalRef=this.modalService.show(he.M,{itemDescription:"RBD",itemNames:[d],bodyTemplate:this.deleteTpl,bodyContext:{expiresAt:s,isExpired:a},submitActionObservable:()=>this.taskWrapper.wrapTaskAroundCall({task:new F.R("rbd/trash/remove",{image_id_spec:d.toString()}),call:this.rbdService.removeTrash(d,!0)})})}purgeModal(){this.modalService.show(Ea)}}return n.\u0275fac=function(t){return new(t||n)(e.Y36(oe.j),e.Y36(x),e.Y36(re.Z),e.Y36(ze.N),e.Y36(se.j),e.Y36(Q.P),e.Y36(D.p4))},n.\u0275cmp=e.Xpm({type:n,selectors:[["cd-rbd-trash-list"]],viewQuery:function(t,o){if(1&t&&(e.Gf(ee.a,7),e.Gf(Oa,7),e.Gf(Aa,7)),2&t){let i;e.iGM(i=e.CRH())&&(o.table=i.first),e.iGM(i=e.CRH())&&(o.expiresTpl=i.first),e.iGM(i=e.CRH())&&(o.deleteTpl=i.first)}},features:[e._Bn([se.j])],decls:9,vars:8,consts:function(){let _,t,o,i;return _="Purge Trash",t="Expired at",o="Protected until",i="This image is protected until " + "\ufffd0\ufffd" + ".",[["columnMode","flex","identifier","id","forceIdentifier","true","selectionType","single",3,"data","columns","status","autoReload","fetchData","updateSelection"],[1,"table-actions","btn-toolbar"],[1,"btn-group",3,"permission","selection","tableActions"],["class","btn btn-light","type","button",3,"disabled","click",4,"ngIf"],["expiresTpl",""],["deleteTpl",""],["type","button",1,"btn","btn-light",3,"disabled","click"],["aria-hidden","true",3,"ngClass"],_,[4,"ngIf"],t,o,["class","text-danger",4,"ngIf"],[1,"text-danger"],i]},template:function(t,o){1&t&&(e._UZ(0,"cd-rbd-tabs"),e.TgZ(1,"cd-table",0),e.NdJ("fetchData",function(){return o.taskListService.fetch()})("updateSelection",function(s){return o.updateSelection(s)}),e.TgZ(2,"div",1),e._UZ(3,"cd-table-actions",2),e.YNc(4,Pa,4,4,"button",3),e.qZA(),e.qZA(),e.YNc(5,Na,4,5,"ng-template",null,4,e.W1O),e.YNc(7,va,1,1,"ng-template",null,5,e.W1O)),2&t&&(e.xp6(1),e.Q6J("data",o.images)("columns",o.columns)("status",o.tableStatus)("autoReload",-1),e.xp6(2),e.Q6J("permission",o.permission)("selection",o.selection)("tableActions",o.tableActions),e.xp6(1),e.Q6J("ngIf",o.permission.delete))},directives:[Qe,ee.a,Re.K,l.O5,f.o,l.mk],pipes:[ze.N],styles:[""]}),n})(),Mt=(()=>{class n{}return n.\u0275fac=function(t){return new(t||n)},n.\u0275mod=e.oAB({type:n}),n.\u0275inj=e.cJS({imports:[[l.ez,Jn,r.u5,r.UX,I.Oz,I.dT,I.HK,Ie.b,_e.m,m.Bz,ne.xc]]}),n})();const Fa=[{path:"",redirectTo:"rbd",pathMatch:"full"},{path:"rbd",canActivate:[j.T],data:{breadcrumbs:"Images"},children:[{path:"",component:ia},{path:"namespaces",component:ma,data:{breadcrumbs:"Namespaces"}},{path:"trash",component:La,data:{breadcrumbs:"Trash"}},{path:"performance",component:Ta,data:{breadcrumbs:"Overall Performance"}},{path:D.MQ.CREATE,component:$e,data:{breadcrumbs:D.Qn.CREATE}},{path:`${D.MQ.EDIT}/:image_spec`,component:$e,data:{breadcrumbs:D.Qn.EDIT}},{path:`${D.MQ.CLONE}/:image_spec/:snap`,component:$e,data:{breadcrumbs:D.Qn.CLONE}},{path:`${D.MQ.COPY}/:image_spec`,component:$e,data:{breadcrumbs:D.Qn.COPY}},{path:`${D.MQ.COPY}/:image_spec/:snap`,component:$e,data:{breadcrumbs:D.Qn.COPY}}]},{path:"mirroring",component:Ki,canActivate:[j.T],data:{breadcrumbs:"Mirroring"}},{path:"iscsi",canActivate:[j.T],data:{breadcrumbs:"iSCSI"},children:[{path:"",redirectTo:"overview",pathMatch:"full"},{path:"overview",component:zn,data:{breadcrumbs:"Overview"}},{path:"targets",data:{breadcrumbs:"Targets"},children:[{path:"",component:Ln},{path:D.MQ.CREATE,component:rt,data:{breadcrumbs:D.Qn.CREATE}},{path:`${D.MQ.EDIT}/:target_iqn`,component:rt,data:{breadcrumbs:D.Qn.EDIT}}]}]}];let $a=(()=>{class n{}return n.\u0275fac=function(t){return new(t||n)},n.\u0275mod=e.oAB({type:n}),n.\u0275inj=e.cJS({imports:[[Mt,m.Bz.forChild(Fa)]]}),n})()},54555:(it,Oe,p)=>{p.d(Oe,{d:()=>Q});var l=p(74788),r=p(24751),m=p(23815),ne=p.n(m),I=p(80226),Ie=p(65862),D=p(95463),j=p(30633),_e=p(28211),be=p(34089),C=p(41582),W=p(12057),b=p(56310),ie=p(18372),Y=p(87925),e=p(94276);let k=(()=>{class R{constructor(g,f){this.control=g,this.formatter=f}setValue(g){const f=this.formatter.toMilliseconds(g);this.control.control.setValue(`${f} ms`)}ngOnInit(){this.setValue(this.control.value),this.ngDataReady&&this.ngDataReady.subscribe(()=>this.setValue(this.control.value))}onUpdate(g){this.setValue(g)}}return R.\u0275fac=function(g){return new(g||R)(l.Y36(r.a5),l.Y36(_e.H))},R.\u0275dir=l.lG2({type:R,selectors:[["","cdMilliseconds",""]],hostBindings:function(g,f){1&g&&l.NdJ("blur",function(O){return f.onUpdate(O.target.value)})},inputs:{ngDataReady:"ngDataReady"}}),R})();var Ne=p(20044);let Z=(()=>{class R{constructor(g,f,h,O){this.elementRef=g,this.control=f,this.dimlessBinaryPerSecondPipe=h,this.formatter=O,this.ngModelChange=new l.vpe,this.el=this.elementRef.nativeElement}ngOnInit(){this.setValue(this.el.value),this.ngDataReady&&this.ngDataReady.subscribe(()=>this.setValue(this.el.value))}setValue(g){/^[\d.]+$/.test(g)&&(g+=this.defaultUnit||"m");const f=this.formatter.toBytes(g,0),h=this.round(f);this.el.value=this.dimlessBinaryPerSecondPipe.transform(h),null!==f?(this.ngModelChange.emit(this.el.value),this.control.control.setValue(this.el.value)):(this.ngModelChange.emit(null),this.control.control.setValue(null))}round(g){if(null!==g&&0!==g){if(!ne().isUndefined(this.minBytes)&&gthis.maxBytes)return this.maxBytes;if(!ne().isUndefined(this.roundPower)){const f=Math.round(Math.log(g)/Math.log(this.roundPower));return Math.pow(this.roundPower,f)}}return g}onBlur(g){this.setValue(g)}}return R.\u0275fac=function(g){return new(g||R)(l.Y36(l.SBq),l.Y36(r.a5),l.Y36(Ne.O),l.Y36(_e.H))},R.\u0275dir=l.lG2({type:R,selectors:[["","cdDimlessBinaryPerSecond",""]],hostBindings:function(g,f){1&g&&l.NdJ("blur",function(O){return f.onBlur(O.target.value)})},inputs:{ngDataReady:"ngDataReady",minBytes:"minBytes",maxBytes:"maxBytes",roundPower:"roundPower",defaultUnit:"defaultUnit"},outputs:{ngModelChange:"ngModelChange"}}),R})(),Ae=(()=>{class R{constructor(g,f){this.formatter=g,this.ngControl=f}setValue(g){const f=this.formatter.toIops(g);this.ngControl.control.setValue(`${f} IOPS`)}ngOnInit(){this.setValue(this.ngControl.value),this.ngDataReady&&this.ngDataReady.subscribe(()=>this.setValue(this.ngControl.value))}onUpdate(g){this.setValue(g)}}return R.\u0275fac=function(g){return new(g||R)(l.Y36(_e.H),l.Y36(r.a5))},R.\u0275dir=l.lG2({type:R,selectors:[["","cdIops",""]],hostBindings:function(g,f){1&g&&l.NdJ("blur",function(O){return f.onUpdate(O.target.value)})},inputs:{ngDataReady:"ngDataReady"}}),R})();function x(R,v){if(1&R&&(l.ynx(0),l._UZ(1,"input",18),l.BQk()),2&R){const g=l.oxw().$implicit,f=l.oxw(2);l.xp6(1),l.Q6J("id",g.name)("name",g.name)("formControlName",g.name)("ngDataReady",f.ngDataReady)}}function ae(R,v){if(1&R&&(l.ynx(0),l._UZ(1,"input",19),l.BQk()),2&R){const g=l.oxw().$implicit,f=l.oxw(2);l.xp6(1),l.Q6J("id",g.name)("name",g.name)("formControlName",g.name)("ngDataReady",f.ngDataReady)}}function V(R,v){if(1&R&&(l.ynx(0),l._UZ(1,"input",20),l.BQk()),2&R){const g=l.oxw().$implicit,f=l.oxw(2);l.xp6(1),l.Q6J("id",g.name)("name",g.name)("formControlName",g.name)("ngDataReady",f.ngDataReady)}}function T(R,v){1&R&&(l.TgZ(0,"span",21),l.SDv(1,22),l.qZA())}const q=function(R){return{active:R}},M=function(R){return[R]};function B(R,v){if(1&R){const g=l.EpF();l.TgZ(0,"div",10),l.TgZ(1,"label",11),l._uU(2),l.TgZ(3,"cd-helper"),l._uU(4),l.qZA(),l.qZA(),l.TgZ(5,"div"),l.TgZ(6,"div",12),l.ynx(7,13),l.YNc(8,x,2,4,"ng-container",14),l.YNc(9,ae,2,4,"ng-container",14),l.YNc(10,V,2,4,"ng-container",14),l.BQk(),l.TgZ(11,"span",15),l.TgZ(12,"button",16),l.NdJ("click",function(){const O=l.CHM(g).$implicit;return l.oxw(2).reset(O.name)}),l._UZ(13,"i",7),l.qZA(),l.qZA(),l.qZA(),l.YNc(14,T,2,0,"span",17),l.qZA(),l.qZA()}if(2&R){const g=v.$implicit,f=l.oxw().$implicit,h=l.oxw(),O=l.MAs(1);l.xp6(1),l.Q6J("for",g.name),l.xp6(1),l.Oqu(g.displayName),l.xp6(2),l.Oqu(g.description),l.xp6(1),l.Gre("cd-col-form-input ",f.heading,""),l.xp6(2),l.Q6J("ngSwitch",g.type),l.xp6(1),l.Q6J("ngSwitchCase",h.configurationType.milliseconds),l.xp6(1),l.Q6J("ngSwitchCase",h.configurationType.bps),l.xp6(1),l.Q6J("ngSwitchCase",h.configurationType.iops),l.xp6(2),l.Q6J("ngClass",l.VKq(13,q,h.isDisabled(g.name))),l.xp6(1),l.Q6J("ngClass",l.VKq(15,M,h.icons.erase)),l.xp6(1),l.Q6J("ngIf",h.form.showError("configuration."+g.name,O,"min"))}}function F(R,v){if(1&R){const g=l.EpF();l.TgZ(0,"div",4),l.TgZ(1,"h4",5),l.TgZ(2,"span",6),l.NdJ("click",function(){const O=l.CHM(g).$implicit;return l.oxw().toggleSectionVisibility(O.class)}),l._uU(3),l._UZ(4,"i",7),l.qZA(),l.qZA(),l.TgZ(5,"div",8),l.YNc(6,B,15,17,"div",9),l.qZA(),l.qZA()}if(2&R){const g=v.$implicit,f=l.oxw();l.xp6(3),l.hij(" ",g.heading," "),l.xp6(1),l.Q6J("ngClass",f.sectionVisibility[g.class]?f.icons.minusCircle:f.icons.addCircle),l.xp6(1),l.Tol(g.class),l.Q6J("hidden",!f.sectionVisibility[g.class]),l.xp6(1),l.Q6J("ngForOf",g.options)}}let Q=(()=>{class R{constructor(g,f){this.formatterService=g,this.rbdConfigurationService=f,this.initializeData=new I.t(1),this.changes=new l.vpe,this.icons=Ie.P,this.ngDataReady=new l.vpe,this.configurationType=j.r,this.sectionVisibility={}}ngOnInit(){const g=this.createConfigurationFormGroup();this.form.addControl("configuration",g),g.valueChanges.subscribe(()=>{this.changes.emit(this.getDirtyValues.bind(this))}),this.initializeData&&this.initializeData.subscribe(f=>{this.initialData=f.initialData;const h=f.sourceType;this.rbdConfigurationService.getWritableOptionFields().forEach(O=>{const H=f.initialData.filter(De=>De.name===O.name).pop();H&&H.source===h&&this.form.get(`configuration.${O.name}`).setValue(H.value)}),this.ngDataReady.emit()}),this.rbdConfigurationService.getWritableSections().forEach(f=>this.sectionVisibility[f.class]=!1)}getDirtyValues(g=!1,f){if(g&&!f)throw new Error("ProgrammingError: If local values shall be included, a proper localFieldType argument has to be provided, too");const h={};return this.rbdConfigurationService.getWritableOptionFields().forEach(O=>{const H=this.form.get("configuration").get(O.name);this.initialData&&this.initialData[O.name]===H.value||(H.dirty||g&&H.source===f)&&(h[O.name]=null===H.value?H.value:O.type===j.r.bps?this.formatterService.toBytes(H.value):O.type===j.r.milliseconds?this.formatterService.toMilliseconds(H.value):O.type===j.r.iops?this.formatterService.toIops(H.value):H.value)}),h}createConfigurationFormGroup(){const g=new D.d({});return this.rbdConfigurationService.getWritableOptionFields().forEach(f=>{let h;if(f.type!==j.r.milliseconds&&f.type!==j.r.iops&&f.type!==j.r.bps)throw new Error(`Type ${f.type} is unknown, you may need to add it to RbdConfiguration class`);{let O=0;ne().forEach(this.initialData,H=>{H.name===f.name&&(O=H.value)}),h=new r.NI(O,r.kI.min(0))}g.addControl(f.name,h)}),g}reset(g){const f=this.form.get("configuration").get(g);f.disabled?(f.setValue(f.previousValue||0),f.enable(),f.previousValue||f.markAsPristine()):(f.previousValue=f.value,f.setValue(null),f.markAsDirty(),f.disable())}isDisabled(g){return this.form.get("configuration").get(g).disabled}toggleSectionVisibility(g){this.sectionVisibility[g]=!this.sectionVisibility[g]}}return R.\u0275fac=function(g){return new(g||R)(l.Y36(_e.H),l.Y36(be.n))},R.\u0275cmp=l.Xpm({type:R,selectors:[["cd-rbd-configuration-form"]],inputs:{form:"form",initializeData:"initializeData"},outputs:{changes:"changes"},decls:5,vars:2,consts:function(){let v,g,f;return v="RBD Configuration",g="Remove the local configuration value. The parent configuration value will be inherited and used instead.",f="The minimum value is 0",[[3,"formGroup"],["cfgFormGroup",""],v,["class","col-12",4,"ngFor","ngForOf"],[1,"col-12"],[1,"cd-header"],[1,"collapsible",3,"click"],["aria-hidden","true",3,"ngClass"],[3,"hidden"],["class","form-group row",4,"ngFor","ngForOf"],[1,"form-group","row"],[1,"cd-col-form-label",3,"for"],[1,"input-group"],[3,"ngSwitch"],[4,"ngSwitchCase"],[1,"input-group-append"],["type","button","data-toggle","button","title",g,1,"btn","btn-light",3,"ngClass","click"],["class","invalid-feedback",4,"ngIf"],["type","text","cdMilliseconds","",1,"form-control",3,"id","name","formControlName","ngDataReady"],["type","text","defaultUnit","b","cdDimlessBinaryPerSecond","",1,"form-control",3,"id","name","formControlName","ngDataReady"],["type","text","cdIops","",1,"form-control",3,"id","name","formControlName","ngDataReady"],[1,"invalid-feedback"],f]},template:function(g,f){1&g&&(l.TgZ(0,"fieldset",0,1),l.TgZ(2,"legend"),l.SDv(3,2),l.qZA(),l.YNc(4,F,7,7,"div",3),l.qZA()),2&g&&(l.Q6J("formGroup",f.form.get("configuration")),l.xp6(4),l.Q6J("ngForOf",f.rbdConfigurationService.sections))},directives:[r.JL,r.sg,C.V,W.sg,W.mk,b.P,ie.S,W.RF,W.n9,Y.o,W.O5,r.Fj,e.b,k,r.JJ,r.u,Z,Ae],styles:[".collapsible[_ngcontent-%COMP%]{cursor:pointer;user-select:none}"]}),R})()},71752:(it,Oe,p)=>{p.d(Oe,{P:()=>V});var l=p(64337),r=p(30633),m=p(74788);let ne=(()=>{class T{transform(M){return{0:"global",1:"pool",2:"image"}[M]}}return T.\u0275fac=function(M){return new(M||T)},T.\u0275pipe=m.Yjl({name:"rbdConfigurationSource",type:T,pure:!0}),T})();var I=p(28211),Ie=p(34089),D=p(12057),j=p(20044),_e=p(48537),be=p(21766);const C=["configurationSourceTpl"],W=["configurationValueTpl"],b=["poolConfTable"];function ie(T,q){1&T&&(m.TgZ(0,"span"),m.SDv(1,6),m.qZA())}function Y(T,q){1&T&&(m.TgZ(0,"strong"),m.SDv(1,7),m.qZA())}function e(T,q){1&T&&(m.TgZ(0,"strong"),m.SDv(1,8),m.qZA())}function k(T,q){1&T&&(m.TgZ(0,"div",4),m.YNc(1,ie,2,0,"span",5),m.YNc(2,Y,2,0,"strong",5),m.YNc(3,e,2,0,"strong",5),m.qZA()),2&T&&(m.Q6J("ngSwitch",q.value),m.xp6(1),m.Q6J("ngSwitchCase","global"),m.xp6(1),m.Q6J("ngSwitchCase","image"),m.xp6(1),m.Q6J("ngSwitchCase","pool"))}function Ne(T,q){if(1&T&&(m.TgZ(0,"span"),m._uU(1),m.ALo(2,"dimlessBinaryPerSecond"),m.qZA()),2&T){const M=m.oxw().value;m.xp6(1),m.Oqu(m.lcZ(2,1,M))}}function Z(T,q){if(1&T&&(m.TgZ(0,"span"),m._uU(1),m.ALo(2,"milliseconds"),m.qZA()),2&T){const M=m.oxw().value;m.xp6(1),m.Oqu(m.lcZ(2,1,M))}}function Ae(T,q){if(1&T&&(m.TgZ(0,"span"),m._uU(1),m.ALo(2,"iops"),m.qZA()),2&T){const M=m.oxw().value;m.xp6(1),m.Oqu(m.lcZ(2,1,M))}}function x(T,q){if(1&T&&(m.TgZ(0,"span"),m._uU(1),m.qZA()),2&T){const M=m.oxw().value;m.xp6(1),m.Oqu(M)}}function ae(T,q){if(1&T&&(m.TgZ(0,"div",4),m.YNc(1,Ne,3,3,"span",5),m.YNc(2,Z,3,3,"span",5),m.YNc(3,Ae,3,3,"span",5),m.YNc(4,x,2,1,"span",9),m.qZA()),2&T){const M=q.row,B=m.oxw();m.Q6J("ngSwitch",M.type),m.xp6(1),m.Q6J("ngSwitchCase",B.typeField.bps),m.xp6(1),m.Q6J("ngSwitchCase",B.typeField.milliseconds),m.xp6(1),m.Q6J("ngSwitchCase",B.typeField.iops)}}let V=(()=>{class T{constructor(M,B){this.formatterService=M,this.rbdConfigurationService=B,this.sourceField=r.h,this.typeField=r.r}ngOnInit(){this.poolConfigurationColumns=[{prop:"displayName",name:"Name"},{prop:"description",name:"Description"},{prop:"name",name:"Key"},{prop:"source",name:"Source",cellTemplate:this.configurationSourceTpl,pipe:new ne},{prop:"value",name:"Value",cellTemplate:this.configurationValueTpl}]}ngOnChanges(){!this.data||(this.data=this.data.filter(M=>this.rbdConfigurationService.getOptionFields().map(B=>B.name).includes(M.name)))}}return T.\u0275fac=function(M){return new(M||T)(m.Y36(I.H),m.Y36(Ie.n))},T.\u0275cmp=m.Xpm({type:T,selectors:[["cd-rbd-configuration-table"]],viewQuery:function(M,B){if(1&M&&(m.Gf(C,7),m.Gf(W,7),m.Gf(b,7)),2&M){let F;m.iGM(F=m.CRH())&&(B.configurationSourceTpl=F.first),m.iGM(F=m.CRH())&&(B.configurationValueTpl=F.first),m.iGM(F=m.CRH())&&(B.poolConfTable=F.first)}},inputs:{data:"data"},features:[m.TTD],decls:6,vars:2,consts:function(){let q,M,B;return q="Global",M="Image",B="Pool",[["identifier","name",3,"data","columns"],["poolConfTable",""],["configurationSourceTpl",""],["configurationValueTpl",""],[3,"ngSwitch"],[4,"ngSwitchCase"],q,M,B,[4,"ngSwitchDefault"]]},template:function(M,B){1&M&&(m._UZ(0,"cd-table",0,1),m.YNc(2,k,4,4,"ng-template",null,2,m.W1O),m.YNc(4,ae,5,4,"ng-template",null,3,m.W1O)),2&M&&m.Q6J("data",B.data)("columns",B.poolConfigurationColumns)},directives:[l.a,D.RF,D.n9,D.ED],pipes:[j.O,_e.J,be.A],styles:[""]}),T})()}}]); \ No newline at end of file diff --git a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/585.062485a57141de14ef7c.js b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/585.062485a57141de14ef7c.js deleted file mode 100644 index a2b5152da..000000000 --- a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/585.062485a57141de14ef7c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkceph_dashboard=self.webpackChunkceph_dashboard||[]).push([[585],{24585:(Fi,Fe,r)=>{r.r(Fe),r.d(Fe,{PoolModule:()=>ze,RoutedPoolModule:()=>Ai});var C=r(12057),a=r(24751),Oe=r(6283),g=r(38549),M=r(79512),d_=r(44466),u_=r(91330),f_=r(370),P_=r(23815),u=r.n(P_),E_=r(80226),g_=r(26504),ue=r(80842);class S{constructor(){this.nodes=[],this.idTree={},this.allDevices=[],this.buckets=[],this.failureDomains={},this.failureDomainKeys=[],this.devices=[],this.deviceCount=0}static searchFailureDomains(i,_){return this.getFailureDomains(this.search(i,_))}static search(i,_){const[o,n]=_.split("~"),s=i.find(c=>["name","id","type"].some(d=>c[d]===o));return s?(i=this.getSubNodes(s,this.createIdTreeFromNodes(i)),n&&(i=this.filterNodesByDeviceType(i,n)),i):[]}static createIdTreeFromNodes(i){const _={};return i.forEach(o=>{_[o.id]=o}),_}static getSubNodes(i,_){let o=[i];return i.children&&i.children.forEach(n=>{o=o.concat(this.getSubNodes(_[n],_))}),o}static filterNodesByDeviceType(i,_){let n,o=i.filter(c=>c.device_class&&c.device_class!==_).map(c=>c.id),s=o;do{n=!1,i=i.filter(d=>!o.includes(d.id));const c=[];i.forEach(d=>{d.children&&d.children.every(P=>o.includes(P))&&(c.push(d.id),n=!0)}),n&&(o=c,s=s.concat(c))}while(n);return(i=u().cloneDeep(i)).map(c=>(c.children&&(c.children=c.children.filter(d=>!s.includes(d))),c))}static getFailureDomains(i){const _={};return i.forEach(o=>{const n=o.type;_[n]||(_[n]=[]),_[n].push(o)}),_}initCrushNodeSelection(i,_,o,n){this.nodes=i,this.idTree=S.createIdTreeFromNodes(i),i.forEach(s=>{this.idTree[s.id]=s}),this.buckets=u().sortBy(i.filter(s=>s.children),"name"),this.controls={root:_,failure:o,device:n},this.preSelectRoot(),this.controls.root.valueChanges.subscribe(()=>this.onRootChange()),this.controls.failure.valueChanges.subscribe(()=>this.onFailureDomainChange()),this.controls.device.valueChanges.subscribe(()=>this.onDeviceChange())}preSelectRoot(){const i=this.nodes.find(_=>"root"===_.type);this.silentSet(this.controls.root,i),this.onRootChange()}silentSet(i,_){i.setValue(_,{emitEvent:!1})}onRootChange(){const i=S.getSubNodes(this.controls.root.value,this.idTree),_=S.getFailureDomains(i);Object.keys(_).forEach(o=>{_[o].length<=1&&delete _[o]}),this.failureDomains=_,this.failureDomainKeys=Object.keys(_).sort(),this.updateFailureDomain()}updateFailureDomain(){let i=this.getIncludedCustomValue(this.controls.failure,Object.keys(this.failureDomains));""===i&&(i=this.setMostCommonDomain(this.controls.failure)),this.updateDevices(i)}getIncludedCustomValue(i,_){return i.dirty&&_.includes(i.value)?i.value:""}setMostCommonDomain(i){let _={n:0,type:""};return Object.keys(this.failureDomains).forEach(o=>{const n=this.failureDomains[o].length;_.nS.getSubNodes(n,this.idTree)));this.allDevices=_.filter(n=>n.device_class).map(n=>n.device_class),this.devices=u().uniq(this.allDevices).sort();const o=1===this.devices.length?this.devices[0]:this.getIncludedCustomValue(this.controls.device,this.devices);this.silentSet(this.controls.device,o),this.onDeviceChange(o)}onDeviceChange(i=this.controls.device.value){this.deviceCount=""===i?this.allDevices.length:this.allDevices.filter(_=>_===i).length}}var Ne=r(30982),p_=r(14745),b=r(65862),m_=r(93614),be=r(95463),E=r(77205),R_=r(30633),v=r(76111),C_=r(47557),M_=r(28211),de=r(32337),e=r(74788),ve=r(62862),Ie=r(83608),$e=r(60312),fe=r(41582),Pe=r(56310),Ee=r(87925),ge=r(94276),pe=r(82945),me=r(18372),Re=r(30839),Ce=r(10545);function h_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,31),e.qZA())}function T_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,32),e.qZA())}function S_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,33),e.qZA())}function L_(t,i){1&t&&(e.TgZ(0,"option",26),e.SDv(1,34),e.qZA())}function A_(t,i){if(1&t&&(e.TgZ(0,"option",35),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_.name," ")}}function F_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,36),e.qZA())}function N_(t,i){1&t&&(e.TgZ(0,"option",26),e.SDv(1,37),e.qZA())}function b_(t,i){if(1&t&&(e.TgZ(0,"option",35),e._uU(1),e.qZA()),2&t){const _=i.$implicit,o=e.oxw();e.Q6J("ngValue",_),e.xp6(1),e.AsE(" ",_," ( ",o.failureDomains[_].length," ) ")}}function v_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,38),e.qZA())}function I_(t,i){if(1&t&&(e.TgZ(0,"option",35),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}let $_=(()=>{class t extends S{constructor(_,o,n,s,c){super(),this.formBuilder=_,this.activeModal=o,this.taskWrapper=n,this.crushRuleService=s,this.actionLabels=c,this.submitAction=new e.vpe,this.tooltips=this.crushRuleService.formTooltips,this.action=this.actionLabels.CREATE,this.resource="Crush Rule",this.createForm()}createForm(){this.form=this.formBuilder.group({name:["",[a.kI.required,a.kI.pattern("[A-Za-z0-9_-]+"),E.h.custom("uniqueName",_=>this.names&&-1!==this.names.indexOf(_))]],root:null,failure_domain:"",device_class:""})}ngOnInit(){this.crushRuleService.getInfo().subscribe(({names:_,nodes:o})=>{this.initCrushNodeSelection(o,this.form.get("root"),this.form.get("failure_domain"),this.form.get("device_class")),this.names=_})}onSubmit(){if(this.form.invalid)return void this.form.setErrors({cdSubmitButton:!0});const _=u().cloneDeep(this.form.value);_.root=_.root.name,""===_.device_class&&delete _.device_class,this.taskWrapper.wrapTaskAroundCall({task:new v.R("crushRule/create",_),call:this.crushRuleService.create(_)}).subscribe({error:()=>{this.form.setErrors({cdSubmitButton:!0})},complete:()=>{this.activeModal.close(),this.submitAction.emit(_)}})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ve.O),e.Y36(g.Kz),e.Y36(de.P),e.Y36(Ie.H),e.Y36(M.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-crush-rule-form-modal"]],outputs:{submitAction:"submitAction"},features:[e.qOj],decls:55,vars:27,consts:function(){let i,_,o,n,s,c,d,P,p,m,h,T,R;return i="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Name",o="Root",n="Failure domain type",s="Device class",c="Let Ceph decide",d="This field is required!",P="The name can only consist of alphanumeric characters, dashes and underscores.",p="The chosen erasure code profile name is already in use.",m="Loading...",h="This field is required!",T="Loading...",R="This field is required!",[[3,"modalRef"],[1,"modal-title"],i,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","name",1,"cd-col-form-label"],_,[1,"required"],[1,"cd-col-form-input"],["type","text","id","name","name","name","placeholder","Name...","formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","root",1,"cd-col-form-label"],o,[3,"html"],["id","root","name","root","formControlName","root",1,"form-control","custom-select"],["ngValue","",4,"ngIf"],[3,"ngValue",4,"ngFor","ngForOf"],["for","failure_domain",1,"cd-col-form-label"],n,["id","failure_domain","name","failure_domain","formControlName","failure_domain",1,"form-control","custom-select"],["for","device_class",1,"cd-col-form-label"],s,["id","device_class","name","device_class","formControlName","device_class",1,"form-control","custom-select"],["ngValue",""],c,[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],d,P,p,m,[3,"ngValue"],h,T,R]},template:function(_,o){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.ynx(11),e.SDv(12,9),e.BQk(),e._UZ(13,"span",10),e.qZA(),e.TgZ(14,"div",11),e._UZ(15,"input",12),e.YNc(16,h_,2,0,"span",13),e.YNc(17,T_,2,0,"span",13),e.YNc(18,S_,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(19,"div",7),e.TgZ(20,"label",14),e.ynx(21),e.SDv(22,15),e.BQk(),e._UZ(23,"cd-helper",16),e._UZ(24,"span",10),e.qZA(),e.TgZ(25,"div",11),e.TgZ(26,"select",17),e.YNc(27,L_,2,0,"option",18),e.YNc(28,A_,2,2,"option",19),e.qZA(),e.YNc(29,F_,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(30,"div",7),e.TgZ(31,"label",20),e.ynx(32),e.SDv(33,21),e.BQk(),e._UZ(34,"cd-helper",16),e._UZ(35,"span",10),e.qZA(),e.TgZ(36,"div",11),e.TgZ(37,"select",22),e.YNc(38,N_,2,0,"option",18),e.YNc(39,b_,2,3,"option",19),e.qZA(),e.YNc(40,v_,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(41,"div",7),e.TgZ(42,"label",23),e.ynx(43),e.SDv(44,24),e.BQk(),e._UZ(45,"cd-helper",16),e.qZA(),e.TgZ(46,"div",11),e.TgZ(47,"select",25),e.TgZ(48,"option",26),e.SDv(49,27),e.qZA(),e.YNc(50,I_,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.TgZ(51,"div",28),e.TgZ(52,"cd-form-button-panel",29),e.NdJ("submitActionEvent",function(){return o.onSubmit()}),e.ALo(53,"titlecase"),e.ALo(54,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const n=e.MAs(7);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.pQV(e.lcZ(3,19,o.action))(e.lcZ(4,21,o.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",o.form),e.xp6(10),e.Q6J("ngIf",o.form.showError("name",n,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"pattern")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"uniqueName")),e.xp6(5),e.Q6J("html",o.tooltips.root),e.xp6(4),e.Q6J("ngIf",!o.buckets),e.xp6(1),e.Q6J("ngForOf",o.buckets),e.xp6(1),e.Q6J("ngIf",o.form.showError("root",n,"required")),e.xp6(5),e.Q6J("html",o.tooltips.failure_domain),e.xp6(4),e.Q6J("ngIf",!o.failureDomains),e.xp6(1),e.Q6J("ngForOf",o.failureDomainKeys),e.xp6(1),e.Q6J("ngIf",o.form.showError("failure_domain",n,"required")),e.xp6(5),e.Q6J("html",o.tooltips.device_class),e.xp6(5),e.Q6J("ngForOf",o.devices),e.xp6(2),e.Q6J("form",o.form)("submitText",e.lcZ(53,23,o.action)+" "+e.lcZ(54,25,o.resource))}},directives:[$e.z,a._Y,a.JL,fe.V,a.sg,Pe.P,Ee.o,a.Fj,ge.b,a.JJ,a.u,pe.U,C.O5,me.S,a.EJ,C.sg,a.YN,a.Kr,Re.p],pipes:[C.rS,Ce.m],styles:[""]}),t})();class D_{}var Z_=r(58497);let Me=(()=>{class t{constructor(_){this.http=_,this.apiPath="api/erasure_code_profile",this.formTooltips={k:"Each object is split in data-chunks parts, each stored on a different OSD.",m:"Compute coding chunks for each object and store them on different OSDs.\n The number of coding chunks is also the number of OSDs that can be down without losing data.",plugins:{jerasure:{description:"The jerasure plugin is the most generic and flexible plugin,\n it is also the default for Ceph erasure coded pools.",technique:"The more flexible technique is reed_sol_van : it is enough to set k\n and m. The cauchy_good technique can be faster but you need to chose the packetsize\n carefully. All of reed_sol_r6_op, liberation, blaum_roth, liber8tion are RAID6 equivalents\n in the sense that they can only be configured with m=2.",packetSize:"The encoding will be done on packets of bytes size at a time.\n Choosing the right packet size is difficult.\n The jerasure documentation contains extensive information on this topic."},lrc:{description:"With the jerasure plugin, when an erasure coded object is stored on\n multiple OSDs, recovering from the loss of one OSD requires reading from all the others.\n For instance if jerasure is configured with k=8 and m=4, losing one OSD requires reading\n from the eleven others to repair.\n\n The lrc erasure code plugin creates local parity chunks to be able to recover using\n less OSDs. For instance if lrc is configured with k=8, m=4 and l=4, it will create\n an additional parity chunk for every four OSDs. When a single OSD is lost, it can be\n recovered with only four OSDs instead of eleven.",l:"Group the coding and data chunks into sets of size locality. For instance,\n for k=4 and m=2, when locality=3 two groups of three are created. Each set can\n be recovered without reading chunks from another set.",crushLocality:"The type of the crush bucket in which each set of chunks defined\n by l will be stored. For instance, if it is set to rack, each group of l chunks will be\n placed in a different rack. It is used to create a CRUSH rule step such as step choose\n rack. If it is not set, no such grouping is done."},isa:{description:"The isa plugin encapsulates the ISA library. It only runs on Intel processors.",technique:"The ISA plugin comes in two Reed Solomon forms.\n If reed_sol_van is set, it is Vandermonde, if cauchy is set, it is Cauchy."},shec:{description:"The shec plugin encapsulates the multiple SHEC library.\n It allows ceph to recover data more efficiently than Reed Solomon codes.",c:"The number of parity chunks each of which includes each data chunk in its\n calculation range. The number is used as a durability estimator. For instance, if c=2,\n 2 OSDs can be down without losing data."},clay:{description:"CLAY (short for coupled-layer) codes are erasure codes designed to\n bring about significant savings in terms of network bandwidth and disk IO when a failed\n node/OSD/rack is being repaired.",d:"Number of OSDs requested to send data during recovery of a single chunk.\n d needs to be chosen such that k+1 <= d <= k+m-1. The larger the d, the better\n the savings.",scalar_mds:"scalar_mds specifies the plugin that is used as a building block\n in the layered construction. It can be one of jerasure, isa, shec.",technique:"technique specifies the technique that will be picked\n within the 'scalar_mds' plugin specified. Supported techniques\n are 'reed_sol_van', 'reed_sol_r6_op', 'cauchy_orig',\n 'cauchy_good', 'liber8tion' for jerasure, 'reed_sol_van',\n 'cauchy' for isa and 'single', 'multiple' for shec."}},crushRoot:"The name of the crush bucket used for the first step of the CRUSH rule.\n For instance step take default.",crushFailureDomain:"Ensure that no two chunks are in a bucket with the same failure\n domain. For instance, if the failure domain is host no two chunks will be stored on the same\n host. It is used to create a CRUSH rule step such as step chooseleaf host.",crushDeviceClass:"Restrict placement to devices of a specific class\n (e.g., ssd or hdd), using the crush device class names in the CRUSH map.",directory:"Set the directory name from which the erasure code plugin is loaded."}}list(){return this.http.get(this.apiPath)}create(_){return this.http.post(this.apiPath,_,{observe:"response"})}delete(_){return this.http.delete(`${this.apiPath}/${_}`,{observe:"response"})}getInfo(){return this.http.get(`ui-${this.apiPath}/info`)}}return t.\u0275fac=function(_){return new(_||t)(e.LFG(Z_.eN))},t.\u0275prov=e.Yz7({token:t,factory:t.\u0275fac,providedIn:"root"}),t})();function x_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,47),e.qZA())}function y_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,48),e.qZA())}function U_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,49),e.qZA())}function q_(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,50),e.qZA())}function H_(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}function G_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,52),e.qZA())}function z_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,53),e.qZA())}function X_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,54),e.qZA())}function w_(t,i){if(1&t&&(e.TgZ(0,"span",46),e.SDv(1,55),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.pQV(_.deviceCount),e.QtT(1)}}function Q_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,56),e.qZA())}function J_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,57),e.qZA())}function k_(t,i){if(1&t&&(e.TgZ(0,"span",39),e.SDv(1,58),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.pQV(_.lrcMultiK),e.QtT(1)}}function V_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,59),e.qZA())}function Y_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,60),e.qZA())}function B_(t,i){if(1&t&&(e.TgZ(0,"span",46),e.SDv(1,61),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.pQV(_.deviceCount),e.QtT(1)}}function j_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,65),e.qZA())}function K_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,66),e.qZA())}function W_(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",62),e.TgZ(2,"span",14),e.SDv(3,63),e.qZA(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e._UZ(6,"input",64),e.YNc(7,j_,2,0,"span",12),e.YNc(8,K_,2,0,"span",12),e.qZA(),e.qZA()),2&t){const _=e.oxw(),o=e.MAs(7);e.xp6(4),e.Q6J("html",_.tooltips.plugins.shec.c),e.xp6(3),e.Q6J("ngIf",_.form.showError("c",o,"min")),e.xp6(1),e.Q6J("ngIf",_.form.showError("c",o,"cGreaterM"))}}function eo(t,i){1&t&&(e.TgZ(0,"span",39),e.SDv(1,75),e.qZA())}function _o(t,i){if(1&t&&(e.TgZ(0,"span",39),e.SDv(1,76),e.qZA()),2&t){const _=e.oxw(3);e.xp6(1),e.pQV(_.getDMin())(_.getDMax()),e.QtT(1)}}function oo(t,i){if(1&t&&(e.TgZ(0,"span",39),e.SDv(1,77),e.qZA()),2&t){const _=e.oxw(3);e.xp6(1),e.pQV(_.getDMax()),e.QtT(1)}}function to(t,i){if(1&t&&(e.ynx(0),e.YNc(1,_o,2,2,"span",23),e.YNc(2,oo,2,1,"span",23),e.BQk()),2&t){const _=e.oxw(2);e.xp6(1),e.Q6J("ngIf",_.getDMin()<_.getDMax()),e.xp6(1),e.Q6J("ngIf",_.getDMin()===_.getDMax())}}function io(t,i){if(1&t&&(e.TgZ(0,"span",46),e.SDv(1,78),e.qZA()),2&t){const _=e.oxw(2);e.xp6(1),e.pQV(_.getDMin()),e.QtT(1)}}function no(t,i){if(1&t&&(e.TgZ(0,"span",46),e.SDv(1,79),e.qZA()),2&t){const _=e.oxw(2);e.xp6(1),e.pQV(_.getDMax()),e.QtT(1)}}function so(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div",7),e.TgZ(1,"label",67),e.TgZ(2,"span",14),e.SDv(3,68),e.qZA(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e.TgZ(6,"div",69),e._UZ(7,"input",70),e.TgZ(8,"span",71),e.TgZ(9,"button",72),e.NdJ("click",function(){return e.CHM(_),e.oxw().toggleDCalc()}),e._UZ(10,"i",73),e.qZA(),e.qZA(),e.qZA(),e.YNc(11,eo,2,0,"span",23),e.YNc(12,to,3,2,"ng-container",74),e.YNc(13,io,2,1,"span",12),e.YNc(14,no,2,1,"span",12),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(),o=e.MAs(7);e.xp6(4),e.Q6J("html",_.tooltips.plugins.clay.d),e.xp6(6),e.Q6J("ngClass",_.dCalc?_.icons.unlock:_.icons.lock),e.xp6(1),e.Q6J("ngIf",_.dCalc),e.xp6(1),e.Q6J("ngIf",!_.dCalc),e.xp6(1),e.Q6J("ngIf",_.form.showError("d",o,"dMin")),e.xp6(1),e.Q6J("ngIf",_.form.showError("d",o,"dMax"))}}function ao(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,84),e.qZA())}function lo(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,85),e.qZA())}function ro(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,86),e.qZA())}function co(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",80),e.TgZ(2,"span",14),e.SDv(3,81),e.qZA(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e._UZ(6,"input",82),e.YNc(7,ao,2,0,"span",12),e.YNc(8,lo,2,0,"span",12),e.YNc(9,ro,2,0,"span",12),e.TgZ(10,"span",39),e.SDv(11,83),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(),o=e.MAs(7);e.xp6(4),e.Q6J("html",_.tooltips.plugins.lrc.l),e.xp6(3),e.Q6J("ngIf",_.form.showError("l",o,"required")),e.xp6(1),e.Q6J("ngIf",_.form.showError("l",o,"min")),e.xp6(1),e.Q6J("ngIf",_.form.showError("l",o,"unequal")),e.xp6(2),e.pQV(_.lrcGroups),e.QtT(11)}}function Oo(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,87),e.qZA())}function uo(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit,o=e.oxw();e.Q6J("ngValue",_),e.xp6(1),e.AsE(" ",_," ( ",o.failureDomains[_].length," ) ")}}function fo(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,91),e.qZA())}function Po(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,92),e.qZA())}function Eo(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit,o=e.oxw(2);e.Q6J("ngValue",_),e.xp6(1),e.AsE(" ",_," ( ",o.failureDomains[_].length," ) ")}}function go(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",88),e.ynx(2),e.SDv(3,89),e.BQk(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e.TgZ(6,"select",90),e.YNc(7,fo,2,0,"option",18),e.YNc(8,Po,2,0,"option",18),e.YNc(9,Eo,2,3,"option",19),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw();e.xp6(4),e.Q6J("html",_.tooltips.plugins.lrc.crushLocality),e.xp6(3),e.Q6J("ngIf",!_.failureDomains),e.xp6(1),e.Q6J("ngIf",_.failureDomainKeys.length>0),e.xp6(1),e.Q6J("ngForOf",_.failureDomainKeys)}}function po(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}const De=function(t,i,_){return[t,i,_]};function mo(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",93),e.ynx(2),e.SDv(3,94),e.BQk(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e.TgZ(6,"select",95),e.YNc(7,po,2,2,"option",19),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw();e.xp6(4),e.Q6J("html",_.tooltips.plugins.clay.scalar_mds),e.xp6(3),e.Q6J("ngForOf",e.kEZ(2,De,_.PLUGIN.JERASURE,_.PLUGIN.ISA,_.PLUGIN.SHEC))}}function Ro(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}function Co(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",96),e.ynx(2),e.SDv(3,97),e.BQk(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e.TgZ(6,"select",98),e.YNc(7,Ro,2,2,"option",19),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw();e.xp6(4),e.Q6J("html",_.tooltips.plugins[_.plugin].technique),e.xp6(3),e.Q6J("ngForOf",_.techniques)}}function Mo(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,102),e.qZA())}function ho(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",99),e.ynx(2),e.SDv(3,100),e.BQk(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e._UZ(6,"input",101),e.YNc(7,Mo,2,0,"span",12),e.qZA(),e.qZA()),2&t){const _=e.oxw(),o=e.MAs(7);e.xp6(4),e.Q6J("html",_.tooltips.plugins.jerasure.packetSize),e.xp6(3),e.Q6J("ngIf",_.form.showError("packetSize",o,"min"))}}function To(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,103),e.qZA())}function So(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_.name," ")}}function Lo(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}let Ao=(()=>{class t extends S{constructor(_,o,n,s,c){super(),this.formBuilder=_,this.activeModal=o,this.taskWrapper=n,this.ecpService=s,this.actionLabels=c,this.submitAction=new e.vpe,this.tooltips=this.ecpService.formTooltips,this.PLUGIN={LRC:"lrc",SHEC:"shec",CLAY:"clay",JERASURE:"jerasure",ISA:"isa"},this.plugin=this.PLUGIN.JERASURE,this.icons=b.P,this.action=this.actionLabels.CREATE,this.resource="EC Profile",this.createForm(),this.setJerasureDefaults()}createForm(){this.form=this.formBuilder.group({name:[null,[a.kI.required,a.kI.pattern("[A-Za-z0-9_-]+"),E.h.custom("uniqueName",_=>this.names&&-1!==this.names.indexOf(_))]],plugin:[this.PLUGIN.JERASURE,[a.kI.required]],k:[4,[a.kI.required,E.h.custom("max",()=>this.baseValueValidation(!0)),E.h.custom("unequal",_=>this.lrcDataValidation(_)),E.h.custom("kLowerM",_=>this.shecDataValidation(_))]],m:[2,[a.kI.required,E.h.custom("max",()=>this.baseValueValidation())]],crushFailureDomain:"",crushRoot:null,crushDeviceClass:"",directory:"",technique:"reed_sol_van",packetSize:[2048],l:[3,[a.kI.required,E.h.custom("unequal",_=>this.lrcLocalityValidation(_))]],crushLocality:"",c:[2,[a.kI.required,E.h.custom("cGreaterM",_=>this.shecDurabilityValidation(_))]],d:[5,[a.kI.required,E.h.custom("dMin",_=>this.dMinValidation(_)),E.h.custom("dMax",_=>this.dMaxValidation(_))]],scalar_mds:[this.PLUGIN.JERASURE,[a.kI.required]]}),this.toggleDCalc(),this.form.get("k").valueChanges.subscribe(()=>this.updateValidityOnChange(["m","l","d"])),this.form.get("m").valueChanges.subscribe(()=>this.updateValidityOnChange(["k","l","c","d"])),this.form.get("l").valueChanges.subscribe(()=>this.updateValidityOnChange(["k","m"])),this.form.get("plugin").valueChanges.subscribe(_=>this.onPluginChange(_)),this.form.get("scalar_mds").valueChanges.subscribe(()=>this.setClayDefaultsForScalar())}baseValueValidation(_=!1){return this.validValidation(()=>this.getKMSum()>this.deviceCount&&this.form.getValue("k")>this.form.getValue("m")===_)}validValidation(_,o){return!((!this.form||o)&&this.plugin!==o)&&_()}getKMSum(){return this.form.getValue("k")+this.form.getValue("m")}lrcDataValidation(_){return this.validValidation(()=>{const o=this.form.getValue("m"),n=this.form.getValue("l"),s=_+o;return this.lrcMultiK=_/(s/n),_%(s/n)!=0},"lrc")}shecDataValidation(_){return this.validValidation(()=>this.form.getValue("m")>_,"shec")}lrcLocalityValidation(_){return this.validValidation(()=>{const o=this.getKMSum();return this.lrcGroups=_>0?o/_:0,_>0&&o%_!=0},"lrc")}shecDurabilityValidation(_){return this.validValidation(()=>{const o=this.form.getValue("m");return _>o},"shec")}dMinValidation(_){return this.validValidation(()=>this.getDMin()>_,"clay")}getDMin(){return this.form.getValue("k")+1}dMaxValidation(_){return this.validValidation(()=>_>this.getDMax(),"clay")}getDMax(){const _=this.form.getValue("m");return this.form.getValue("k")+_-1}toggleDCalc(){this.dCalc=!this.dCalc,this.form.get("d")[this.dCalc?"disable":"enable"](),this.calculateD()}calculateD(){this.plugin!==this.PLUGIN.CLAY||!this.dCalc||this.form.silentSet("d",this.getDMax())}updateValidityOnChange(_){_.forEach(o=>{"d"===o&&this.calculateD(),this.form.get(o).updateValueAndValidity({emitEvent:!1})})}onPluginChange(_){this.plugin=_,_===this.PLUGIN.JERASURE?this.setJerasureDefaults():_===this.PLUGIN.LRC?this.setLrcDefaults():_===this.PLUGIN.ISA?this.setIsaDefaults():_===this.PLUGIN.SHEC?this.setShecDefaults():_===this.PLUGIN.CLAY&&this.setClayDefaults(),this.updateValidityOnChange(["m"])}setJerasureDefaults(){this.techniques=["reed_sol_van","reed_sol_r6_op","cauchy_orig","cauchy_good","liberation","blaum_roth","liber8tion"],this.setDefaults({k:4,m:2,technique:"reed_sol_van"})}setLrcDefaults(){this.setDefaults({k:4,m:2,l:3})}setIsaDefaults(){this.techniques=["reed_sol_van","cauchy"],this.setDefaults({k:7,m:3,technique:"reed_sol_van"})}setShecDefaults(){this.setDefaults({k:4,m:3,c:2})}setClayDefaults(){this.setDefaults({k:4,m:2,scalar_mds:this.PLUGIN.JERASURE}),this.setClayDefaultsForScalar()}setClayDefaultsForScalar(){const _=this.form.getValue("scalar_mds");let o="reed_sol_van";_===this.PLUGIN.JERASURE?this.techniques=["reed_sol_van","reed_sol_r6_op","cauchy_orig","cauchy_good","liber8tion"]:_===this.PLUGIN.ISA?this.techniques=["reed_sol_van","cauchy"]:(o="single",this.techniques=["single","multiple"]),this.setDefaults({technique:o})}setDefaults(_){Object.keys(_).forEach(o=>{const n=this.form.get(o),s=n.value;n.pristine||"technique"===o&&!this.techniques.includes(s)||"k"===o&&[4,7].includes(s)||"m"===o&&[2,3].includes(s)?n.setValue(_[o]):n.updateValueAndValidity()})}ngOnInit(){this.ecpService.getInfo().subscribe(({plugins:_,names:o,directory:n,nodes:s})=>{this.initCrushNodeSelection(s,this.form.get("crushRoot"),this.form.get("crushFailureDomain"),this.form.get("crushDeviceClass")),this.plugins=_,this.names=o,this.form.silentSet("directory",n),this.preValidateNumericInputFields()})}preValidateNumericInputFields(){const _=["k","m","l","c","d"].map(o=>this.form.get(o));_.forEach(o=>{o.markAsTouched(),o.markAsDirty()}),_[1].updateValueAndValidity()}onSubmit(){if(this.form.invalid)return void this.form.setErrors({cdSubmitButton:!0});const _=this.createJson();this.taskWrapper.wrapTaskAroundCall({task:new v.R("ecp/create",{name:_.name}),call:this.ecpService.create(_)}).subscribe({error:()=>{this.form.setErrors({cdSubmitButton:!0})},complete:()=>{this.activeModal.close(),this.submitAction.emit(_)}})}createJson(){const _={technique:[this.PLUGIN.ISA,this.PLUGIN.JERASURE,this.PLUGIN.CLAY],packetSize:[this.PLUGIN.JERASURE],l:[this.PLUGIN.LRC],crushLocality:[this.PLUGIN.LRC],c:[this.PLUGIN.SHEC],d:[this.PLUGIN.CLAY],scalar_mds:[this.PLUGIN.CLAY]},o=new D_,n=this.form.getValue("plugin");return Object.keys(this.form.controls).filter(s=>{const c=_[s],d=this.form.getValue(s);return(c&&c.includes(n)||!c)&&d&&""!==d}).forEach(s=>{this.extendJson(s,o)}),o}extendJson(_,o){const s=this.form.getValue(_);o[{crushFailureDomain:"crush-failure-domain",crushRoot:"crush-root",crushDeviceClass:"crush-device-class",packetSize:"packetsize",crushLocality:"crush-locality"}[_]||_]="crushRoot"===_?s.name:s}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ve.O),e.Y36(g.Kz),e.Y36(de.P),e.Y36(Me),e.Y36(M.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-erasure-code-profile-form-modal"]],outputs:{submitAction:"submitAction"},features:[e.qOj],decls:98,vars:53,consts:function(){let i,_,o,n,s,c,d,P,p,m,h,T,R,f,A,I,$,D,Z,x,y,U,q,H,G,z,X,w,Q,J,k,V,Y,B,j,K,N,W,ee,_e,oe,te,ie,ne,se,ae,le,re,ce;return i="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Name",o="Plugin",n="Data chunks (k)",s="Coding chunks (m)",c="Crush failure domain",d="Crush root",P="Crush device class",p="Let Ceph decide",m="Available OSDs: " + "\ufffd0\ufffd" + "",h="Directory",T="This field is required!",R="The name can only consist of alphanumeric characters, dashes and underscores.",f="The chosen erasure code profile name is already in use.",A="Loading...",I="This field is required!",$="This field is required!",D="Must be equal to or greater than 2.",Z="Chunks (k+m) have exceeded the available OSDs of " + "\ufffd0\ufffd" + ".",x="For an equal distribution k has to be a multiple of (k+m)/l.",y="K has to be equal to or greater than m in order to recover data correctly through c.",U="Distribution factor: " + "\ufffd0\ufffd" + "",q="This field is required!",H="Must be equal to or greater than 1.",G="Chunks (k+m) have exceeded the available OSDs of " + "\ufffd0\ufffd" + ".",z="Durability estimator (c)",X="Must be equal to or greater than 1.",w="C has to be equal to or lower than m as m defines the amount of chunks that can be used.",Q="Helper chunks (d)",J="Set d manually or use the plugin's default calculation that maximizes d.",k="D is automatically updated on k and m changes",V="D can be set from " + "\ufffd0\ufffd" + " to " + "\ufffd1\ufffd" + "",Y="D can only be set to " + "\ufffd0\ufffd" + "",B="D has to be greater than k (" + "\ufffd0\ufffd" + ").",j="D has to be lower than k + m (" + "\ufffd0\ufffd" + ").",K="Locality (l)",N="Locality groups: " + "\ufffd0\ufffd" + "",W="This field is required!",ee="Must be equal to or greater than 1.",_e="Can't split up chunks (k+m) correctly with the current locality.",oe="Loading...",te="Crush Locality",ie="Loading...",ne="None",se="Scalar mds",ae="Technique",le="Packetsize",re="Must be equal to or greater than 1.",ce="Loading...",[[3,"modalRef"],[1,"modal-title"],i,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","name",1,"cd-col-form-label"],_,[1,"cd-col-form-input"],["type","text","id","name","name","name","placeholder","Name...","formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","plugin",1,"cd-col-form-label"],[1,"required"],o,[3,"html"],["id","plugin","name","plugin","formControlName","plugin",1,"form-control","custom-select"],["ngValue","",4,"ngIf"],[3,"ngValue",4,"ngFor","ngForOf"],["for","k",1,"cd-col-form-label"],n,["type","number","id","k","name","k","ng-model","$ctrl.erasureCodeProfile.k","placeholder","Data chunks...","formControlName","k","min","2",1,"form-control"],["class","form-text text-muted",4,"ngIf"],["for","m",1,"cd-col-form-label"],s,["type","number","id","m","name","m","placeholder","Coding chunks...","formControlName","m","min","1",1,"form-control"],["class","form-group row",4,"ngIf"],["for","crushFailureDomain",1,"cd-col-form-label"],c,["id","crushFailureDomain","name","crushFailureDomain","formControlName","crushFailureDomain",1,"form-control","custom-select"],["for","crushRoot",1,"cd-col-form-label"],d,["id","crushRoot","name","crushRoot","formControlName","crushRoot",1,"form-control","custom-select"],["for","crushDeviceClass",1,"cd-col-form-label"],P,["id","crushDeviceClass","name","crushDeviceClass","formControlName","crushDeviceClass",1,"form-control","custom-select"],["ngValue",""],p,[1,"form-text","text-muted"],m,["for","directory",1,"cd-col-form-label"],h,["type","text","id","directory","name","directory","placeholder","Path...","formControlName","directory",1,"form-control"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],T,R,f,A,[3,"ngValue"],I,$,D,Z,x,y,U,q,H,G,["for","c",1,"cd-col-form-label"],z,["type","number","id","c","name","c","placeholder","Coding chunks...","formControlName","c","min","1",1,"form-control"],X,w,["for","d",1,"cd-col-form-label"],Q,[1,"input-group"],["type","number","id","d","name","d","placeholder","Helper chunks...","formControlName","d",1,"form-control"],[1,"input-group-append"],["id","d-calc-btn","ngbTooltip",J,"type","button",1,"btn","btn-light",3,"click"],["aria-hidden","true",3,"ngClass"],[4,"ngIf"],k,V,Y,B,j,["for","l",1,"cd-col-form-label"],K,["type","number","id","l","name","l","placeholder","Coding chunks...","formControlName","l","min","1",1,"form-control"],N,W,ee,_e,oe,["for","crushLocality",1,"cd-col-form-label"],te,["id","crushLocality","name","crushLocality","formControlName","crushLocality",1,"form-control","custom-select"],ie,ne,["for","scalar_mds",1,"cd-col-form-label"],se,["id","scalar_mds","name","scalar_mds","formControlName","scalar_mds",1,"form-control","custom-select"],["for","technique",1,"cd-col-form-label"],ae,["id","technique","name","technique","formControlName","technique",1,"form-control","custom-select"],["for","packetSize",1,"cd-col-form-label"],le,["type","number","id","packetSize","name","packetSize","placeholder","Packetsize...","formControlName","packetSize","min","1",1,"form-control"],re,ce]},template:function(_,o){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e._UZ(13,"input",11),e.YNc(14,x_,2,0,"span",12),e.YNc(15,y_,2,0,"span",12),e.YNc(16,U_,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(17,"div",7),e.TgZ(18,"label",13),e.TgZ(19,"span",14),e.SDv(20,15),e.qZA(),e._UZ(21,"cd-helper",16),e.qZA(),e.TgZ(22,"div",10),e.TgZ(23,"select",17),e.YNc(24,q_,2,0,"option",18),e.YNc(25,H_,2,2,"option",19),e.qZA(),e.YNc(26,G_,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(27,"div",7),e.TgZ(28,"label",20),e.TgZ(29,"span",14),e.SDv(30,21),e.qZA(),e._UZ(31,"cd-helper",16),e.qZA(),e.TgZ(32,"div",10),e._UZ(33,"input",22),e.YNc(34,z_,2,0,"span",12),e.YNc(35,X_,2,0,"span",12),e.YNc(36,w_,2,1,"span",12),e.YNc(37,Q_,2,0,"span",12),e.YNc(38,J_,2,0,"span",12),e.YNc(39,k_,2,1,"span",23),e.qZA(),e.qZA(),e.TgZ(40,"div",7),e.TgZ(41,"label",24),e.TgZ(42,"span",14),e.SDv(43,25),e.qZA(),e._UZ(44,"cd-helper",16),e.qZA(),e.TgZ(45,"div",10),e._UZ(46,"input",26),e.YNc(47,V_,2,0,"span",12),e.YNc(48,Y_,2,0,"span",12),e.YNc(49,B_,2,1,"span",12),e.qZA(),e.qZA(),e.YNc(50,W_,9,3,"div",27),e.YNc(51,so,15,6,"div",27),e.YNc(52,co,12,5,"div",27),e.TgZ(53,"div",7),e.TgZ(54,"label",28),e.ynx(55),e.SDv(56,29),e.BQk(),e._UZ(57,"cd-helper",16),e.qZA(),e.TgZ(58,"div",10),e.TgZ(59,"select",30),e.YNc(60,Oo,2,0,"option",18),e.YNc(61,uo,2,3,"option",19),e.qZA(),e.qZA(),e.qZA(),e.YNc(62,go,10,4,"div",27),e.YNc(63,mo,8,6,"div",27),e.YNc(64,Co,8,2,"div",27),e.YNc(65,ho,8,2,"div",27),e.TgZ(66,"div",7),e.TgZ(67,"label",31),e.ynx(68),e.SDv(69,32),e.BQk(),e._UZ(70,"cd-helper",16),e.qZA(),e.TgZ(71,"div",10),e.TgZ(72,"select",33),e.YNc(73,To,2,0,"option",18),e.YNc(74,So,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.TgZ(75,"div",7),e.TgZ(76,"label",34),e.ynx(77),e.SDv(78,35),e.BQk(),e._UZ(79,"cd-helper",16),e.qZA(),e.TgZ(80,"div",10),e.TgZ(81,"select",36),e.TgZ(82,"option",37),e.SDv(83,38),e.qZA(),e.YNc(84,Lo,2,2,"option",19),e.qZA(),e.TgZ(85,"span",39),e.SDv(86,40),e.qZA(),e.qZA(),e.qZA(),e.TgZ(87,"div",7),e.TgZ(88,"label",41),e.ynx(89),e.SDv(90,42),e.BQk(),e._UZ(91,"cd-helper",16),e.qZA(),e.TgZ(92,"div",10),e._UZ(93,"input",43),e.qZA(),e.qZA(),e.qZA(),e.TgZ(94,"div",44),e.TgZ(95,"cd-form-button-panel",45),e.NdJ("submitActionEvent",function(){return o.onSubmit()}),e.ALo(96,"titlecase"),e.ALo(97,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const n=e.MAs(7);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.pQV(e.lcZ(3,41,o.action))(e.lcZ(4,43,o.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",o.form),e.xp6(8),e.Q6J("ngIf",o.form.showError("name",n,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"pattern")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"uniqueName")),e.xp6(5),e.Q6J("html",o.tooltips.plugins[o.plugin].description),e.xp6(3),e.Q6J("ngIf",!o.plugins),e.xp6(1),e.Q6J("ngForOf",o.plugins),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"required")),e.xp6(5),e.Q6J("html",o.tooltips.k),e.xp6(3),e.Q6J("ngIf",o.form.showError("k",n,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("k",n,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("k",n,"max")),e.xp6(1),e.Q6J("ngIf",o.form.showError("k",n,"unequal")),e.xp6(1),e.Q6J("ngIf",o.form.showError("k",n,"kLowerM")),e.xp6(1),e.Q6J("ngIf","lrc"===o.plugin),e.xp6(5),e.Q6J("html",o.tooltips.m),e.xp6(3),e.Q6J("ngIf",o.form.showError("m",n,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("m",n,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("m",n,"max")),e.xp6(1),e.Q6J("ngIf","shec"===o.plugin),e.xp6(1),e.Q6J("ngIf","clay"===o.plugin),e.xp6(1),e.Q6J("ngIf",o.plugin===o.PLUGIN.LRC),e.xp6(5),e.Q6J("html",o.tooltips.crushFailureDomain),e.xp6(3),e.Q6J("ngIf",!o.failureDomains),e.xp6(1),e.Q6J("ngForOf",o.failureDomainKeys),e.xp6(1),e.Q6J("ngIf",o.plugin===o.PLUGIN.LRC),e.xp6(1),e.Q6J("ngIf",o.PLUGIN.CLAY===o.plugin),e.xp6(1),e.Q6J("ngIf",e.kEZ(49,De,o.PLUGIN.JERASURE,o.PLUGIN.ISA,o.PLUGIN.CLAY).includes(o.plugin)),e.xp6(1),e.Q6J("ngIf",o.plugin===o.PLUGIN.JERASURE),e.xp6(5),e.Q6J("html",o.tooltips.crushRoot),e.xp6(3),e.Q6J("ngIf",!o.buckets),e.xp6(1),e.Q6J("ngForOf",o.buckets),e.xp6(5),e.Q6J("html",o.tooltips.crushDeviceClass),e.xp6(5),e.Q6J("ngForOf",o.devices),e.xp6(2),e.pQV(o.deviceCount),e.QtT(86),e.xp6(5),e.Q6J("html",o.tooltips.directory),e.xp6(4),e.Q6J("form",o.form)("submitText",e.lcZ(96,45,o.action)+" "+e.lcZ(97,47,o.resource))}},directives:[$e.z,a._Y,a.JL,fe.V,a.sg,Pe.P,Ee.o,a.Fj,ge.b,a.JJ,a.u,pe.U,C.O5,me.S,a.EJ,C.sg,a.wV,a.qQ,a.YN,a.Kr,Re.p,g._L,C.mk],pipes:[C.rS,Ce.m],styles:[""]}),t})();var Fo=r(7022);class No{constructor(){this.erasureInfo=!1,this.crushInfo=!1,this.pgs=1,this.poolTypes=["erasure","replicated"],this.applications={selected:[],default:["cephfs","rbd","rgw"],available:[],validators:[a.kI.pattern("[A-Za-z0-9_]+"),a.kI.maxLength(128)],messages:new Fo.a({empty:"No applications added",selectionLimit:{text:"Applications limit reached",tooltip:"A pool can only have up to four applications definitions."},customValidations:{pattern:"Allowed characters '_a-zA-Z0-9'",maxlength:"Maximum length is 128 characters"},filter:"Filter or add applications'",add:"Add application"})}}}var Ze=r(63285),he=r(74937),bo=r(63622),vo=r(60192),Io=r(17932),$o=r(54555),Do=r(30490),xe=r(61350);const Zo=["crushInfoTabs"],xo=["crushDeletionBtn"],yo=["ecpInfoTabs"],Uo=["ecpDeletionBtn"];function qo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,42),e.qZA())}function Ho(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,43),e.qZA())}function Go(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,44),e.qZA())}function zo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,45),e.qZA())}function Xo(t,i){if(1&t&&(e.TgZ(0,"option",46),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function wo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,47),e.qZA())}function Qo(t,i){if(1&t&&(e.TgZ(0,"option",46),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Jo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,58),e.qZA())}function ko(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,59),e.qZA())}function Vo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,60),e.qZA())}function Yo(t,i){1&t&&(e.TgZ(0,"span",55),e.SDv(1,61),e.qZA())}function Bo(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div",8),e.TgZ(1,"label",52),e.SDv(2,53),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"input",54),e.NdJ("focus",function(){return e.CHM(_),e.oxw(3).externalPgChange=!1})("blur",function(){return e.CHM(_),e.oxw(3).alignPgs()}),e.qZA(),e.YNc(5,Jo,2,0,"span",13),e.YNc(6,ko,2,0,"span",13),e.YNc(7,Vo,2,0,"span",13),e.TgZ(8,"span",55),e._UZ(9,"cd-doc",56),e.qZA(),e.YNc(10,Yo,2,0,"span",57),e.qZA(),e.qZA()}if(2&t){e.oxw(2);const _=e.MAs(2),o=e.oxw();e.xp6(5),e.Q6J("ngIf",o.form.showError("pgNum",_,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("pgNum",_,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("pgNum",_,"34")),e.xp6(3),e.Q6J("ngIf",o.externalPgChange)}}function jo(t,i){if(1&t&&(e.TgZ(0,"span",41),e.TgZ(1,"ul",66),e.TgZ(2,"li"),e.SDv(3,67),e.qZA(),e.TgZ(4,"li"),e.SDv(5,68),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(3),e.pQV(_.getMinSize()),e.QtT(3),e.xp6(2),e.pQV(_.getMaxSize()),e.QtT(5)}}function Ko(t,i){if(1&t&&(e.TgZ(0,"span",41),e.SDv(1,69),e.qZA()),2&t){const _=e.oxw(4);e.xp6(1),e.pQV(_.getMinSize())(_.getMaxSize()),e.QtT(1)}}function Wo(t,i){1&t&&(e.TgZ(0,"span",70),e.SDv(1,71),e.qZA())}function et(t,i){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",62),e.SDv(2,63),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",64),e.YNc(5,jo,6,2,"span",13),e.YNc(6,Ko,2,2,"span",13),e.YNc(7,Wo,2,0,"span",65),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),o=e.oxw();e.xp6(4),e.Q6J("max",o.getMaxSize())("min",o.getMinSize()),e.xp6(1),e.Q6J("ngIf",o.form.showError("size",_)),e.xp6(1),e.Q6J("ngIf",o.form.showError("size",_)),e.xp6(1),e.Q6J("ngIf",1===o.form.getValue("size"))}}function _t(t,i){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",72),e.SDv(2,73),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"div",74),e._UZ(5,"input",75),e.TgZ(6,"label",76),e.SDv(7,77),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function ot(t,i){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"div",8),e.TgZ(2,"label",48),e.SDv(3,49),e.qZA(),e.TgZ(4,"div",11),e.TgZ(5,"select",50),e.YNc(6,Qo,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.YNc(7,Bo,11,4,"div",51),e.YNc(8,et,8,5,"div",51),e.YNc(9,_t,8,0,"div",51),e.qZA()),2&t){const _=e.oxw(2);e.xp6(6),e.Q6J("ngForOf",_.pgAutoscaleModes),e.xp6(1),e.Q6J("ngIf","on"!==_.form.getValue("pgAutoscaleMode")),e.xp6(1),e.Q6J("ngIf",_.isReplicated),e.xp6(1),e.Q6J("ngIf",_.info.is_all_bluestore&&_.isErasure)}}function tt(t,i){if(1&t&&e._UZ(0,"i",78),2&t){const _=e.oxw(2);e.Gre("",_.icons.warning," icon-warning-color")}}function it(t,i){1&t&&(e.TgZ(0,"option",17),e.SDv(1,93),e.qZA())}function nt(t,i){1&t&&(e.TgZ(0,"option",94),e.SDv(1,95),e.qZA()),2&t&&e.Q6J("ngValue",null)}function st(t,i){1&t&&(e.TgZ(0,"option",94),e.SDv(1,96),e.qZA()),2&t&&e.Q6J("ngValue",null)}function at(t,i){if(1&t&&(e.TgZ(0,"option",94),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_.name," ")}}const F=function(t){return[t]};function lt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"button",97),e.NdJ("click",function(){return e.CHM(_),e.oxw(4).addErasureCodeProfile()}),e._UZ(1,"i",89),e.qZA()}if(2&t){const _=e.oxw(4);e.xp6(1),e.Q6J("ngClass",e.VKq(1,F,_.icons.add))}}function rt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"button",98,99),e.NdJ("click",function(){return e.CHM(_),e.oxw(4).deleteErasureCodeProfile()}),e._UZ(2,"i",89),e.qZA()}if(2&t){const _=e.oxw(4);e.xp6(2),e.Q6J("ngClass",e.VKq(1,F,_.icons.trash))}}const ct=function(){return["name"]};function Ot(t,i){if(1&t&&e._UZ(0,"cd-table-key-value",110),2&t){const _=e.oxw(5);e.Q6J("renderObjects",!0)("hideKeys",e.DdM(4,ct))("data",_.form.getValue("erasureProfile"))("autoReload",!1)}}function dt(t,i){1&t&&(e.TgZ(0,"span"),e.SDv(1,113),e.qZA())}function ut(t,i){if(1&t&&(e.TgZ(0,"li"),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.xp6(1),e.hij(" ",_," ")}}function ft(t,i){if(1&t&&(e.TgZ(0,"ul"),e.YNc(1,ut,2,1,"li",114),e.qZA()),2&t){const _=e.oxw(6);e.xp6(1),e.Q6J("ngForOf",_.ecpUsage)}}function Pt(t,i){if(1&t&&(e.YNc(0,dt,2,0,"ng-template",null,111,e.W1O),e.YNc(2,ft,2,1,"ul",112)),2&t){const _=e.MAs(1),o=e.oxw(5);e.xp6(2),e.Q6J("ngIf",o.ecpUsage)("ngIfElse",_)}}function Et(t,i){if(1&t&&(e.TgZ(0,"span",100),e.TgZ(1,"ul",101,102),e.TgZ(3,"li",103),e.TgZ(4,"a",104),e.SDv(5,105),e.qZA(),e.YNc(6,Ot,1,5,"ng-template",106),e.qZA(),e.TgZ(7,"li",107),e.TgZ(8,"a",104),e.SDv(9,108),e.qZA(),e.YNc(10,Pt,3,2,"ng-template",106),e.qZA(),e.qZA(),e._UZ(11,"div",109),e.qZA()),2&t){const _=e.MAs(2);e.xp6(11),e.Q6J("ngbNavOutlet",_)}}const ye=function(t){return{active:t}};function gt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div",8),e.TgZ(1,"label",80),e.SDv(2,81),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"div",82),e.TgZ(5,"select",83),e.YNc(6,it,2,0,"option",84),e.YNc(7,nt,2,1,"option",85),e.YNc(8,st,2,1,"option",85),e.YNc(9,at,2,2,"option",86),e.qZA(),e.TgZ(10,"span",87),e.TgZ(11,"button",88),e.NdJ("click",function(){e.CHM(_);const n=e.oxw(3);return n.data.erasureInfo=!n.data.erasureInfo}),e._UZ(12,"i",89),e.qZA(),e.YNc(13,lt,2,3,"button",90),e.YNc(14,rt,3,3,"button",91),e.qZA(),e.qZA(),e.YNc(15,Et,12,1,"span",92),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(3);e.xp6(6),e.Q6J("ngIf",!_.ecProfiles),e.xp6(1),e.Q6J("ngIf",_.ecProfiles&&0===_.ecProfiles.length),e.xp6(1),e.Q6J("ngIf",_.ecProfiles&&_.ecProfiles.length>0),e.xp6(1),e.Q6J("ngForOf",_.ecProfiles),e.xp6(2),e.Q6J("ngClass",e.VKq(9,ye,_.data.erasureInfo)),e.xp6(1),e.Q6J("ngClass",e.VKq(11,F,_.icons.questionCircle)),e.xp6(1),e.Q6J("ngIf",!_.editing),e.xp6(1),e.Q6J("ngIf",!_.editing),e.xp6(1),e.Q6J("ngIf",_.data.erasureInfo&&_.form.getValue("erasureProfile"))}}function pt(t,i){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",115),e.SDv(2,116),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"span",55),e.SDv(5,117),e.qZA(),e.qZA(),e.qZA())}function mt(t,i){1&t&&(e.TgZ(0,"span",55),e.TgZ(1,"span"),e.SDv(2,120),e.qZA(),e._uU(3,"\xa0 "),e.qZA())}function Rt(t,i){if(1&t&&(e.TgZ(0,"option",94),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_.rule_name," ")}}function Ct(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"button",97),e.NdJ("click",function(){return e.CHM(_),e.oxw(5).addCrushRule()}),e._UZ(1,"i",89),e.qZA()}if(2&t){const _=e.oxw(5);e.xp6(1),e.Q6J("ngClass",e.VKq(1,F,_.icons.add))}}function Mt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"button",126,127),e.NdJ("click",function(){return e.CHM(_),e.oxw(5).deleteCrushRule()}),e._UZ(2,"i",89),e.qZA()}if(2&t){const _=e.oxw(5);e.xp6(2),e.Q6J("ngClass",e.VKq(1,F,_.icons.trash))}}const ht=function(){return["steps","type","rule_name"]};function Tt(t,i){if(1&t&&e._UZ(0,"cd-table-key-value",110),2&t){const _=e.oxw(6);e.Q6J("renderObjects",!1)("hideKeys",e.DdM(4,ht))("data",_.form.getValue("crushRule"))("autoReload",!1)}}function St(t,i){if(1&t&&(e.TgZ(0,"li"),e._uU(1),e.qZA()),2&t){const _=i.$implicit,o=e.oxw(7);e.xp6(1),e.hij(" ",o.describeCrushStep(_)," ")}}function Lt(t,i){if(1&t&&(e.TgZ(0,"ol"),e.YNc(1,St,2,1,"li",114),e.qZA()),2&t){const _=e.oxw(6);e.xp6(1),e.Q6J("ngForOf",_.form.get("crushRule").value.steps)}}function At(t,i){1&t&&(e.TgZ(0,"span"),e.SDv(1,136),e.qZA())}function Ft(t,i){if(1&t&&(e.TgZ(0,"li"),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.xp6(1),e.hij(" ",_," ")}}function Nt(t,i){if(1&t&&(e.TgZ(0,"ul"),e.YNc(1,Ft,2,1,"li",114),e.qZA()),2&t){const _=e.oxw(7);e.xp6(1),e.Q6J("ngForOf",_.crushUsage)}}function bt(t,i){if(1&t&&(e.YNc(0,At,2,0,"ng-template",null,135,e.W1O),e.YNc(2,Nt,2,1,"ul",112)),2&t){const _=e.MAs(1),o=e.oxw(6);e.xp6(2),e.Q6J("ngIf",o.crushUsage)("ngIfElse",_)}}function vt(t,i){if(1&t&&(e.TgZ(0,"div",128),e.TgZ(1,"ul",101,129),e.TgZ(3,"li",130),e.TgZ(4,"a",104),e.SDv(5,131),e.qZA(),e.YNc(6,Tt,1,5,"ng-template",106),e.qZA(),e.TgZ(7,"li",132),e.TgZ(8,"a",104),e.SDv(9,133),e.qZA(),e.YNc(10,Lt,2,1,"ng-template",106),e.qZA(),e.TgZ(11,"li",107),e.TgZ(12,"a",104),e.SDv(13,134),e.qZA(),e.YNc(14,bt,3,2,"ng-template",106),e.qZA(),e.qZA(),e._UZ(15,"div",109),e.qZA()),2&t){const _=e.MAs(2);e.xp6(15),e.Q6J("ngbNavOutlet",_)}}function It(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,137),e.qZA())}function $t(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,138),e.qZA())}function Dt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div"),e.TgZ(1,"div",82),e.TgZ(2,"select",121),e.TgZ(3,"option",94),e.SDv(4,122),e.qZA(),e.YNc(5,Rt,2,2,"option",86),e.qZA(),e.TgZ(6,"span",87),e.TgZ(7,"button",123),e.NdJ("click",function(){e.CHM(_);const n=e.oxw(4);return n.data.crushInfo=!n.data.crushInfo}),e._UZ(8,"i",89),e.qZA(),e.YNc(9,Ct,2,3,"button",90),e.YNc(10,Mt,3,3,"button",124),e.qZA(),e.qZA(),e.YNc(11,vt,16,1,"div",125),e.YNc(12,It,2,0,"span",13),e.YNc(13,$t,2,0,"span",13),e.qZA()}if(2&t){e.oxw(3);const _=e.MAs(2),o=e.oxw();e.xp6(3),e.Q6J("ngValue",null),e.xp6(2),e.Q6J("ngForOf",o.current.rules),e.xp6(2),e.Q6J("ngClass",e.VKq(9,ye,o.data.crushInfo)),e.xp6(1),e.Q6J("ngClass",e.VKq(11,F,o.icons.questionCircle)),e.xp6(1),e.Q6J("ngIf",o.isReplicated&&!o.editing),e.xp6(1),e.Q6J("ngIf",o.isReplicated&&!o.editing),e.xp6(1),e.Q6J("ngIf",o.data.crushInfo&&o.form.getValue("crushRule")),e.xp6(1),e.Q6J("ngIf",o.form.showError("crushRule",_,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("crushRule",_,"tooFewOsds"))}}function Zt(t,i){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",115),e.SDv(2,118),e.qZA(),e.TgZ(3,"div",11),e.YNc(4,mt,4,0,"ng-template",null,119,e.W1O),e.YNc(6,Dt,14,13,"div",112),e.qZA(),e.qZA()),2&t){const _=e.MAs(5),o=e.oxw(3);e.xp6(6),e.Q6J("ngIf",o.current.rules.length>0)("ngIfElse",_)}}function xt(t,i){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"legend"),e.SDv(2,79),e.qZA(),e.YNc(3,gt,16,13,"div",51),e.YNc(4,pt,6,0,"div",51),e.YNc(5,Zt,7,2,"div",51),e.qZA()),2&t){const _=e.oxw(2);e.xp6(3),e.Q6J("ngIf",_.isErasure),e.xp6(1),e.Q6J("ngIf",_.isErasure&&!_.editing),e.xp6(1),e.Q6J("ngIf",_.isReplicated||_.editing)}}function yt(t,i){if(1&t&&(e.TgZ(0,"option",46),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Ut(t,i){1&t&&(e.TgZ(0,"option",17),e.SDv(1,156),e.qZA())}function qt(t,i){1&t&&(e.TgZ(0,"option",17),e.SDv(1,157),e.qZA())}function Ht(t,i){if(1&t&&(e.TgZ(0,"option",46),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Gt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,158),e.qZA())}function zt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,159),e.qZA())}function Xt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,160),e.qZA())}function wt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,161),e.qZA())}function Qt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,162),e.qZA())}function Jt(t,i){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"div",8),e.TgZ(2,"label",144),e.SDv(3,145),e.qZA(),e.TgZ(4,"div",11),e.TgZ(5,"select",146),e.YNc(6,Ut,2,0,"option",84),e.YNc(7,qt,2,0,"option",84),e.YNc(8,Ht,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.TgZ(9,"div",8),e.TgZ(10,"label",147),e.SDv(11,148),e.qZA(),e.TgZ(12,"div",11),e._UZ(13,"input",149),e.YNc(14,Gt,2,0,"span",13),e.YNc(15,zt,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(16,"div",8),e.TgZ(17,"label",150),e.SDv(18,151),e.qZA(),e.TgZ(19,"div",11),e._UZ(20,"input",152),e.YNc(21,Xt,2,0,"span",13),e.YNc(22,wt,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(23,"div",8),e.TgZ(24,"label",153),e.SDv(25,154),e.qZA(),e.TgZ(26,"div",11),e._UZ(27,"input",155),e.YNc(28,Qt,2,0,"span",13),e.qZA(),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),o=e.oxw();e.xp6(6),e.Q6J("ngIf",!o.info.compression_algorithms),e.xp6(1),e.Q6J("ngIf",o.info.compression_algorithms&&0===o.info.compression_algorithms.length),e.xp6(1),e.Q6J("ngForOf",o.info.compression_algorithms),e.xp6(6),e.Q6J("ngIf",o.form.showError("minBlobSize",_,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("minBlobSize",_,"maximum")),e.xp6(6),e.Q6J("ngIf",o.form.showError("maxBlobSize",_,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("maxBlobSize",_,"minimum")),e.xp6(6),e.Q6J("ngIf",o.form.showError("ratio",_,"min")||o.form.showError("ratio",_,"max"))}}function kt(t,i){if(1&t&&(e.TgZ(0,"div",139),e.TgZ(1,"legend"),e.SDv(2,140),e.qZA(),e.TgZ(3,"div",8),e.TgZ(4,"label",141),e.SDv(5,142),e.qZA(),e.TgZ(6,"div",11),e.TgZ(7,"select",143),e.YNc(8,yt,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.YNc(9,Jt,29,8,"div",20),e.qZA()),2&t){const _=e.oxw(2);e.xp6(8),e.Q6J("ngForOf",_.info.compression_modes),e.xp6(1),e.Q6J("ngIf",_.hasCompressionEnabled())}}function Vt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,163),e.qZA())}function Yt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.TgZ(9,"div",8),e.TgZ(10,"label",9),e.SDv(11,10),e.qZA(),e.TgZ(12,"div",11),e._UZ(13,"input",12),e.YNc(14,qo,2,0,"span",13),e.YNc(15,Ho,2,0,"span",13),e.YNc(16,Go,2,0,"span",13),e.YNc(17,zo,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(18,"div",8),e.TgZ(19,"label",14),e.SDv(20,15),e.qZA(),e.TgZ(21,"div",11),e.TgZ(22,"select",16),e.TgZ(23,"option",17),e.SDv(24,18),e.qZA(),e.YNc(25,Xo,2,2,"option",19),e.qZA(),e.YNc(26,wo,2,0,"span",13),e.qZA(),e.qZA(),e.YNc(27,ot,10,4,"div",20),e.TgZ(28,"div",8),e.TgZ(29,"label",21),e.SDv(30,22),e.qZA(),e.TgZ(31,"div",11),e.TgZ(32,"cd-select-badges",23),e.NdJ("selection",function(){return e.CHM(_),e.oxw().appSelection()}),e.qZA(),e.YNc(33,tt,1,3,"i",24),e.qZA(),e.qZA(),e.YNc(34,xt,6,3,"div",20),e.YNc(35,kt,10,2,"div",25),e.TgZ(36,"div"),e.TgZ(37,"legend"),e.SDv(38,26),e.qZA(),e.TgZ(39,"div",8),e.TgZ(40,"label",27),e.ynx(41),e.SDv(42,28),e.BQk(),e.TgZ(43,"cd-helper"),e.TgZ(44,"span"),e.SDv(45,29),e.qZA(),e._UZ(46,"br"),e.TgZ(47,"span"),e.SDv(48,30),e.qZA(),e.qZA(),e.qZA(),e.TgZ(49,"div",11),e._UZ(50,"input",31),e.qZA(),e.qZA(),e.TgZ(51,"div",8),e.TgZ(52,"label",32),e.ynx(53),e.SDv(54,33),e.BQk(),e.TgZ(55,"cd-helper"),e.TgZ(56,"span"),e.SDv(57,34),e.qZA(),e._UZ(58,"br"),e.TgZ(59,"span"),e.SDv(60,35),e.qZA(),e.qZA(),e.qZA(),e.TgZ(61,"div",11),e._UZ(62,"input",36),e.YNc(63,Vt,2,0,"span",13),e.qZA(),e.qZA(),e.qZA(),e.TgZ(64,"div",37),e.TgZ(65,"cd-rbd-configuration-form",38),e.NdJ("changes",function(n){return e.CHM(_),e.oxw().currentConfigurationValues=n()}),e.qZA(),e.qZA(),e.qZA(),e.TgZ(66,"div",39),e.TgZ(67,"cd-form-button-panel",40),e.NdJ("submitActionEvent",function(){return e.CHM(_),e.oxw().submit()}),e.ALo(68,"titlecase"),e.ALo(69,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.MAs(2),o=e.oxw();e.xp6(1),e.Q6J("formGroup",o.form),e.xp6(6),e.pQV(e.lcZ(6,25,o.action))(e.lcZ(7,27,o.resource)),e.QtT(5),e.xp6(7),e.Q6J("ngIf",o.form.showError("name",_,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",_,"uniqueName")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",_,"rbdPool")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",_,"pattern")),e.xp6(8),e.Q6J("ngForOf",o.data.poolTypes),e.xp6(1),e.Q6J("ngIf",o.form.showError("poolType",_,"required")),e.xp6(1),e.Q6J("ngIf",o.isReplicated||o.isErasure),e.xp6(5),e.Q6J("customBadges",!0)("customBadgeValidators",o.data.applications.validators)("messages",o.data.applications.messages)("data",o.data.applications.selected)("options",o.data.applications.available)("selectionLimit",4),e.xp6(1),e.Q6J("ngIf",o.data.applications.selected<=0),e.xp6(1),e.Q6J("ngIf",o.isErasure||o.isReplicated),e.xp6(1),e.Q6J("ngIf",o.info.is_all_bluestore),e.xp6(28),e.Q6J("ngIf",o.form.showError("max_objects",_,"min")),e.xp6(1),e.Q6J("hidden",o.isErasure||-1===o.data.applications.selected.indexOf("rbd")),e.xp6(1),e.Q6J("form",o.form)("initializeData",o.initializeConfigData),e.xp6(2),e.Q6J("form",o.form)("submitText",e.lcZ(68,29,o.action)+" "+e.lcZ(69,31,o.resource))}}let Ue=(()=>{class t extends m_.E{constructor(_,o,n,s,c,d,P,p,m,h,T){super(),this.dimlessBinaryPipe=_,this.route=o,this.router=n,this.modalService=s,this.poolService=c,this.authStorageService=d,this.formatter=P,this.taskWrapper=p,this.ecpService=m,this.crushRuleService=h,this.actionLabels=T,this.editing=!1,this.isReplicated=!1,this.isErasure=!1,this.data=new No,this.externalPgChange=!1,this.current={rules:[]},this.initializeConfigData=new E_.t(1),this.currentConfigurationValues={},this.icons=b.P,this.crushUsage=void 0,this.ecpUsage=void 0,this.crushRuleMaxSize=10,this.editing=this.router.url.startsWith(`/pool/${M.MQ.EDIT}`),this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.CREATE,this.resource="pool",this.authenticate(),this.createForm()}authenticate(){if(this.permission=this.authStorageService.getPermissions().pool,!this.permission.read||!this.permission.update&&this.editing||!this.permission.create&&!this.editing)throw new g_._2}createForm(){const _=new be.d({mode:new a.NI("none"),algorithm:new a.NI(""),minBlobSize:new a.NI("",{updateOn:"blur"}),maxBlobSize:new a.NI("",{updateOn:"blur"}),ratio:new a.NI("",{updateOn:"blur"})});this.form=new be.d({name:new a.NI("",{validators:[a.kI.pattern(/^[.A-Za-z0-9_/-]+$/),a.kI.required,E.h.custom("rbdPool",()=>this.form&&this.form.getValue("name").includes("/")&&this.data&&-1!==this.data.applications.selected.indexOf("rbd"))]}),poolType:new a.NI("",{validators:[a.kI.required]}),crushRule:new a.NI(null,{validators:[E.h.custom("tooFewOsds",o=>this.info&&o&&this.info.osd_count<1),E.h.custom("required",o=>this.isReplicated&&this.info.crush_rules_replicated.length>0&&!o)]}),size:new a.NI("",{updateOn:"blur"}),erasureProfile:new a.NI(null),pgNum:new a.NI("",{validators:[a.kI.required]}),pgAutoscaleMode:new a.NI(null),ecOverwrites:new a.NI(!1),compression:_,max_bytes:new a.NI(""),max_objects:new a.NI(0)},[E.h.custom("form",()=>null)])}ngOnInit(){this.poolService.getInfo().subscribe(_=>{this.initInfo(_),this.editing?this.initEditMode():(this.setAvailableApps(),this.loadingReady()),this.listenToChanges(),this.setComplexValidators()})}initInfo(_){this.pgAutoscaleModes=_.pg_autoscale_modes,this.form.silentSet("pgAutoscaleMode",_.pg_autoscale_default_mode),this.form.silentSet("algorithm",_.bluestore_compression_algorithm),this.info=_,this.initEcp(_.erasure_code_profiles)}initEcp(_){this.setListControlStatus("erasureProfile",_),this.ecProfiles=_}setListControlStatus(_,o){const n=this.form.get(_),s=n.value;1!==o.length||s&&u().isEqual(s,o[0])?0===o.length&&s&&n.setValue(null):n.setValue(o[0]),o.length<=1?n.enabled&&n.disable():n.disabled&&n.enable()}initEditMode(){this.disableForEdit(),this.routeParamsSubscribe=this.route.params.subscribe(_=>this.poolService.get(_.name).subscribe(o=>{this.data.pool=o,this.initEditFormData(o),this.loadingReady()}))}disableForEdit(){["poolType","crushRule","size","erasureProfile","ecOverwrites"].forEach(_=>this.form.get(_).disable())}initEditFormData(_){this.initializeConfigData.next({initialData:_.configuration,sourceType:R_.h.pool}),this.poolTypeChange(_.type);const o=this.info.crush_rules_replicated.concat(this.info.crush_rules_erasure),n={name:_.pool_name,poolType:_.type,crushRule:o.find(s=>s.rule_name===_.crush_rule),size:_.size,erasureProfile:this.ecProfiles.find(s=>s.name===_.erasure_code_profile),pgAutoscaleMode:_.pg_autoscale_mode,pgNum:_.pg_num,ecOverwrites:_.flags_names.includes("ec_overwrites"),mode:_.options.compression_mode,algorithm:_.options.compression_algorithm,minBlobSize:this.dimlessBinaryPipe.transform(_.options.compression_min_blob_size),maxBlobSize:this.dimlessBinaryPipe.transform(_.options.compression_max_blob_size),ratio:_.options.compression_required_ratio,max_bytes:this.dimlessBinaryPipe.transform(_.quota_max_bytes),max_objects:_.quota_max_objects};Object.keys(n).forEach(s=>{const c=n[s];!u().isUndefined(c)&&""!==c&&this.form.silentSet(s,c)}),this.data.pgs=this.form.getValue("pgNum"),this.setAvailableApps(this.data.applications.default.concat(_.application_metadata)),this.data.applications.selected=_.application_metadata}setAvailableApps(_=this.data.applications.default){this.data.applications.available=u().uniq(_.sort()).map(o=>new p_.$(!1,o,""))}listenToChanges(){this.listenToChangesDuringAddEdit(),this.editing||this.listenToChangesDuringAdd()}listenToChangesDuringAddEdit(){this.form.get("pgNum").valueChanges.subscribe(_=>{const o=_-this.data.pgs;1===Math.abs(o)&&2!==_?this.doPgPowerJump(o):this.data.pgs=_})}doPgPowerJump(_){const o=this.calculatePgPower()+_;this.setPgs(-1===_?Math.round(o):Math.floor(o))}calculatePgPower(_=this.form.getValue("pgNum")){return Math.log(_)/Math.log(2)}setPgs(_){const o=Math.pow(2,_<0?0:_);this.data.pgs=o,this.form.silentSet("pgNum",o)}listenToChangesDuringAdd(){this.form.get("poolType").valueChanges.subscribe(_=>{this.poolTypeChange(_)}),this.form.get("crushRule").valueChanges.subscribe(_=>{this.crushDeletionBtn&&this.crushDeletionBtn.isOpen()&&this.crushDeletionBtn.close(),_&&(this.setCorrectMaxSize(_),this.crushRuleIsUsedBy(_.rule_name),this.replicatedRuleChange(),this.pgCalc())}),this.form.get("size").valueChanges.subscribe(()=>{this.pgCalc()}),this.form.get("erasureProfile").valueChanges.subscribe(_=>{this.ecpDeletionBtn&&this.ecpDeletionBtn.isOpen()&&this.ecpDeletionBtn.close(),_&&(this.ecpIsUsedBy(_.name),this.pgCalc())}),this.form.get("mode").valueChanges.subscribe(()=>{["minBlobSize","maxBlobSize","ratio"].forEach(_=>{this.form.get(_).updateValueAndValidity({emitEvent:!1})})}),this.form.get("minBlobSize").valueChanges.subscribe(()=>{this.form.get("maxBlobSize").updateValueAndValidity({emitEvent:!1})}),this.form.get("maxBlobSize").valueChanges.subscribe(()=>{this.form.get("minBlobSize").updateValueAndValidity({emitEvent:!1})})}poolTypeChange(_){if("replicated"===_?this.setTypeBooleans(!0,!1):this.setTypeBooleans(!1,"erasure"===_),!_||!this.info)return void(this.current.rules=[]);const o=this.info["crush_rules_"+_]||[];this.current.rules=o,!this.editing&&(this.isReplicated&&this.setListControlStatus("crushRule",o),this.replicatedRuleChange(),this.pgCalc())}setTypeBooleans(_,o){this.isReplicated=_,this.isErasure=o}replicatedRuleChange(){if(!this.isReplicated)return;const _=this.form.get("size");let o=this.form.getValue("size")||3;const n=this.getMinSize(),s=this.getMaxSize();os&&(o=s),o!==_.value&&this.form.silentSet("size",o)}getMinSize(){return!this.info||this.info.osd_count<1?0:1}getMaxSize(){const _=this.form.getValue("crushRule");return this.info?_?_.usable_size:Math.min(this.info.osd_count,3):0}pgCalc(){const _=this.form.getValue("poolType");if(!this.info||this.form.get("pgNum").dirty||!_)return;const o=100*this.info.osd_count,n=this.isReplicated?this.replicatedPgCalc(o):this.erasurePgCalc(o);if(!n)return;const s=this.data.pgs;this.alignPgs(n),this.externalPgChange||(this.externalPgChange=s!==this.data.pgs)}setCorrectMaxSize(_=this.form.getValue("crushRule")){if(!_)return;const n=S.searchFailureDomains(this.info.nodes,_.steps[0].item_name)[_.steps[1].type];_.usable_size=Math.min(n?n.length:this.crushRuleMaxSize,this.crushRuleMaxSize)}replicatedPgCalc(_){const o=this.form.get("size"),n=o.value;return o.valid&&n>0?_/n:0}erasurePgCalc(_){const o=this.form.get("erasureProfile"),n=o.value;return(o.valid||o.disabled)&&n?_/(n.k+n.m):0}alignPgs(_=this.form.getValue("pgNum")){this.setPgs(Math.round(this.calculatePgPower(_<1?1:_)))}setComplexValidators(){this.editing?this.form.get("name").setValidators([this.form.get("name").validator,E.h.custom("uniqueName",_=>this.data.pool&&this.info&&-1!==this.info.pool_names.indexOf(_)&&this.info.pool_names.indexOf(_)!==this.info.pool_names.indexOf(this.data.pool.pool_name))]):(E.h.validateIf(this.form.get("size"),()=>this.isReplicated,[E.h.custom("min",_=>this.form.getValue("size")&&_this.form.getValue("size")&&this.getMaxSize()<_)]),this.form.get("name").setValidators([this.form.get("name").validator,E.h.custom("uniqueName",_=>this.info&&-1!==this.info.pool_names.indexOf(_))])),this.setCompressionValidators()}setCompressionValidators(){E.h.validateIf(this.form.get("minBlobSize"),()=>this.hasCompressionEnabled(),[a.kI.min(0),E.h.custom("maximum",_=>this.oddBlobSize(_,this.form.getValue("maxBlobSize")))]),E.h.validateIf(this.form.get("maxBlobSize"),()=>this.hasCompressionEnabled(),[a.kI.min(0),E.h.custom("minimum",_=>this.oddBlobSize(this.form.getValue("minBlobSize"),_))]),E.h.validateIf(this.form.get("ratio"),()=>this.hasCompressionEnabled(),[a.kI.min(0),a.kI.max(1)])}oddBlobSize(_,o){const n=this.formatter.toBytes(_),s=this.formatter.toBytes(o);return Boolean(n&&s&&n>=s)}hasCompressionEnabled(){return this.form.getValue("mode")&&"none"!==this.form.get("mode").value.toLowerCase()}describeCrushStep(_){return[_.op.replace("_"," "),_.item_name||"",_.type?_.num+" type "+_.type:""].join(" ")}addErasureCodeProfile(){this.addModal(Ao,_=>this.reloadECPs(_))}addModal(_,o){this.hideOpenTooltips(),this.modalService.show(_).componentInstance.submitAction.subscribe(s=>{o(s.name)})}hideOpenTooltips(){const _=o=>o&&o.isOpen()&&o.close();_(this.ecpDeletionBtn),_(this.crushDeletionBtn)}reloadECPs(_){this.reloadList({newItemName:_,getInfo:()=>this.ecpService.list(),initInfo:o=>this.initEcp(o),findNewItem:()=>this.ecProfiles.find(o=>o.name===_),controlName:"erasureProfile"})}reloadList({newItemName:_,getInfo:o,initInfo:n,findNewItem:s,controlName:c}){this.modalSubscription&&this.modalSubscription.unsubscribe(),o().subscribe(d=>{if(n(d),!_)return;const P=s();P&&this.form.get(c).setValue(P)})}deleteErasureCodeProfile(){this.deletionModal({value:this.form.getValue("erasureProfile"),usage:this.ecpUsage,deletionBtn:this.ecpDeletionBtn,dataName:"erasureInfo",getTabs:()=>this.ecpInfoTabs,tabPosition:"used-by-pools",nameAttribute:"name",itemDescription:"erasure code profile",reloadFn:()=>this.reloadECPs(),deleteFn:_=>this.ecpService.delete(_),taskName:"ecp/delete"})}deletionModal({value:_,usage:o,deletionBtn:n,dataName:s,getTabs:c,tabPosition:d,nameAttribute:P,itemDescription:p,reloadFn:m,deleteFn:h,taskName:T}){if(!_)return;if(o)return n.animation=!1,n.toggle(),this.data[s]=!0,void setTimeout(()=>{const f=c();f&&f.select(d)},50);const R=_[P];this.modalService.show(Ne.M,{itemDescription:p,itemNames:[R],submitActionObservable:()=>{const f=h(R);return f.subscribe(()=>m()),this.taskWrapper.wrapTaskAroundCall({task:new v.R(T,{name:R}),call:f})}})}addCrushRule(){this.addModal($_,_=>this.reloadCrushRules(_))}reloadCrushRules(_){this.reloadList({newItemName:_,getInfo:()=>this.poolService.getInfo(),initInfo:o=>{this.initInfo(o),this.poolTypeChange("replicated")},findNewItem:()=>this.info.crush_rules_replicated.find(o=>o.rule_name===_),controlName:"crushRule"})}deleteCrushRule(){this.deletionModal({value:this.form.getValue("crushRule"),usage:this.crushUsage,deletionBtn:this.crushDeletionBtn,dataName:"crushInfo",getTabs:()=>this.crushInfoTabs,tabPosition:"used-by-pools",nameAttribute:"rule_name",itemDescription:"crush rule",reloadFn:()=>this.reloadCrushRules(),deleteFn:_=>this.crushRuleService.delete(_),taskName:"crushRule/delete"})}crushRuleIsUsedBy(_){this.crushUsage=_?this.info.used_rules[_]:void 0}ecpIsUsedBy(_){this.ecpUsage=_?this.info.used_profiles[_]:void 0}submit(){if(this.form.invalid)return void this.form.setErrors({cdSubmitButton:!0});const _={pool:this.form.getValue("name")};this.assignFormFields(_,[{externalFieldName:"pool_type",formControlName:"poolType"},{externalFieldName:"pg_autoscale_mode",formControlName:"pgAutoscaleMode",editable:!0},{externalFieldName:"pg_num",formControlName:"pgNum",replaceFn:n=>"on"===this.form.getValue("pgAutoscaleMode")?1:n,editable:!0},this.isReplicated?{externalFieldName:"size",formControlName:"size"}:{externalFieldName:"erasure_code_profile",formControlName:"erasureProfile",attr:"name"},{externalFieldName:"rule_name",formControlName:"crushRule",replaceFn:n=>this.isReplicated?n&&n.rule_name:void 0},{externalFieldName:"quota_max_bytes",formControlName:"max_bytes",replaceFn:this.formatter.toBytes,editable:!0,resetValue:this.editing?0:void 0},{externalFieldName:"quota_max_objects",formControlName:"max_objects",editable:!0,resetValue:this.editing?0:void 0}]),this.info.is_all_bluestore&&(this.assignFormField(_,{externalFieldName:"flags",formControlName:"ecOverwrites",replaceFn:()=>this.isErasure?["ec_overwrites"]:void 0}),"none"!==this.form.getValue("mode")?this.assignFormFields(_,[{externalFieldName:"compression_mode",formControlName:"mode",editable:!0,replaceFn:n=>this.hasCompressionEnabled()&&n},{externalFieldName:"compression_algorithm",formControlName:"algorithm",editable:!0},{externalFieldName:"compression_min_blob_size",formControlName:"minBlobSize",replaceFn:this.formatter.toBytes,editable:!0,resetValue:0},{externalFieldName:"compression_max_blob_size",formControlName:"maxBlobSize",replaceFn:this.formatter.toBytes,editable:!0,resetValue:0},{externalFieldName:"compression_required_ratio",formControlName:"ratio",editable:!0,resetValue:0}]):this.editing&&this.assignFormFields(_,[{externalFieldName:"compression_mode",formControlName:"mode",editable:!0,replaceFn:()=>"unset"},{externalFieldName:"srcpool",formControlName:"name",editable:!0,replaceFn:()=>this.data.pool.pool_name}]));const o=this.data.applications.selected;(o.length>0||this.editing)&&(_.application_metadata=o),this.isReplicated&&!u().isEmpty(this.currentConfigurationValues)&&(_.configuration=this.currentConfigurationValues),this.triggerApiTask(_)}assignFormFields(_,o){o.forEach(n=>this.assignFormField(_,n))}assignFormField(_,{externalFieldName:o,formControlName:n,attr:s,replaceFn:c,editable:d,resetValue:P}){if(this.editing&&(!d||this.form.get(n).pristine))return;const p=this.form.getValue(n);let m=c?c(p):s?u().get(p,s):p;if(!p||!m){if(!d||u().isUndefined(P))return;m=P}_[o]=m}triggerApiTask(_){this.taskWrapper.wrapTaskAroundCall({task:new v.R("pool/"+(this.editing?M.MQ.EDIT:M.MQ.CREATE),{pool_name:_.hasOwnProperty("srcpool")?_.srcpool:_.pool}),call:this.poolService[this.editing?M.MQ.UPDATE:M.MQ.CREATE](_)}).subscribe({error:o=>{u().isObject(o.error)&&"34"===o.error.code&&this.form.get("pgNum").setErrors({34:!0}),this.form.setErrors({cdSubmitButton:!0})},complete:()=>this.router.navigate(["/pool"])})}appSelection(){this.form.get("name").updateValueAndValidity({emitEvent:!1,onlySelf:!0})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(C_.$),e.Y36(Oe.gz),e.Y36(Oe.F0),e.Y36(Ze.Z),e.Y36(ue.q),e.Y36(he.j),e.Y36(M_.H),e.Y36(de.P),e.Y36(Me),e.Y36(Ie.H),e.Y36(M.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-pool-form"]],viewQuery:function(_,o){if(1&_&&(e.Gf(Zo,5),e.Gf(xo,5),e.Gf(yo,5),e.Gf(Uo,5)),2&_){let n;e.iGM(n=e.CRH())&&(o.crushInfoTabs=n.first),e.iGM(n=e.CRH())&&(o.crushDeletionBtn=n.first),e.iGM(n=e.CRH())&&(o.ecpInfoTabs=n.first),e.iGM(n=e.CRH())&&(o.ecpDeletionBtn=n.first)}},features:[e.qOj],decls:1,vars:1,consts:function(){let i,_,o,n,s,c,d,P,p,m,h,T,R,f,A,I,$,D,Z,x,y,U,q,H,G,z,X,w,Q,J,k,V,Y,B,j,K,N,W,ee,_e,oe,te,ie,ne,se,ae,le,re,ce,O,Xe,we,Qe,Je,ke,Ve,Ye,Be,je,Ke,We,e_,__,o_,t_,i_,n_,s_,a_,l_,r_,c_,O_;return i="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Name",o="Name...",n="Pool type",s="-- Select a pool type --",c="Applications",d="Pools should be associated with an application tag",P="Quotas",p="Max bytes",m="Leave it blank or specify 0 to disable this quota.",h="A valid quota should be greater than 0.",T="e.g., 10GiB",R="Max objects",f="Leave it blank or specify 0 to disable this quota.",A="A valid quota should be greater than 0.",I="This field is required!",$="The chosen Ceph pool name is already in use.",D="It's not possible to create an RBD pool with '/' in the name. Please change the name or remove 'rbd' from the applications list.",Z="Pool name can only contain letters, numbers, '.', '-', '_' or '/'.",x="This field is required!",y="PG Autoscale",U="Placement groups",q="Calculation help",H="This field is required!",G="At least one placement group is needed!",z="Your cluster can't handle this many PGs. Please recalculate the PG amount needed.",X="The current PGs settings were calculated for you, you should make sure the values suit your needs before submit.",w="Replicated size",Q="Minimum: " + "\ufffd0\ufffd" + "",J="Maximum: " + "\ufffd0\ufffd" + "",k="The size specified is out of range. A value from " + "\ufffd0\ufffd" + " to " + "\ufffd1\ufffd" + " is usable.",V="A size of 1 will not create a replication of the object. The 'Replicated size' includes the object itself.",Y="Flags",B="EC Overwrites",j="CRUSH",K="Erasure code profile",N="This profile can't be deleted as it is in use.",W="Loading...",ee="-- No erasure code profile available --",_e="-- Select an erasure code profile --",oe="Profile",te="Used by pools",ie="Profile is not in use.",ne="Crush ruleset",se="A new crush ruleset will be implicitly created.",ae="Crush ruleset",le="There are no rules.",re="-- Select a crush rule --",ce="Placement and\n replication strategies or distribution policies that allow to\n specify how CRUSH places data replicas.",O="This rule can't be deleted as it is in use.",Xe="Crush rule",we="Crush steps",Qe="Used by pools",Je="Rule is not in use.",ke="This field is required!",Ve="The rule can't be used in the current cluster as it has too few OSDs to meet the minimum required OSD by this rule.",Ye="Compression",Be="Mode",je="Algorithm",Ke="Minimum blob size",We="e.g., 128KiB",e_="Maximum blob size",__="e.g., 512KiB",o_="Ratio",t_="Compression ratio",i_="Loading...",n_="-- No erasure compression algorithm available --",s_="Value should be greater than 0",a_="Value should be less than the maximum blob size",l_="Value should be greater than 0",r_="Value should be greater than the minimum blob size",c_="Value should be between 0.0 and 1.0",O_="The value should be greater or equal to 0",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["name","form","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"card"],[1,"card-header"],i,[1,"card-body"],[1,"form-group","row"],["for","name",1,"cd-col-form-label","required"],_,[1,"cd-col-form-input"],["id","name","name","name","type","text","placeholder",o,"formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","poolType",1,"cd-col-form-label","required"],n,["id","poolType","formControlName","poolType","name","poolType",1,"form-control","custom-select"],["ngValue",""],s,[3,"value",4,"ngFor","ngForOf"],[4,"ngIf"],["for","applications",1,"cd-col-form-label"],c,["id","applications",3,"customBadges","customBadgeValidators","messages","data","options","selectionLimit","selection"],["title",d,3,"class",4,"ngIf"],["formGroupName","compression",4,"ngIf"],P,["for","max_bytes",1,"cd-col-form-label"],p,m,h,["id","max_bytes","name","max_bytes","type","text","formControlName","max_bytes","placeholder",T,"defaultUnit","GiB","cdDimlessBinary","",1,"form-control"],["for","max_objects",1,"cd-col-form-label"],R,f,A,["id","max_objects","min","0","name","max_objects","type","number","formControlName","max_objects",1,"form-control"],[3,"hidden"],[3,"form","initializeData","changes"],[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],I,$,D,Z,[3,"value"],x,["for","pgAutoscaleMode",1,"cd-col-form-label"],y,["id","pgAutoscaleMode","name","pgAutoscaleMode","formControlName","pgAutoscaleMode",1,"form-control","custom-select"],["class","form-group row",4,"ngIf"],["for","pgNum",1,"cd-col-form-label","required"],U,["id","pgNum","name","pgNum","formControlName","pgNum","min","1","type","number","required","",1,"form-control",3,"focus","blur"],[1,"form-text","text-muted"],["section","pgs","docText",q],["class","form-text text-muted",4,"ngIf"],H,G,z,X,["for","size",1,"cd-col-form-label","required"],w,["id","size","name","size","type","number","formControlName","size",1,"form-control",3,"max","min"],["class","text-warning-dark",4,"ngIf"],[1,"list-inline"],Q,J,k,[1,"text-warning-dark"],V,[1,"cd-col-form-label"],Y,[1,"custom-control","custom-checkbox"],["type","checkbox","id","ec-overwrites","formControlName","ecOverwrites",1,"custom-control-input"],["for","ec-overwrites",1,"custom-control-label"],B,["title",d],j,["for","erasureProfile",1,"cd-col-form-label"],K,[1,"input-group"],["id","erasureProfile","name","erasureProfile","formControlName","erasureProfile",1,"form-control","custom-select"],["ngValue","",4,"ngIf"],[3,"ngValue",4,"ngIf"],[3,"ngValue",4,"ngFor","ngForOf"],[1,"input-group-append"],["id","ecp-info-button","type","button",1,"btn","btn-light",3,"ngClass","click"],["aria-hidden","true",3,"ngClass"],["class","btn btn-light","type","button",3,"click",4,"ngIf"],["class","btn btn-light","type","button","ngbTooltip",N,"triggers","manual",3,"click",4,"ngIf"],["class","form-text text-muted","id","ecp-info-block",4,"ngIf"],W,[3,"ngValue"],ee,_e,["type","button",1,"btn","btn-light",3,"click"],["type","button","ngbTooltip",N,"triggers","manual",1,"btn","btn-light",3,"click"],["ecpDeletionBtn","ngbTooltip"],["id","ecp-info-block",1,"form-text","text-muted"],["ngbNav","",1,"nav-tabs"],["ecpInfoTabs","ngbNav"],["ngbNavItem","ecp-info"],["ngbNavLink",""],oe,["ngbNavContent",""],["ngbNavItem","used-by-pools"],te,[3,"ngbNavOutlet"],[3,"renderObjects","hideKeys","data","autoReload"],["ecpIsNotUsed",""],[4,"ngIf","ngIfElse"],ie,[4,"ngFor","ngForOf"],["for","crushRule",1,"cd-col-form-label"],ne,se,ae,["noRules",""],le,["id","crushRule","formControlName","crushRule","name","crushSet",1,"form-control","custom-select"],re,["id","crush-info-button","type","button","ngbTooltip",ce,1,"btn","btn-light",3,"ngClass","click"],["class","btn btn-light","type","button","ngbTooltip",O,"triggers","manual",3,"click",4,"ngIf"],["class","form-text text-muted","id","crush-info-block",4,"ngIf"],["type","button","ngbTooltip",O,"triggers","manual",1,"btn","btn-light",3,"click"],["crushDeletionBtn","ngbTooltip"],["id","crush-info-block",1,"form-text","text-muted"],["crushInfoTabs","ngbNav"],["ngbNavItem","crush-rule-info"],Xe,["ngbNavItem","crush-rule-steps"],we,Qe,["ruleIsNotUsed",""],Je,ke,Ve,["formGroupName","compression"],Ye,["for","mode",1,"cd-col-form-label"],Be,["id","mode","name","mode","formControlName","mode",1,"form-control","custom-select"],["for","algorithm",1,"cd-col-form-label"],je,["id","algorithm","name","algorithm","formControlName","algorithm",1,"form-control","custom-select"],["for","minBlobSize",1,"cd-col-form-label"],Ke,["id","minBlobSize","name","minBlobSize","formControlName","minBlobSize","type","text","min","0","placeholder",We,"defaultUnit","KiB","cdDimlessBinary","",1,"form-control"],["for","maxBlobSize",1,"cd-col-form-label"],e_,["id","maxBlobSize","type","text","min","0","formControlName","maxBlobSize","placeholder",__,"defaultUnit","KiB","cdDimlessBinary","",1,"form-control"],["for","ratio",1,"cd-col-form-label"],o_,["id","ratio","name","ratio","formControlName","ratio","type","number","min","0","max","1","step","0.1","placeholder",t_,1,"form-control"],i_,n_,s_,a_,l_,r_,c_,O_]},template:function(_,o){1&_&&e.YNc(0,Yt,70,33,"div",0),2&_&&e.Q6J("cdFormLoading",o.loading)},directives:[bo.y,a._Y,a.JL,fe.V,a.sg,Pe.P,Ee.o,a.Fj,ge.b,a.JJ,a.u,pe.U,C.O5,a.EJ,a.YN,a.Kr,C.sg,vo.m,me.S,Io.Q,a.qQ,a.wV,$o.d,Re.p,a.Q7,Do.K,a.Fd,a.Wl,C.mk,g._L,g.Pz,g.nv,g.Vx,g.uN,g.tO,xe.b,a.x0],pipes:[C.rS,Ce.m],styles:[".icon-warning-color[_ngcontent-%COMP%]{margin-left:3px}"]}),t})();var Bt=r(19773),jt=r(49671),Kt=r(68136),Te=r(69158),Se=r(64337),L=r(99466),Wt=r(91801),ei=r(68774),_i=r(66369),qe=r(38047),Le=r(51847);class oi{constructor(i){this.pool_name=i}}var ti=r(64724);let ii=(()=>{class t{constructor(_,o,n){this.templateRef=_,this.viewContainer=o,this.authStorageService=n,this.cdScopeMatchAll=!0}set cdScope(_){this.permissions=this.authStorageService.getPermissions(),this.isAuthorized(_)?this.viewContainer.createEmbeddedView(this.templateRef):this.viewContainer.clear()}isAuthorized(_){const o=this.cdScopeMatchAll?u().every:u().some;return u().isString(_)?u().get(this.permissions,[_,"read"],!1):u().isArray(_)?o(_,n=>this.permissions[n].read):!!u().isObject(_)&&o(_,(n,s)=>o(n,c=>this.permissions[s][c]))}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(e.Rgc),e.Y36(e.s_b),e.Y36(he.j))},t.\u0275dir=e.lG2({type:t,selectors:[["","cdScope",""]],inputs:{cdScope:"cdScope",cdScopeMatchAll:"cdScopeMatchAll"}}),t})();var ni=r(94928),He=r(51295),si=r(59376),Ge=r(76317),ai=r(71752);function li(t,i){if(1&t&&e._UZ(0,"cd-table-key-value",12),2&t){const _=e.oxw(2);e.Q6J("renderObjects",!0)("data",_.poolDetails)("autoReload",!1)}}function ri(t,i){if(1&t&&e._UZ(0,"cd-grafana",15),2&t){const _=e.oxw(3);e.MGl("grafanaPath","ceph-pool-detail?var-pool_name=",_.selection.pool_name,"")}}function ci(t,i){1&t&&(e.TgZ(0,"li",13),e.TgZ(1,"a",5),e.SDv(2,14),e.qZA(),e.YNc(3,ri,1,1,"ng-template",7),e.qZA())}function Oi(t,i){if(1&t&&e._UZ(0,"cd-rbd-configuration-table",18),2&t){const _=e.oxw(3);e.Q6J("data",_.selectedPoolConfiguration)}}function di(t,i){1&t&&(e.TgZ(0,"li",16),e.TgZ(1,"a",5),e.SDv(2,17),e.qZA(),e.YNc(3,Oi,1,1,"ng-template",7),e.qZA())}function ui(t,i){if(1&t&&e._UZ(0,"cd-table",21),2&t){const _=e.oxw(3);e.Q6J("data",_.cacheTiers)("columns",_.cacheTierColumns)("autoSave",!1)}}function fi(t,i){1&t&&(e.TgZ(0,"li",19),e.TgZ(1,"a",5),e.SDv(2,20),e.qZA(),e.YNc(3,ui,1,3,"ng-template",7),e.qZA())}function Pi(t,i){if(1&t&&(e.ynx(0,1),e.TgZ(1,"ul",2,3),e.TgZ(3,"li",4),e.TgZ(4,"a",5),e.SDv(5,6),e.qZA(),e.YNc(6,li,1,3,"ng-template",7),e.qZA(),e.YNc(7,ci,4,0,"li",8),e.YNc(8,di,4,0,"li",9),e.YNc(9,fi,4,0,"li",10),e.qZA(),e._UZ(10,"div",11),e.BQk()),2&t){const _=e.MAs(2),o=e.oxw();e.xp6(7),e.Q6J("ngIf",o.permissions.grafana.read),e.xp6(1),e.Q6J("ngIf","replicated"===o.selection.type),e.xp6(1),e.Q6J("ngIf",(null==o.selection.tiers?null:o.selection.tiers.length)>0),e.xp6(1),e.Q6J("ngbNavOutlet",_)}}let Ei=(()=>{class t{constructor(_){this.poolService=_,this.cacheTierColumns=[],this.omittedPoolAttributes=["cdExecuting","cdIsBinary","stats"],this.cacheTierColumns=[{prop:"pool_name",name:"Name",flexGrow:3},{prop:"cache_mode",name:"Cache Mode",flexGrow:2},{prop:"cache_min_evict_age",name:"Min Evict Age",flexGrow:2},{prop:"cache_min_flush_age",name:"Min Flush Age",flexGrow:2},{prop:"target_max_bytes",name:"Target Max Bytes",flexGrow:2},{prop:"target_max_objects",name:"Target Max Objects",flexGrow:2}]}ngOnChanges(){this.selection&&(this.poolService.getConfiguration(this.selection.pool_name).subscribe(_=>{He.T.updateChanged(this,{selectedPoolConfiguration:_})}),He.T.updateChanged(this,{poolDetails:u().omit(this.selection,this.omittedPoolAttributes)}))}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ue.q))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-pool-details"]],inputs:{cacheTiers:"cacheTiers",permissions:"permissions",selection:"selection"},features:[e.TTD],decls:1,vars:1,consts:function(){let i,_,o,n;return i="Details",_="Performance Details",o="Configuration",n="Cache Tiers Details",[["cdTableDetail","",4,"ngIf"],["cdTableDetail",""],["ngbNav","","cdStatefulTab","pool-details",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","details"],["ngbNavLink",""],i,["ngbNavContent",""],["ngbNavItem","performance-details",4,"ngIf"],["ngbNavItem","configuration",4,"ngIf"],["ngbNavItem","cache-tiers-details",4,"ngIf"],[3,"ngbNavOutlet"],[3,"renderObjects","data","autoReload"],["ngbNavItem","performance-details"],_,["uid","-xyV8KCiz","grafanaStyle","three",3,"grafanaPath"],["ngbNavItem","configuration"],o,[3,"data"],["ngbNavItem","cache-tiers-details"],n,["columnMode","flex",3,"data","columns","autoSave"]]},template:function(_,o){1&_&&e.YNc(0,Pi,11,4,"ng-container",0),2&_&&e.Q6J("ngIf",o.selection)},directives:[C.O5,g.Pz,si.m,g.nv,g.Vx,g.uN,g.tO,xe.b,Ge.F,ai.P,Se.a],styles:[""],changeDetection:0}),t})();var gi=r(60251);const pi=["poolUsageTpl"],mi=["poolConfigurationSourceTpl"];function Ri(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"cd-table",9,10),e.NdJ("fetchData",function(){return e.CHM(_),e.oxw().taskListService.fetch()})("setExpandedRow",function(n){return e.CHM(_),e.oxw().setExpandedRow(n)})("updateSelection",function(n){return e.CHM(_),e.oxw().updateSelection(n)}),e._UZ(2,"cd-table-actions",11),e._UZ(3,"cd-pool-details",12),e.qZA()}if(2&t){const _=e.oxw();e.Q6J("data",_.pools)("columns",_.columns)("hasDetails",!0)("status",_.tableStatus)("autoReload",-1),e.xp6(2),e.Q6J("permission",_.permissions.pool)("selection",_.selection)("tableActions",_.tableActions),e.xp6(1),e.Q6J("selection",_.expandedRow)("permissions",_.permissions)("cacheTiers",_.cacheTiers)}}function Ci(t,i){1&t&&e._UZ(0,"cd-grafana",14),2&t&&e.Q6J("grafanaPath","ceph-pools-overview?")}function Mi(t,i){1&t&&(e.TgZ(0,"li",2),e.TgZ(1,"a",3),e.SDv(2,13),e.qZA(),e.YNc(3,Ci,1,1,"ng-template",5),e.qZA())}function hi(t,i){if(1&t&&e._UZ(0,"cd-usage-bar",16),2&t){const _=e.oxw().row;e.Q6J("total",_.stats.bytes_used.latest+_.stats.avail_raw.latest)("used",_.stats.bytes_used.latest)}}function Ti(t,i){if(1&t&&e.YNc(0,hi,1,2,"cd-usage-bar",15),2&t){const _=i.row;e.Q6J("ngIf",null==_.stats||null==_.stats.avail_raw?null:_.stats.avail_raw.latest)}}let Si=(()=>{class t extends Kt.o{constructor(_,o,n,s,c,d,P,p,m,h,T){super(),this.poolService=_,this.taskWrapper=o,this.ecpService=n,this.authStorageService=s,this.taskListService=c,this.modalService=d,this.pgCategoryService=P,this.dimlessPipe=p,this.urlBuilder=m,this.configurationService=h,this.actionLabels=T,this.selection=new ei.r,this.executingTasks=[],this.tableStatus=new Te.E,this.cacheTiers=[],this.monAllowPoolDelete=!1,this.permissions=this.authStorageService.getPermissions(),this.tableActions=[{permission:"create",icon:b.P.add,routerLink:()=>this.urlBuilder.getCreate(),name:this.actionLabels.CREATE},{permission:"update",icon:b.P.edit,routerLink:()=>this.urlBuilder.getEdit(encodeURIComponent(this.selection.first().pool_name)),name:this.actionLabels.EDIT},{permission:"delete",icon:b.P.destroy,click:()=>this.deletePoolModal(),name:this.actionLabels.DELETE,disable:this.getDisableDesc.bind(this)}],this.permissions.configOpt.read&&this.configurationService.get("mon_allow_pool_delete").subscribe(R=>{if(u().has(R,"value")){const f=u().find(R.value,A=>"mon"===A.section)||{value:!1};this.monAllowPoolDelete="true"===f.value}})}ngOnInit(){const _=(o,n,s)=>u().get(n,o)>u().get(s,o)?1:-1;this.columns=[{prop:"pool_name",name:"Name",flexGrow:4,cellTransformation:L.e.executing},{prop:"data_protection",name:"Data Protection",cellTransformation:L.e.badge,customTemplateConfig:{class:"badge-background-gray"},flexGrow:1.3},{prop:"application_metadata",name:"Applications",cellTransformation:L.e.badge,customTemplateConfig:{class:"badge-background-primary"},flexGrow:1.5},{prop:"pg_status",name:"PG Status",flexGrow:1.2,cellClass:({row:o,column:n,value:s})=>this.getPgStatusCellClass(o,n,s)},{prop:"crush_rule",name:"Crush Ruleset",isHidden:!0,flexGrow:2},{name:"Usage",prop:"usage",cellTemplate:this.poolUsageTpl,flexGrow:1.2},{prop:"stats.rd_bytes.rates",name:"Read bytes",comparator:(o,n,s,c)=>_("stats.rd_bytes.latest",s,c),cellTransformation:L.e.sparkline,flexGrow:1.5},{prop:"stats.wr_bytes.rates",name:"Write bytes",comparator:(o,n,s,c)=>_("stats.wr_bytes.latest",s,c),cellTransformation:L.e.sparkline,flexGrow:1.5},{prop:"stats.rd.rate",name:"Read ops",flexGrow:1,pipe:this.dimlessPipe,cellTransformation:L.e.perSecond},{prop:"stats.wr.rate",name:"Write ops",flexGrow:1,pipe:this.dimlessPipe,cellTransformation:L.e.perSecond}],this.taskListService.init(()=>this.ecpService.list().pipe((0,Bt.zg)(o=>(this.ecProfileList=o,this.poolService.getList()))),void 0,o=>{this.pools=this.transformPoolsData(o),this.tableStatus=new Te.E},()=>{this.table.reset(),this.tableStatus=new Te.E(Wt.T.ValueException)},o=>o.name.startsWith("pool/"),(o,n)=>n.metadata.pool_name===o.pool_name,{default:o=>new oi(o.pool_name)})}updateSelection(_){this.selection=_}deletePoolModal(){const _=this.selection.first().pool_name;this.modalService.show(Ne.M,{itemDescription:"Pool",itemNames:[_],submitActionObservable:()=>this.taskWrapper.wrapTaskAroundCall({task:new v.R(`pool/${M.MQ.DELETE}`,{pool_name:_}),call:this.poolService.delete(_)})})}getPgStatusCellClass(_,o,n){return{"text-right":!0,[`pg-${this.pgCategoryService.getTypeByStates(n)}`]:!0}}getErasureCodeProfile(_){let o="";return u().forEach(this.ecProfileList,n=>{n.name===_&&(o=`EC: ${n.k}+${n.m}`)}),o}transformPoolsData(_){const o=["bytes_used","max_avail","avail_raw","percent_used","rd_bytes","wr_bytes","rd","wr"],n={latest:0,rate:0,rates:[]};return u().forEach(_,s=>{s.pg_status=this.transformPgStatus(s.pg_status);const c={};u().forEach(o,d=>{c[d]=s.stats&&s.stats[d]?s.stats[d]:n}),s.stats=c,s.usage=c.percent_used.latest,!s.cdExecuting&&s.pg_num+s.pg_placement_num!==s.pg_num_target+s.pg_placement_num_target&&(s.cdExecuting="Updating"),["rd_bytes","wr_bytes"].forEach(d=>{s.stats[d].rates=s.stats[d].rates.map(P=>P[1])}),s.cdIsBinary=!0,"erasure"===s.type&&(s.data_protection=this.getErasureCodeProfile(s.erasure_code_profile)),"replicated"===s.type&&(s.data_protection=`replica: \xd7${s.size}`)}),_}transformPgStatus(_){const o=[];return u().forEach(_,(n,s)=>{o.push(`${n} ${s}`)}),o.join(", ")}getSelectionTiers(){if(void 0!==this.expandedRow){const _=this.expandedRow.tiers;this.cacheTiers=this.pools.filter(o=>_.includes(o.pool))}}getDisableDesc(){var _;return!(null===(_=this.selection)||void 0===_?void 0:_.hasSelection)||!this.monAllowPoolDelete&&"Pool deletion is disabled by the mon_allow_pool_delete configuration setting."}setExpandedRow(_){super.setExpandedRow(_),this.getSelectionTiers()}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ue.q),e.Y36(de.P),e.Y36(Me),e.Y36(he.j),e.Y36(qe.j),e.Y36(Ze.Z),e.Y36(jt.j),e.Y36(_i.n),e.Y36(Le.F),e.Y36(ti.e),e.Y36(M.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-pool-list"]],viewQuery:function(_,o){if(1&_&&(e.Gf(Se.a,5),e.Gf(pi,7),e.Gf(mi,5)),2&_){let n;e.iGM(n=e.CRH())&&(o.table=n.first),e.iGM(n=e.CRH())&&(o.poolUsageTpl=n.first),e.iGM(n=e.CRH())&&(o.poolConfigurationSourceTpl=n.first)}},features:[e._Bn([qe.j,{provide:Le.F,useValue:new Le.F("pool")}]),e.qOj],decls:10,vars:2,consts:function(){let i,_;return i="Pools List",_="Overall Performance",[["ngbNav","",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem",""],["ngbNavLink",""],i,["ngbNavContent",""],["ngbNavItem","",4,"cdScope"],[3,"ngbNavOutlet"],["poolUsageTpl",""],["id","pool-list","selectionType","single",3,"data","columns","hasDetails","status","autoReload","fetchData","setExpandedRow","updateSelection"],["table",""],["id","pool-list-actions",1,"table-actions",3,"permission","selection","tableActions"],["cdTableDetail","","id","pool-list-details",3,"selection","permissions","cacheTiers"],_,["uid","z99hzWtmk","grafanaStyle","two",3,"grafanaPath"],["decimals","2",3,"total","used",4,"ngIf"],["decimals","2",3,"total","used"]]},template:function(_,o){if(1&_&&(e.TgZ(0,"ul",0,1),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.YNc(5,Ri,4,11,"ng-template",5),e.qZA(),e.YNc(6,Mi,4,0,"li",6),e.qZA(),e._UZ(7,"div",7),e.YNc(8,Ti,1,1,"ng-template",null,8,e.W1O)),2&_){const n=e.MAs(1);e.xp6(6),e.Q6J("cdScope","grafana"),e.xp6(1),e.Q6J("ngbNavOutlet",n)}},directives:[g.Pz,g.nv,g.Vx,g.uN,ii,g.tO,Se.a,ni.K,Ei,Ge.F,C.O5,gi.O],styles:["cd-pool-list .pg-clean{color:#0b0} cd-pool-list .pg-working{color:#2b99a8} cd-pool-list .pg-warning{color:#ffc200} cd-pool-list .pg-unknown{color:#ef5c55}"]}),t})(),ze=(()=>{class t{}return t.\u0275fac=function(_){return new(_||t)},t.\u0275mod=e.oAB({type:t}),t.\u0275inj=e.cJS({imports:[[f_.t,C.ez,g.Oz,d_.m,Oe.Bz,a.UX,g.HK,u_.BlockModule]]}),t})();const Li=[{path:"",component:Si},{path:M.MQ.CREATE,component:Ue,data:{breadcrumbs:M.Qn.CREATE}},{path:`${M.MQ.EDIT}/:name`,component:Ue,data:{breadcrumbs:M.Qn.EDIT}}];let Ai=(()=>{class t{}return t.\u0275fac=function(_){return new(_||t)},t.\u0275mod=e.oAB({type:t}),t.\u0275inj=e.cJS({imports:[[ze,Oe.Bz.forChild(Li)]]}),t})()}}]); \ No newline at end of file diff --git a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/585.764bfab2e2f489fdfd7f.js b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/585.764bfab2e2f489fdfd7f.js new file mode 100644 index 000000000..810294007 --- /dev/null +++ b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/585.764bfab2e2f489fdfd7f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkceph_dashboard=self.webpackChunkceph_dashboard||[]).push([[585],{24585:(Fi,Fe,r)=>{r.r(Fe),r.d(Fe,{PoolModule:()=>ze,RoutedPoolModule:()=>Ai});var C=r(12057),a=r(24751),Oe=r(6283),g=r(38549),M=r(79512),d_=r(44466),u_=r(91330),f_=r(370),P_=r(23815),u=r.n(P_),E_=r(80226),g_=r(26504),ue=r(80842);class S{constructor(){this.nodes=[],this.idTree={},this.allDevices=[],this.buckets=[],this.failureDomains={},this.failureDomainKeys=[],this.devices=[],this.deviceCount=0}static searchFailureDomains(i,_){return this.getFailureDomains(this.search(i,_))}static search(i,_){const[o,n]=_.split("~"),s=i.find(c=>["name","id","type"].some(d=>c[d]===o));return s?(i=this.getSubNodes(s,this.createIdTreeFromNodes(i)),n&&(i=this.filterNodesByDeviceType(i,n)),i):[]}static createIdTreeFromNodes(i){const _={};return i.forEach(o=>{_[o.id]=o}),_}static getSubNodes(i,_){let o=[i];return i.children&&i.children.forEach(n=>{o=o.concat(this.getSubNodes(_[n],_))}),o}static filterNodesByDeviceType(i,_){let n,o=i.filter(c=>c.device_class&&c.device_class!==_).map(c=>c.id),s=o;do{n=!1,i=i.filter(d=>!o.includes(d.id));const c=[];i.forEach(d=>{d.children&&d.children.every(P=>o.includes(P))&&(c.push(d.id),n=!0)}),n&&(o=c,s=s.concat(c))}while(n);return(i=u().cloneDeep(i)).map(c=>(c.children&&(c.children=c.children.filter(d=>!s.includes(d))),c))}static getFailureDomains(i){const _={};return i.forEach(o=>{const n=o.type;_[n]||(_[n]=[]),_[n].push(o)}),_}initCrushNodeSelection(i,_,o,n){this.nodes=i,this.idTree=S.createIdTreeFromNodes(i),i.forEach(s=>{this.idTree[s.id]=s}),this.buckets=u().sortBy(i.filter(s=>s.children),"name"),this.controls={root:_,failure:o,device:n},this.preSelectRoot(),this.controls.root.valueChanges.subscribe(()=>this.onRootChange()),this.controls.failure.valueChanges.subscribe(()=>this.onFailureDomainChange()),this.controls.device.valueChanges.subscribe(()=>this.onDeviceChange())}preSelectRoot(){const i=this.nodes.find(_=>"root"===_.type);this.silentSet(this.controls.root,i),this.onRootChange()}silentSet(i,_){i.setValue(_,{emitEvent:!1})}onRootChange(){const i=S.getSubNodes(this.controls.root.value,this.idTree),_=S.getFailureDomains(i);Object.keys(_).forEach(o=>{_[o].length<=1&&delete _[o]}),this.failureDomains=_,this.failureDomainKeys=Object.keys(_).sort(),this.updateFailureDomain()}updateFailureDomain(){let i=this.getIncludedCustomValue(this.controls.failure,Object.keys(this.failureDomains));""===i&&(i=this.setMostCommonDomain(this.controls.failure)),this.updateDevices(i)}getIncludedCustomValue(i,_){return i.dirty&&_.includes(i.value)?i.value:""}setMostCommonDomain(i){let _={n:0,type:""};return Object.keys(this.failureDomains).forEach(o=>{const n=this.failureDomains[o].length;_.nS.getSubNodes(n,this.idTree)));this.allDevices=_.filter(n=>n.device_class).map(n=>n.device_class),this.devices=u().uniq(this.allDevices).sort();const o=1===this.devices.length?this.devices[0]:this.getIncludedCustomValue(this.controls.device,this.devices);this.silentSet(this.controls.device,o),this.onDeviceChange(o)}onDeviceChange(i=this.controls.device.value){this.deviceCount=""===i?this.allDevices.length:this.allDevices.filter(_=>_===i).length}}var Ne=r(30982),p_=r(14745),b=r(65862),R_=r(93614),be=r(95463),E=r(77205),m_=r(30633),v=r(76111),C_=r(47557),M_=r(28211),de=r(32337),e=r(74788),ve=r(62862),Ie=r(83608),$e=r(60312),fe=r(41582),Pe=r(56310),Ee=r(87925),ge=r(94276),pe=r(82945),Re=r(18372),me=r(30839),Ce=r(10545);function h_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,31),e.qZA())}function T_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,32),e.qZA())}function S_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,33),e.qZA())}function L_(t,i){1&t&&(e.TgZ(0,"option",26),e.SDv(1,34),e.qZA())}function A_(t,i){if(1&t&&(e.TgZ(0,"option",35),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_.name," ")}}function F_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,36),e.qZA())}function N_(t,i){1&t&&(e.TgZ(0,"option",26),e.SDv(1,37),e.qZA())}function b_(t,i){if(1&t&&(e.TgZ(0,"option",35),e._uU(1),e.qZA()),2&t){const _=i.$implicit,o=e.oxw();e.Q6J("ngValue",_),e.xp6(1),e.AsE(" ",_," ( ",o.failureDomains[_].length," ) ")}}function v_(t,i){1&t&&(e.TgZ(0,"span",30),e.SDv(1,38),e.qZA())}function I_(t,i){if(1&t&&(e.TgZ(0,"option",35),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}let $_=(()=>{class t extends S{constructor(_,o,n,s,c){super(),this.formBuilder=_,this.activeModal=o,this.taskWrapper=n,this.crushRuleService=s,this.actionLabels=c,this.submitAction=new e.vpe,this.tooltips=this.crushRuleService.formTooltips,this.action=this.actionLabels.CREATE,this.resource="Crush Rule",this.createForm()}createForm(){this.form=this.formBuilder.group({name:["",[a.kI.required,a.kI.pattern("[A-Za-z0-9_-]+"),E.h.custom("uniqueName",_=>this.names&&-1!==this.names.indexOf(_))]],root:null,failure_domain:"",device_class:""})}ngOnInit(){this.crushRuleService.getInfo().subscribe(({names:_,nodes:o})=>{this.initCrushNodeSelection(o,this.form.get("root"),this.form.get("failure_domain"),this.form.get("device_class")),this.names=_})}onSubmit(){if(this.form.invalid)return void this.form.setErrors({cdSubmitButton:!0});const _=u().cloneDeep(this.form.value);_.root=_.root.name,""===_.device_class&&delete _.device_class,this.taskWrapper.wrapTaskAroundCall({task:new v.R("crushRule/create",_),call:this.crushRuleService.create(_)}).subscribe({error:()=>{this.form.setErrors({cdSubmitButton:!0})},complete:()=>{this.activeModal.close(),this.submitAction.emit(_)}})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ve.O),e.Y36(g.Kz),e.Y36(de.P),e.Y36(Ie.H),e.Y36(M.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-crush-rule-form-modal"]],outputs:{submitAction:"submitAction"},features:[e.qOj],decls:55,vars:27,consts:function(){let i,_,o,n,s,c,d,P,p,R,h,T,m;return i="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Name",o="Root",n="Failure domain type",s="Device class",c="Let Ceph decide",d="This field is required!",P="The name can only consist of alphanumeric characters, dashes and underscores.",p="The chosen erasure code profile name is already in use.",R="Loading...",h="This field is required!",T="Loading...",m="This field is required!",[[3,"modalRef"],[1,"modal-title"],i,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","name",1,"cd-col-form-label"],_,[1,"required"],[1,"cd-col-form-input"],["type","text","id","name","name","name","placeholder","Name...","formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","root",1,"cd-col-form-label"],o,[3,"html"],["id","root","name","root","formControlName","root",1,"form-control"],["ngValue","",4,"ngIf"],[3,"ngValue",4,"ngFor","ngForOf"],["for","failure_domain",1,"cd-col-form-label"],n,["id","failure_domain","name","failure_domain","formControlName","failure_domain",1,"form-control"],["for","device_class",1,"cd-col-form-label"],s,["id","device_class","name","device_class","formControlName","device_class",1,"form-control"],["ngValue",""],c,[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],d,P,p,R,[3,"ngValue"],h,T,m]},template:function(_,o){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.ynx(11),e.SDv(12,9),e.BQk(),e._UZ(13,"span",10),e.qZA(),e.TgZ(14,"div",11),e._UZ(15,"input",12),e.YNc(16,h_,2,0,"span",13),e.YNc(17,T_,2,0,"span",13),e.YNc(18,S_,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(19,"div",7),e.TgZ(20,"label",14),e.ynx(21),e.SDv(22,15),e.BQk(),e._UZ(23,"cd-helper",16),e._UZ(24,"span",10),e.qZA(),e.TgZ(25,"div",11),e.TgZ(26,"select",17),e.YNc(27,L_,2,0,"option",18),e.YNc(28,A_,2,2,"option",19),e.qZA(),e.YNc(29,F_,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(30,"div",7),e.TgZ(31,"label",20),e.ynx(32),e.SDv(33,21),e.BQk(),e._UZ(34,"cd-helper",16),e._UZ(35,"span",10),e.qZA(),e.TgZ(36,"div",11),e.TgZ(37,"select",22),e.YNc(38,N_,2,0,"option",18),e.YNc(39,b_,2,3,"option",19),e.qZA(),e.YNc(40,v_,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(41,"div",7),e.TgZ(42,"label",23),e.ynx(43),e.SDv(44,24),e.BQk(),e._UZ(45,"cd-helper",16),e.qZA(),e.TgZ(46,"div",11),e.TgZ(47,"select",25),e.TgZ(48,"option",26),e.SDv(49,27),e.qZA(),e.YNc(50,I_,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.TgZ(51,"div",28),e.TgZ(52,"cd-form-button-panel",29),e.NdJ("submitActionEvent",function(){return o.onSubmit()}),e.ALo(53,"titlecase"),e.ALo(54,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const n=e.MAs(7);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.pQV(e.lcZ(3,19,o.action))(e.lcZ(4,21,o.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",o.form),e.xp6(10),e.Q6J("ngIf",o.form.showError("name",n,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"pattern")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"uniqueName")),e.xp6(5),e.Q6J("html",o.tooltips.root),e.xp6(4),e.Q6J("ngIf",!o.buckets),e.xp6(1),e.Q6J("ngForOf",o.buckets),e.xp6(1),e.Q6J("ngIf",o.form.showError("root",n,"required")),e.xp6(5),e.Q6J("html",o.tooltips.failure_domain),e.xp6(4),e.Q6J("ngIf",!o.failureDomains),e.xp6(1),e.Q6J("ngForOf",o.failureDomainKeys),e.xp6(1),e.Q6J("ngIf",o.form.showError("failure_domain",n,"required")),e.xp6(5),e.Q6J("html",o.tooltips.device_class),e.xp6(5),e.Q6J("ngForOf",o.devices),e.xp6(2),e.Q6J("form",o.form)("submitText",e.lcZ(53,23,o.action)+" "+e.lcZ(54,25,o.resource))}},directives:[$e.z,a._Y,a.JL,fe.V,a.sg,Pe.P,Ee.o,a.Fj,ge.b,a.JJ,a.u,pe.U,C.O5,Re.S,a.EJ,C.sg,a.YN,a.Kr,me.p],pipes:[C.rS,Ce.m],styles:[""]}),t})();class D_{}var Z_=r(58497);let Me=(()=>{class t{constructor(_){this.http=_,this.apiPath="api/erasure_code_profile",this.formTooltips={k:"Each object is split in data-chunks parts, each stored on a different OSD.",m:"Compute coding chunks for each object and store them on different OSDs.\n The number of coding chunks is also the number of OSDs that can be down without losing data.",plugins:{jerasure:{description:"The jerasure plugin is the most generic and flexible plugin,\n it is also the default for Ceph erasure coded pools.",technique:"The more flexible technique is reed_sol_van : it is enough to set k\n and m. The cauchy_good technique can be faster but you need to chose the packetsize\n carefully. All of reed_sol_r6_op, liberation, blaum_roth, liber8tion are RAID6 equivalents\n in the sense that they can only be configured with m=2.",packetSize:"The encoding will be done on packets of bytes size at a time.\n Choosing the right packet size is difficult.\n The jerasure documentation contains extensive information on this topic."},lrc:{description:"With the jerasure plugin, when an erasure coded object is stored on\n multiple OSDs, recovering from the loss of one OSD requires reading from all the others.\n For instance if jerasure is configured with k=8 and m=4, losing one OSD requires reading\n from the eleven others to repair.\n\n The lrc erasure code plugin creates local parity chunks to be able to recover using\n less OSDs. For instance if lrc is configured with k=8, m=4 and l=4, it will create\n an additional parity chunk for every four OSDs. When a single OSD is lost, it can be\n recovered with only four OSDs instead of eleven.",l:"Group the coding and data chunks into sets of size locality. For instance,\n for k=4 and m=2, when locality=3 two groups of three are created. Each set can\n be recovered without reading chunks from another set.",crushLocality:"The type of the crush bucket in which each set of chunks defined\n by l will be stored. For instance, if it is set to rack, each group of l chunks will be\n placed in a different rack. It is used to create a CRUSH rule step such as step choose\n rack. If it is not set, no such grouping is done."},isa:{description:"The isa plugin encapsulates the ISA library. It only runs on Intel processors.",technique:"The ISA plugin comes in two Reed Solomon forms.\n If reed_sol_van is set, it is Vandermonde, if cauchy is set, it is Cauchy."},shec:{description:"The shec plugin encapsulates the multiple SHEC library.\n It allows ceph to recover data more efficiently than Reed Solomon codes.",c:"The number of parity chunks each of which includes each data chunk in its\n calculation range. The number is used as a durability estimator. For instance, if c=2,\n 2 OSDs can be down without losing data."},clay:{description:"CLAY (short for coupled-layer) codes are erasure codes designed to\n bring about significant savings in terms of network bandwidth and disk IO when a failed\n node/OSD/rack is being repaired.",d:"Number of OSDs requested to send data during recovery of a single chunk.\n d needs to be chosen such that k+1 <= d <= k+m-1. The larger the d, the better\n the savings.",scalar_mds:"scalar_mds specifies the plugin that is used as a building block\n in the layered construction. It can be one of jerasure, isa, shec.",technique:"technique specifies the technique that will be picked\n within the 'scalar_mds' plugin specified. Supported techniques\n are 'reed_sol_van', 'reed_sol_r6_op', 'cauchy_orig',\n 'cauchy_good', 'liber8tion' for jerasure, 'reed_sol_van',\n 'cauchy' for isa and 'single', 'multiple' for shec."}},crushRoot:"The name of the crush bucket used for the first step of the CRUSH rule.\n For instance step take default.",crushFailureDomain:"Ensure that no two chunks are in a bucket with the same failure\n domain. For instance, if the failure domain is host no two chunks will be stored on the same\n host. It is used to create a CRUSH rule step such as step chooseleaf host.",crushDeviceClass:"Restrict placement to devices of a specific class\n (e.g., ssd or hdd), using the crush device class names in the CRUSH map.",directory:"Set the directory name from which the erasure code plugin is loaded."}}list(){return this.http.get(this.apiPath)}create(_){return this.http.post(this.apiPath,_,{observe:"response"})}delete(_){return this.http.delete(`${this.apiPath}/${_}`,{observe:"response"})}getInfo(){return this.http.get(`ui-${this.apiPath}/info`)}}return t.\u0275fac=function(_){return new(_||t)(e.LFG(Z_.eN))},t.\u0275prov=e.Yz7({token:t,factory:t.\u0275fac,providedIn:"root"}),t})();function x_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,47),e.qZA())}function y_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,48),e.qZA())}function U_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,49),e.qZA())}function q_(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,50),e.qZA())}function H_(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}function G_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,52),e.qZA())}function z_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,53),e.qZA())}function X_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,54),e.qZA())}function w_(t,i){if(1&t&&(e.TgZ(0,"span",46),e.SDv(1,55),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.pQV(_.deviceCount),e.QtT(1)}}function Q_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,56),e.qZA())}function J_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,57),e.qZA())}function k_(t,i){if(1&t&&(e.TgZ(0,"span",39),e.SDv(1,58),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.pQV(_.lrcMultiK),e.QtT(1)}}function V_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,59),e.qZA())}function Y_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,60),e.qZA())}function B_(t,i){if(1&t&&(e.TgZ(0,"span",46),e.SDv(1,61),e.qZA()),2&t){const _=e.oxw();e.xp6(1),e.pQV(_.deviceCount),e.QtT(1)}}function j_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,65),e.qZA())}function K_(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,66),e.qZA())}function W_(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",62),e.TgZ(2,"span",14),e.SDv(3,63),e.qZA(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e._UZ(6,"input",64),e.YNc(7,j_,2,0,"span",12),e.YNc(8,K_,2,0,"span",12),e.qZA(),e.qZA()),2&t){const _=e.oxw(),o=e.MAs(7);e.xp6(4),e.Q6J("html",_.tooltips.plugins.shec.c),e.xp6(3),e.Q6J("ngIf",_.form.showError("c",o,"min")),e.xp6(1),e.Q6J("ngIf",_.form.showError("c",o,"cGreaterM"))}}function eo(t,i){1&t&&(e.TgZ(0,"span",39),e.SDv(1,75),e.qZA())}function _o(t,i){if(1&t&&(e.TgZ(0,"span",39),e.SDv(1,76),e.qZA()),2&t){const _=e.oxw(3);e.xp6(1),e.pQV(_.getDMin())(_.getDMax()),e.QtT(1)}}function oo(t,i){if(1&t&&(e.TgZ(0,"span",39),e.SDv(1,77),e.qZA()),2&t){const _=e.oxw(3);e.xp6(1),e.pQV(_.getDMax()),e.QtT(1)}}function to(t,i){if(1&t&&(e.ynx(0),e.YNc(1,_o,2,2,"span",23),e.YNc(2,oo,2,1,"span",23),e.BQk()),2&t){const _=e.oxw(2);e.xp6(1),e.Q6J("ngIf",_.getDMin()<_.getDMax()),e.xp6(1),e.Q6J("ngIf",_.getDMin()===_.getDMax())}}function io(t,i){if(1&t&&(e.TgZ(0,"span",46),e.SDv(1,78),e.qZA()),2&t){const _=e.oxw(2);e.xp6(1),e.pQV(_.getDMin()),e.QtT(1)}}function no(t,i){if(1&t&&(e.TgZ(0,"span",46),e.SDv(1,79),e.qZA()),2&t){const _=e.oxw(2);e.xp6(1),e.pQV(_.getDMax()),e.QtT(1)}}function so(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div",7),e.TgZ(1,"label",67),e.TgZ(2,"span",14),e.SDv(3,68),e.qZA(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e.TgZ(6,"div",69),e._UZ(7,"input",70),e.TgZ(8,"span",71),e.TgZ(9,"button",72),e.NdJ("click",function(){return e.CHM(_),e.oxw().toggleDCalc()}),e._UZ(10,"i",73),e.qZA(),e.qZA(),e.qZA(),e.YNc(11,eo,2,0,"span",23),e.YNc(12,to,3,2,"ng-container",74),e.YNc(13,io,2,1,"span",12),e.YNc(14,no,2,1,"span",12),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(),o=e.MAs(7);e.xp6(4),e.Q6J("html",_.tooltips.plugins.clay.d),e.xp6(6),e.Q6J("ngClass",_.dCalc?_.icons.unlock:_.icons.lock),e.xp6(1),e.Q6J("ngIf",_.dCalc),e.xp6(1),e.Q6J("ngIf",!_.dCalc),e.xp6(1),e.Q6J("ngIf",_.form.showError("d",o,"dMin")),e.xp6(1),e.Q6J("ngIf",_.form.showError("d",o,"dMax"))}}function ao(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,84),e.qZA())}function lo(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,85),e.qZA())}function ro(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,86),e.qZA())}function co(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",80),e.TgZ(2,"span",14),e.SDv(3,81),e.qZA(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e._UZ(6,"input",82),e.YNc(7,ao,2,0,"span",12),e.YNc(8,lo,2,0,"span",12),e.YNc(9,ro,2,0,"span",12),e.TgZ(10,"span",39),e.SDv(11,83),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(),o=e.MAs(7);e.xp6(4),e.Q6J("html",_.tooltips.plugins.lrc.l),e.xp6(3),e.Q6J("ngIf",_.form.showError("l",o,"required")),e.xp6(1),e.Q6J("ngIf",_.form.showError("l",o,"min")),e.xp6(1),e.Q6J("ngIf",_.form.showError("l",o,"unequal")),e.xp6(2),e.pQV(_.lrcGroups),e.QtT(11)}}function Oo(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,87),e.qZA())}function uo(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit,o=e.oxw();e.Q6J("ngValue",_),e.xp6(1),e.AsE(" ",_," ( ",o.failureDomains[_].length," ) ")}}function fo(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,91),e.qZA())}function Po(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,92),e.qZA())}function Eo(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit,o=e.oxw(2);e.Q6J("ngValue",_),e.xp6(1),e.AsE(" ",_," ( ",o.failureDomains[_].length," ) ")}}function go(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",88),e.ynx(2),e.SDv(3,89),e.BQk(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e.TgZ(6,"select",90),e.YNc(7,fo,2,0,"option",18),e.YNc(8,Po,2,0,"option",18),e.YNc(9,Eo,2,3,"option",19),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw();e.xp6(4),e.Q6J("html",_.tooltips.plugins.lrc.crushLocality),e.xp6(3),e.Q6J("ngIf",!_.failureDomains),e.xp6(1),e.Q6J("ngIf",_.failureDomainKeys.length>0),e.xp6(1),e.Q6J("ngForOf",_.failureDomainKeys)}}function po(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}const De=function(t,i,_){return[t,i,_]};function Ro(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",93),e.ynx(2),e.SDv(3,94),e.BQk(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e.TgZ(6,"select",95),e.YNc(7,po,2,2,"option",19),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw();e.xp6(4),e.Q6J("html",_.tooltips.plugins.clay.scalar_mds),e.xp6(3),e.Q6J("ngForOf",e.kEZ(2,De,_.PLUGIN.JERASURE,_.PLUGIN.ISA,_.PLUGIN.SHEC))}}function mo(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}function Co(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",96),e.ynx(2),e.SDv(3,97),e.BQk(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e.TgZ(6,"select",98),e.YNc(7,mo,2,2,"option",19),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw();e.xp6(4),e.Q6J("html",_.tooltips.plugins[_.plugin].technique),e.xp6(3),e.Q6J("ngForOf",_.techniques)}}function Mo(t,i){1&t&&(e.TgZ(0,"span",46),e.SDv(1,102),e.qZA())}function ho(t,i){if(1&t&&(e.TgZ(0,"div",7),e.TgZ(1,"label",99),e.ynx(2),e.SDv(3,100),e.BQk(),e._UZ(4,"cd-helper",16),e.qZA(),e.TgZ(5,"div",10),e._UZ(6,"input",101),e.YNc(7,Mo,2,0,"span",12),e.qZA(),e.qZA()),2&t){const _=e.oxw(),o=e.MAs(7);e.xp6(4),e.Q6J("html",_.tooltips.plugins.jerasure.packetSize),e.xp6(3),e.Q6J("ngIf",_.form.showError("packetSize",o,"min"))}}function To(t,i){1&t&&(e.TgZ(0,"option",37),e.SDv(1,103),e.qZA())}function So(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_.name," ")}}function Lo(t,i){if(1&t&&(e.TgZ(0,"option",51),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_," ")}}let Ao=(()=>{class t extends S{constructor(_,o,n,s,c){super(),this.formBuilder=_,this.activeModal=o,this.taskWrapper=n,this.ecpService=s,this.actionLabels=c,this.submitAction=new e.vpe,this.tooltips=this.ecpService.formTooltips,this.PLUGIN={LRC:"lrc",SHEC:"shec",CLAY:"clay",JERASURE:"jerasure",ISA:"isa"},this.plugin=this.PLUGIN.JERASURE,this.icons=b.P,this.action=this.actionLabels.CREATE,this.resource="EC Profile",this.createForm(),this.setJerasureDefaults()}createForm(){this.form=this.formBuilder.group({name:[null,[a.kI.required,a.kI.pattern("[A-Za-z0-9_-]+"),E.h.custom("uniqueName",_=>this.names&&-1!==this.names.indexOf(_))]],plugin:[this.PLUGIN.JERASURE,[a.kI.required]],k:[4,[a.kI.required,E.h.custom("max",()=>this.baseValueValidation(!0)),E.h.custom("unequal",_=>this.lrcDataValidation(_)),E.h.custom("kLowerM",_=>this.shecDataValidation(_))]],m:[2,[a.kI.required,E.h.custom("max",()=>this.baseValueValidation())]],crushFailureDomain:"",crushRoot:null,crushDeviceClass:"",directory:"",technique:"reed_sol_van",packetSize:[2048],l:[3,[a.kI.required,E.h.custom("unequal",_=>this.lrcLocalityValidation(_))]],crushLocality:"",c:[2,[a.kI.required,E.h.custom("cGreaterM",_=>this.shecDurabilityValidation(_))]],d:[5,[a.kI.required,E.h.custom("dMin",_=>this.dMinValidation(_)),E.h.custom("dMax",_=>this.dMaxValidation(_))]],scalar_mds:[this.PLUGIN.JERASURE,[a.kI.required]]}),this.toggleDCalc(),this.form.get("k").valueChanges.subscribe(()=>this.updateValidityOnChange(["m","l","d"])),this.form.get("m").valueChanges.subscribe(()=>this.updateValidityOnChange(["k","l","c","d"])),this.form.get("l").valueChanges.subscribe(()=>this.updateValidityOnChange(["k","m"])),this.form.get("plugin").valueChanges.subscribe(_=>this.onPluginChange(_)),this.form.get("scalar_mds").valueChanges.subscribe(()=>this.setClayDefaultsForScalar())}baseValueValidation(_=!1){return this.validValidation(()=>this.getKMSum()>this.deviceCount&&this.form.getValue("k")>this.form.getValue("m")===_)}validValidation(_,o){return!((!this.form||o)&&this.plugin!==o)&&_()}getKMSum(){return this.form.getValue("k")+this.form.getValue("m")}lrcDataValidation(_){return this.validValidation(()=>{const o=this.form.getValue("m"),n=this.form.getValue("l"),s=_+o;return this.lrcMultiK=_/(s/n),_%(s/n)!=0},"lrc")}shecDataValidation(_){return this.validValidation(()=>this.form.getValue("m")>_,"shec")}lrcLocalityValidation(_){return this.validValidation(()=>{const o=this.getKMSum();return this.lrcGroups=_>0?o/_:0,_>0&&o%_!=0},"lrc")}shecDurabilityValidation(_){return this.validValidation(()=>{const o=this.form.getValue("m");return _>o},"shec")}dMinValidation(_){return this.validValidation(()=>this.getDMin()>_,"clay")}getDMin(){return this.form.getValue("k")+1}dMaxValidation(_){return this.validValidation(()=>_>this.getDMax(),"clay")}getDMax(){const _=this.form.getValue("m");return this.form.getValue("k")+_-1}toggleDCalc(){this.dCalc=!this.dCalc,this.form.get("d")[this.dCalc?"disable":"enable"](),this.calculateD()}calculateD(){this.plugin!==this.PLUGIN.CLAY||!this.dCalc||this.form.silentSet("d",this.getDMax())}updateValidityOnChange(_){_.forEach(o=>{"d"===o&&this.calculateD(),this.form.get(o).updateValueAndValidity({emitEvent:!1})})}onPluginChange(_){this.plugin=_,_===this.PLUGIN.JERASURE?this.setJerasureDefaults():_===this.PLUGIN.LRC?this.setLrcDefaults():_===this.PLUGIN.ISA?this.setIsaDefaults():_===this.PLUGIN.SHEC?this.setShecDefaults():_===this.PLUGIN.CLAY&&this.setClayDefaults(),this.updateValidityOnChange(["m"])}setJerasureDefaults(){this.techniques=["reed_sol_van","reed_sol_r6_op","cauchy_orig","cauchy_good","liberation","blaum_roth","liber8tion"],this.setDefaults({k:4,m:2,technique:"reed_sol_van"})}setLrcDefaults(){this.setDefaults({k:4,m:2,l:3})}setIsaDefaults(){this.techniques=["reed_sol_van","cauchy"],this.setDefaults({k:7,m:3,technique:"reed_sol_van"})}setShecDefaults(){this.setDefaults({k:4,m:3,c:2})}setClayDefaults(){this.setDefaults({k:4,m:2,scalar_mds:this.PLUGIN.JERASURE}),this.setClayDefaultsForScalar()}setClayDefaultsForScalar(){const _=this.form.getValue("scalar_mds");let o="reed_sol_van";_===this.PLUGIN.JERASURE?this.techniques=["reed_sol_van","reed_sol_r6_op","cauchy_orig","cauchy_good","liber8tion"]:_===this.PLUGIN.ISA?this.techniques=["reed_sol_van","cauchy"]:(o="single",this.techniques=["single","multiple"]),this.setDefaults({technique:o})}setDefaults(_){Object.keys(_).forEach(o=>{const n=this.form.get(o),s=n.value;n.pristine||"technique"===o&&!this.techniques.includes(s)||"k"===o&&[4,7].includes(s)||"m"===o&&[2,3].includes(s)?n.setValue(_[o]):n.updateValueAndValidity()})}ngOnInit(){this.ecpService.getInfo().subscribe(({plugins:_,names:o,directory:n,nodes:s})=>{this.initCrushNodeSelection(s,this.form.get("crushRoot"),this.form.get("crushFailureDomain"),this.form.get("crushDeviceClass")),this.plugins=_,this.names=o,this.form.silentSet("directory",n),this.preValidateNumericInputFields()})}preValidateNumericInputFields(){const _=["k","m","l","c","d"].map(o=>this.form.get(o));_.forEach(o=>{o.markAsTouched(),o.markAsDirty()}),_[1].updateValueAndValidity()}onSubmit(){if(this.form.invalid)return void this.form.setErrors({cdSubmitButton:!0});const _=this.createJson();this.taskWrapper.wrapTaskAroundCall({task:new v.R("ecp/create",{name:_.name}),call:this.ecpService.create(_)}).subscribe({error:()=>{this.form.setErrors({cdSubmitButton:!0})},complete:()=>{this.activeModal.close(),this.submitAction.emit(_)}})}createJson(){const _={technique:[this.PLUGIN.ISA,this.PLUGIN.JERASURE,this.PLUGIN.CLAY],packetSize:[this.PLUGIN.JERASURE],l:[this.PLUGIN.LRC],crushLocality:[this.PLUGIN.LRC],c:[this.PLUGIN.SHEC],d:[this.PLUGIN.CLAY],scalar_mds:[this.PLUGIN.CLAY]},o=new D_,n=this.form.getValue("plugin");return Object.keys(this.form.controls).filter(s=>{const c=_[s],d=this.form.getValue(s);return(c&&c.includes(n)||!c)&&d&&""!==d}).forEach(s=>{this.extendJson(s,o)}),o}extendJson(_,o){const s=this.form.getValue(_);o[{crushFailureDomain:"crush-failure-domain",crushRoot:"crush-root",crushDeviceClass:"crush-device-class",packetSize:"packetsize",crushLocality:"crush-locality"}[_]||_]="crushRoot"===_?s.name:s}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ve.O),e.Y36(g.Kz),e.Y36(de.P),e.Y36(Me),e.Y36(M.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-erasure-code-profile-form-modal"]],outputs:{submitAction:"submitAction"},features:[e.qOj],decls:98,vars:53,consts:function(){let i,_,o,n,s,c,d,P,p,R,h,T,m,f,A,I,$,D,Z,x,y,U,q,H,G,z,X,w,Q,J,k,V,Y,B,j,K,N,W,ee,_e,oe,te,ie,ne,se,ae,le,re,ce;return i="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Name",o="Plugin",n="Data chunks (k)",s="Coding chunks (m)",c="Crush failure domain",d="Crush root",P="Crush device class",p="Let Ceph decide",R="Available OSDs: " + "\ufffd0\ufffd" + "",h="Directory",T="This field is required!",m="The name can only consist of alphanumeric characters, dashes and underscores.",f="The chosen erasure code profile name is already in use.",A="Loading...",I="This field is required!",$="This field is required!",D="Must be equal to or greater than 2.",Z="Chunks (k+m) have exceeded the available OSDs of " + "\ufffd0\ufffd" + ".",x="For an equal distribution k has to be a multiple of (k+m)/l.",y="K has to be equal to or greater than m in order to recover data correctly through c.",U="Distribution factor: " + "\ufffd0\ufffd" + "",q="This field is required!",H="Must be equal to or greater than 1.",G="Chunks (k+m) have exceeded the available OSDs of " + "\ufffd0\ufffd" + ".",z="Durability estimator (c)",X="Must be equal to or greater than 1.",w="C has to be equal to or lower than m as m defines the amount of chunks that can be used.",Q="Helper chunks (d)",J="Set d manually or use the plugin's default calculation that maximizes d.",k="D is automatically updated on k and m changes",V="D can be set from " + "\ufffd0\ufffd" + " to " + "\ufffd1\ufffd" + "",Y="D can only be set to " + "\ufffd0\ufffd" + "",B="D has to be greater than k (" + "\ufffd0\ufffd" + ").",j="D has to be lower than k + m (" + "\ufffd0\ufffd" + ").",K="Locality (l)",N="Locality groups: " + "\ufffd0\ufffd" + "",W="This field is required!",ee="Must be equal to or greater than 1.",_e="Can't split up chunks (k+m) correctly with the current locality.",oe="Loading...",te="Crush Locality",ie="Loading...",ne="None",se="Scalar mds",ae="Technique",le="Packetsize",re="Must be equal to or greater than 1.",ce="Loading...",[[3,"modalRef"],[1,"modal-title"],i,[1,"modal-content"],["novalidate","",3,"formGroup"],["frm","ngForm"],[1,"modal-body"],[1,"form-group","row"],["for","name",1,"cd-col-form-label"],_,[1,"cd-col-form-input"],["type","text","id","name","name","name","placeholder","Name...","formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","plugin",1,"cd-col-form-label"],[1,"required"],o,[3,"html"],["id","plugin","name","plugin","formControlName","plugin",1,"form-control"],["ngValue","",4,"ngIf"],[3,"ngValue",4,"ngFor","ngForOf"],["for","k",1,"cd-col-form-label"],n,["type","number","id","k","name","k","ng-model","$ctrl.erasureCodeProfile.k","placeholder","Data chunks...","formControlName","k","min","2",1,"form-control"],["class","form-text text-muted",4,"ngIf"],["for","m",1,"cd-col-form-label"],s,["type","number","id","m","name","m","placeholder","Coding chunks...","formControlName","m","min","1",1,"form-control"],["class","form-group row",4,"ngIf"],["for","crushFailureDomain",1,"cd-col-form-label"],c,["id","crushFailureDomain","name","crushFailureDomain","formControlName","crushFailureDomain",1,"form-control"],["for","crushRoot",1,"cd-col-form-label"],d,["id","crushRoot","name","crushRoot","formControlName","crushRoot",1,"form-control"],["for","crushDeviceClass",1,"cd-col-form-label"],P,["id","crushDeviceClass","name","crushDeviceClass","formControlName","crushDeviceClass",1,"form-control"],["ngValue",""],p,[1,"form-text","text-muted"],R,["for","directory",1,"cd-col-form-label"],h,["type","text","id","directory","name","directory","placeholder","Path...","formControlName","directory",1,"form-control"],[1,"modal-footer"],[3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],T,m,f,A,[3,"ngValue"],I,$,D,Z,x,y,U,q,H,G,["for","c",1,"cd-col-form-label"],z,["type","number","id","c","name","c","placeholder","Coding chunks...","formControlName","c","min","1",1,"form-control"],X,w,["for","d",1,"cd-col-form-label"],Q,[1,"input-group"],["type","number","id","d","name","d","placeholder","Helper chunks...","formControlName","d",1,"form-control"],[1,"input-group-append"],["id","d-calc-btn","ngbTooltip",J,"type","button",1,"btn","btn-light",3,"click"],["aria-hidden","true",3,"ngClass"],[4,"ngIf"],k,V,Y,B,j,["for","l",1,"cd-col-form-label"],K,["type","number","id","l","name","l","placeholder","Coding chunks...","formControlName","l","min","1",1,"form-control"],N,W,ee,_e,oe,["for","crushLocality",1,"cd-col-form-label"],te,["id","crushLocality","name","crushLocality","formControlName","crushLocality",1,"form-control"],ie,ne,["for","scalar_mds",1,"cd-col-form-label"],se,["id","scalar_mds","name","scalar_mds","formControlName","scalar_mds",1,"form-control"],["for","technique",1,"cd-col-form-label"],ae,["id","technique","name","technique","formControlName","technique",1,"form-control"],["for","packetSize",1,"cd-col-form-label"],le,["type","number","id","packetSize","name","packetSize","placeholder","Packetsize...","formControlName","packetSize","min","1",1,"form-control"],re,ce]},template:function(_,o){if(1&_&&(e.TgZ(0,"cd-modal",0),e.ynx(1,1),e.SDv(2,2),e.ALo(3,"titlecase"),e.ALo(4,"upperFirst"),e.BQk(),e.ynx(5,3),e.TgZ(6,"form",4,5),e.TgZ(8,"div",6),e.TgZ(9,"div",7),e.TgZ(10,"label",8),e.SDv(11,9),e.qZA(),e.TgZ(12,"div",10),e._UZ(13,"input",11),e.YNc(14,x_,2,0,"span",12),e.YNc(15,y_,2,0,"span",12),e.YNc(16,U_,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(17,"div",7),e.TgZ(18,"label",13),e.TgZ(19,"span",14),e.SDv(20,15),e.qZA(),e._UZ(21,"cd-helper",16),e.qZA(),e.TgZ(22,"div",10),e.TgZ(23,"select",17),e.YNc(24,q_,2,0,"option",18),e.YNc(25,H_,2,2,"option",19),e.qZA(),e.YNc(26,G_,2,0,"span",12),e.qZA(),e.qZA(),e.TgZ(27,"div",7),e.TgZ(28,"label",20),e.TgZ(29,"span",14),e.SDv(30,21),e.qZA(),e._UZ(31,"cd-helper",16),e.qZA(),e.TgZ(32,"div",10),e._UZ(33,"input",22),e.YNc(34,z_,2,0,"span",12),e.YNc(35,X_,2,0,"span",12),e.YNc(36,w_,2,1,"span",12),e.YNc(37,Q_,2,0,"span",12),e.YNc(38,J_,2,0,"span",12),e.YNc(39,k_,2,1,"span",23),e.qZA(),e.qZA(),e.TgZ(40,"div",7),e.TgZ(41,"label",24),e.TgZ(42,"span",14),e.SDv(43,25),e.qZA(),e._UZ(44,"cd-helper",16),e.qZA(),e.TgZ(45,"div",10),e._UZ(46,"input",26),e.YNc(47,V_,2,0,"span",12),e.YNc(48,Y_,2,0,"span",12),e.YNc(49,B_,2,1,"span",12),e.qZA(),e.qZA(),e.YNc(50,W_,9,3,"div",27),e.YNc(51,so,15,6,"div",27),e.YNc(52,co,12,5,"div",27),e.TgZ(53,"div",7),e.TgZ(54,"label",28),e.ynx(55),e.SDv(56,29),e.BQk(),e._UZ(57,"cd-helper",16),e.qZA(),e.TgZ(58,"div",10),e.TgZ(59,"select",30),e.YNc(60,Oo,2,0,"option",18),e.YNc(61,uo,2,3,"option",19),e.qZA(),e.qZA(),e.qZA(),e.YNc(62,go,10,4,"div",27),e.YNc(63,Ro,8,6,"div",27),e.YNc(64,Co,8,2,"div",27),e.YNc(65,ho,8,2,"div",27),e.TgZ(66,"div",7),e.TgZ(67,"label",31),e.ynx(68),e.SDv(69,32),e.BQk(),e._UZ(70,"cd-helper",16),e.qZA(),e.TgZ(71,"div",10),e.TgZ(72,"select",33),e.YNc(73,To,2,0,"option",18),e.YNc(74,So,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.TgZ(75,"div",7),e.TgZ(76,"label",34),e.ynx(77),e.SDv(78,35),e.BQk(),e._UZ(79,"cd-helper",16),e.qZA(),e.TgZ(80,"div",10),e.TgZ(81,"select",36),e.TgZ(82,"option",37),e.SDv(83,38),e.qZA(),e.YNc(84,Lo,2,2,"option",19),e.qZA(),e.TgZ(85,"span",39),e.SDv(86,40),e.qZA(),e.qZA(),e.qZA(),e.TgZ(87,"div",7),e.TgZ(88,"label",41),e.ynx(89),e.SDv(90,42),e.BQk(),e._UZ(91,"cd-helper",16),e.qZA(),e.TgZ(92,"div",10),e._UZ(93,"input",43),e.qZA(),e.qZA(),e.qZA(),e.TgZ(94,"div",44),e.TgZ(95,"cd-form-button-panel",45),e.NdJ("submitActionEvent",function(){return o.onSubmit()}),e.ALo(96,"titlecase"),e.ALo(97,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.BQk(),e.qZA()),2&_){const n=e.MAs(7);e.Q6J("modalRef",o.activeModal),e.xp6(4),e.pQV(e.lcZ(3,41,o.action))(e.lcZ(4,43,o.resource)),e.QtT(2),e.xp6(2),e.Q6J("formGroup",o.form),e.xp6(8),e.Q6J("ngIf",o.form.showError("name",n,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"pattern")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"uniqueName")),e.xp6(5),e.Q6J("html",o.tooltips.plugins[o.plugin].description),e.xp6(3),e.Q6J("ngIf",!o.plugins),e.xp6(1),e.Q6J("ngForOf",o.plugins),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",n,"required")),e.xp6(5),e.Q6J("html",o.tooltips.k),e.xp6(3),e.Q6J("ngIf",o.form.showError("k",n,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("k",n,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("k",n,"max")),e.xp6(1),e.Q6J("ngIf",o.form.showError("k",n,"unequal")),e.xp6(1),e.Q6J("ngIf",o.form.showError("k",n,"kLowerM")),e.xp6(1),e.Q6J("ngIf","lrc"===o.plugin),e.xp6(5),e.Q6J("html",o.tooltips.m),e.xp6(3),e.Q6J("ngIf",o.form.showError("m",n,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("m",n,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("m",n,"max")),e.xp6(1),e.Q6J("ngIf","shec"===o.plugin),e.xp6(1),e.Q6J("ngIf","clay"===o.plugin),e.xp6(1),e.Q6J("ngIf",o.plugin===o.PLUGIN.LRC),e.xp6(5),e.Q6J("html",o.tooltips.crushFailureDomain),e.xp6(3),e.Q6J("ngIf",!o.failureDomains),e.xp6(1),e.Q6J("ngForOf",o.failureDomainKeys),e.xp6(1),e.Q6J("ngIf",o.plugin===o.PLUGIN.LRC),e.xp6(1),e.Q6J("ngIf",o.PLUGIN.CLAY===o.plugin),e.xp6(1),e.Q6J("ngIf",e.kEZ(49,De,o.PLUGIN.JERASURE,o.PLUGIN.ISA,o.PLUGIN.CLAY).includes(o.plugin)),e.xp6(1),e.Q6J("ngIf",o.plugin===o.PLUGIN.JERASURE),e.xp6(5),e.Q6J("html",o.tooltips.crushRoot),e.xp6(3),e.Q6J("ngIf",!o.buckets),e.xp6(1),e.Q6J("ngForOf",o.buckets),e.xp6(5),e.Q6J("html",o.tooltips.crushDeviceClass),e.xp6(5),e.Q6J("ngForOf",o.devices),e.xp6(2),e.pQV(o.deviceCount),e.QtT(86),e.xp6(5),e.Q6J("html",o.tooltips.directory),e.xp6(4),e.Q6J("form",o.form)("submitText",e.lcZ(96,45,o.action)+" "+e.lcZ(97,47,o.resource))}},directives:[$e.z,a._Y,a.JL,fe.V,a.sg,Pe.P,Ee.o,a.Fj,ge.b,a.JJ,a.u,pe.U,C.O5,Re.S,a.EJ,C.sg,a.wV,a.qQ,a.YN,a.Kr,me.p,g._L,C.mk],pipes:[C.rS,Ce.m],styles:[""]}),t})();var Fo=r(7022);class No{constructor(){this.erasureInfo=!1,this.crushInfo=!1,this.pgs=1,this.poolTypes=["erasure","replicated"],this.applications={selected:[],default:["cephfs","rbd","rgw"],available:[],validators:[a.kI.pattern("[A-Za-z0-9_]+"),a.kI.maxLength(128)],messages:new Fo.a({empty:"No applications added",selectionLimit:{text:"Applications limit reached",tooltip:"A pool can only have up to four applications definitions."},customValidations:{pattern:"Allowed characters '_a-zA-Z0-9'",maxlength:"Maximum length is 128 characters"},filter:"Filter or add applications'",add:"Add application"})}}}var Ze=r(63285),he=r(74937),bo=r(63622),vo=r(60192),Io=r(17932),$o=r(54555),Do=r(30490),xe=r(61350);const Zo=["crushInfoTabs"],xo=["crushDeletionBtn"],yo=["ecpInfoTabs"],Uo=["ecpDeletionBtn"];function qo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,42),e.qZA())}function Ho(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,43),e.qZA())}function Go(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,44),e.qZA())}function zo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,45),e.qZA())}function Xo(t,i){if(1&t&&(e.TgZ(0,"option",46),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function wo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,47),e.qZA())}function Qo(t,i){if(1&t&&(e.TgZ(0,"option",46),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Jo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,58),e.qZA())}function ko(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,59),e.qZA())}function Vo(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,60),e.qZA())}function Yo(t,i){1&t&&(e.TgZ(0,"span",55),e.SDv(1,61),e.qZA())}function Bo(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div",8),e.TgZ(1,"label",52),e.SDv(2,53),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"input",54),e.NdJ("focus",function(){return e.CHM(_),e.oxw(3).externalPgChange=!1})("blur",function(){return e.CHM(_),e.oxw(3).alignPgs()}),e.qZA(),e.YNc(5,Jo,2,0,"span",13),e.YNc(6,ko,2,0,"span",13),e.YNc(7,Vo,2,0,"span",13),e.TgZ(8,"span",55),e._UZ(9,"cd-doc",56),e.qZA(),e.YNc(10,Yo,2,0,"span",57),e.qZA(),e.qZA()}if(2&t){e.oxw(2);const _=e.MAs(2),o=e.oxw();e.xp6(5),e.Q6J("ngIf",o.form.showError("pgNum",_,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("pgNum",_,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("pgNum",_,"34")),e.xp6(3),e.Q6J("ngIf",o.externalPgChange)}}function jo(t,i){if(1&t&&(e.TgZ(0,"span",41),e.TgZ(1,"ul",66),e.TgZ(2,"li"),e.SDv(3,67),e.qZA(),e.TgZ(4,"li"),e.SDv(5,68),e.qZA(),e.qZA(),e.qZA()),2&t){const _=e.oxw(4);e.xp6(3),e.pQV(_.getMinSize()),e.QtT(3),e.xp6(2),e.pQV(_.getMaxSize()),e.QtT(5)}}function Ko(t,i){if(1&t&&(e.TgZ(0,"span",41),e.SDv(1,69),e.qZA()),2&t){const _=e.oxw(4);e.xp6(1),e.pQV(_.getMinSize())(_.getMaxSize()),e.QtT(1)}}function Wo(t,i){1&t&&(e.TgZ(0,"span",70),e.SDv(1,71),e.qZA())}function et(t,i){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",62),e.SDv(2,63),e.qZA(),e.TgZ(3,"div",11),e._UZ(4,"input",64),e.YNc(5,jo,6,2,"span",13),e.YNc(6,Ko,2,2,"span",13),e.YNc(7,Wo,2,0,"span",65),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),o=e.oxw();e.xp6(4),e.Q6J("max",o.getMaxSize())("min",o.getMinSize()),e.xp6(1),e.Q6J("ngIf",o.form.showError("size",_)),e.xp6(1),e.Q6J("ngIf",o.form.showError("size",_)),e.xp6(1),e.Q6J("ngIf",1===o.form.getValue("size"))}}function _t(t,i){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",72),e.SDv(2,73),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"div",74),e._UZ(5,"input",75),e.TgZ(6,"label",76),e.SDv(7,77),e.qZA(),e.qZA(),e.qZA(),e.qZA())}function ot(t,i){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"div",8),e.TgZ(2,"label",48),e.SDv(3,49),e.qZA(),e.TgZ(4,"div",11),e.TgZ(5,"select",50),e.YNc(6,Qo,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.YNc(7,Bo,11,4,"div",51),e.YNc(8,et,8,5,"div",51),e.YNc(9,_t,8,0,"div",51),e.qZA()),2&t){const _=e.oxw(2);e.xp6(6),e.Q6J("ngForOf",_.pgAutoscaleModes),e.xp6(1),e.Q6J("ngIf","on"!==_.form.getValue("pgAutoscaleMode")),e.xp6(1),e.Q6J("ngIf",_.isReplicated),e.xp6(1),e.Q6J("ngIf",_.info.is_all_bluestore&&_.isErasure)}}function tt(t,i){if(1&t&&e._UZ(0,"i",78),2&t){const _=e.oxw(2);e.Gre("",_.icons.warning," icon-warning-color")}}function it(t,i){1&t&&(e.TgZ(0,"option",17),e.SDv(1,93),e.qZA())}function nt(t,i){1&t&&(e.TgZ(0,"option",94),e.SDv(1,95),e.qZA()),2&t&&e.Q6J("ngValue",null)}function st(t,i){1&t&&(e.TgZ(0,"option",94),e.SDv(1,96),e.qZA()),2&t&&e.Q6J("ngValue",null)}function at(t,i){if(1&t&&(e.TgZ(0,"option",94),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_.name," ")}}const F=function(t){return[t]};function lt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"button",97),e.NdJ("click",function(){return e.CHM(_),e.oxw(4).addErasureCodeProfile()}),e._UZ(1,"i",89),e.qZA()}if(2&t){const _=e.oxw(4);e.xp6(1),e.Q6J("ngClass",e.VKq(1,F,_.icons.add))}}function rt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"button",98,99),e.NdJ("click",function(){return e.CHM(_),e.oxw(4).deleteErasureCodeProfile()}),e._UZ(2,"i",89),e.qZA()}if(2&t){const _=e.oxw(4);e.xp6(2),e.Q6J("ngClass",e.VKq(1,F,_.icons.trash))}}const ct=function(){return["name"]};function Ot(t,i){if(1&t&&e._UZ(0,"cd-table-key-value",110),2&t){const _=e.oxw(5);e.Q6J("renderObjects",!0)("hideKeys",e.DdM(4,ct))("data",_.form.getValue("erasureProfile"))("autoReload",!1)}}function dt(t,i){1&t&&(e.TgZ(0,"span"),e.SDv(1,113),e.qZA())}function ut(t,i){if(1&t&&(e.TgZ(0,"li"),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.xp6(1),e.hij(" ",_," ")}}function ft(t,i){if(1&t&&(e.TgZ(0,"ul"),e.YNc(1,ut,2,1,"li",114),e.qZA()),2&t){const _=e.oxw(6);e.xp6(1),e.Q6J("ngForOf",_.ecpUsage)}}function Pt(t,i){if(1&t&&(e.YNc(0,dt,2,0,"ng-template",null,111,e.W1O),e.YNc(2,ft,2,1,"ul",112)),2&t){const _=e.MAs(1),o=e.oxw(5);e.xp6(2),e.Q6J("ngIf",o.ecpUsage)("ngIfElse",_)}}function Et(t,i){if(1&t&&(e.TgZ(0,"span",100),e.TgZ(1,"ul",101,102),e.TgZ(3,"li",103),e.TgZ(4,"a",104),e.SDv(5,105),e.qZA(),e.YNc(6,Ot,1,5,"ng-template",106),e.qZA(),e.TgZ(7,"li",107),e.TgZ(8,"a",104),e.SDv(9,108),e.qZA(),e.YNc(10,Pt,3,2,"ng-template",106),e.qZA(),e.qZA(),e._UZ(11,"div",109),e.qZA()),2&t){const _=e.MAs(2);e.xp6(11),e.Q6J("ngbNavOutlet",_)}}const ye=function(t){return{active:t}};function gt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div",8),e.TgZ(1,"label",80),e.SDv(2,81),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"div",82),e.TgZ(5,"select",83),e.YNc(6,it,2,0,"option",84),e.YNc(7,nt,2,1,"option",85),e.YNc(8,st,2,1,"option",85),e.YNc(9,at,2,2,"option",86),e.qZA(),e.TgZ(10,"span",87),e.TgZ(11,"button",88),e.NdJ("click",function(){e.CHM(_);const n=e.oxw(3);return n.data.erasureInfo=!n.data.erasureInfo}),e._UZ(12,"i",89),e.qZA(),e.YNc(13,lt,2,3,"button",90),e.YNc(14,rt,3,3,"button",91),e.qZA(),e.qZA(),e.YNc(15,Et,12,1,"span",92),e.qZA(),e.qZA()}if(2&t){const _=e.oxw(3);e.xp6(6),e.Q6J("ngIf",!_.ecProfiles),e.xp6(1),e.Q6J("ngIf",_.ecProfiles&&0===_.ecProfiles.length),e.xp6(1),e.Q6J("ngIf",_.ecProfiles&&_.ecProfiles.length>0),e.xp6(1),e.Q6J("ngForOf",_.ecProfiles),e.xp6(2),e.Q6J("ngClass",e.VKq(9,ye,_.data.erasureInfo)),e.xp6(1),e.Q6J("ngClass",e.VKq(11,F,_.icons.questionCircle)),e.xp6(1),e.Q6J("ngIf",!_.editing),e.xp6(1),e.Q6J("ngIf",!_.editing),e.xp6(1),e.Q6J("ngIf",_.data.erasureInfo&&_.form.getValue("erasureProfile"))}}function pt(t,i){1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",115),e.SDv(2,116),e.qZA(),e.TgZ(3,"div",11),e.TgZ(4,"span",55),e.SDv(5,117),e.qZA(),e.qZA(),e.qZA())}function Rt(t,i){1&t&&(e.TgZ(0,"span",55),e.TgZ(1,"span"),e.SDv(2,120),e.qZA(),e._uU(3,"\xa0 "),e.qZA())}function mt(t,i){if(1&t&&(e.TgZ(0,"option",94),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("ngValue",_),e.xp6(1),e.hij(" ",_.rule_name," ")}}function Ct(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"button",97),e.NdJ("click",function(){return e.CHM(_),e.oxw(5).addCrushRule()}),e._UZ(1,"i",89),e.qZA()}if(2&t){const _=e.oxw(5);e.xp6(1),e.Q6J("ngClass",e.VKq(1,F,_.icons.add))}}function Mt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"button",126,127),e.NdJ("click",function(){return e.CHM(_),e.oxw(5).deleteCrushRule()}),e._UZ(2,"i",89),e.qZA()}if(2&t){const _=e.oxw(5);e.xp6(2),e.Q6J("ngClass",e.VKq(1,F,_.icons.trash))}}const ht=function(){return["steps","type","rule_name"]};function Tt(t,i){if(1&t&&e._UZ(0,"cd-table-key-value",110),2&t){const _=e.oxw(6);e.Q6J("renderObjects",!1)("hideKeys",e.DdM(4,ht))("data",_.form.getValue("crushRule"))("autoReload",!1)}}function St(t,i){if(1&t&&(e.TgZ(0,"li"),e._uU(1),e.qZA()),2&t){const _=i.$implicit,o=e.oxw(7);e.xp6(1),e.hij(" ",o.describeCrushStep(_)," ")}}function Lt(t,i){if(1&t&&(e.TgZ(0,"ol"),e.YNc(1,St,2,1,"li",114),e.qZA()),2&t){const _=e.oxw(6);e.xp6(1),e.Q6J("ngForOf",_.form.get("crushRule").value.steps)}}function At(t,i){1&t&&(e.TgZ(0,"span"),e.SDv(1,136),e.qZA())}function Ft(t,i){if(1&t&&(e.TgZ(0,"li"),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.xp6(1),e.hij(" ",_," ")}}function Nt(t,i){if(1&t&&(e.TgZ(0,"ul"),e.YNc(1,Ft,2,1,"li",114),e.qZA()),2&t){const _=e.oxw(7);e.xp6(1),e.Q6J("ngForOf",_.crushUsage)}}function bt(t,i){if(1&t&&(e.YNc(0,At,2,0,"ng-template",null,135,e.W1O),e.YNc(2,Nt,2,1,"ul",112)),2&t){const _=e.MAs(1),o=e.oxw(6);e.xp6(2),e.Q6J("ngIf",o.crushUsage)("ngIfElse",_)}}function vt(t,i){if(1&t&&(e.TgZ(0,"div",128),e.TgZ(1,"ul",101,129),e.TgZ(3,"li",130),e.TgZ(4,"a",104),e.SDv(5,131),e.qZA(),e.YNc(6,Tt,1,5,"ng-template",106),e.qZA(),e.TgZ(7,"li",132),e.TgZ(8,"a",104),e.SDv(9,133),e.qZA(),e.YNc(10,Lt,2,1,"ng-template",106),e.qZA(),e.TgZ(11,"li",107),e.TgZ(12,"a",104),e.SDv(13,134),e.qZA(),e.YNc(14,bt,3,2,"ng-template",106),e.qZA(),e.qZA(),e._UZ(15,"div",109),e.qZA()),2&t){const _=e.MAs(2);e.xp6(15),e.Q6J("ngbNavOutlet",_)}}function It(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,137),e.qZA())}function $t(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,138),e.qZA())}function Dt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div"),e.TgZ(1,"div",82),e.TgZ(2,"select",121),e.TgZ(3,"option",94),e.SDv(4,122),e.qZA(),e.YNc(5,mt,2,2,"option",86),e.qZA(),e.TgZ(6,"span",87),e.TgZ(7,"button",123),e.NdJ("click",function(){e.CHM(_);const n=e.oxw(4);return n.data.crushInfo=!n.data.crushInfo}),e._UZ(8,"i",89),e.qZA(),e.YNc(9,Ct,2,3,"button",90),e.YNc(10,Mt,3,3,"button",124),e.qZA(),e.qZA(),e.YNc(11,vt,16,1,"div",125),e.YNc(12,It,2,0,"span",13),e.YNc(13,$t,2,0,"span",13),e.qZA()}if(2&t){e.oxw(3);const _=e.MAs(2),o=e.oxw();e.xp6(3),e.Q6J("ngValue",null),e.xp6(2),e.Q6J("ngForOf",o.current.rules),e.xp6(2),e.Q6J("ngClass",e.VKq(9,ye,o.data.crushInfo)),e.xp6(1),e.Q6J("ngClass",e.VKq(11,F,o.icons.questionCircle)),e.xp6(1),e.Q6J("ngIf",o.isReplicated&&!o.editing),e.xp6(1),e.Q6J("ngIf",o.isReplicated&&!o.editing),e.xp6(1),e.Q6J("ngIf",o.data.crushInfo&&o.form.getValue("crushRule")),e.xp6(1),e.Q6J("ngIf",o.form.showError("crushRule",_,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("crushRule",_,"tooFewOsds"))}}function Zt(t,i){if(1&t&&(e.TgZ(0,"div",8),e.TgZ(1,"label",115),e.SDv(2,118),e.qZA(),e.TgZ(3,"div",11),e.YNc(4,Rt,4,0,"ng-template",null,119,e.W1O),e.YNc(6,Dt,14,13,"div",112),e.qZA(),e.qZA()),2&t){const _=e.MAs(5),o=e.oxw(3);e.xp6(6),e.Q6J("ngIf",o.current.rules.length>0)("ngIfElse",_)}}function xt(t,i){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"legend"),e.SDv(2,79),e.qZA(),e.YNc(3,gt,16,13,"div",51),e.YNc(4,pt,6,0,"div",51),e.YNc(5,Zt,7,2,"div",51),e.qZA()),2&t){const _=e.oxw(2);e.xp6(3),e.Q6J("ngIf",_.isErasure),e.xp6(1),e.Q6J("ngIf",_.isErasure&&!_.editing),e.xp6(1),e.Q6J("ngIf",_.isReplicated||_.editing)}}function yt(t,i){if(1&t&&(e.TgZ(0,"option",46),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Ut(t,i){1&t&&(e.TgZ(0,"option",17),e.SDv(1,156),e.qZA())}function qt(t,i){1&t&&(e.TgZ(0,"option",17),e.SDv(1,157),e.qZA())}function Ht(t,i){if(1&t&&(e.TgZ(0,"option",46),e._uU(1),e.qZA()),2&t){const _=i.$implicit;e.Q6J("value",_),e.xp6(1),e.hij(" ",_," ")}}function Gt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,158),e.qZA())}function zt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,159),e.qZA())}function Xt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,160),e.qZA())}function wt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,161),e.qZA())}function Qt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,162),e.qZA())}function Jt(t,i){if(1&t&&(e.TgZ(0,"div"),e.TgZ(1,"div",8),e.TgZ(2,"label",144),e.SDv(3,145),e.qZA(),e.TgZ(4,"div",11),e.TgZ(5,"select",146),e.YNc(6,Ut,2,0,"option",84),e.YNc(7,qt,2,0,"option",84),e.YNc(8,Ht,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.TgZ(9,"div",8),e.TgZ(10,"label",147),e.SDv(11,148),e.qZA(),e.TgZ(12,"div",11),e._UZ(13,"input",149),e.YNc(14,Gt,2,0,"span",13),e.YNc(15,zt,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(16,"div",8),e.TgZ(17,"label",150),e.SDv(18,151),e.qZA(),e.TgZ(19,"div",11),e._UZ(20,"input",152),e.YNc(21,Xt,2,0,"span",13),e.YNc(22,wt,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(23,"div",8),e.TgZ(24,"label",153),e.SDv(25,154),e.qZA(),e.TgZ(26,"div",11),e._UZ(27,"input",155),e.YNc(28,Qt,2,0,"span",13),e.qZA(),e.qZA(),e.qZA()),2&t){e.oxw(2);const _=e.MAs(2),o=e.oxw();e.xp6(6),e.Q6J("ngIf",!o.info.compression_algorithms),e.xp6(1),e.Q6J("ngIf",o.info.compression_algorithms&&0===o.info.compression_algorithms.length),e.xp6(1),e.Q6J("ngForOf",o.info.compression_algorithms),e.xp6(6),e.Q6J("ngIf",o.form.showError("minBlobSize",_,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("minBlobSize",_,"maximum")),e.xp6(6),e.Q6J("ngIf",o.form.showError("maxBlobSize",_,"min")),e.xp6(1),e.Q6J("ngIf",o.form.showError("maxBlobSize",_,"minimum")),e.xp6(6),e.Q6J("ngIf",o.form.showError("ratio",_,"min")||o.form.showError("ratio",_,"max"))}}function kt(t,i){if(1&t&&(e.TgZ(0,"div",139),e.TgZ(1,"legend"),e.SDv(2,140),e.qZA(),e.TgZ(3,"div",8),e.TgZ(4,"label",141),e.SDv(5,142),e.qZA(),e.TgZ(6,"div",11),e.TgZ(7,"select",143),e.YNc(8,yt,2,2,"option",19),e.qZA(),e.qZA(),e.qZA(),e.YNc(9,Jt,29,8,"div",20),e.qZA()),2&t){const _=e.oxw(2);e.xp6(8),e.Q6J("ngForOf",_.info.compression_modes),e.xp6(1),e.Q6J("ngIf",_.hasCompressionEnabled())}}function Vt(t,i){1&t&&(e.TgZ(0,"span",41),e.SDv(1,163),e.qZA())}function Yt(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"div",1),e.TgZ(1,"form",2,3),e.TgZ(3,"div",4),e.TgZ(4,"div",5),e.SDv(5,6),e.ALo(6,"titlecase"),e.ALo(7,"upperFirst"),e.qZA(),e.TgZ(8,"div",7),e.TgZ(9,"div",8),e.TgZ(10,"label",9),e.SDv(11,10),e.qZA(),e.TgZ(12,"div",11),e._UZ(13,"input",12),e.YNc(14,qo,2,0,"span",13),e.YNc(15,Ho,2,0,"span",13),e.YNc(16,Go,2,0,"span",13),e.YNc(17,zo,2,0,"span",13),e.qZA(),e.qZA(),e.TgZ(18,"div",8),e.TgZ(19,"label",14),e.SDv(20,15),e.qZA(),e.TgZ(21,"div",11),e.TgZ(22,"select",16),e.TgZ(23,"option",17),e.SDv(24,18),e.qZA(),e.YNc(25,Xo,2,2,"option",19),e.qZA(),e.YNc(26,wo,2,0,"span",13),e.qZA(),e.qZA(),e.YNc(27,ot,10,4,"div",20),e.TgZ(28,"div",8),e.TgZ(29,"label",21),e.SDv(30,22),e.qZA(),e.TgZ(31,"div",11),e.TgZ(32,"cd-select-badges",23),e.NdJ("selection",function(){return e.CHM(_),e.oxw().appSelection()}),e.qZA(),e.YNc(33,tt,1,3,"i",24),e.qZA(),e.qZA(),e.YNc(34,xt,6,3,"div",20),e.YNc(35,kt,10,2,"div",25),e.TgZ(36,"div"),e.TgZ(37,"legend"),e.SDv(38,26),e.qZA(),e.TgZ(39,"div",8),e.TgZ(40,"label",27),e.ynx(41),e.SDv(42,28),e.BQk(),e.TgZ(43,"cd-helper"),e.TgZ(44,"span"),e.SDv(45,29),e.qZA(),e._UZ(46,"br"),e.TgZ(47,"span"),e.SDv(48,30),e.qZA(),e.qZA(),e.qZA(),e.TgZ(49,"div",11),e._UZ(50,"input",31),e.qZA(),e.qZA(),e.TgZ(51,"div",8),e.TgZ(52,"label",32),e.ynx(53),e.SDv(54,33),e.BQk(),e.TgZ(55,"cd-helper"),e.TgZ(56,"span"),e.SDv(57,34),e.qZA(),e._UZ(58,"br"),e.TgZ(59,"span"),e.SDv(60,35),e.qZA(),e.qZA(),e.qZA(),e.TgZ(61,"div",11),e._UZ(62,"input",36),e.YNc(63,Vt,2,0,"span",13),e.qZA(),e.qZA(),e.qZA(),e.TgZ(64,"div",37),e.TgZ(65,"cd-rbd-configuration-form",38),e.NdJ("changes",function(n){return e.CHM(_),e.oxw().currentConfigurationValues=n()}),e.qZA(),e.qZA(),e.qZA(),e.TgZ(66,"div",39),e.TgZ(67,"cd-form-button-panel",40),e.NdJ("submitActionEvent",function(){return e.CHM(_),e.oxw().submit()}),e.ALo(68,"titlecase"),e.ALo(69,"upperFirst"),e.qZA(),e.qZA(),e.qZA(),e.qZA(),e.qZA()}if(2&t){const _=e.MAs(2),o=e.oxw();e.xp6(1),e.Q6J("formGroup",o.form),e.xp6(6),e.pQV(e.lcZ(6,25,o.action))(e.lcZ(7,27,o.resource)),e.QtT(5),e.xp6(7),e.Q6J("ngIf",o.form.showError("name",_,"required")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",_,"uniqueName")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",_,"rbdPool")),e.xp6(1),e.Q6J("ngIf",o.form.showError("name",_,"pattern")),e.xp6(8),e.Q6J("ngForOf",o.data.poolTypes),e.xp6(1),e.Q6J("ngIf",o.form.showError("poolType",_,"required")),e.xp6(1),e.Q6J("ngIf",o.isReplicated||o.isErasure),e.xp6(5),e.Q6J("customBadges",!0)("customBadgeValidators",o.data.applications.validators)("messages",o.data.applications.messages)("data",o.data.applications.selected)("options",o.data.applications.available)("selectionLimit",4),e.xp6(1),e.Q6J("ngIf",o.data.applications.selected<=0),e.xp6(1),e.Q6J("ngIf",o.isErasure||o.isReplicated),e.xp6(1),e.Q6J("ngIf",o.info.is_all_bluestore),e.xp6(28),e.Q6J("ngIf",o.form.showError("max_objects",_,"min")),e.xp6(1),e.Q6J("hidden",o.isErasure||-1===o.data.applications.selected.indexOf("rbd")),e.xp6(1),e.Q6J("form",o.form)("initializeData",o.initializeConfigData),e.xp6(2),e.Q6J("form",o.form)("submitText",e.lcZ(68,29,o.action)+" "+e.lcZ(69,31,o.resource))}}let Ue=(()=>{class t extends R_.E{constructor(_,o,n,s,c,d,P,p,R,h,T){super(),this.dimlessBinaryPipe=_,this.route=o,this.router=n,this.modalService=s,this.poolService=c,this.authStorageService=d,this.formatter=P,this.taskWrapper=p,this.ecpService=R,this.crushRuleService=h,this.actionLabels=T,this.editing=!1,this.isReplicated=!1,this.isErasure=!1,this.data=new No,this.externalPgChange=!1,this.current={rules:[]},this.initializeConfigData=new E_.t(1),this.currentConfigurationValues={},this.icons=b.P,this.crushUsage=void 0,this.ecpUsage=void 0,this.crushRuleMaxSize=10,this.editing=this.router.url.startsWith(`/pool/${M.MQ.EDIT}`),this.action=this.editing?this.actionLabels.EDIT:this.actionLabels.CREATE,this.resource="pool",this.authenticate(),this.createForm()}authenticate(){if(this.permission=this.authStorageService.getPermissions().pool,!this.permission.read||!this.permission.update&&this.editing||!this.permission.create&&!this.editing)throw new g_._2}createForm(){const _=new be.d({mode:new a.NI("none"),algorithm:new a.NI(""),minBlobSize:new a.NI("",{updateOn:"blur"}),maxBlobSize:new a.NI("",{updateOn:"blur"}),ratio:new a.NI("",{updateOn:"blur"})});this.form=new be.d({name:new a.NI("",{validators:[a.kI.pattern(/^[.A-Za-z0-9_/-]+$/),a.kI.required,E.h.custom("rbdPool",()=>this.form&&this.form.getValue("name").includes("/")&&this.data&&-1!==this.data.applications.selected.indexOf("rbd"))]}),poolType:new a.NI("",{validators:[a.kI.required]}),crushRule:new a.NI(null,{validators:[E.h.custom("tooFewOsds",o=>this.info&&o&&this.info.osd_count<1),E.h.custom("required",o=>this.isReplicated&&this.info.crush_rules_replicated.length>0&&!o)]}),size:new a.NI("",{updateOn:"blur"}),erasureProfile:new a.NI(null),pgNum:new a.NI("",{validators:[a.kI.required]}),pgAutoscaleMode:new a.NI(null),ecOverwrites:new a.NI(!1),compression:_,max_bytes:new a.NI(""),max_objects:new a.NI(0)},[E.h.custom("form",()=>null)])}ngOnInit(){this.poolService.getInfo().subscribe(_=>{this.initInfo(_),this.editing?this.initEditMode():(this.setAvailableApps(),this.loadingReady()),this.listenToChanges(),this.setComplexValidators()})}initInfo(_){this.pgAutoscaleModes=_.pg_autoscale_modes,this.form.silentSet("pgAutoscaleMode",_.pg_autoscale_default_mode),this.form.silentSet("algorithm",_.bluestore_compression_algorithm),this.info=_,this.initEcp(_.erasure_code_profiles)}initEcp(_){this.setListControlStatus("erasureProfile",_),this.ecProfiles=_}setListControlStatus(_,o){const n=this.form.get(_),s=n.value;1!==o.length||s&&u().isEqual(s,o[0])?0===o.length&&s&&n.setValue(null):n.setValue(o[0]),o.length<=1?n.enabled&&n.disable():n.disabled&&n.enable()}initEditMode(){this.disableForEdit(),this.routeParamsSubscribe=this.route.params.subscribe(_=>this.poolService.get(_.name).subscribe(o=>{this.data.pool=o,this.initEditFormData(o),this.loadingReady()}))}disableForEdit(){["poolType","crushRule","size","erasureProfile","ecOverwrites"].forEach(_=>this.form.get(_).disable())}initEditFormData(_){this.initializeConfigData.next({initialData:_.configuration,sourceType:m_.h.pool}),this.poolTypeChange(_.type);const o=this.info.crush_rules_replicated.concat(this.info.crush_rules_erasure),n={name:_.pool_name,poolType:_.type,crushRule:o.find(s=>s.rule_name===_.crush_rule),size:_.size,erasureProfile:this.ecProfiles.find(s=>s.name===_.erasure_code_profile),pgAutoscaleMode:_.pg_autoscale_mode,pgNum:_.pg_num,ecOverwrites:_.flags_names.includes("ec_overwrites"),mode:_.options.compression_mode,algorithm:_.options.compression_algorithm,minBlobSize:this.dimlessBinaryPipe.transform(_.options.compression_min_blob_size),maxBlobSize:this.dimlessBinaryPipe.transform(_.options.compression_max_blob_size),ratio:_.options.compression_required_ratio,max_bytes:this.dimlessBinaryPipe.transform(_.quota_max_bytes),max_objects:_.quota_max_objects};Object.keys(n).forEach(s=>{const c=n[s];!u().isUndefined(c)&&""!==c&&this.form.silentSet(s,c)}),this.data.pgs=this.form.getValue("pgNum"),this.setAvailableApps(this.data.applications.default.concat(_.application_metadata)),this.data.applications.selected=_.application_metadata}setAvailableApps(_=this.data.applications.default){this.data.applications.available=u().uniq(_.sort()).map(o=>new p_.$(!1,o,""))}listenToChanges(){this.listenToChangesDuringAddEdit(),this.editing||this.listenToChangesDuringAdd()}listenToChangesDuringAddEdit(){this.form.get("pgNum").valueChanges.subscribe(_=>{const o=_-this.data.pgs;1===Math.abs(o)&&2!==_?this.doPgPowerJump(o):this.data.pgs=_})}doPgPowerJump(_){const o=this.calculatePgPower()+_;this.setPgs(-1===_?Math.round(o):Math.floor(o))}calculatePgPower(_=this.form.getValue("pgNum")){return Math.log(_)/Math.log(2)}setPgs(_){const o=Math.pow(2,_<0?0:_);this.data.pgs=o,this.form.silentSet("pgNum",o)}listenToChangesDuringAdd(){this.form.get("poolType").valueChanges.subscribe(_=>{this.poolTypeChange(_)}),this.form.get("crushRule").valueChanges.subscribe(_=>{this.crushDeletionBtn&&this.crushDeletionBtn.isOpen()&&this.crushDeletionBtn.close(),_&&(this.setCorrectMaxSize(_),this.crushRuleIsUsedBy(_.rule_name),this.replicatedRuleChange(),this.pgCalc())}),this.form.get("size").valueChanges.subscribe(()=>{this.pgCalc()}),this.form.get("erasureProfile").valueChanges.subscribe(_=>{this.ecpDeletionBtn&&this.ecpDeletionBtn.isOpen()&&this.ecpDeletionBtn.close(),_&&(this.ecpIsUsedBy(_.name),this.pgCalc())}),this.form.get("mode").valueChanges.subscribe(()=>{["minBlobSize","maxBlobSize","ratio"].forEach(_=>{this.form.get(_).updateValueAndValidity({emitEvent:!1})})}),this.form.get("minBlobSize").valueChanges.subscribe(()=>{this.form.get("maxBlobSize").updateValueAndValidity({emitEvent:!1})}),this.form.get("maxBlobSize").valueChanges.subscribe(()=>{this.form.get("minBlobSize").updateValueAndValidity({emitEvent:!1})})}poolTypeChange(_){if("replicated"===_?this.setTypeBooleans(!0,!1):this.setTypeBooleans(!1,"erasure"===_),!_||!this.info)return void(this.current.rules=[]);const o=this.info["crush_rules_"+_]||[];this.current.rules=o,!this.editing&&(this.isReplicated&&this.setListControlStatus("crushRule",o),this.replicatedRuleChange(),this.pgCalc())}setTypeBooleans(_,o){this.isReplicated=_,this.isErasure=o}replicatedRuleChange(){if(!this.isReplicated)return;const _=this.form.get("size");let o=this.form.getValue("size")||3;const n=this.getMinSize(),s=this.getMaxSize();os&&(o=s),o!==_.value&&this.form.silentSet("size",o)}getMinSize(){return!this.info||this.info.osd_count<1?0:1}getMaxSize(){const _=this.form.getValue("crushRule");return this.info?_?_.usable_size:Math.min(this.info.osd_count,3):0}pgCalc(){const _=this.form.getValue("poolType");if(!this.info||this.form.get("pgNum").dirty||!_)return;const o=100*this.info.osd_count,n=this.isReplicated?this.replicatedPgCalc(o):this.erasurePgCalc(o);if(!n)return;const s=this.data.pgs;this.alignPgs(n),this.externalPgChange||(this.externalPgChange=s!==this.data.pgs)}setCorrectMaxSize(_=this.form.getValue("crushRule")){if(!_)return;const n=S.searchFailureDomains(this.info.nodes,_.steps[0].item_name)[_.steps[1].type];_.usable_size=Math.min(n?n.length:this.crushRuleMaxSize,this.crushRuleMaxSize)}replicatedPgCalc(_){const o=this.form.get("size"),n=o.value;return o.valid&&n>0?_/n:0}erasurePgCalc(_){const o=this.form.get("erasureProfile"),n=o.value;return(o.valid||o.disabled)&&n?_/(n.k+n.m):0}alignPgs(_=this.form.getValue("pgNum")){this.setPgs(Math.round(this.calculatePgPower(_<1?1:_)))}setComplexValidators(){this.editing?this.form.get("name").setValidators([this.form.get("name").validator,E.h.custom("uniqueName",_=>this.data.pool&&this.info&&-1!==this.info.pool_names.indexOf(_)&&this.info.pool_names.indexOf(_)!==this.info.pool_names.indexOf(this.data.pool.pool_name))]):(E.h.validateIf(this.form.get("size"),()=>this.isReplicated,[E.h.custom("min",_=>this.form.getValue("size")&&_this.form.getValue("size")&&this.getMaxSize()<_)]),this.form.get("name").setValidators([this.form.get("name").validator,E.h.custom("uniqueName",_=>this.info&&-1!==this.info.pool_names.indexOf(_))])),this.setCompressionValidators()}setCompressionValidators(){E.h.validateIf(this.form.get("minBlobSize"),()=>this.hasCompressionEnabled(),[a.kI.min(0),E.h.custom("maximum",_=>this.oddBlobSize(_,this.form.getValue("maxBlobSize")))]),E.h.validateIf(this.form.get("maxBlobSize"),()=>this.hasCompressionEnabled(),[a.kI.min(0),E.h.custom("minimum",_=>this.oddBlobSize(this.form.getValue("minBlobSize"),_))]),E.h.validateIf(this.form.get("ratio"),()=>this.hasCompressionEnabled(),[a.kI.min(0),a.kI.max(1)])}oddBlobSize(_,o){const n=this.formatter.toBytes(_),s=this.formatter.toBytes(o);return Boolean(n&&s&&n>=s)}hasCompressionEnabled(){return this.form.getValue("mode")&&"none"!==this.form.get("mode").value.toLowerCase()}describeCrushStep(_){return[_.op.replace("_"," "),_.item_name||"",_.type?_.num+" type "+_.type:""].join(" ")}addErasureCodeProfile(){this.addModal(Ao,_=>this.reloadECPs(_))}addModal(_,o){this.hideOpenTooltips(),this.modalService.show(_).componentInstance.submitAction.subscribe(s=>{o(s.name)})}hideOpenTooltips(){const _=o=>o&&o.isOpen()&&o.close();_(this.ecpDeletionBtn),_(this.crushDeletionBtn)}reloadECPs(_){this.reloadList({newItemName:_,getInfo:()=>this.ecpService.list(),initInfo:o=>this.initEcp(o),findNewItem:()=>this.ecProfiles.find(o=>o.name===_),controlName:"erasureProfile"})}reloadList({newItemName:_,getInfo:o,initInfo:n,findNewItem:s,controlName:c}){this.modalSubscription&&this.modalSubscription.unsubscribe(),o().subscribe(d=>{if(n(d),!_)return;const P=s();P&&this.form.get(c).setValue(P)})}deleteErasureCodeProfile(){this.deletionModal({value:this.form.getValue("erasureProfile"),usage:this.ecpUsage,deletionBtn:this.ecpDeletionBtn,dataName:"erasureInfo",getTabs:()=>this.ecpInfoTabs,tabPosition:"used-by-pools",nameAttribute:"name",itemDescription:"erasure code profile",reloadFn:()=>this.reloadECPs(),deleteFn:_=>this.ecpService.delete(_),taskName:"ecp/delete"})}deletionModal({value:_,usage:o,deletionBtn:n,dataName:s,getTabs:c,tabPosition:d,nameAttribute:P,itemDescription:p,reloadFn:R,deleteFn:h,taskName:T}){if(!_)return;if(o)return n.animation=!1,n.toggle(),this.data[s]=!0,void setTimeout(()=>{const f=c();f&&f.select(d)},50);const m=_[P];this.modalService.show(Ne.M,{itemDescription:p,itemNames:[m],submitActionObservable:()=>{const f=h(m);return f.subscribe(()=>R()),this.taskWrapper.wrapTaskAroundCall({task:new v.R(T,{name:m}),call:f})}})}addCrushRule(){this.addModal($_,_=>this.reloadCrushRules(_))}reloadCrushRules(_){this.reloadList({newItemName:_,getInfo:()=>this.poolService.getInfo(),initInfo:o=>{this.initInfo(o),this.poolTypeChange("replicated")},findNewItem:()=>this.info.crush_rules_replicated.find(o=>o.rule_name===_),controlName:"crushRule"})}deleteCrushRule(){this.deletionModal({value:this.form.getValue("crushRule"),usage:this.crushUsage,deletionBtn:this.crushDeletionBtn,dataName:"crushInfo",getTabs:()=>this.crushInfoTabs,tabPosition:"used-by-pools",nameAttribute:"rule_name",itemDescription:"crush rule",reloadFn:()=>this.reloadCrushRules(),deleteFn:_=>this.crushRuleService.delete(_),taskName:"crushRule/delete"})}crushRuleIsUsedBy(_){this.crushUsage=_?this.info.used_rules[_]:void 0}ecpIsUsedBy(_){this.ecpUsage=_?this.info.used_profiles[_]:void 0}submit(){if(this.form.invalid)return void this.form.setErrors({cdSubmitButton:!0});const _={pool:this.form.getValue("name")};this.assignFormFields(_,[{externalFieldName:"pool_type",formControlName:"poolType"},{externalFieldName:"pg_autoscale_mode",formControlName:"pgAutoscaleMode",editable:!0},{externalFieldName:"pg_num",formControlName:"pgNum",replaceFn:n=>"on"===this.form.getValue("pgAutoscaleMode")?1:n,editable:!0},this.isReplicated?{externalFieldName:"size",formControlName:"size"}:{externalFieldName:"erasure_code_profile",formControlName:"erasureProfile",attr:"name"},{externalFieldName:"rule_name",formControlName:"crushRule",replaceFn:n=>this.isReplicated?n&&n.rule_name:void 0},{externalFieldName:"quota_max_bytes",formControlName:"max_bytes",replaceFn:this.formatter.toBytes,editable:!0,resetValue:this.editing?0:void 0},{externalFieldName:"quota_max_objects",formControlName:"max_objects",editable:!0,resetValue:this.editing?0:void 0}]),this.info.is_all_bluestore&&(this.assignFormField(_,{externalFieldName:"flags",formControlName:"ecOverwrites",replaceFn:()=>this.isErasure?["ec_overwrites"]:void 0}),"none"!==this.form.getValue("mode")?this.assignFormFields(_,[{externalFieldName:"compression_mode",formControlName:"mode",editable:!0,replaceFn:n=>this.hasCompressionEnabled()&&n},{externalFieldName:"compression_algorithm",formControlName:"algorithm",editable:!0},{externalFieldName:"compression_min_blob_size",formControlName:"minBlobSize",replaceFn:this.formatter.toBytes,editable:!0,resetValue:0},{externalFieldName:"compression_max_blob_size",formControlName:"maxBlobSize",replaceFn:this.formatter.toBytes,editable:!0,resetValue:0},{externalFieldName:"compression_required_ratio",formControlName:"ratio",editable:!0,resetValue:0}]):this.editing&&this.assignFormFields(_,[{externalFieldName:"compression_mode",formControlName:"mode",editable:!0,replaceFn:()=>"unset"},{externalFieldName:"srcpool",formControlName:"name",editable:!0,replaceFn:()=>this.data.pool.pool_name}]));const o=this.data.applications.selected;(o.length>0||this.editing)&&(_.application_metadata=o),this.isReplicated&&!u().isEmpty(this.currentConfigurationValues)&&(_.configuration=this.currentConfigurationValues),this.triggerApiTask(_)}assignFormFields(_,o){o.forEach(n=>this.assignFormField(_,n))}assignFormField(_,{externalFieldName:o,formControlName:n,attr:s,replaceFn:c,editable:d,resetValue:P}){if(this.editing&&(!d||this.form.get(n).pristine))return;const p=this.form.getValue(n);let R=c?c(p):s?u().get(p,s):p;if(!p||!R){if(!d||u().isUndefined(P))return;R=P}_[o]=R}triggerApiTask(_){this.taskWrapper.wrapTaskAroundCall({task:new v.R("pool/"+(this.editing?M.MQ.EDIT:M.MQ.CREATE),{pool_name:_.hasOwnProperty("srcpool")?_.srcpool:_.pool}),call:this.poolService[this.editing?M.MQ.UPDATE:M.MQ.CREATE](_)}).subscribe({error:o=>{u().isObject(o.error)&&"34"===o.error.code&&this.form.get("pgNum").setErrors({34:!0}),this.form.setErrors({cdSubmitButton:!0})},complete:()=>this.router.navigate(["/pool"])})}appSelection(){this.form.get("name").updateValueAndValidity({emitEvent:!1,onlySelf:!0})}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(C_.$),e.Y36(Oe.gz),e.Y36(Oe.F0),e.Y36(Ze.Z),e.Y36(ue.q),e.Y36(he.j),e.Y36(M_.H),e.Y36(de.P),e.Y36(Me),e.Y36(Ie.H),e.Y36(M.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-pool-form"]],viewQuery:function(_,o){if(1&_&&(e.Gf(Zo,5),e.Gf(xo,5),e.Gf(yo,5),e.Gf(Uo,5)),2&_){let n;e.iGM(n=e.CRH())&&(o.crushInfoTabs=n.first),e.iGM(n=e.CRH())&&(o.crushDeletionBtn=n.first),e.iGM(n=e.CRH())&&(o.ecpInfoTabs=n.first),e.iGM(n=e.CRH())&&(o.ecpDeletionBtn=n.first)}},features:[e.qOj],decls:1,vars:1,consts:function(){let i,_,o,n,s,c,d,P,p,R,h,T,m,f,A,I,$,D,Z,x,y,U,q,H,G,z,X,w,Q,J,k,V,Y,B,j,K,N,W,ee,_e,oe,te,ie,ne,se,ae,le,re,ce,O,Xe,we,Qe,Je,ke,Ve,Ye,Be,je,Ke,We,e_,__,o_,t_,i_,n_,s_,a_,l_,r_,c_,O_;return i="" + "\ufffd0\ufffd" + " " + "\ufffd1\ufffd" + "",_="Name",o="Name...",n="Pool type",s="-- Select a pool type --",c="Applications",d="Pools should be associated with an application tag",P="Quotas",p="Max bytes",R="Leave it blank or specify 0 to disable this quota.",h="A valid quota should be greater than 0.",T="e.g., 10GiB",m="Max objects",f="Leave it blank or specify 0 to disable this quota.",A="A valid quota should be greater than 0.",I="This field is required!",$="The chosen Ceph pool name is already in use.",D="It's not possible to create an RBD pool with '/' in the name. Please change the name or remove 'rbd' from the applications list.",Z="Pool name can only contain letters, numbers, '.', '-', '_' or '/'.",x="This field is required!",y="PG Autoscale",U="Placement groups",q="Calculation help",H="This field is required!",G="At least one placement group is needed!",z="Your cluster can't handle this many PGs. Please recalculate the PG amount needed.",X="The current PGs settings were calculated for you, you should make sure the values suit your needs before submit.",w="Replicated size",Q="Minimum: " + "\ufffd0\ufffd" + "",J="Maximum: " + "\ufffd0\ufffd" + "",k="The size specified is out of range. A value from " + "\ufffd0\ufffd" + " to " + "\ufffd1\ufffd" + " is usable.",V="A size of 1 will not create a replication of the object. The 'Replicated size' includes the object itself.",Y="Flags",B="EC Overwrites",j="CRUSH",K="Erasure code profile",N="This profile can't be deleted as it is in use.",W="Loading...",ee="-- No erasure code profile available --",_e="-- Select an erasure code profile --",oe="Profile",te="Used by pools",ie="Profile is not in use.",ne="Crush ruleset",se="A new crush ruleset will be implicitly created.",ae="Crush ruleset",le="There are no rules.",re="-- Select a crush rule --",ce="Placement and\n replication strategies or distribution policies that allow to\n specify how CRUSH places data replicas.",O="This rule can't be deleted as it is in use.",Xe="Crush rule",we="Crush steps",Qe="Used by pools",Je="Rule is not in use.",ke="This field is required!",Ve="The rule can't be used in the current cluster as it has too few OSDs to meet the minimum required OSD by this rule.",Ye="Compression",Be="Mode",je="Algorithm",Ke="Minimum blob size",We="e.g., 128KiB",e_="Maximum blob size",__="e.g., 512KiB",o_="Ratio",t_="Compression ratio",i_="Loading...",n_="-- No erasure compression algorithm available --",s_="Value should be greater than 0",a_="Value should be less than the maximum blob size",l_="Value should be greater than 0",r_="Value should be greater than the minimum blob size",c_="Value should be between 0.0 and 1.0",O_="The value should be greater or equal to 0",[["class","cd-col-form",4,"cdFormLoading"],[1,"cd-col-form"],["name","form","novalidate","",3,"formGroup"],["formDir","ngForm"],[1,"card"],[1,"card-header"],i,[1,"card-body"],[1,"form-group","row"],["for","name",1,"cd-col-form-label","required"],_,[1,"cd-col-form-input"],["id","name","name","name","type","text","placeholder",o,"formControlName","name","autofocus","",1,"form-control"],["class","invalid-feedback",4,"ngIf"],["for","poolType",1,"cd-col-form-label","required"],n,["id","poolType","formControlName","poolType","name","poolType",1,"form-control"],["ngValue",""],s,[3,"value",4,"ngFor","ngForOf"],[4,"ngIf"],["for","applications",1,"cd-col-form-label"],c,["id","applications",3,"customBadges","customBadgeValidators","messages","data","options","selectionLimit","selection"],["title",d,3,"class",4,"ngIf"],["formGroupName","compression",4,"ngIf"],P,["for","max_bytes",1,"cd-col-form-label"],p,R,h,["id","max_bytes","name","max_bytes","type","text","formControlName","max_bytes","placeholder",T,"defaultUnit","GiB","cdDimlessBinary","",1,"form-control"],["for","max_objects",1,"cd-col-form-label"],m,f,A,["id","max_objects","min","0","name","max_objects","type","number","formControlName","max_objects",1,"form-control"],[3,"hidden"],[3,"form","initializeData","changes"],[1,"card-footer"],["wrappingClass","text-right",3,"form","submitText","submitActionEvent"],[1,"invalid-feedback"],I,$,D,Z,[3,"value"],x,["for","pgAutoscaleMode",1,"cd-col-form-label"],y,["id","pgAutoscaleMode","name","pgAutoscaleMode","formControlName","pgAutoscaleMode",1,"form-control"],["class","form-group row",4,"ngIf"],["for","pgNum",1,"cd-col-form-label","required"],U,["id","pgNum","name","pgNum","formControlName","pgNum","min","1","type","number","required","",1,"form-control",3,"focus","blur"],[1,"form-text","text-muted"],["section","pgs","docText",q],["class","form-text text-muted",4,"ngIf"],H,G,z,X,["for","size",1,"cd-col-form-label","required"],w,["id","size","name","size","type","number","formControlName","size",1,"form-control",3,"max","min"],["class","text-warning-dark",4,"ngIf"],[1,"list-inline"],Q,J,k,[1,"text-warning-dark"],V,[1,"cd-col-form-label"],Y,[1,"custom-control","custom-checkbox"],["type","checkbox","id","ec-overwrites","formControlName","ecOverwrites",1,"custom-control-input"],["for","ec-overwrites",1,"custom-control-label"],B,["title",d],j,["for","erasureProfile",1,"cd-col-form-label"],K,[1,"input-group"],["id","erasureProfile","name","erasureProfile","formControlName","erasureProfile",1,"form-control"],["ngValue","",4,"ngIf"],[3,"ngValue",4,"ngIf"],[3,"ngValue",4,"ngFor","ngForOf"],[1,"input-group-append"],["id","ecp-info-button","type","button",1,"btn","btn-light",3,"ngClass","click"],["aria-hidden","true",3,"ngClass"],["class","btn btn-light","type","button",3,"click",4,"ngIf"],["class","btn btn-light","type","button","ngbTooltip",N,"triggers","manual",3,"click",4,"ngIf"],["class","form-text text-muted","id","ecp-info-block",4,"ngIf"],W,[3,"ngValue"],ee,_e,["type","button",1,"btn","btn-light",3,"click"],["type","button","ngbTooltip",N,"triggers","manual",1,"btn","btn-light",3,"click"],["ecpDeletionBtn","ngbTooltip"],["id","ecp-info-block",1,"form-text","text-muted"],["ngbNav","",1,"nav-tabs"],["ecpInfoTabs","ngbNav"],["ngbNavItem","ecp-info"],["ngbNavLink",""],oe,["ngbNavContent",""],["ngbNavItem","used-by-pools"],te,[3,"ngbNavOutlet"],[3,"renderObjects","hideKeys","data","autoReload"],["ecpIsNotUsed",""],[4,"ngIf","ngIfElse"],ie,[4,"ngFor","ngForOf"],["for","crushRule",1,"cd-col-form-label"],ne,se,ae,["noRules",""],le,["id","crushRule","formControlName","crushRule","name","crushSet",1,"form-control"],re,["id","crush-info-button","type","button","ngbTooltip",ce,1,"btn","btn-light",3,"ngClass","click"],["class","btn btn-light","type","button","ngbTooltip",O,"triggers","manual",3,"click",4,"ngIf"],["class","form-text text-muted","id","crush-info-block",4,"ngIf"],["type","button","ngbTooltip",O,"triggers","manual",1,"btn","btn-light",3,"click"],["crushDeletionBtn","ngbTooltip"],["id","crush-info-block",1,"form-text","text-muted"],["crushInfoTabs","ngbNav"],["ngbNavItem","crush-rule-info"],Xe,["ngbNavItem","crush-rule-steps"],we,Qe,["ruleIsNotUsed",""],Je,ke,Ve,["formGroupName","compression"],Ye,["for","mode",1,"cd-col-form-label"],Be,["id","mode","name","mode","formControlName","mode",1,"form-control"],["for","algorithm",1,"cd-col-form-label"],je,["id","algorithm","name","algorithm","formControlName","algorithm",1,"form-control"],["for","minBlobSize",1,"cd-col-form-label"],Ke,["id","minBlobSize","name","minBlobSize","formControlName","minBlobSize","type","text","min","0","placeholder",We,"defaultUnit","KiB","cdDimlessBinary","",1,"form-control"],["for","maxBlobSize",1,"cd-col-form-label"],e_,["id","maxBlobSize","type","text","min","0","formControlName","maxBlobSize","placeholder",__,"defaultUnit","KiB","cdDimlessBinary","",1,"form-control"],["for","ratio",1,"cd-col-form-label"],o_,["id","ratio","name","ratio","formControlName","ratio","type","number","min","0","max","1","step","0.1","placeholder",t_,1,"form-control"],i_,n_,s_,a_,l_,r_,c_,O_]},template:function(_,o){1&_&&e.YNc(0,Yt,70,33,"div",0),2&_&&e.Q6J("cdFormLoading",o.loading)},directives:[bo.y,a._Y,a.JL,fe.V,a.sg,Pe.P,Ee.o,a.Fj,ge.b,a.JJ,a.u,pe.U,C.O5,a.EJ,a.YN,a.Kr,C.sg,vo.m,Re.S,Io.Q,a.qQ,a.wV,$o.d,me.p,a.Q7,Do.K,a.Fd,a.Wl,C.mk,g._L,g.Pz,g.nv,g.Vx,g.uN,g.tO,xe.b,a.x0],pipes:[C.rS,Ce.m],styles:[".icon-warning-color[_ngcontent-%COMP%]{margin-left:3px}"]}),t})();var Bt=r(19773),jt=r(49671),Kt=r(68136),Te=r(69158),Se=r(64337),L=r(99466),Wt=r(91801),ei=r(68774),_i=r(66369),qe=r(38047),Le=r(51847);class oi{constructor(i){this.pool_name=i}}var ti=r(64724);let ii=(()=>{class t{constructor(_,o,n){this.templateRef=_,this.viewContainer=o,this.authStorageService=n,this.cdScopeMatchAll=!0}set cdScope(_){this.permissions=this.authStorageService.getPermissions(),this.isAuthorized(_)?this.viewContainer.createEmbeddedView(this.templateRef):this.viewContainer.clear()}isAuthorized(_){const o=this.cdScopeMatchAll?u().every:u().some;return u().isString(_)?u().get(this.permissions,[_,"read"],!1):u().isArray(_)?o(_,n=>this.permissions[n].read):!!u().isObject(_)&&o(_,(n,s)=>o(n,c=>this.permissions[s][c]))}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(e.Rgc),e.Y36(e.s_b),e.Y36(he.j))},t.\u0275dir=e.lG2({type:t,selectors:[["","cdScope",""]],inputs:{cdScope:"cdScope",cdScopeMatchAll:"cdScopeMatchAll"}}),t})();var ni=r(94928),He=r(51295),si=r(59376),Ge=r(76317),ai=r(71752);function li(t,i){if(1&t&&e._UZ(0,"cd-table-key-value",12),2&t){const _=e.oxw(2);e.Q6J("renderObjects",!0)("data",_.poolDetails)("autoReload",!1)}}function ri(t,i){if(1&t&&e._UZ(0,"cd-grafana",15),2&t){const _=e.oxw(3);e.MGl("grafanaPath","ceph-pool-detail?var-pool_name=",_.selection.pool_name,"")}}function ci(t,i){1&t&&(e.TgZ(0,"li",13),e.TgZ(1,"a",5),e.SDv(2,14),e.qZA(),e.YNc(3,ri,1,1,"ng-template",7),e.qZA())}function Oi(t,i){if(1&t&&e._UZ(0,"cd-rbd-configuration-table",18),2&t){const _=e.oxw(3);e.Q6J("data",_.selectedPoolConfiguration)}}function di(t,i){1&t&&(e.TgZ(0,"li",16),e.TgZ(1,"a",5),e.SDv(2,17),e.qZA(),e.YNc(3,Oi,1,1,"ng-template",7),e.qZA())}function ui(t,i){if(1&t&&e._UZ(0,"cd-table",21),2&t){const _=e.oxw(3);e.Q6J("data",_.cacheTiers)("columns",_.cacheTierColumns)("autoSave",!1)}}function fi(t,i){1&t&&(e.TgZ(0,"li",19),e.TgZ(1,"a",5),e.SDv(2,20),e.qZA(),e.YNc(3,ui,1,3,"ng-template",7),e.qZA())}function Pi(t,i){if(1&t&&(e.ynx(0,1),e.TgZ(1,"ul",2,3),e.TgZ(3,"li",4),e.TgZ(4,"a",5),e.SDv(5,6),e.qZA(),e.YNc(6,li,1,3,"ng-template",7),e.qZA(),e.YNc(7,ci,4,0,"li",8),e.YNc(8,di,4,0,"li",9),e.YNc(9,fi,4,0,"li",10),e.qZA(),e._UZ(10,"div",11),e.BQk()),2&t){const _=e.MAs(2),o=e.oxw();e.xp6(7),e.Q6J("ngIf",o.permissions.grafana.read),e.xp6(1),e.Q6J("ngIf","replicated"===o.selection.type),e.xp6(1),e.Q6J("ngIf",(null==o.selection.tiers?null:o.selection.tiers.length)>0),e.xp6(1),e.Q6J("ngbNavOutlet",_)}}let Ei=(()=>{class t{constructor(_){this.poolService=_,this.cacheTierColumns=[],this.omittedPoolAttributes=["cdExecuting","cdIsBinary","stats"],this.cacheTierColumns=[{prop:"pool_name",name:"Name",flexGrow:3},{prop:"cache_mode",name:"Cache Mode",flexGrow:2},{prop:"cache_min_evict_age",name:"Min Evict Age",flexGrow:2},{prop:"cache_min_flush_age",name:"Min Flush Age",flexGrow:2},{prop:"target_max_bytes",name:"Target Max Bytes",flexGrow:2},{prop:"target_max_objects",name:"Target Max Objects",flexGrow:2}]}ngOnChanges(){this.selection&&(this.poolService.getConfiguration(this.selection.pool_name).subscribe(_=>{He.T.updateChanged(this,{selectedPoolConfiguration:_})}),He.T.updateChanged(this,{poolDetails:u().omit(this.selection,this.omittedPoolAttributes)}))}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ue.q))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-pool-details"]],inputs:{cacheTiers:"cacheTiers",permissions:"permissions",selection:"selection"},features:[e.TTD],decls:1,vars:1,consts:function(){let i,_,o,n;return i="Details",_="Performance Details",o="Configuration",n="Cache Tiers Details",[["cdTableDetail","",4,"ngIf"],["cdTableDetail",""],["ngbNav","","cdStatefulTab","pool-details",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem","details"],["ngbNavLink",""],i,["ngbNavContent",""],["ngbNavItem","performance-details",4,"ngIf"],["ngbNavItem","configuration",4,"ngIf"],["ngbNavItem","cache-tiers-details",4,"ngIf"],[3,"ngbNavOutlet"],[3,"renderObjects","data","autoReload"],["ngbNavItem","performance-details"],_,["uid","-xyV8KCiz","grafanaStyle","three",3,"grafanaPath"],["ngbNavItem","configuration"],o,[3,"data"],["ngbNavItem","cache-tiers-details"],n,["columnMode","flex",3,"data","columns","autoSave"]]},template:function(_,o){1&_&&e.YNc(0,Pi,11,4,"ng-container",0),2&_&&e.Q6J("ngIf",o.selection)},directives:[C.O5,g.Pz,si.m,g.nv,g.Vx,g.uN,g.tO,xe.b,Ge.F,ai.P,Se.a],styles:[""],changeDetection:0}),t})();var gi=r(60251);const pi=["poolUsageTpl"],Ri=["poolConfigurationSourceTpl"];function mi(t,i){if(1&t){const _=e.EpF();e.TgZ(0,"cd-table",9,10),e.NdJ("fetchData",function(){return e.CHM(_),e.oxw().taskListService.fetch()})("setExpandedRow",function(n){return e.CHM(_),e.oxw().setExpandedRow(n)})("updateSelection",function(n){return e.CHM(_),e.oxw().updateSelection(n)}),e._UZ(2,"cd-table-actions",11),e._UZ(3,"cd-pool-details",12),e.qZA()}if(2&t){const _=e.oxw();e.Q6J("data",_.pools)("columns",_.columns)("hasDetails",!0)("status",_.tableStatus)("autoReload",-1),e.xp6(2),e.Q6J("permission",_.permissions.pool)("selection",_.selection)("tableActions",_.tableActions),e.xp6(1),e.Q6J("selection",_.expandedRow)("permissions",_.permissions)("cacheTiers",_.cacheTiers)}}function Ci(t,i){1&t&&e._UZ(0,"cd-grafana",14),2&t&&e.Q6J("grafanaPath","ceph-pools-overview?")}function Mi(t,i){1&t&&(e.TgZ(0,"li",2),e.TgZ(1,"a",3),e.SDv(2,13),e.qZA(),e.YNc(3,Ci,1,1,"ng-template",5),e.qZA())}function hi(t,i){if(1&t&&e._UZ(0,"cd-usage-bar",16),2&t){const _=e.oxw().row;e.Q6J("total",_.stats.bytes_used.latest+_.stats.avail_raw.latest)("used",_.stats.bytes_used.latest)}}function Ti(t,i){if(1&t&&e.YNc(0,hi,1,2,"cd-usage-bar",15),2&t){const _=i.row;e.Q6J("ngIf",null==_.stats||null==_.stats.avail_raw?null:_.stats.avail_raw.latest)}}let Si=(()=>{class t extends Kt.o{constructor(_,o,n,s,c,d,P,p,R,h,T){super(),this.poolService=_,this.taskWrapper=o,this.ecpService=n,this.authStorageService=s,this.taskListService=c,this.modalService=d,this.pgCategoryService=P,this.dimlessPipe=p,this.urlBuilder=R,this.configurationService=h,this.actionLabels=T,this.selection=new ei.r,this.executingTasks=[],this.tableStatus=new Te.E,this.cacheTiers=[],this.monAllowPoolDelete=!1,this.permissions=this.authStorageService.getPermissions(),this.tableActions=[{permission:"create",icon:b.P.add,routerLink:()=>this.urlBuilder.getCreate(),name:this.actionLabels.CREATE},{permission:"update",icon:b.P.edit,routerLink:()=>this.urlBuilder.getEdit(encodeURIComponent(this.selection.first().pool_name)),name:this.actionLabels.EDIT},{permission:"delete",icon:b.P.destroy,click:()=>this.deletePoolModal(),name:this.actionLabels.DELETE,disable:this.getDisableDesc.bind(this)}],this.permissions.configOpt.read&&this.configurationService.get("mon_allow_pool_delete").subscribe(m=>{if(u().has(m,"value")){const f=u().find(m.value,A=>"mon"===A.section)||{value:!1};this.monAllowPoolDelete="true"===f.value}})}ngOnInit(){const _=(o,n,s)=>u().get(n,o)>u().get(s,o)?1:-1;this.columns=[{prop:"pool_name",name:"Name",flexGrow:4,cellTransformation:L.e.executing},{prop:"data_protection",name:"Data Protection",cellTransformation:L.e.badge,customTemplateConfig:{class:"badge-background-gray"},flexGrow:1.3},{prop:"application_metadata",name:"Applications",cellTransformation:L.e.badge,customTemplateConfig:{class:"badge-background-primary"},flexGrow:1.5},{prop:"pg_status",name:"PG Status",flexGrow:1.2,cellClass:({row:o,column:n,value:s})=>this.getPgStatusCellClass(o,n,s)},{prop:"crush_rule",name:"Crush Ruleset",isHidden:!0,flexGrow:2},{name:"Usage",prop:"usage",cellTemplate:this.poolUsageTpl,flexGrow:1.2},{prop:"stats.rd_bytes.rates",name:"Read bytes",comparator:(o,n,s,c)=>_("stats.rd_bytes.latest",s,c),cellTransformation:L.e.sparkline,flexGrow:1.5},{prop:"stats.wr_bytes.rates",name:"Write bytes",comparator:(o,n,s,c)=>_("stats.wr_bytes.latest",s,c),cellTransformation:L.e.sparkline,flexGrow:1.5},{prop:"stats.rd.rate",name:"Read ops",flexGrow:1,pipe:this.dimlessPipe,cellTransformation:L.e.perSecond},{prop:"stats.wr.rate",name:"Write ops",flexGrow:1,pipe:this.dimlessPipe,cellTransformation:L.e.perSecond}],this.taskListService.init(()=>this.ecpService.list().pipe((0,Bt.zg)(o=>(this.ecProfileList=o,this.poolService.getList()))),void 0,o=>{this.pools=this.transformPoolsData(o),this.tableStatus=new Te.E},()=>{this.table.reset(),this.tableStatus=new Te.E(Wt.T.ValueException)},o=>o.name.startsWith("pool/"),(o,n)=>n.metadata.pool_name===o.pool_name,{default:o=>new oi(o.pool_name)})}updateSelection(_){this.selection=_}deletePoolModal(){const _=this.selection.first().pool_name;this.modalService.show(Ne.M,{itemDescription:"Pool",itemNames:[_],submitActionObservable:()=>this.taskWrapper.wrapTaskAroundCall({task:new v.R(`pool/${M.MQ.DELETE}`,{pool_name:_}),call:this.poolService.delete(_)})})}getPgStatusCellClass(_,o,n){return{"text-right":!0,[`pg-${this.pgCategoryService.getTypeByStates(n)}`]:!0}}getErasureCodeProfile(_){let o="";return u().forEach(this.ecProfileList,n=>{n.name===_&&(o=`EC: ${n.k}+${n.m}`)}),o}transformPoolsData(_){const o=["bytes_used","max_avail","avail_raw","percent_used","rd_bytes","wr_bytes","rd","wr"],n={latest:0,rate:0,rates:[]};return u().forEach(_,s=>{s.pg_status=this.transformPgStatus(s.pg_status);const c={};u().forEach(o,d=>{c[d]=s.stats&&s.stats[d]?s.stats[d]:n}),s.stats=c,s.usage=c.percent_used.latest,!s.cdExecuting&&s.pg_num+s.pg_placement_num!==s.pg_num_target+s.pg_placement_num_target&&(s.cdExecuting="Updating"),["rd_bytes","wr_bytes"].forEach(d=>{s.stats[d].rates=s.stats[d].rates.map(P=>P[1])}),s.cdIsBinary=!0,"erasure"===s.type&&(s.data_protection=this.getErasureCodeProfile(s.erasure_code_profile)),"replicated"===s.type&&(s.data_protection=`replica: \xd7${s.size}`)}),_}transformPgStatus(_){const o=[];return u().forEach(_,(n,s)=>{o.push(`${n} ${s}`)}),o.join(", ")}getSelectionTiers(){if(void 0!==this.expandedRow){const _=this.expandedRow.tiers;this.cacheTiers=this.pools.filter(o=>_.includes(o.pool))}}getDisableDesc(){var _;return!(null===(_=this.selection)||void 0===_?void 0:_.hasSelection)||!this.monAllowPoolDelete&&"Pool deletion is disabled by the mon_allow_pool_delete configuration setting."}setExpandedRow(_){super.setExpandedRow(_),this.getSelectionTiers()}}return t.\u0275fac=function(_){return new(_||t)(e.Y36(ue.q),e.Y36(de.P),e.Y36(Me),e.Y36(he.j),e.Y36(qe.j),e.Y36(Ze.Z),e.Y36(jt.j),e.Y36(_i.n),e.Y36(Le.F),e.Y36(ti.e),e.Y36(M.p4))},t.\u0275cmp=e.Xpm({type:t,selectors:[["cd-pool-list"]],viewQuery:function(_,o){if(1&_&&(e.Gf(Se.a,5),e.Gf(pi,7),e.Gf(Ri,5)),2&_){let n;e.iGM(n=e.CRH())&&(o.table=n.first),e.iGM(n=e.CRH())&&(o.poolUsageTpl=n.first),e.iGM(n=e.CRH())&&(o.poolConfigurationSourceTpl=n.first)}},features:[e._Bn([qe.j,{provide:Le.F,useValue:new Le.F("pool")}]),e.qOj],decls:10,vars:2,consts:function(){let i,_;return i="Pools List",_="Overall Performance",[["ngbNav","",1,"nav-tabs"],["nav","ngbNav"],["ngbNavItem",""],["ngbNavLink",""],i,["ngbNavContent",""],["ngbNavItem","",4,"cdScope"],[3,"ngbNavOutlet"],["poolUsageTpl",""],["id","pool-list","selectionType","single",3,"data","columns","hasDetails","status","autoReload","fetchData","setExpandedRow","updateSelection"],["table",""],["id","pool-list-actions",1,"table-actions",3,"permission","selection","tableActions"],["cdTableDetail","","id","pool-list-details",3,"selection","permissions","cacheTiers"],_,["uid","z99hzWtmk","grafanaStyle","two",3,"grafanaPath"],["decimals","2",3,"total","used",4,"ngIf"],["decimals","2",3,"total","used"]]},template:function(_,o){if(1&_&&(e.TgZ(0,"ul",0,1),e.TgZ(2,"li",2),e.TgZ(3,"a",3),e.SDv(4,4),e.qZA(),e.YNc(5,mi,4,11,"ng-template",5),e.qZA(),e.YNc(6,Mi,4,0,"li",6),e.qZA(),e._UZ(7,"div",7),e.YNc(8,Ti,1,1,"ng-template",null,8,e.W1O)),2&_){const n=e.MAs(1);e.xp6(6),e.Q6J("cdScope","grafana"),e.xp6(1),e.Q6J("ngbNavOutlet",n)}},directives:[g.Pz,g.nv,g.Vx,g.uN,ii,g.tO,Se.a,ni.K,Ei,Ge.F,C.O5,gi.O],styles:["cd-pool-list .pg-clean{color:#0b0} cd-pool-list .pg-working{color:#2b99a8} cd-pool-list .pg-warning{color:#ffc200} cd-pool-list .pg-unknown{color:#ef5c55}"]}),t})(),ze=(()=>{class t{}return t.\u0275fac=function(_){return new(_||t)},t.\u0275mod=e.oAB({type:t}),t.\u0275inj=e.cJS({imports:[[f_.t,C.ez,g.Oz,d_.m,Oe.Bz,a.UX,g.HK,u_.BlockModule]]}),t})();const Li=[{path:"",component:Si},{path:M.MQ.CREATE,component:Ue,data:{breadcrumbs:M.Qn.CREATE}},{path:`${M.MQ.EDIT}/:name`,component:Ue,data:{breadcrumbs:M.Qn.EDIT}}];let Ai=(()=>{class t{}return t.\u0275fac=function(_){return new(_||t)},t.\u0275mod=e.oAB({type:t}),t.\u0275inj=e.cJS({imports:[[ze,Oe.Bz.forChild(Li)]]}),t})()}}]); \ No newline at end of file diff --git a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/index.html b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/index.html index 0336c2a98..80c67410d 100644 --- a/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/index.html +++ b/ceph/src/pybind/mgr/dashboard/frontend/dist/en-US/index.html @@ -4,7 +4,7 @@ - +