From: Pedro Gonzalez Gomez Date: Fri, 19 Jun 2026 08:21:58 +0000 (+0200) Subject: mgr/dashboard: add cephfs mirror fs overview page X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=03c932e46942e4a295ff7bb4f44dc2609f2e40b0;p=ceph.git mgr/dashboard: add cephfs mirror fs overview page Fixes: https://tracker.ceph.com/issues/77520 Signed-off-by: Pedro Gonzalez Gomez --- diff --git a/src/pybind/mgr/dashboard/controllers/cephfs.py b/src/pybind/mgr/dashboard/controllers/cephfs.py index f2157381e4d..afc146bd587 100644 --- a/src/pybind/mgr/dashboard/controllers/cephfs.py +++ b/src/pybind/mgr/dashboard/controllers/cephfs.py @@ -63,6 +63,74 @@ DAEMON_STATUS_SCHEMA = [{ }], 'List of filesystems on daemon'), }] +MIRROR_SYNC_PHASE_SCHEMA = { + 'state': (str, 'Phase state, e.g. completed, in-progress, or waiting'), + 'duration': (str, 'Phase duration'), +} + +MIRROR_SYNC_BYTES_PROGRESS_SCHEMA = { + 'sync_bytes': (str, 'Bytes synced so far'), + 'total_bytes': (str, 'Total bytes to sync'), + 'sync_percent': (str, 'Sync completion percentage'), +} + +MIRROR_SYNC_FILES_PROGRESS_SCHEMA = { + 'sync_files': (str, 'Files synced so far'), + 'total_files': (str, 'Total files to sync'), + 'sync_percent': (str, 'Sync completion percentage'), +} + +MIRROR_LAST_SYNCED_SNAP_SCHEMA = { + 'id': (int, 'Snapshot ID'), + 'name': (str, 'Snapshot name'), + 'crawl_duration': (str, 'Time taken to scan directory'), + 'datasync_queue_wait_duration': (str, 'Time in data sync queue'), + 'sync_duration': (str, 'Snapshot sync duration'), + 'sync_time_stamp': (str, 'Time of the last sync'), + 'sync_bytes': (str, 'Bytes synced for the snapshot'), + 'sync_files': (int, 'Number of files synced for the snapshot'), +} + +MIRROR_CURRENT_SYNCING_SNAP_SCHEMA = { + 'id': (int, 'Snapshot ID'), + 'name': (str, 'Snapshot name'), + 'sync-mode': (str, 'Snapshot sync mode: full or delta'), + 'avg_read_throughput_bytes': (str, 'Average read throughput'), + 'avg_write_throughput_bytes': (str, 'Average write throughput'), + 'crawl': (MIRROR_SYNC_PHASE_SCHEMA, 'Directory crawl progress'), + 'datasync_queue_wait': (MIRROR_SYNC_PHASE_SCHEMA, 'Data sync queue wait progress'), + 'bytes': (MIRROR_SYNC_BYTES_PROGRESS_SCHEMA, 'Byte sync progress'), + 'files': (MIRROR_SYNC_FILES_PROGRESS_SCHEMA, 'File sync progress'), + 'eta': (str, 'Estimated time remaining for the current sync'), +} + +MIRROR_PEER_SYNC_STAT_SCHEMA = { + 'state': (str, 'Mirror sync state: idle, syncing, stale, or failed'), + 'failure_reason': (str, 'Last sync failure reason when state is failed'), + 'current_syncing_snap': (MIRROR_CURRENT_SYNCING_SNAP_SCHEMA, + 'Snapshot currently being synchronized'), + 'last_synced_snap': (MIRROR_LAST_SYNCED_SNAP_SCHEMA, + 'Last successfully synchronized snapshot'), + 'snaps_synced': (int, 'Total number of snapshots synchronized'), + 'snaps_deleted': (int, 'Total number of snapshots deleted on the peer'), + 'snaps_renamed': (int, 'Total number of snapshots renamed on the peer'), + 'metrics_updated_at': (float, 'Wall-clock time when metrics were last updated'), +} + +MIRROR_DIR_METRICS_SCHEMA = { + 'peer': ({ + 'peer_uuid': (MIRROR_PEER_SYNC_STAT_SCHEMA, + 'Sync statistics keyed by peer UUID'), + }, 'Peer sync statistics for the mirrored directory'), +} + +MIRROR_STATUS_SCHEMA = { + 'metrics': ({ + '/dir_path': (MIRROR_DIR_METRICS_SCHEMA, + 'Mirror directory metrics keyed by absolute path'), + }, 'Snapshot mirror sync metrics grouped by mirrored directory path'), +} + # pylint: disable=R0904 @APIRouter('/cephfs', Scope.CEPHFS) @@ -1444,14 +1512,22 @@ class CephFSMirror(RESTController): ) return json.loads(out) - @Endpoint('GET', path='/snapshot-mirror-status') + @EndpointDoc("Get snapshot mirror sync metrics", + parameters={ + 'fs_name': (str, 'File system name'), + 'path': (str, 'Mirrored directory path'), + 'peer_id': (str, 'Peer UUID'), + }, + responses={200: MIRROR_STATUS_SCHEMA}) + @Endpoint('GET', path='/{fs_name}/status') @ReadPermission - def snapshot_mirror_status(self, fs_name: str, mirrored_dir_path=None, peer_uuid=None): - error_code, out, err = mgr.remote('mirroring', 'snapshot_mirror_status', - fs_name, mirrored_dir_path, peer_uuid) + def mirror_fs_status(self, fs_name: str, path: str = '', peer_id: str = ''): + error_code, out, err = mgr.remote( + 'mirroring', 'snapshot_mirror_status', fs_name, + path or None, peer_id or None) if error_code != 0: raise DashboardException( - msg=f'Failed to get Cephfs snapshot mirror status: {err}', + msg=f'Failed to get Cephfs mirror status: {err}', code=error_code, component='cephfs.mirror' ) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-mirror-paths/cephfs-mirroring-fs-mirror-paths.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-mirror-paths/cephfs-mirroring-fs-mirror-paths.component.spec.ts index 9670380c0be..0569eba8ef7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-mirror-paths/cephfs-mirroring-fs-mirror-paths.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-mirror-paths/cephfs-mirroring-fs-mirror-paths.component.spec.ts @@ -3,7 +3,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; import { of, throwError } from 'rxjs'; -import { CephfsService, SnapshotMirrorStatusResponse } from '~/app/shared/api/cephfs.service'; +import { CephfsService } from '~/app/shared/api/cephfs.service'; +import { MirrorStatusResponse } from '~/app/shared/models/cephfs.model'; import { FormatterService } from '~/app/shared/services/formatter.service'; import { CephfsMirroringFsMirrorPathsComponent } from './cephfs-mirroring-fs-mirror-paths.component'; @@ -13,7 +14,7 @@ describe('CephfsMirroringFsMirrorPathsComponent', () => { let cephfsService: any; let formatterService: any; - const mockSnapshotMirrorStatusResponse: SnapshotMirrorStatusResponse = { + const mockMirrorStatusResponse: MirrorStatusResponse = { metrics: { '/path1': { peer: { @@ -89,7 +90,7 @@ describe('CephfsMirroringFsMirrorPathsComponent', () => { beforeEach(async () => { const cephfsServiceMock = { - getSnapshotMirrorStatus: jest.fn() + getMirrorStatus: jest.fn() }; const formatterServiceMock = { @@ -148,7 +149,7 @@ describe('CephfsMirroringFsMirrorPathsComponent', () => { }); it('should initialize columns and fetch fsName on init', () => { - cephfsService.getSnapshotMirrorStatus.mockReturnValue(of(mockSnapshotMirrorStatusResponse)); + cephfsService.getMirrorStatus.mockReturnValue(of(mockMirrorStatusResponse)); component.ngOnInit(); @@ -162,7 +163,7 @@ describe('CephfsMirroringFsMirrorPathsComponent', () => { // Verify fsName is fetched and data is loaded expect(component.fsName).toBe('test-fs'); - expect(cephfsService.getSnapshotMirrorStatus).toHaveBeenCalledWith('test-fs'); + expect(cephfsService.getMirrorStatus).toHaveBeenCalledWith('test-fs'); }); describe('parseMirrorStatus', () => { @@ -177,7 +178,7 @@ describe('CephfsMirroringFsMirrorPathsComponent', () => { }); it('should parse mirror status with current_syncing_snap structure', () => { - const result = component.parseMirrorStatus(mockSnapshotMirrorStatusResponse); + const result = component.parseMirrorStatus(mockMirrorStatusResponse); expect(result.length).toBe(2); @@ -215,7 +216,7 @@ describe('CephfsMirroringFsMirrorPathsComponent', () => { }); it('should skip paths without peer data', () => { - const dataWithoutPeer: SnapshotMirrorStatusResponse = { + const dataWithoutPeer: MirrorStatusResponse = { metrics: { '/path1': {} as any } @@ -226,7 +227,7 @@ describe('CephfsMirroringFsMirrorPathsComponent', () => { }); it('should use default values when optional fields are missing', () => { - const minimalData: SnapshotMirrorStatusResponse = { + const minimalData: MirrorStatusResponse = { metrics: { '/path1': { peer: { @@ -361,19 +362,17 @@ describe('CephfsMirroringFsMirrorPathsComponent', () => { describe('loadMirrorPaths', () => { it('should load mirror paths successfully', () => { - cephfsService.getSnapshotMirrorStatus.mockReturnValue(of(mockSnapshotMirrorStatusResponse)); + cephfsService.getMirrorStatus.mockReturnValue(of(mockMirrorStatusResponse)); component.fsName = 'test-fs'; component.loadMirrorPaths(); - expect(cephfsService.getSnapshotMirrorStatus).toHaveBeenCalledWith('test-fs'); + expect(cephfsService.getMirrorStatus).toHaveBeenCalledWith('test-fs'); expect(component.mirrorPaths.length).toBe(2); }); it('should set empty array on error', () => { - cephfsService.getSnapshotMirrorStatus.mockReturnValue( - throwError(() => new Error('API Error')) - ); + cephfsService.getMirrorStatus.mockReturnValue(throwError(() => new Error('API Error'))); component.fsName = 'test-fs'; component.loadMirrorPaths(); @@ -386,7 +385,7 @@ describe('CephfsMirroringFsMirrorPathsComponent', () => { component.loadMirrorPaths(); - expect(cephfsService.getSnapshotMirrorStatus).not.toHaveBeenCalled(); + expect(cephfsService.getMirrorStatus).not.toHaveBeenCalled(); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-mirror-paths/cephfs-mirroring-fs-mirror-paths.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-mirror-paths/cephfs-mirroring-fs-mirror-paths.component.ts index 991d663c7c1..af19d4002e7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-mirror-paths/cephfs-mirroring-fs-mirror-paths.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-mirror-paths/cephfs-mirroring-fs-mirror-paths.component.ts @@ -8,11 +8,12 @@ import { } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; -import { CephfsService, SnapshotMirrorStatusResponse } from '~/app/shared/api/cephfs.service'; +import { CephfsService } from '~/app/shared/api/cephfs.service'; import { CdTableColumn } from '~/app/shared/models/cd-table-column'; import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; import { ICON_TYPE } from '~/app/shared/enum/icons.enum'; import { FormatterService } from '~/app/shared/services/formatter.service'; +import { MirrorStatusResponse } from '~/app/shared/models/cephfs.model'; interface MirrorPath { path: string; @@ -184,8 +185,8 @@ export class CephfsMirroringFsMirrorPathsComponent implements OnInit, OnDestroy return; } - this.cephfsService.getSnapshotMirrorStatus(this.fsName).subscribe( - (data: SnapshotMirrorStatusResponse) => { + this.cephfsService.getMirrorStatus(this.fsName).subscribe( + (data: MirrorStatusResponse) => { this.mirrorPaths = this.parseMirrorStatus(data); if (this.selectedPath) { this.selectedPath = @@ -202,7 +203,7 @@ export class CephfsMirroringFsMirrorPathsComponent implements OnInit, OnDestroy ); } - parseMirrorStatus(data: SnapshotMirrorStatusResponse): MirrorPath[] { + parseMirrorStatus(data: MirrorStatusResponse): MirrorPath[] { if (!data?.metrics) { return []; } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.html index 8bdd4bb2146..f40cca0503e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.html @@ -1,6 +1,144 @@ -
- -

Overview content will be available here.

-
-
+@let fsData = (fsData$ | async); + +
+
+
+ Mirror paths + {{ fsData?.stats?.mirrorPaths }} +
+
+
+ Syncing paths + {{ fsData?.stats?.syncingPaths }} +
+
+
+
+ Last successful sync + + @if (fsData?.sync?.syncedAt) { + {{ fsData.sync.path }} {{ fsData.sync.snapName }}: {{ fsData.sync.syncedAt | relativeDate }} + } @else if (fsData?.sync?.path || fsData?.sync?.snapName) { + {{ fsData.sync.path }} {{ fsData.sync.snapName }} + } @else { + - + } + +
+
+
+
+ Failures + + @if ((fsData?.stats?.failures ?? 0) > 0) { + + } + {{ fsData?.stats?.failures }} + +
+
+
+
+
+ + +
+
+
+

Destination cluster

+
+
+
+
+ Cluster name + {{ fsData?.destination?.clusterName }} +
+
+ Filesystem name + {{ fsData?.destination?.destinationFsName }} +
+
+
+
+ FSID + {{ fsData?.destination?.fsid }} +
+
+ Monitor endpoint + {{ fsData?.destination?.monitorEndpoint }} +
+
+
+
+ Bytes synced + {{ fsData?.sync?.bytesSynced }} +
+
+ Last synced + + @if (fsData?.sync?.syncedAt) { + {{ fsData.sync.syncedAt | relativeDate }} + } @else { + - + } + +
+
+
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.spec.ts index 2312db990e5..2f8ce03c5ee 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.spec.ts @@ -1,17 +1,104 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; -import { of } from 'rxjs'; +import { BehaviorSubject, of } from 'rxjs'; +import { take } from 'rxjs/operators'; + +import { CephfsService } from '~/app/shared/api/cephfs.service'; +import { + Daemon, + DaemonOverviewInfo, + MirroringFsOverviewData, + MirroringFsSyncInfo +} from '~/app/shared/models/cephfs.model'; +import { RelativeDatePipe } from '~/app/shared/pipes/relative-date.pipe'; +import { RefreshIntervalService } from '~/app/shared/services/refresh-interval.service'; import { CephfsMirroringFsOverviewComponent } from './cephfs-mirroring-fs-overview.component'; describe('CephfsMirroringFsOverviewComponent', () => { let component: CephfsMirroringFsOverviewComponent; let fixture: ComponentFixture; + let cephfsServiceMock: { + listDaemonStatus: jest.Mock; + listMirrorPeers: jest.Mock; + getMirrorStatus: jest.Mock; + }; + let refreshInterval$: BehaviorSubject; + + const mockDaemonStatus: Daemon[] = [ + { + daemon_id: 1, + filesystems: [ + { + filesystem_id: 1, + id: '1', + name: 'myfs', + directory_count: 5, + peers: [ + { + uuid: 'peer-uuid', + remote: { + client_name: 'client.mirror', + cluster_name: 'remote-cluster', + fs_name: 'remote_fs', + fsid: 'abc-123', + mon_host: '10.0.0.1:6789' + }, + stats: { + failure_count: 2, + recovery_count: 1 + } + } + ] + } + ] + } + ]; + + const mockMirrorStatus = { + metrics: { + '/dir1': { + peer: { + 'peer-uuid': { + state: 'idle', + last_synced_snap: { + name: 'snap1', + sync_bytes: '1.00 KiB', + sync_time_stamp: '9000.000000s' + }, + metrics_updated_at: 1_700_000_000 + } + } + }, + '/dir2': { + peer: { + 'peer-uuid': { + state: 'syncing' + } + } + } + } + }; beforeEach(async () => { + refreshInterval$ = new BehaviorSubject(null); + cephfsServiceMock = { + listDaemonStatus: jest.fn().mockReturnValue(of(mockDaemonStatus)), + listMirrorPeers: jest.fn().mockReturnValue( + of({ + 'peer-uuid': { + client_name: 'client.mirror', + site_name: 'remote-site', + fs_name: 'remote_fs' + } + }) + ), + getMirrorStatus: jest.fn().mockReturnValue(of(mockMirrorStatus)) + }; + await TestBed.configureTestingModule({ - declarations: [CephfsMirroringFsOverviewComponent], + declarations: [CephfsMirroringFsOverviewComponent, RelativeDatePipe], providers: [ { provide: ActivatedRoute, @@ -20,6 +107,11 @@ describe('CephfsMirroringFsOverviewComponent', () => { paramMap: of(convertToParamMap({ fsName: 'myfs' })) } } + }, + { provide: CephfsService, useValue: cephfsServiceMock }, + { + provide: RefreshIntervalService, + useValue: { intervalData$: refreshInterval$.asObservable() } } ], schemas: [NO_ERRORS_SCHEMA] @@ -29,13 +121,179 @@ describe('CephfsMirroringFsOverviewComponent', () => { component = fixture.componentInstance; fixture.detectChanges(); + return fixture.whenStable(); }); it('should create', () => { expect(component).toBeTruthy(); }); - it('should read fsName from parent route params', () => { - expect(component.fsName).toBe('myfs'); + it('should read fsName from parent route params', async () => { + const fsData = await component.fsData$.pipe(take(1)).toPromise(); + expect(fsData?.fsName).toBe('myfs'); + }); + + it('should populate fsData from daemon and mirror status', async () => { + const fsData = await component.fsData$.pipe(take(1)).toPromise(); + + expect(cephfsServiceMock.listDaemonStatus).toHaveBeenCalled(); + expect(cephfsServiceMock.listMirrorPeers).toHaveBeenCalledWith('myfs'); + expect(cephfsServiceMock.getMirrorStatus).toHaveBeenCalledWith('myfs', undefined, 'peer-uuid'); + expect(fsData?.stats.mirrorPaths).toBe(5); + expect(fsData?.stats.failures).toBe(2); + expect(fsData?.destination.clusterName).toBe('remote-cluster'); + expect(fsData?.destination.destinationFsName).toBe('remote_fs'); + expect(fsData?.destination.fsid).toBe('abc-123'); + expect(fsData?.destination.monitorEndpoint).toBe('10.0.0.1:6789'); + expect(fsData?.destination.siteName).toBe('remote-site'); + expect(fsData?.stats.syncingPaths).toBe(1); + expect(fsData?.sync.bytesSynced).toBe('1.00 KiB'); + expect(fsData?.sync.path).toBe('/dir1'); + expect(fsData?.sync.snapName).toBe('snap1'); + expect(fsData?.sync.syncedAt).toBe(1_700_000_000); + }); + + it('should refresh overview data on interval tick', async () => { + await component.fsData$.pipe(take(1)).toPromise(); + expect(cephfsServiceMock.listDaemonStatus).toHaveBeenCalledTimes(1); + + refreshInterval$.next(null); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(cephfsServiceMock.listDaemonStatus).toHaveBeenCalledTimes(2); + expect(cephfsServiceMock.listMirrorPeers).toHaveBeenCalledTimes(2); + expect(cephfsServiceMock.getMirrorStatus).toHaveBeenCalledTimes(2); + }); +}); + +describe('CephfsMirroringFsOverviewComponent helpers', () => { + let component: CephfsMirroringFsOverviewComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [CephfsMirroringFsOverviewComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { parent: { paramMap: of(convertToParamMap({ fsName: 'myfs' })) } } + }, + { + provide: CephfsService, + useValue: { + listDaemonStatus: jest.fn(), + listMirrorPeers: jest.fn(), + getMirrorStatus: jest.fn() + } + }, + { provide: RefreshIntervalService, useValue: { intervalData$: of(null) } } + ], + schemas: [NO_ERRORS_SCHEMA] + }); + component = TestBed.createComponent(CephfsMirroringFsOverviewComponent).componentInstance; + }); + + const call = (method: string, ...args: unknown[]): T => + ((component as unknown) as Record T>)[method](...args); + + it('getDaemonOverviewInfo returns defaults when filesystem is missing', () => { + const info = call('getDaemonOverviewInfo', [], 'missing'); + expect(info.mirrorPaths).toBe(0); + expect(info.peerUuid).toBeUndefined(); + }); + + it('getDaemonOverviewInfo maps daemon peer data', () => { + const info = call( + 'getDaemonOverviewInfo', + [ + { + filesystems: [ + { + name: 'myfs', + directory_count: 3, + peers: [ + { + uuid: 'peer-1', + remote: { cluster_name: 'remote', fs_name: 'dest', fsid: 'id', mon_host: 'mon' }, + stats: { failure_count: 1 } + } + ] + } + ] + } + ], + 'myfs' + ); + + expect(info.mirrorPaths).toBe(3); + expect(info.failures).toBe(1); + expect(info.peerUuid).toBe('peer-1'); + expect(info.clusterName).toBe('remote'); + }); + + it('buildMirroringFsOverviewData uses empty sync when status is null', () => { + const data = call( + 'buildMirroringFsOverviewData', + 'myfs', + { + mirrorPaths: 2, + failures: 0, + clusterName: 'c', + destinationFsName: 'd', + fsid: 'f', + monitorEndpoint: 'm', + peerUuid: 'peer-1' + }, + { 'peer-1': { site_name: 'site-a' } }, + null + ); + + expect(data.stats.syncingPaths).toBe(0); + expect(data.destination.siteName).toBe('site-a'); + expect(data.sync.bytesSynced).toBe('-'); + }); + + it('extractLatestSync counts syncing paths and picks latest snapshot', () => { + const sync = call<{ syncingPaths: number; info: MirroringFsSyncInfo }>('extractLatestSync', { + metrics: { + '/old': { + peer: { + p1: { + state: 'idle', + last_synced_snap: { name: 'old', sync_bytes: '1 B', sync_time_stamp: '1s' }, + metrics_updated_at: 100 + } + } + }, + '/new': { + peer: { + p2: { + state: 'syncing', + last_synced_snap: { name: 'new', sync_bytes: '2 B', sync_time_stamp: '2s' }, + metrics_updated_at: 200 + } + } + } + } + }); + + expect(sync.syncingPaths).toBe(1); + expect(sync.info.snapName).toBe('new'); + expect(sync.info.path).toBe('/new'); + expect(sync.info.syncedAt).toBe(200); + }); + + it('isNewerMirrorSync prefers metrics_updated_at over sync timestamp', () => { + expect(call('isNewerMirrorSync', '1s', 300, '9s', 200)).toBe(true); + expect(call('isNewerMirrorSync', '9s', 100, '1s', 200)).toBe(false); + }); + + it('mirrorMetricsUpdatedAtToEpoch parses valid values and rejects invalid ones', () => { + expect(call('mirrorMetricsUpdatedAtToEpoch', 1_700_000_000.9)).toBe( + 1_700_000_000 + ); + expect(call('mirrorMetricsUpdatedAtToEpoch', '1234.5')).toBe(1234); + expect(call('mirrorMetricsUpdatedAtToEpoch', '')).toBeNull(); + expect(call('mirrorMetricsUpdatedAtToEpoch', 0)).toBeNull(); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.ts index 52e401a699a..f0f94752eee 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-fs-overview/cephfs-mirroring-fs-overview.component.ts @@ -1,21 +1,231 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; -import { ActivatedRoute, ParamMap } from '@angular/router'; +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + inject, + ViewEncapsulation +} from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { ActivatedRoute, convertToParamMap, ParamMap } from '@angular/router'; +import { EMPTY, forkJoin, Observable, of } from 'rxjs'; +import { catchError, exhaustMap, map, shareReplay, switchMap } from 'rxjs/operators'; + +import { CephfsService } from '~/app/shared/api/cephfs.service'; +import { + Daemon, + DaemonOverviewInfo, + MirroringFsOverviewData, + MirroringFsSyncInfo, + MirrorPeerList, + MirrorStatusResponse +} from '~/app/shared/models/cephfs.model'; +import { RefreshIntervalService } from '~/app/shared/services/refresh-interval.service'; +import { IconSize } from '~/app/shared/enum/icons.enum'; @Component({ selector: 'cd-cephfs-mirroring-fs-overview', templateUrl: './cephfs-mirroring-fs-overview.component.html', styleUrls: ['./cephfs-mirroring-fs-overview.component.scss'], standalone: false, + changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None }) -export class CephfsMirroringFsOverviewComponent implements OnInit { - fsName = ''; +export class CephfsMirroringFsOverviewComponent { + private readonly route = inject(ActivatedRoute); + private readonly cephfsService = inject(CephfsService); + private readonly refreshIntervalService = inject(RefreshIntervalService); + private readonly destroyRef = inject(DestroyRef); + + readonly iconSize = IconSize; + + private readonly fsName$ = (this.route.parent?.paramMap ?? of(convertToParamMap({}))).pipe( + map((paramMap: ParamMap) => paramMap.get('fsName') ?? '-') + ); + + readonly fsData$ = this.fsName$.pipe( + switchMap((fsName) => this.refreshIntervalObs(() => this.fetchFsData(fsName))), + shareReplay({ bufferSize: 1, refCount: true }) + ); + + private refreshIntervalObs(fn: () => Observable): Observable { + return this.refreshIntervalService.intervalData$.pipe( + exhaustMap(() => fn().pipe(catchError(() => EMPTY))), + takeUntilDestroyed(this.destroyRef) + ); + } + + private fetchFsData(fsName: string): Observable { + return forkJoin({ + daemons: this.cephfsService.listDaemonStatus().pipe(catchError(() => of([] as Daemon[]))), + peers: this.cephfsService + .listMirrorPeers(fsName) + .pipe(catchError(() => of({} as MirrorPeerList))) + }).pipe( + switchMap(({ daemons, peers }) => { + const daemonInfo = this.getDaemonOverviewInfo(daemons, fsName); + if (!daemonInfo.peerUuid) { + return of(this.buildMirroringFsOverviewData(fsName, daemonInfo, peers, null)); + } + + return this.cephfsService.getMirrorStatus(fsName, undefined, daemonInfo.peerUuid).pipe( + catchError(() => of({} as MirrorStatusResponse)), + map((status) => this.buildMirroringFsOverviewData(fsName, daemonInfo, peers, status)) + ); + }) + ); + } + + private getDaemonOverviewInfo(daemons: Daemon[], fsName: string): DaemonOverviewInfo { + const empty: DaemonOverviewInfo = { + mirrorPaths: 0, + failures: 0, + clusterName: '-', + destinationFsName: '-', + fsid: '-', + monitorEndpoint: '-' + }; + + for (const daemon of daemons) { + const fs = daemon.filesystems?.find((filesystem) => filesystem.name === fsName); + if (!fs) { + continue; + } + + const peer = fs.peers?.[0]; + return { + mirrorPaths: fs.directory_count ?? 0, + failures: (fs.peers ?? []).reduce((sum, item) => sum + (item.stats?.failure_count ?? 0), 0), + clusterName: peer?.remote?.cluster_name ?? '-', + destinationFsName: peer?.remote?.fs_name ?? '-', + fsid: peer?.remote?.fsid ?? '-', + monitorEndpoint: peer?.remote?.mon_host ?? '-', + peerUuid: peer?.uuid + }; + } + + return empty; + } + + private buildMirroringFsOverviewData( + fsName: string, + daemonInfo: DaemonOverviewInfo, + peers: MirrorPeerList, + status: MirrorStatusResponse | null + ): MirroringFsOverviewData { + const sync = status ? this.extractLatestSync(status) : this.emptySyncInfo(); + + return { + fsName, + stats: { + mirrorPaths: daemonInfo.mirrorPaths, + failures: daemonInfo.failures, + syncingPaths: sync.syncingPaths + }, + destination: { + clusterName: daemonInfo.clusterName, + siteName: daemonInfo.peerUuid ? peers[daemonInfo.peerUuid]?.site_name ?? '-' : '-', + destinationFsName: daemonInfo.destinationFsName, + fsid: daemonInfo.fsid, + monitorEndpoint: daemonInfo.monitorEndpoint + }, + sync: sync.info + }; + } + + private emptySyncInfo(): { syncingPaths: number; info: MirroringFsSyncInfo } { + return { + syncingPaths: 0, + info: { + bytesSynced: '-', + path: '', + snapName: '', + syncedAt: null + } + }; + } + + private extractLatestSync( + status: MirrorStatusResponse + ): { + syncingPaths: number; + info: MirroringFsSyncInfo; + } { + let syncingPaths = 0; + let latestSyncTime = ''; + let latestMetricsUpdatedAt: number | string | undefined; + let latestSnapName = ''; + let latestBytes = ''; + let latestSyncPath = ''; - constructor(private route: ActivatedRoute) {} + for (const [dirPath, dirMetrics] of Object.entries(status.metrics ?? {})) { + for (const dir of Object.values(dirMetrics.peer ?? {})) { + if (dir.state === 'syncing') { + syncingPaths++; + } + + const snap = dir.last_synced_snap; + if (!snap) { + continue; + } + + const syncTime = String(snap.sync_time_stamp ?? ''); + const snapName = snap.name ?? ''; + const metricsUpdatedAt = dir.metrics_updated_at; + if ( + this.isNewerMirrorSync(syncTime, metricsUpdatedAt, latestSyncTime, latestMetricsUpdatedAt) + ) { + latestSyncTime = syncTime; + latestMetricsUpdatedAt = metricsUpdatedAt; + latestSnapName = snapName; + latestBytes = String(snap.sync_bytes ?? ''); + latestSyncPath = dirPath; + } else if (!latestSnapName && snapName) { + latestSnapName = snapName; + latestBytes = latestBytes || String(snap.sync_bytes ?? ''); + latestSyncPath = dirPath; + latestMetricsUpdatedAt = latestMetricsUpdatedAt ?? metricsUpdatedAt; + } + } + } + + return { + syncingPaths, + info: { + bytesSynced: latestBytes || '-', + path: latestSyncPath, + snapName: latestSnapName, + syncedAt: this.mirrorMetricsUpdatedAtToEpoch(latestMetricsUpdatedAt) + } + }; + } + + private isNewerMirrorSync( + syncTime: string, + metricsUpdatedAt: number | string | undefined, + latestSyncTime: string, + latestMetricsUpdatedAt: number | string | undefined + ): boolean { + const newEpoch = this.mirrorMetricsUpdatedAtToEpoch(metricsUpdatedAt); + const latestEpoch = this.mirrorMetricsUpdatedAtToEpoch(latestMetricsUpdatedAt); + if (newEpoch !== null && latestEpoch !== null) { + return newEpoch >= latestEpoch; + } + return Boolean(syncTime && syncTime >= latestSyncTime); + } - ngOnInit(): void { - this.route.parent?.paramMap.subscribe((paramMap: ParamMap) => { - this.fsName = paramMap.get('fsName') ?? ''; - }); + private mirrorMetricsUpdatedAtToEpoch( + metricsUpdatedAt: number | string | undefined + ): number | null { + if (metricsUpdatedAt === undefined || metricsUpdatedAt === null || metricsUpdatedAt === '') { + return null; + } + const epoch = + typeof metricsUpdatedAt === 'number' + ? metricsUpdatedAt + : parseFloat(String(metricsUpdatedAt)); + if (!Number.isFinite(epoch) || epoch <= 0) { + return null; + } + return Math.floor(epoch); } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs.service.ts index 9baf1d277d5..b2fe9411d5a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs.service.ts @@ -7,7 +7,7 @@ import { Observable } from 'rxjs'; import { cdEncode, cdEncodeNot } from '../decorators/cd-encode'; import { CephfsDir, CephfsDirStatfs, CephfsQuotas } from '../models/cephfs-directory-models'; import { shareReplay } from 'rxjs/operators'; -import { Daemon } from '../models/cephfs.model'; +import { Daemon, MirrorPeerList, MirrorStatusResponse } from '../models/cephfs.model'; export interface SnapshotMirrorStatusResponse { metrics: { @@ -219,22 +219,25 @@ export class CephfsService { }); } - getSnapshotMirrorStatus( + listMirrorPeers(fsName: string): Observable { + return this.http.get(`${this.baseURL}/mirror/${fsName}`); + } + + getMirrorStatus( fsName: string, - mirroredDirPath?: string, - peerUuid?: string - ): Observable { + path?: string, + peerId?: string + ): Observable { let params = new HttpParams(); - if (mirroredDirPath) { - params = params.append('mirrored_dir_path', mirroredDirPath); + if (path) { + params = params.set('path', path); } - if (peerUuid) { - params = params.append('peer_uuid', peerUuid); + if (peerId) { + params = params.set('peer_id', peerId); } - return this.http.get( - `${this.baseURL}/mirror/snapshot-mirror-status/${fsName}`, - { params } - ); + return this.http.get(`${this.baseURL}/mirror/${fsName}/status`, { + params + }); } addMirrorDirectory(@cdEncodeNot fsName: string, @cdEncodeNot path: string): Observable { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs.model.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs.model.ts index 6053e86346c..733c15bcbc9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs.model.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs.model.ts @@ -16,6 +16,8 @@ export interface RemoteInfo { client_name: string; cluster_name: string; fs_name: string; + fsid?: string; + mon_host?: string; } export interface PeerStats { @@ -132,6 +134,78 @@ export function mdsStateToStatus(state: string | undefined): MdsStatus { export type DaemonResponse = Daemon[]; +export interface MirrorPeerListEntry { + client_name: string; + site_name: string; + fs_name: string; +} + +export type MirrorPeerList = Record; + +export interface MirrorSyncedSnap { + id?: number; + name?: string; + sync_bytes?: number | string; + sync_duration?: number | string; + sync_time_stamp?: number | string; +} + +export interface MirrorDirStatus { + state?: string; + last_synced_snap?: MirrorSyncedSnap; + current_syncing_snap?: MirrorSyncedSnap; + snaps_synced?: number; + metrics_updated_at?: number | string; +} + +export interface MirrorDirMetrics { + peer?: Record; +} + +export type MirrorStatusMetrics = Record; + +export interface MirrorStatusResponse { + metrics?: MirrorStatusMetrics; +} + +export interface DaemonOverviewInfo { + mirrorPaths: number; + failures: number; + clusterName: string; + destinationFsName: string; + fsid: string; + monitorEndpoint: string; + peerUuid?: string; +} + +export interface MirroringFsOverviewStats { + mirrorPaths: number; + syncingPaths: number; + failures: number; +} + +export interface MirroringFsDestinationCluster { + clusterName: string; + siteName: string; + destinationFsName: string; + fsid: string; + monitorEndpoint: string; +} + +export interface MirroringFsSyncInfo { + bytesSynced: string; + path: string; + snapName: string; + syncedAt: number | null; +} + +export interface MirroringFsOverviewData { + fsName: string; + stats: MirroringFsOverviewStats; + destination: MirroringFsDestinationCluster; + sync: MirroringFsSyncInfo; +} + export type MirroringEntityRow = { entity: string; mdsCaps: string; diff --git a/src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_spacings.scss b/src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_spacings.scss index 2795bfa0552..8c3c22d7956 100644 --- a/src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_spacings.scss +++ b/src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_spacings.scss @@ -137,3 +137,17 @@ .cds-pt-6 { padding-top: layout.$spacing-06; } + +.cds-text-start { + text-align: start; +} + +.cds-center-inline-content { + display: flex; + justify-content: center; + + > .cds--stack-vertical { + width: fit-content; + text-align: start; + } +} diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index df6dabbb487..bfc8a52823a 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -3457,47 +3457,6 @@ paths: summary: Enable mirroring for a filesystem tags: - CephfsMirror - /api/cephfs/mirror/snapshot-mirror-status/{fs_name}: - get: - parameters: - - in: path - name: fs_name - required: true - schema: - type: string - - allowEmptyValue: true - in: query - name: mirrored_dir_path - schema: - type: string - - allowEmptyValue: true - in: query - name: peer_uuid - schema: - type: string - responses: - '200': - content: - application/json: - schema: - type: object - application/vnd.ceph.api.v1.0+json: - schema: - type: object - description: OK - '400': - description: Operation exception. Please check the response body for details. - '401': - description: Unauthenticated access. Please login first. - '403': - description: Unauthorized access. Please check your permissions. - '500': - description: Unexpected error. Please check the response body for the stack - trace. - security: - - jwt: [] - tags: - - CephfsMirror /api/cephfs/mirror/token: post: parameters: [] @@ -3626,6 +3585,397 @@ paths: summary: Get peers tags: - CephfsMirror + /api/cephfs/mirror/{fs_name}/status: + get: + parameters: + - description: File system name + in: path + name: fs_name + required: true + schema: + type: string + - default: '' + description: Mirrored directory path + in: query + name: path + schema: + type: string + - default: '' + description: Peer UUID + in: query + name: peer_id + schema: + type: string + responses: + '200': + content: + application/json: + schema: + properties: + metrics: + description: Snapshot mirror sync metrics grouped by mirrored + directory path + properties: + /dir_path: + description: Mirror directory metrics keyed by absolute path + properties: + peer: + description: Peer sync statistics for the mirrored directory + properties: + peer_uuid: + description: Sync statistics keyed by peer UUID + properties: + current_syncing_snap: + description: Snapshot currently being synchronized + properties: + avg_read_throughput_bytes: + description: Average read throughput + type: string + avg_write_throughput_bytes: + description: Average write throughput + type: string + bytes: + description: Byte sync progress + properties: + sync_bytes: + description: Bytes synced so far + type: string + sync_percent: + description: Sync completion percentage + type: string + total_bytes: + description: Total bytes to sync + type: string + required: &id024 + - sync_bytes + - total_bytes + - sync_percent + type: object + crawl: + description: Directory crawl progress + properties: + duration: + description: Phase duration + type: string + state: + description: Phase state, e.g. completed, + in-progress, or waiting + type: string + required: &id025 + - state + - duration + type: object + datasync_queue_wait: + description: Data sync queue wait progress + properties: + duration: + description: Phase duration + type: string + state: + description: Phase state, e.g. completed, + in-progress, or waiting + type: string + required: &id026 + - state + - duration + type: object + eta: + description: Estimated time remaining for + the current sync + type: string + files: + description: File sync progress + properties: + sync_files: + description: Files synced so far + type: string + sync_percent: + description: Sync completion percentage + type: string + total_files: + description: Total files to sync + type: string + required: &id027 + - sync_files + - total_files + - sync_percent + type: object + id: + description: Snapshot ID + type: integer + name: + description: Snapshot name + type: string + sync-mode: + description: 'Snapshot sync mode: full or + delta' + type: string + required: &id028 + - id + - name + - sync-mode + - avg_read_throughput_bytes + - avg_write_throughput_bytes + - crawl + - datasync_queue_wait + - bytes + - files + - eta + type: object + failure_reason: + description: Last sync failure reason when state + is failed + type: string + last_synced_snap: + description: Last successfully synchronized snapshot + properties: + crawl_duration: + description: Time taken to scan directory + type: string + datasync_queue_wait_duration: + description: Time in data sync queue + type: string + id: + description: Snapshot ID + type: integer + name: + description: Snapshot name + type: string + sync_bytes: + description: Bytes synced for the snapshot + type: string + sync_duration: + description: Snapshot sync duration + type: string + sync_files: + description: Number of files synced for the + snapshot + type: integer + sync_time_stamp: + description: Time of the last sync + type: string + required: &id029 + - id + - name + - crawl_duration + - datasync_queue_wait_duration + - sync_duration + - sync_time_stamp + - sync_bytes + - sync_files + type: object + metrics_updated_at: + description: Wall-clock time when metrics were + last updated + type: number + snaps_deleted: + description: Total number of snapshots deleted + on the peer + type: integer + snaps_renamed: + description: Total number of snapshots renamed + on the peer + type: integer + snaps_synced: + description: Total number of snapshots synchronized + type: integer + state: + description: 'Mirror sync state: idle, syncing, + stale, or failed' + type: string + required: &id030 + - state + - failure_reason + - current_syncing_snap + - last_synced_snap + - snaps_synced + - snaps_deleted + - snaps_renamed + - metrics_updated_at + type: object + required: &id031 + - peer_uuid + type: object + required: &id032 + - peer + type: object + required: &id033 + - /dir_path + type: object + required: &id034 + - metrics + type: object + application/vnd.ceph.api.v1.0+json: + schema: + properties: + metrics: + description: Snapshot mirror sync metrics grouped by mirrored + directory path + properties: + /dir_path: + description: Mirror directory metrics keyed by absolute path + properties: + peer: + description: Peer sync statistics for the mirrored directory + properties: + peer_uuid: + description: Sync statistics keyed by peer UUID + properties: + current_syncing_snap: + description: Snapshot currently being synchronized + properties: + avg_read_throughput_bytes: + description: Average read throughput + type: string + avg_write_throughput_bytes: + description: Average write throughput + type: string + bytes: + description: Byte sync progress + properties: + sync_bytes: + description: Bytes synced so far + type: string + sync_percent: + description: Sync completion percentage + type: string + total_bytes: + description: Total bytes to sync + type: string + required: *id024 + type: object + crawl: + description: Directory crawl progress + properties: + duration: + description: Phase duration + type: string + state: + description: Phase state, e.g. completed, + in-progress, or waiting + type: string + required: *id025 + type: object + datasync_queue_wait: + description: Data sync queue wait progress + properties: + duration: + description: Phase duration + type: string + state: + description: Phase state, e.g. completed, + in-progress, or waiting + type: string + required: *id026 + type: object + eta: + description: Estimated time remaining for + the current sync + type: string + files: + description: File sync progress + properties: + sync_files: + description: Files synced so far + type: string + sync_percent: + description: Sync completion percentage + type: string + total_files: + description: Total files to sync + type: string + required: *id027 + type: object + id: + description: Snapshot ID + type: integer + name: + description: Snapshot name + type: string + sync-mode: + description: 'Snapshot sync mode: full or + delta' + type: string + required: *id028 + type: object + failure_reason: + description: Last sync failure reason when state + is failed + type: string + last_synced_snap: + description: Last successfully synchronized snapshot + properties: + crawl_duration: + description: Time taken to scan directory + type: string + datasync_queue_wait_duration: + description: Time in data sync queue + type: string + id: + description: Snapshot ID + type: integer + name: + description: Snapshot name + type: string + sync_bytes: + description: Bytes synced for the snapshot + type: string + sync_duration: + description: Snapshot sync duration + type: string + sync_files: + description: Number of files synced for the + snapshot + type: integer + sync_time_stamp: + description: Time of the last sync + type: string + required: *id029 + type: object + metrics_updated_at: + description: Wall-clock time when metrics were + last updated + type: number + snaps_deleted: + description: Total number of snapshots deleted + on the peer + type: integer + snaps_renamed: + description: Total number of snapshots renamed + on the peer + type: integer + snaps_synced: + description: Total number of snapshots synchronized + type: integer + state: + description: 'Mirror sync state: idle, syncing, + stale, or failed' + type: string + required: *id030 + type: object + required: *id031 + type: object + required: *id032 + type: object + required: *id033 + type: object + required: *id034 + type: object + description: OK + '400': + description: Operation exception. Please check the response body for details. + '401': + description: Unauthenticated access. Please login first. + '403': + description: Unauthorized access. Please check your permissions. + '500': + description: Unexpected error. Please check the response body for the stack + trace. + security: + - jwt: [] + summary: Get snapshot mirror sync metrics + tags: + - CephfsMirror /api/cephfs/mirror/{fs_name}/{peer_uuid}: delete: parameters: @@ -5230,7 +5580,7 @@ paths: max_files: description: '' type: integer - required: &id024 + required: &id035 - max_bytes - max_files type: object @@ -5243,7 +5593,7 @@ paths: max_files: description: '' type: integer - required: *id024 + required: *id035 type: object description: OK '400': @@ -5517,7 +5867,7 @@ paths: subdirs: description: '' type: integer - required: &id025 + required: &id036 - bytes - files - subdirs @@ -5534,7 +5884,7 @@ paths: subdirs: description: '' type: integer - required: *id025 + required: *id036 type: object description: OK '400': @@ -6517,7 +6867,7 @@ paths: description: Config option type type: string type: object - required: &id026 + required: &id037 - name - type - level @@ -6594,7 +6944,7 @@ paths: description: Config option type type: string type: object - required: *id026 + required: *id037 type: array description: OK '400': @@ -6718,7 +7068,7 @@ paths: type: description: Type of Rule type: integer - required: &id027 + required: &id038 - rule_id - rule_name - ruleset @@ -6753,7 +7103,7 @@ paths: type: description: Type of Rule type: integer - required: *id027 + required: *id038 type: object description: OK '400': @@ -7016,7 +7366,7 @@ paths: description: '' type: string type: object - required: &id028 + required: &id039 - crush-failure-domain - k - m @@ -7047,7 +7397,7 @@ paths: description: '' type: string type: object - required: *id028 + required: *id039 type: array description: OK '400': @@ -7205,7 +7555,7 @@ paths: rgw: description: '' type: boolean - required: &id029 + required: &id040 - rbd - mirroring - iscsi @@ -7234,7 +7584,7 @@ paths: rgw: description: '' type: boolean - required: *id029 + required: *id040 type: object description: OK '400': @@ -7492,7 +7842,7 @@ paths: instance: description: grafana instance type: string - required: &id030 + required: &id041 - instance type: object application/vnd.ceph.api.v1.0+json: @@ -7501,7 +7851,7 @@ paths: instance: description: grafana instance type: string - required: *id030 + required: *id041 type: object description: OK '400': @@ -7722,7 +8072,7 @@ paths: write_op_per_sec: description: '' type: integer - required: &id031 + required: &id042 - read_bytes_sec - read_op_per_sec - recovering_bytes_per_sec @@ -7744,12 +8094,12 @@ paths: total_used_raw_bytes: description: '' type: integer - required: &id032 + required: &id043 - total_avail_bytes - total_bytes - total_used_raw_bytes type: object - required: &id033 + required: &id044 - stats type: object fs_map: @@ -7780,7 +8130,7 @@ paths: ro_compat: description: '' type: string - required: &id034 + required: &id045 - compat - ro_compat - incompat @@ -7873,7 +8223,7 @@ paths: up: description: '' type: string - required: &id035 + required: &id046 - session_autoclose - balancer - up @@ -7907,12 +8257,12 @@ paths: standbys: description: '' type: string - required: &id036 + required: &id047 - mdsmap - standbys type: object type: array - required: &id037 + required: &id048 - filesystems type: object health: @@ -7927,7 +8277,7 @@ paths: status: description: '' type: string - required: &id038 + required: &id049 - checks - mutes - status @@ -7944,7 +8294,7 @@ paths: up: description: '' type: integer - required: &id039 + required: &id050 - up - down type: object @@ -7957,7 +8307,7 @@ paths: standbys: description: '' type: string - required: &id040 + required: &id051 - active_name - standbys type: object @@ -7970,7 +8320,7 @@ paths: mons: description: '' type: string - required: &id041 + required: &id052 - mons type: object quorum: @@ -7978,7 +8328,7 @@ paths: items: type: integer type: array - required: &id042 + required: &id053 - monmap - quorum type: object @@ -7995,12 +8345,12 @@ paths: up: description: '' type: integer - required: &id043 + required: &id054 - in - up type: object type: array - required: &id044 + required: &id055 - osds type: object pg_info: @@ -8024,7 +8374,7 @@ paths: num_objects_unfound: description: '' type: integer - required: &id045 + required: &id056 - num_objects - num_object_copies - num_objects_degraded @@ -8037,7 +8387,7 @@ paths: statuses: description: '' type: string - required: &id046 + required: &id057 - object_stats - pgs_per_osd - statuses @@ -8051,7 +8401,7 @@ paths: scrub_status: description: '' type: string - required: &id047 + required: &id058 - client_perf - df - fs_map @@ -8087,7 +8437,7 @@ paths: write_op_per_sec: description: '' type: integer - required: *id031 + required: *id042 type: object df: description: '' @@ -8104,9 +8454,9 @@ paths: total_used_raw_bytes: description: '' type: integer - required: *id032 + required: *id043 type: object - required: *id033 + required: *id044 type: object fs_map: description: '' @@ -8136,7 +8486,7 @@ paths: ro_compat: description: '' type: string - required: *id034 + required: *id045 type: object created: description: '' @@ -8226,15 +8576,15 @@ paths: up: description: '' type: string - required: *id035 + required: *id046 type: object standbys: description: '' type: string - required: *id036 + required: *id047 type: object type: array - required: *id037 + required: *id048 type: object health: description: '' @@ -8248,7 +8598,7 @@ paths: status: description: '' type: string - required: *id038 + required: *id049 type: object hosts: description: '' @@ -8262,7 +8612,7 @@ paths: up: description: '' type: integer - required: *id039 + required: *id050 type: object mgr_map: description: '' @@ -8273,7 +8623,7 @@ paths: standbys: description: '' type: string - required: *id040 + required: *id051 type: object mon_status: description: '' @@ -8284,14 +8634,14 @@ paths: mons: description: '' type: string - required: *id041 + required: *id052 type: object quorum: description: '' items: type: integer type: array - required: *id042 + required: *id053 type: object osd_map: description: '' @@ -8306,10 +8656,10 @@ paths: up: description: '' type: integer - required: *id043 + required: *id054 type: object type: array - required: *id044 + required: *id055 type: object pg_info: description: '' @@ -8332,7 +8682,7 @@ paths: num_objects_unfound: description: '' type: integer - required: *id045 + required: *id056 type: object pgs_per_osd: description: '' @@ -8340,7 +8690,7 @@ paths: statuses: description: '' type: string - required: *id046 + required: *id057 type: object pools: description: '' @@ -8351,7 +8701,7 @@ paths: scrub_status: description: '' type: string - required: *id047 + required: *id058 type: object description: OK '400': @@ -8389,7 +8739,7 @@ paths: num_standbys: description: Standby MDS count type: integer - required: &id048 + required: &id059 - num_active - num_standbys type: object @@ -8417,16 +8767,16 @@ paths: message: description: Human-readable summary type: string - required: &id049 + required: &id060 - message - count type: object - required: &id050 + required: &id061 - severity - summary - muted type: object - required: &id051 + required: &id062 - type: object mutes: @@ -8437,7 +8787,7 @@ paths: status: description: Overall health status type: string - required: &id052 + required: &id063 - status - checks - mutes @@ -8451,7 +8801,7 @@ paths: num_standbys: description: Standby manager count type: integer - required: &id053 + required: &id064 - num_active - num_standbys type: object @@ -8466,7 +8816,7 @@ paths: items: type: integer type: array - required: &id054 + required: &id065 - num_mons - quorum type: object @@ -8485,7 +8835,7 @@ paths: up: description: Count of iSCSI gateways running type: integer - required: &id055 + required: &id066 - up - down type: object @@ -8504,7 +8854,7 @@ paths: up: description: Number of OSDs up type: integer - required: &id056 + required: &id067 - in - up - num_osds @@ -8534,7 +8884,7 @@ paths: state_name: description: Placement group state type: string - required: &id057 + required: &id068 - state_name - count type: object @@ -8542,7 +8892,7 @@ paths: recovering_bytes_per_sec: description: Total recovery in bytes type: integer - required: &id058 + required: &id069 - pgs_by_state - num_pools - num_pgs @@ -8550,7 +8900,7 @@ paths: - bytes_total - recovering_bytes_per_sec type: object - required: &id059 + required: &id070 - fsid - health - monmap @@ -8578,7 +8928,7 @@ paths: num_standbys: description: Standby MDS count type: integer - required: *id048 + required: *id059 type: object health: description: Cluster health overview @@ -8604,11 +8954,11 @@ paths: message: description: Human-readable summary type: string - required: *id049 + required: *id060 type: object - required: *id050 + required: *id061 type: object - required: *id051 + required: *id062 type: object mutes: description: List of muted check names @@ -8618,7 +8968,7 @@ paths: status: description: Overall health status type: string - required: *id052 + required: *id063 type: object mgrmap: description: Manager map details @@ -8629,7 +8979,7 @@ paths: num_standbys: description: Standby manager count type: integer - required: *id053 + required: *id064 type: object monmap: description: Monitor map details @@ -8642,7 +8992,7 @@ paths: items: type: integer type: array - required: *id054 + required: *id065 type: object num_hosts: description: Count of hosts @@ -8659,7 +9009,7 @@ paths: up: description: Count of iSCSI gateways running type: integer - required: *id055 + required: *id066 type: object num_rgw_gateways: description: Count of RGW gateway daemons running @@ -8676,7 +9026,7 @@ paths: up: description: Number of OSDs up type: integer - required: *id056 + required: *id067 type: object pgmap: description: Placement group map details @@ -8703,15 +9053,15 @@ paths: state_name: description: Placement group state type: string - required: *id057 + required: *id068 type: object type: array recovering_bytes_per_sec: description: Total recovery in bytes type: integer - required: *id058 + required: *id069 type: object - required: *id059 + required: *id070 type: object description: OK '400': @@ -8800,7 +9150,7 @@ paths: type: description: type of service type: string - required: &id060 + required: &id071 - type - count type: object @@ -8818,7 +9168,7 @@ paths: type: description: type of service type: string - required: &id061 + required: &id072 - type - id type: object @@ -8832,14 +9182,14 @@ paths: orchestrator: description: '' type: boolean - required: &id062 + required: &id073 - ceph - orchestrator type: object status: description: '' type: string - required: &id063 + required: &id074 - hostname - services - service_instances @@ -8877,7 +9227,7 @@ paths: type: description: type of service type: string - required: *id060 + required: *id071 type: object type: array service_type: @@ -8893,7 +9243,7 @@ paths: type: description: type of service type: string - required: *id061 + required: *id072 type: object type: array sources: @@ -8905,12 +9255,12 @@ paths: orchestrator: description: '' type: boolean - required: *id062 + required: *id073 type: object status: description: '' type: string - required: *id063 + required: *id074 type: object description: OK '400': @@ -9313,7 +9663,7 @@ paths: IDENTsupport: description: '' type: string - required: &id064 + required: &id075 - IDENTsupport - IDENTstatus - FAILsupport @@ -9334,7 +9684,7 @@ paths: transport: description: '' type: string - required: &id065 + required: &id076 - serialNum - transport - mediaType @@ -9372,7 +9722,7 @@ paths: type: description: '' type: string - required: &id066 + required: &id077 - name - osd_id - cluster_name @@ -9437,7 +9787,7 @@ paths: start: description: '' type: string - required: &id067 + required: &id078 - start - sectors - sectorsize @@ -9445,7 +9795,7 @@ paths: - human_readable_size - holders type: object - required: &id068 + required: &id079 - partition_name type: object path: @@ -9487,7 +9837,7 @@ paths: vendor: description: '' type: string - required: &id069 + required: &id080 - removable - ro - vendor @@ -9507,7 +9857,7 @@ paths: - path - locked type: object - required: &id070 + required: &id081 - rejected_reasons - available - path @@ -9527,7 +9877,7 @@ paths: name: description: Hostname type: string - required: &id071 + required: &id082 - name - addr - devices @@ -9578,7 +9928,7 @@ paths: IDENTsupport: description: '' type: string - required: *id064 + required: *id075 type: object linkSpeed: description: '' @@ -9595,7 +9945,7 @@ paths: transport: description: '' type: string - required: *id065 + required: *id076 type: object lvs: description: '' @@ -9625,7 +9975,7 @@ paths: type: description: '' type: string - required: *id066 + required: *id077 type: object type: array osd_ids: @@ -9682,9 +10032,9 @@ paths: start: description: '' type: string - required: *id067 + required: *id078 type: object - required: *id068 + required: *id079 type: object path: description: '' @@ -9725,9 +10075,9 @@ paths: vendor: description: '' type: string - required: *id069 + required: *id080 type: object - required: *id070 + required: *id081 type: object type: array labels: @@ -9738,7 +10088,7 @@ paths: name: description: Hostname type: string - required: *id071 + required: *id082 type: object description: OK '400': @@ -9809,7 +10159,7 @@ paths: description: username type: string type: object - required: &id072 + required: &id083 - user - password - mutual_user @@ -9832,7 +10182,7 @@ paths: description: username type: string type: object - required: *id072 + required: *id083 type: array description: OK '400': @@ -10173,13 +10523,13 @@ paths: type: description: '' type: string - required: &id073 + required: &id084 - type - addr - nonce type: object type: array - required: &id074 + required: &id085 - addrvec type: object channel: @@ -10203,7 +10553,7 @@ paths: stamp: description: '' type: string - required: &id075 + required: &id086 - name - rank - addrs @@ -10219,7 +10569,7 @@ paths: items: type: string type: array - required: &id076 + required: &id087 - clog - audit_log type: object @@ -10246,10 +10596,10 @@ paths: type: description: '' type: string - required: *id073 + required: *id084 type: object type: array - required: *id074 + required: *id085 type: object channel: description: '' @@ -10272,7 +10622,7 @@ paths: stamp: description: '' type: string - required: *id075 + required: *id086 type: object type: array clog: @@ -10280,7 +10630,7 @@ paths: items: type: string type: array - required: *id076 + required: *id087 type: object description: OK '400': @@ -10367,7 +10717,7 @@ paths: type: description: Type of the option type: string - required: &id077 + required: &id088 - name - type - level @@ -10381,11 +10731,11 @@ paths: - tags - see_also type: object - required: &id078 + required: &id089 - Option_name type: object type: object - required: &id079 + required: &id090 - name - enabled - always_on @@ -10452,12 +10802,12 @@ paths: type: description: Type of the option type: string - required: *id077 + required: *id088 type: object - required: *id078 + required: *id089 type: object type: object - required: *id079 + required: *id090 type: array description: OK '400': @@ -10737,13 +11087,13 @@ paths: type: description: '' type: string - required: &id080 + required: &id091 - type - addr - nonce type: object type: array - required: &id081 + required: &id092 - addrvec type: object rank: @@ -10757,13 +11107,13 @@ paths: items: type: integer type: array - required: &id082 + required: &id093 - num_sessions type: object weight: description: '' type: integer - required: &id083 + required: &id094 - rank - name - public_addrs @@ -10801,7 +11151,7 @@ paths: release: description: '' type: string - required: &id084 + required: &id095 - features - release - num @@ -10820,7 +11170,7 @@ paths: release: description: '' type: string - required: &id085 + required: &id096 - features - release - num @@ -10839,7 +11189,7 @@ paths: release: description: '' type: string - required: &id086 + required: &id097 - features - release - num @@ -10858,13 +11208,13 @@ paths: release: description: '' type: string - required: &id087 + required: &id098 - features - release - num type: object type: array - required: &id088 + required: &id099 - mon - mds - client @@ -10889,7 +11239,7 @@ paths: items: type: integer type: array - required: &id089 + required: &id100 - required_con - required_mon - quorum_con @@ -10917,7 +11267,7 @@ paths: items: type: string type: array - required: &id090 + required: &id101 - persistent - optional type: object @@ -10965,13 +11315,13 @@ paths: type: description: '' type: string - required: &id091 + required: &id102 - type - addr - nonce type: object type: array - required: &id092 + required: &id103 - addrvec type: object rank: @@ -10985,13 +11335,13 @@ paths: items: type: integer type: array - required: &id093 + required: &id104 - num_sessions type: object weight: description: '' type: integer - required: &id094 + required: &id105 - rank - name - public_addrs @@ -11002,7 +11352,7 @@ paths: - stats type: object type: array - required: &id095 + required: &id106 - epoch - fsid - modified @@ -11039,7 +11389,7 @@ paths: items: type: string type: array - required: &id096 + required: &id107 - name - rank - state @@ -11058,7 +11408,7 @@ paths: items: type: integer type: array - required: &id097 + required: &id108 - mon_status - in_quorum - out_quorum @@ -11098,10 +11448,10 @@ paths: type: description: '' type: string - required: *id080 + required: *id091 type: object type: array - required: *id081 + required: *id092 type: object rank: description: '' @@ -11114,12 +11464,12 @@ paths: items: type: integer type: array - required: *id082 + required: *id093 type: object weight: description: '' type: integer - required: *id083 + required: *id094 type: object type: array mon_status: @@ -11149,7 +11499,7 @@ paths: release: description: '' type: string - required: *id084 + required: *id095 type: object type: array mds: @@ -11165,7 +11515,7 @@ paths: release: description: '' type: string - required: *id085 + required: *id096 type: object type: array mgr: @@ -11181,7 +11531,7 @@ paths: release: description: '' type: string - required: *id086 + required: *id097 type: object type: array mon: @@ -11197,10 +11547,10 @@ paths: release: description: '' type: string - required: *id087 + required: *id098 type: object type: array - required: *id088 + required: *id099 type: object features: description: '' @@ -11221,7 +11571,7 @@ paths: items: type: integer type: array - required: *id089 + required: *id100 type: object monmap: description: '' @@ -11245,7 +11595,7 @@ paths: items: type: string type: array - required: *id090 + required: *id101 type: object fsid: description: '' @@ -11291,10 +11641,10 @@ paths: type: description: '' type: string - required: *id091 + required: *id102 type: object type: array - required: *id092 + required: *id103 type: object rank: description: '' @@ -11307,15 +11657,15 @@ paths: items: type: integer type: array - required: *id093 + required: *id104 type: object weight: description: '' type: integer - required: *id094 + required: *id105 type: object type: array - required: *id095 + required: *id106 type: object name: description: '' @@ -11344,14 +11694,14 @@ paths: items: type: string type: array - required: *id096 + required: *id107 type: object out_quorum: description: '' items: type: integer type: array - required: *id097 + required: *id108 type: object description: OK '400': @@ -11892,7 +12242,7 @@ paths: squash: description: Client squash policy type: string - required: &id098 + required: &id109 - addresses - access_type - squash @@ -11919,7 +12269,7 @@ paths: user_id: description: User id type: string - required: &id099 + required: &id110 - name type: object path: @@ -11945,7 +12295,7 @@ paths: type: string type: array type: object - required: &id100 + required: &id111 - export_id - path - cluster_id @@ -11980,7 +12330,7 @@ paths: squash: description: Client squash policy type: string - required: *id098 + required: *id109 type: object type: array cluster_id: @@ -12004,7 +12354,7 @@ paths: user_id: description: User id type: string - required: *id099 + required: *id110 type: object path: description: Export path @@ -12029,7 +12379,7 @@ paths: type: string type: array type: object - required: *id100 + required: *id111 type: array description: OK '400': @@ -12153,7 +12503,7 @@ paths: squash: description: Client squash policy type: string - required: &id101 + required: &id112 - addresses - access_type - squash @@ -12180,7 +12530,7 @@ paths: user_id: description: User id type: string - required: &id102 + required: &id113 - name type: object path: @@ -12205,7 +12555,7 @@ paths: items: type: string type: array - required: &id103 + required: &id114 - export_id - path - cluster_id @@ -12239,7 +12589,7 @@ paths: squash: description: Client squash policy type: string - required: *id101 + required: *id112 type: object type: array cluster_id: @@ -12263,7 +12613,7 @@ paths: user_id: description: User id type: string - required: *id102 + required: *id113 type: object path: description: Export path @@ -12287,7 +12637,7 @@ paths: items: type: string type: array - required: *id103 + required: *id114 type: object description: Resource created. '202': @@ -12399,7 +12749,7 @@ paths: squash: description: Client squash policy type: string - required: &id104 + required: &id115 - addresses - access_type - squash @@ -12426,7 +12776,7 @@ paths: user_id: description: User id type: string - required: &id105 + required: &id116 - name type: object path: @@ -12451,7 +12801,7 @@ paths: items: type: string type: array - required: &id106 + required: &id117 - export_id - path - cluster_id @@ -12485,7 +12835,7 @@ paths: squash: description: Client squash policy type: string - required: *id104 + required: *id115 type: object type: array cluster_id: @@ -12509,7 +12859,7 @@ paths: user_id: description: User id type: string - required: *id105 + required: *id116 type: object path: description: Export path @@ -12533,7 +12883,7 @@ paths: items: type: string type: array - required: *id106 + required: *id117 type: object description: OK '400': @@ -12665,7 +13015,7 @@ paths: squash: description: Client squash policy type: string - required: &id107 + required: &id118 - addresses - access_type - squash @@ -12692,7 +13042,7 @@ paths: user_id: description: User id type: string - required: &id108 + required: &id119 - name type: object path: @@ -12717,7 +13067,7 @@ paths: items: type: string type: array - required: &id109 + required: &id120 - export_id - path - cluster_id @@ -12751,7 +13101,7 @@ paths: squash: description: Client squash policy type: string - required: *id107 + required: *id118 type: object type: array cluster_id: @@ -12775,7 +13125,7 @@ paths: user_id: description: User id type: string - required: *id108 + required: *id119 type: object path: description: Export path @@ -12799,7 +13149,7 @@ paths: items: type: string type: array - required: *id109 + required: *id120 type: object description: Resource updated. '202': @@ -15826,7 +16176,7 @@ paths: items: type: string type: array - required: &id110 + required: &id121 - list_of_flags type: object application/vnd.ceph.api.v1.0+json: @@ -15837,7 +16187,7 @@ paths: items: type: string type: array - required: *id110 + required: *id121 type: object description: OK '400': @@ -15886,7 +16236,7 @@ paths: items: type: string type: array - required: &id111 + required: &id122 - list_of_flags type: object application/vnd.ceph.api.v1.0+json: @@ -15897,7 +16247,7 @@ paths: items: type: string type: array - required: *id111 + required: *id122 type: object description: Resource updated. '202': @@ -15940,7 +16290,7 @@ paths: osd: description: OSD ID type: integer - required: &id112 + required: &id123 - osd - flags type: object @@ -15955,7 +16305,7 @@ paths: osd: description: OSD ID type: integer - required: *id112 + required: *id123 type: object description: OK '400': @@ -16028,7 +16378,7 @@ paths: items: type: string type: array - required: &id113 + required: &id124 - added - removed - ids @@ -16051,7 +16401,7 @@ paths: items: type: string type: array - required: *id113 + required: *id124 type: object description: Resource updated. '202': @@ -16148,7 +16498,7 @@ paths: items: type: string type: array - required: &id114 + required: &id125 - safe_to_destroy - active - missing_stats @@ -16181,7 +16531,7 @@ paths: items: type: string type: array - required: *id114 + required: *id125 type: object description: OK '400': @@ -16734,7 +17084,7 @@ paths: value: description: '' type: integer - required: &id115 + required: &id126 - description - nick - type @@ -16742,10 +17092,10 @@ paths: - units - value type: object - required: &id116 + required: &id127 - .cache_bytes type: object - required: &id117 + required: &id128 - mon.a type: object application/vnd.ceph.api.v1.0+json: @@ -16775,11 +17125,11 @@ paths: value: description: '' type: integer - required: *id115 + required: *id126 type: object - required: *id116 + required: *id127 type: object - required: *id117 + required: *id128 type: object description: OK '400': @@ -17099,7 +17449,7 @@ paths: type: description: '' type: string - required: &id118 + required: &id129 - type type: object hit_set_period: @@ -17141,7 +17491,7 @@ paths: target_version: description: '' type: string - required: &id119 + required: &id130 - ready_epoch - last_epoch_started - last_epoch_clean @@ -17170,7 +17520,7 @@ paths: pg_num_min: description: '' type: integer - required: &id120 + required: &id131 - pg_num_min - pg_num_max type: object @@ -17256,7 +17606,7 @@ paths: description: '' type: integer type: object - required: &id121 + required: &id132 - pool - pool_name - flags @@ -17382,7 +17732,7 @@ paths: type: description: '' type: string - required: *id118 + required: *id129 type: object hit_set_period: description: '' @@ -17423,7 +17773,7 @@ paths: target_version: description: '' type: string - required: *id119 + required: *id130 type: object min_read_recency_for_promote: description: '' @@ -17446,7 +17796,7 @@ paths: pg_num_min: description: '' type: integer - required: *id120 + required: *id131 type: object pg_autoscale_mode: description: '' @@ -17530,7 +17880,7 @@ paths: description: '' type: integer type: object - required: *id121 + required: *id132 type: array description: OK '400': @@ -19601,7 +19951,7 @@ paths: description: Zone Group type: string type: object - required: &id122 + required: &id133 - id - version - server_hostname @@ -19632,7 +19982,7 @@ paths: description: Zone Group type: string type: object - required: *id122 + required: *id133 type: array description: OK '400': @@ -20859,7 +21209,7 @@ paths: items: type: string type: array - required: &id123 + required: &id134 - list_of_users type: object application/vnd.ceph.api.v1.0+json: @@ -20870,7 +21220,7 @@ paths: items: type: string type: array - required: *id123 + required: *id134 type: object description: OK '400': @@ -22695,14 +23045,14 @@ paths: items: type: string type: array - required: &id124 + required: &id135 - cephfs type: object system: description: '' type: boolean type: object - required: &id125 + required: &id136 - name - description - scopes_permissions @@ -22726,13 +23076,13 @@ paths: items: type: string type: array - required: *id124 + required: *id135 type: object system: description: '' type: boolean type: object - required: *id125 + required: *id136 type: array description: OK '400': @@ -23139,7 +23489,7 @@ paths: description: Certificate target (service name or hostname) type: string type: object - required: &id126 + required: &id137 - cert_name - scope - signed_by @@ -23182,7 +23532,7 @@ paths: description: Certificate target (service name or hostname) type: string type: object - required: *id126 + required: *id137 type: array description: OK '400': @@ -23216,7 +23566,7 @@ paths: certificate: description: Root CA certificate in PEM format type: string - required: &id127 + required: &id138 - certificate type: object application/vnd.ceph.api.v1.0+json: @@ -23225,7 +23575,7 @@ paths: certificate: description: Root CA certificate in PEM format type: string - required: *id127 + required: *id138 type: object description: OK '400': @@ -23493,7 +23843,7 @@ paths: description: Settings Value type: boolean type: object - required: &id128 + required: &id139 - name - default - type @@ -23516,7 +23866,7 @@ paths: description: Settings Value type: boolean type: object - required: *id128 + required: *id139 type: array description: OK '400': @@ -23727,7 +24077,7 @@ paths: source_type: description: resource type: string - required: &id129 + required: &id140 - source_type - ref type: object @@ -23735,7 +24085,7 @@ paths: realm: description: Domain realm, e.g., 'DOMAIN1.SINK.TEST' type: string - required: &id130 + required: &id141 - realm - join_sources type: object @@ -23749,7 +24099,7 @@ paths: count: description: Number of instances to place type: integer - required: &id131 + required: &id142 - count type: object public_addrs: @@ -23764,7 +24114,7 @@ paths: description: Defines where the system will assign the managed IPs. type: string - required: &id132 + required: &id143 - address - destination type: object @@ -23782,13 +24132,13 @@ paths: source_type: description: resource type: string - required: &id133 + required: &id144 - source_type - ref type: object type: array type: object - required: &id134 + required: &id145 - resource_type - cluster_id - auth_mode @@ -23829,13 +24179,13 @@ paths: source_type: description: resource type: string - required: *id129 + required: *id140 type: object type: array realm: description: Domain realm, e.g., 'DOMAIN1.SINK.TEST' type: string - required: *id130 + required: *id141 type: object intent: description: Desired state of the resource, e.g., 'present' @@ -23847,7 +24197,7 @@ paths: count: description: Number of instances to place type: integer - required: *id131 + required: *id142 type: object public_addrs: description: Public Address @@ -23861,7 +24211,7 @@ paths: description: Defines where the system will assign the managed IPs. type: string - required: *id132 + required: *id143 type: object type: array resource_type: @@ -23877,11 +24227,11 @@ paths: source_type: description: resource type: string - required: *id133 + required: *id144 type: object type: array type: object - required: *id134 + required: *id145 type: array description: OK '400': @@ -23954,7 +24304,7 @@ paths: source_type: description: resource type: string - required: &id135 + required: &id146 - source_type - ref type: object @@ -23962,7 +24312,7 @@ paths: realm: description: Domain realm, e.g., 'DOMAIN1.SINK.TEST' type: string - required: &id136 + required: &id147 - realm - join_sources type: object @@ -23976,7 +24326,7 @@ paths: count: description: Number of instances to place type: integer - required: &id137 + required: &id148 - count type: object public_addrs: @@ -23991,7 +24341,7 @@ paths: description: Defines where the system will assign the managed IPs. type: string - required: &id138 + required: &id149 - address - destination type: object @@ -24010,12 +24360,12 @@ paths: source_type: description: resource type: string - required: &id139 + required: &id150 - source_type - ref type: object type: array - required: &id140 + required: &id151 - resource_type - cluster_id - auth_mode @@ -24033,7 +24383,7 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: &id141 + required: &id152 - resource - state - success @@ -24042,7 +24392,7 @@ paths: success: description: Indicates if the overall operation was successful type: boolean - required: &id142 + required: &id153 - results - success type: object @@ -24083,13 +24433,13 @@ paths: source_type: description: resource type: string - required: *id135 + required: *id146 type: object type: array realm: description: Domain realm, e.g., 'DOMAIN1.SINK.TEST' type: string - required: *id136 + required: *id147 type: object intent: description: Desired state of the resource, e.g., 'present' @@ -24101,7 +24451,7 @@ paths: count: description: Number of instances to place type: integer - required: *id137 + required: *id148 type: object public_addrs: description: Public Address @@ -24115,7 +24465,7 @@ paths: description: Defines where the system will assign the managed IPs. type: string - required: *id138 + required: *id149 type: object type: array resource_type: @@ -24132,10 +24482,10 @@ paths: source_type: description: resource type: string - required: *id139 + required: *id150 type: object type: array - required: *id140 + required: *id151 type: object state: description: The current state of the resource, e.g., @@ -24144,13 +24494,13 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: *id141 + required: *id152 type: object type: array success: description: Indicates if the overall operation was successful type: boolean - required: *id142 + required: *id153 type: object description: Resource created. '202': @@ -24263,7 +24613,7 @@ paths: source_type: description: resource type: string - required: &id143 + required: &id154 - source_type - ref type: object @@ -24271,7 +24621,7 @@ paths: realm: description: Domain realm, e.g., 'DOMAIN1.SINK.TEST' type: string - required: &id144 + required: &id155 - realm - join_sources type: object @@ -24285,7 +24635,7 @@ paths: count: description: Number of instances to place type: integer - required: &id145 + required: &id156 - count type: object public_addrs: @@ -24300,7 +24650,7 @@ paths: description: Defines where the system will assign the managed IPs. type: string - required: &id146 + required: &id157 - address - destination type: object @@ -24318,12 +24668,12 @@ paths: source_type: description: resource type: string - required: &id147 + required: &id158 - source_type - ref type: object type: array - required: &id148 + required: &id159 - resource_type - cluster_id - auth_mode @@ -24363,13 +24713,13 @@ paths: source_type: description: resource type: string - required: *id143 + required: *id154 type: object type: array realm: description: Domain realm, e.g., 'DOMAIN1.SINK.TEST' type: string - required: *id144 + required: *id155 type: object intent: description: Desired state of the resource, e.g., 'present' or @@ -24381,7 +24731,7 @@ paths: count: description: Number of instances to place type: integer - required: *id145 + required: *id156 type: object public_addrs: description: Public Address @@ -24395,7 +24745,7 @@ paths: description: Defines where the system will assign the managed IPs. type: string - required: *id146 + required: *id157 type: object type: array resource_type: @@ -24411,10 +24761,10 @@ paths: source_type: description: resource type: string - required: *id147 + required: *id158 type: object type: array - required: *id148 + required: *id159 type: object description: OK '400': @@ -24452,7 +24802,7 @@ paths: username: description: Username for authentication type: string - required: &id149 + required: &id160 - username - password type: object @@ -24472,7 +24822,7 @@ paths: description: ceph.smb.join.auth type: string type: object - required: &id150 + required: &id161 - resource_type - auth_id - intent @@ -24492,7 +24842,7 @@ paths: username: description: Username for authentication type: string - required: *id149 + required: *id160 type: object auth_id: description: Unique identifier for the join auth resource @@ -24510,7 +24860,7 @@ paths: description: ceph.smb.join.auth type: string type: object - required: *id150 + required: *id161 type: array description: OK '400': @@ -24563,7 +24913,7 @@ paths: username: description: Username for authentication type: string - required: &id151 + required: &id162 - username - password type: object @@ -24582,7 +24932,7 @@ paths: resource_type: description: ceph.smb.join.auth type: string - required: &id152 + required: &id163 - resource_type - auth_id - intent @@ -24596,7 +24946,7 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: &id153 + required: &id164 - resource - state - success @@ -24605,7 +24955,7 @@ paths: success: description: Indicates if the overall operation was successful type: boolean - required: &id154 + required: &id165 - results - success type: object @@ -24628,7 +24978,7 @@ paths: username: description: Username for authentication type: string - required: *id151 + required: *id162 type: object auth_id: description: Unique identifier for the join auth resource @@ -24645,7 +24995,7 @@ paths: resource_type: description: ceph.smb.join.auth type: string - required: *id152 + required: *id163 type: object state: description: The current state of the resource, e.g., @@ -24654,13 +25004,13 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: *id153 + required: *id164 type: object type: array success: description: Indicates if the overall operation was successful type: boolean - required: *id154 + required: *id165 type: object description: Resource created. '202': @@ -24756,7 +25106,7 @@ paths: username: description: Username for authentication type: string - required: &id155 + required: &id166 - username - password type: object @@ -24775,7 +25125,7 @@ paths: resource_type: description: ceph.smb.join.auth type: string - required: &id156 + required: &id167 - resource_type - auth_id - intent @@ -24794,7 +25144,7 @@ paths: username: description: Username for authentication type: string - required: *id155 + required: *id166 type: object auth_id: description: Unique identifier for the join auth resource @@ -24811,7 +25161,7 @@ paths: resource_type: description: ceph.smb.join.auth type: string - required: *id156 + required: *id167 type: object description: OK '400': @@ -24867,7 +25217,7 @@ paths: volume: description: Name of the CephFS file system type: string - required: &id157 + required: &id168 - volume - path - provider @@ -24911,7 +25261,7 @@ paths: write_iops_limit: description: 'QoS: max write IOPS (0=disabled)' type: integer - required: &id158 + required: &id169 - resource_type - cluster_id - share_id @@ -24951,7 +25301,7 @@ paths: volume: description: Name of the CephFS file system type: string - required: *id157 + required: *id168 type: object cluster_id: description: Unique identifier for the cluster @@ -24990,7 +25340,7 @@ paths: write_iops_limit: description: 'QoS: max write IOPS (0=disabled)' type: integer - required: *id158 + required: *id169 type: object description: OK '400': @@ -25058,7 +25408,7 @@ paths: volume: description: Name of the CephFS file system type: string - required: &id159 + required: &id170 - volume - path - provider @@ -25102,7 +25452,7 @@ paths: write_iops_limit: description: 'QoS: max write IOPS (0=disabled)' type: integer - required: &id160 + required: &id171 - resource_type - cluster_id - share_id @@ -25125,7 +25475,7 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: &id161 + required: &id172 - resource - state - success @@ -25134,7 +25484,7 @@ paths: success: description: Indicates if the overall operation was successful type: boolean - required: &id162 + required: &id173 - results - success type: object @@ -25170,7 +25520,7 @@ paths: volume: description: Name of the CephFS file system type: string - required: *id159 + required: *id170 type: object cluster_id: description: Unique identifier for the cluster @@ -25209,7 +25559,7 @@ paths: write_iops_limit: description: 'QoS: max write IOPS (0=disabled)' type: integer - required: *id160 + required: *id171 type: object state: description: The current state of the resource, e.g., @@ -25218,13 +25568,13 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: *id161 + required: *id172 type: object type: array success: description: Indicates if the overall operation was successful type: boolean - required: *id162 + required: *id173 type: object description: Resource created. '202': @@ -25417,7 +25767,7 @@ paths: volume: description: Name of the CephFS file system type: string - required: &id163 + required: &id174 - volume - path - provider @@ -25461,7 +25811,7 @@ paths: write_iops_limit: description: 'QoS: max write IOPS (0=disabled)' type: integer - required: &id164 + required: &id175 - resource_type - cluster_id - share_id @@ -25501,7 +25851,7 @@ paths: volume: description: Name of the CephFS file system type: string - required: *id163 + required: *id174 type: object cluster_id: description: Unique identifier for the cluster @@ -25540,7 +25890,7 @@ paths: write_iops_limit: description: 'QoS: max write IOPS (0=disabled)' type: integer - required: *id164 + required: *id175 type: object description: OK '400': @@ -25594,7 +25944,7 @@ paths: name: description: The name of the group type: string - required: &id165 + required: &id176 - name type: object type: array @@ -25609,17 +25959,17 @@ paths: password: description: The password for the user type: string - required: &id166 + required: &id177 - name - password type: object type: array - required: &id167 + required: &id178 - users - groups type: object type: object - required: &id168 + required: &id179 - resource_type - users_groups_id - intent @@ -25655,7 +26005,7 @@ paths: name: description: The name of the group type: string - required: *id165 + required: *id176 type: object type: array users: @@ -25669,13 +26019,13 @@ paths: password: description: The password for the user type: string - required: *id166 + required: *id177 type: object type: array - required: *id167 + required: *id178 type: object type: object - required: *id168 + required: *id179 type: array description: OK '400': @@ -25735,7 +26085,7 @@ paths: username: description: Username for authentication type: string - required: &id169 + required: &id180 - username - password type: object @@ -25756,7 +26106,7 @@ paths: resource_type: description: ceph.smb.join.auth type: string - required: &id170 + required: &id181 - resource_type - auth_id - intent @@ -25770,7 +26120,7 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: &id171 + required: &id182 - resource - state - success @@ -25780,7 +26130,7 @@ paths: description: Indicates if the overall operation was successful type: boolean - required: &id172 + required: &id183 - results - success type: object @@ -25791,7 +26141,7 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: &id173 + required: &id184 - resource - state - success @@ -25800,7 +26150,7 @@ paths: success: description: Indicates if the overall operation was successful type: boolean - required: &id174 + required: &id185 - results - success type: object @@ -25830,7 +26180,7 @@ paths: username: description: Username for authentication type: string - required: *id169 + required: *id180 type: object auth_id: description: Unique identifier for the join @@ -25849,7 +26199,7 @@ paths: resource_type: description: ceph.smb.join.auth type: string - required: *id170 + required: *id181 type: object state: description: The current state of the resource, e.g., @@ -25858,14 +26208,14 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: *id171 + required: *id182 type: object type: array success: description: Indicates if the overall operation was successful type: boolean - required: *id172 + required: *id183 type: object state: description: The current state of the resource, e.g., @@ -25874,13 +26224,13 @@ paths: success: description: Indicates if the operation was successful type: boolean - required: *id173 + required: *id184 type: object type: array success: description: Indicates if the overall operation was successful type: boolean - required: *id174 + required: *id185 type: object description: Resource created. '202': @@ -25992,7 +26342,7 @@ paths: name: description: The name of the group type: string - required: &id175 + required: &id186 - name type: object type: array @@ -26007,16 +26357,16 @@ paths: password: description: The password for the user type: string - required: &id176 + required: &id187 - name - password type: object type: array - required: &id177 + required: &id188 - users - groups type: object - required: &id178 + required: &id189 - resource_type - users_groups_id - intent @@ -26051,7 +26401,7 @@ paths: name: description: The name of the group type: string - required: *id175 + required: *id186 type: object type: array users: @@ -26065,12 +26415,12 @@ paths: password: description: The password for the user type: string - required: *id176 + required: *id187 type: object type: array - required: *id177 + required: *id188 type: object - required: *id178 + required: *id189 type: object description: OK '400': @@ -26123,7 +26473,7 @@ paths: pool: description: '' type: integer - required: &id179 + required: &id190 - pool type: object name: @@ -26138,7 +26488,7 @@ paths: success: description: '' type: boolean - required: &id180 + required: &id191 - name - metadata - begin_time @@ -26171,14 +26521,14 @@ paths: warnings: description: '' type: integer - required: &id181 + required: &id192 - warnings - errors type: object version: description: '' type: string - required: &id182 + required: &id193 - health_status - mgr_id - mgr_host @@ -26218,7 +26568,7 @@ paths: pool: description: '' type: integer - required: *id179 + required: *id190 type: object name: description: '' @@ -26232,7 +26582,7 @@ paths: success: description: '' type: boolean - required: *id180 + required: *id191 type: object type: array have_mon_connection: @@ -26256,12 +26606,12 @@ paths: warnings: description: '' type: integer - required: *id181 + required: *id192 type: object version: description: '' type: string - required: *id182 + required: *id193 type: object description: OK '400': @@ -26318,7 +26668,7 @@ paths: pool: description: '' type: integer - required: &id183 + required: &id194 - pool type: object name: @@ -26333,7 +26683,7 @@ paths: success: description: '' type: boolean - required: &id184 + required: &id195 - name - metadata - begin_time @@ -26345,7 +26695,7 @@ paths: - exception type: object type: array - required: &id185 + required: &id196 - executing_tasks - finished_tasks type: object @@ -26377,7 +26727,7 @@ paths: pool: description: '' type: integer - required: *id183 + required: *id194 type: object name: description: finished tasks name @@ -26391,10 +26741,10 @@ paths: success: description: '' type: boolean - required: *id184 + required: *id195 type: object type: array - required: *id185 + required: *id196 type: object description: OK '400': @@ -26489,7 +26839,7 @@ paths: mode: description: '' type: string - required: &id186 + required: &id197 - active - mode type: object @@ -26516,7 +26866,7 @@ paths: items: type: string type: array - required: &id187 + required: &id198 - cluster_changed - active_changed type: object @@ -26537,7 +26887,7 @@ paths: straw2: description: '' type: integer - required: &id188 + required: &id199 - straw2 type: object bucket_sizes: @@ -26549,7 +26899,7 @@ paths: '3': description: '' type: integer - required: &id189 + required: &id200 - '1' - '3' type: object @@ -26562,7 +26912,7 @@ paths: '11': description: '' type: integer - required: &id190 + required: &id201 - '1' - '11' type: object @@ -26652,7 +27002,7 @@ paths: straw_calc_version: description: '' type: integer - required: &id191 + required: &id202 - choose_local_tries - choose_local_fallback_tries - choose_total_tries @@ -26674,7 +27024,7 @@ paths: - require_feature_tunables5 - has_v5_rules type: object - required: &id192 + required: &id203 - num_devices - num_types - num_buckets @@ -26702,7 +27052,7 @@ paths: ever_enabled_multiple: description: '' type: boolean - required: &id193 + required: &id204 - enable_multiple - ever_enabled_multiple type: object @@ -26717,7 +27067,7 @@ paths: total_num_mds: description: '' type: integer - required: &id194 + required: &id205 - count - feature_flags - num_standby_mds @@ -26742,7 +27092,7 @@ paths: num_with_osd: description: '' type: integer - required: &id195 + required: &id206 - num - num_with_mon - num_with_mds @@ -26767,7 +27117,7 @@ paths: x86_64: description: '' type: integer - required: &id196 + required: &id207 - x86_64 type: object ceph_version: @@ -26776,7 +27126,7 @@ paths: ceph version 16.0.0-3151-gf202994fcf: description: '' type: integer - required: &id197 + required: &id208 - ceph version 16.0.0-3151-gf202994fcf type: object cpu: @@ -26785,7 +27135,7 @@ paths: Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz: description: '' type: integer - required: &id198 + required: &id209 - Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz type: object distro: @@ -26794,7 +27144,7 @@ paths: centos: description: '' type: integer - required: &id199 + required: &id210 - centos type: object distro_description: @@ -26803,7 +27153,7 @@ paths: CentOS Linux 8 (Core): description: '' type: integer - required: &id200 + required: &id211 - CentOS Linux 8 (Core) type: object kernel_description: @@ -26812,7 +27162,7 @@ paths: '#1 SMP Wed Jul 1 19:53:01 UTC 2020': description: '' type: integer - required: &id201 + required: &id212 - '#1 SMP Wed Jul 1 19:53:01 UTC 2020' type: object kernel_version: @@ -26821,7 +27171,7 @@ paths: 5.7.7-200.fc32.x86_64: description: '' type: integer - required: &id202 + required: &id213 - 5.7.7-200.fc32.x86_64 type: object os: @@ -26830,10 +27180,10 @@ paths: Linux: description: '' type: integer - required: &id203 + required: &id214 - Linux type: object - required: &id204 + required: &id215 - arch - ceph_version - os @@ -26852,7 +27202,7 @@ paths: x86_64: description: '' type: integer - required: &id205 + required: &id216 - x86_64 type: object ceph_version: @@ -26861,7 +27211,7 @@ paths: ceph version 16.0.0-3151-gf202994fcf: description: '' type: integer - required: &id206 + required: &id217 - ceph version 16.0.0-3151-gf202994fcf type: object cpu: @@ -26870,7 +27220,7 @@ paths: Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz: description: '' type: integer - required: &id207 + required: &id218 - Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz type: object distro: @@ -26879,7 +27229,7 @@ paths: centos: description: '' type: integer - required: &id208 + required: &id219 - centos type: object distro_description: @@ -26888,7 +27238,7 @@ paths: CentOS Linux 8 (Core): description: '' type: integer - required: &id209 + required: &id220 - CentOS Linux 8 (Core) type: object kernel_description: @@ -26897,7 +27247,7 @@ paths: '#1 SMP Wed Jul 1 19:53:01 UTC 2020': description: '' type: integer - required: &id210 + required: &id221 - '#1 SMP Wed Jul 1 19:53:01 UTC 2020' type: object kernel_version: @@ -26906,7 +27256,7 @@ paths: 5.7.7-200.fc32.x86_64: description: '' type: integer - required: &id211 + required: &id222 - 5.7.7-200.fc32.x86_64 type: object os: @@ -26915,7 +27265,7 @@ paths: Linux: description: '' type: integer - required: &id212 + required: &id223 - Linux type: object osd_objectstore: @@ -26924,7 +27274,7 @@ paths: bluestore: description: '' type: integer - required: &id213 + required: &id224 - bluestore type: object rotational: @@ -26933,10 +27283,10 @@ paths: '1': description: '' type: integer - required: &id214 + required: &id225 - '1' type: object - required: &id215 + required: &id226 - osd_objectstore - rotational - arch @@ -26948,7 +27298,7 @@ paths: - distro_description - distro type: object - required: &id216 + required: &id227 - osd - mon type: object @@ -26971,7 +27321,7 @@ paths: items: type: string type: array - required: &id217 + required: &id228 - persistent - optional type: object @@ -26990,7 +27340,7 @@ paths: v2_addr_mons: description: '' type: integer - required: &id218 + required: &id229 - count - features - min_mon_release @@ -27014,7 +27364,7 @@ paths: require_osd_release: description: '' type: string - required: &id219 + required: &id230 - count - require_osd_release - require_min_compat_client @@ -27057,7 +27407,7 @@ paths: type: description: '' type: string - required: &id220 + required: &id231 - pool - type - pg_num @@ -27087,7 +27437,7 @@ paths: num_pools: description: '' type: integer - required: &id221 + required: &id232 - num_pools - num_images_by_pool - mirroring_by_pool @@ -27118,7 +27468,7 @@ paths: zones: description: '' type: integer - required: &id222 + required: &id233 - count - zones - zonegroups @@ -27130,7 +27480,7 @@ paths: rgw: description: '' type: integer - required: &id223 + required: &id234 - rgw type: object usage: @@ -27151,14 +27501,14 @@ paths: total_used_bytes: description: '' type: integer - required: &id224 + required: &id235 - pools - pg_num - total_used_bytes - total_bytes - total_avail_bytes type: object - required: &id225 + required: &id236 - leaderboard - report_version - report_timestamp @@ -27182,7 +27532,7 @@ paths: - balancer - crashes type: object - required: &id226 + required: &id237 - report - device_report type: object @@ -27204,7 +27554,7 @@ paths: mode: description: '' type: string - required: *id186 + required: *id197 type: object channels: description: '' @@ -27229,7 +27579,7 @@ paths: items: type: string type: array - required: *id187 + required: *id198 type: object crashes: description: '' @@ -27248,7 +27598,7 @@ paths: straw2: description: '' type: integer - required: *id188 + required: *id199 type: object bucket_sizes: description: '' @@ -27259,7 +27609,7 @@ paths: '3': description: '' type: integer - required: *id189 + required: *id200 type: object bucket_types: description: '' @@ -27270,7 +27620,7 @@ paths: '11': description: '' type: integer - required: *id190 + required: *id201 type: object compat_weight_set: description: '' @@ -27358,9 +27708,9 @@ paths: straw_calc_version: description: '' type: integer - required: *id191 + required: *id202 type: object - required: *id192 + required: *id203 type: object fs: description: '' @@ -27377,7 +27727,7 @@ paths: ever_enabled_multiple: description: '' type: boolean - required: *id193 + required: *id204 type: object filesystems: description: '' @@ -27390,7 +27740,7 @@ paths: total_num_mds: description: '' type: integer - required: *id194 + required: *id205 type: object hosts: description: '' @@ -27410,7 +27760,7 @@ paths: num_with_osd: description: '' type: integer - required: *id195 + required: *id206 type: object leaderboard: description: '' @@ -27430,7 +27780,7 @@ paths: x86_64: description: '' type: integer - required: *id196 + required: *id207 type: object ceph_version: description: '' @@ -27438,7 +27788,7 @@ paths: ceph version 16.0.0-3151-gf202994fcf: description: '' type: integer - required: *id197 + required: *id208 type: object cpu: description: '' @@ -27446,7 +27796,7 @@ paths: Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz: description: '' type: integer - required: *id198 + required: *id209 type: object distro: description: '' @@ -27454,7 +27804,7 @@ paths: centos: description: '' type: integer - required: *id199 + required: *id210 type: object distro_description: description: '' @@ -27462,7 +27812,7 @@ paths: CentOS Linux 8 (Core): description: '' type: integer - required: *id200 + required: *id211 type: object kernel_description: description: '' @@ -27470,7 +27820,7 @@ paths: '#1 SMP Wed Jul 1 19:53:01 UTC 2020': description: '' type: integer - required: *id201 + required: *id212 type: object kernel_version: description: '' @@ -27478,7 +27828,7 @@ paths: 5.7.7-200.fc32.x86_64: description: '' type: integer - required: *id202 + required: *id213 type: object os: description: '' @@ -27486,9 +27836,9 @@ paths: Linux: description: '' type: integer - required: *id203 + required: *id214 type: object - required: *id204 + required: *id215 type: object osd: description: '' @@ -27499,7 +27849,7 @@ paths: x86_64: description: '' type: integer - required: *id205 + required: *id216 type: object ceph_version: description: '' @@ -27507,7 +27857,7 @@ paths: ceph version 16.0.0-3151-gf202994fcf: description: '' type: integer - required: *id206 + required: *id217 type: object cpu: description: '' @@ -27515,7 +27865,7 @@ paths: Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz: description: '' type: integer - required: *id207 + required: *id218 type: object distro: description: '' @@ -27523,7 +27873,7 @@ paths: centos: description: '' type: integer - required: *id208 + required: *id219 type: object distro_description: description: '' @@ -27531,7 +27881,7 @@ paths: CentOS Linux 8 (Core): description: '' type: integer - required: *id209 + required: *id220 type: object kernel_description: description: '' @@ -27539,7 +27889,7 @@ paths: '#1 SMP Wed Jul 1 19:53:01 UTC 2020': description: '' type: integer - required: *id210 + required: *id221 type: object kernel_version: description: '' @@ -27547,7 +27897,7 @@ paths: 5.7.7-200.fc32.x86_64: description: '' type: integer - required: *id211 + required: *id222 type: object os: description: '' @@ -27555,7 +27905,7 @@ paths: Linux: description: '' type: integer - required: *id212 + required: *id223 type: object osd_objectstore: description: '' @@ -27563,7 +27913,7 @@ paths: bluestore: description: '' type: integer - required: *id213 + required: *id224 type: object rotational: description: '' @@ -27571,11 +27921,11 @@ paths: '1': description: '' type: integer - required: *id214 + required: *id225 type: object - required: *id215 + required: *id226 type: object - required: *id216 + required: *id227 type: object mon: description: '' @@ -27596,7 +27946,7 @@ paths: items: type: string type: array - required: *id217 + required: *id228 type: object ipv4_addr_mons: description: '' @@ -27613,7 +27963,7 @@ paths: v2_addr_mons: description: '' type: integer - required: *id218 + required: *id229 type: object osd: description: '' @@ -27630,7 +27980,7 @@ paths: require_osd_release: description: '' type: string - required: *id219 + required: *id230 type: object pools: description: '' @@ -27669,7 +28019,7 @@ paths: type: description: '' type: string - required: *id220 + required: *id231 type: object type: array rbd: @@ -27688,7 +28038,7 @@ paths: num_pools: description: '' type: integer - required: *id221 + required: *id232 type: object report_id: description: '' @@ -27716,7 +28066,7 @@ paths: zones: description: '' type: integer - required: *id222 + required: *id233 type: object services: description: '' @@ -27724,7 +28074,7 @@ paths: rgw: description: '' type: integer - required: *id223 + required: *id234 type: object usage: description: '' @@ -27744,11 +28094,11 @@ paths: total_used_bytes: description: '' type: integer - required: *id224 + required: *id235 type: object - required: *id225 + required: *id236 type: object - required: *id226 + required: *id237 type: object description: OK '400': @@ -27800,7 +28150,7 @@ paths: username: description: Username of the user type: string - required: &id227 + required: &id238 - username - roles - name @@ -27839,7 +28189,7 @@ paths: username: description: Username of the user type: string - required: *id227 + required: *id238 type: object description: OK '400': diff --git a/src/pybind/mgr/dashboard/tests/test_cephfs.py b/src/pybind/mgr/dashboard/tests/test_cephfs.py index 9fde6b40d78..ec243edb494 100644 --- a/src/pybind/mgr/dashboard/tests/test_cephfs.py +++ b/src/pybind/mgr/dashboard/tests/test_cephfs.py @@ -301,6 +301,47 @@ class CephFSMirrorTest(ControllerTestCase): self.assertIn(error_message, response.get('detail', '')) mgr.remote.assert_called_once_with('mirroring', 'snapshot_mirror_ls', fs_name) + def test_mirror_status_success(self): + fs_name = 'test_fs' + peer_uuid = 'peer-uuid-123' + expected_status = { + 'metrics': { + '/dir1': { + 'peer': { + peer_uuid: { + 'state': 'idle', + 'last_synced_snap': { + 'name': 'snap1', + 'sync_bytes': '1.00 KiB', + 'sync_time_stamp': '1704189600.000000s' + } + } + } + } + } + } + mock_output = json.dumps(expected_status) + mgr.remote = Mock(return_value=(0, mock_output, '')) + + self._get(f'/api/cephfs/mirror/{fs_name}/status?peer_id={peer_uuid}') + self.assertStatus(200) + self.assertJsonBody(expected_status) + mgr.remote.assert_called_once_with( + 'mirroring', 'snapshot_mirror_status', fs_name, None, peer_uuid) + + def test_mirror_status_error(self): + fs_name = 'test_fs' + error_message = 'no cephfs-mirror daemon available' + mgr.remote = Mock(return_value=(1, '', error_message)) + + self._get(f'/api/cephfs/mirror/{fs_name}/status') + self.assertStatus(400) + response = self.json_body() + self.assertIn('Failed to get Cephfs mirror status', response.get('detail', '')) + self.assertIn(error_message, response.get('detail', '')) + mgr.remote.assert_called_once_with( + 'mirroring', 'snapshot_mirror_status', fs_name, None, None) + class CephFSMirrorStatusTest(ControllerTestCase):