Skip to content

Commit eb47ec7

Browse files
authored
Merge pull request ceph#55551 from ivoalmeida/cephfs-mount-details
mgr/dashboard: added cephfs mount details Reviewed-by: Ankush Behl <[email protected]> Reviewed-by: Nizamudeen A <[email protected]> Reviewed-by: Lucian Petrut <[email protected]>
2 parents a3e1d73 + e2f8d35 commit eb47ec7

File tree

15 files changed

+208
-21
lines changed

15 files changed

+208
-21
lines changed

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import { ModalService } from '~/app/shared/services/modal.service';
2121
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
2222
import { FinishedTask } from '~/app/shared/models/finished-task';
2323
import { NotificationService } from '~/app/shared/services/notification.service';
24+
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
25+
import { CephfsMountDetailsComponent } from '../cephfs-mount-details/cephfs-mount-details.component';
26+
import { map, switchMap } from 'rxjs/operators';
27+
import { HealthService } from '~/app/shared/api/health.service';
2428

2529
const BASE_URL = 'cephfs';
2630

@@ -38,6 +42,7 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
3842
permissions: Permissions;
3943
icons = Icons;
4044
monAllowPoolDelete = false;
45+
modalRef!: NgbModalRef;
4146

4247
constructor(
4348
private authStorageService: AuthStorageService,
@@ -48,7 +53,8 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
4853
private configurationService: ConfigurationService,
4954
private modalService: ModalService,
5055
private taskWrapper: TaskWrapperService,
51-
public notificationService: NotificationService
56+
public notificationService: NotificationService,
57+
private healthService: HealthService
5258
) {
5359
super();
5460
this.permissions = this.authStorageService.getPermissions();
@@ -89,6 +95,13 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
8995
click: () =>
9096
this.router.navigate([this.urlBuilder.getEdit(String(this.selection.first().id))])
9197
},
98+
{
99+
name: this.actionLabels.ATTACH,
100+
permission: 'read',
101+
icon: Icons.bars,
102+
disable: () => !this.selection?.hasSelection,
103+
click: () => this.showAttachInfo()
104+
},
92105
{
93106
permission: 'delete',
94107
icon: Icons.destroy,
@@ -125,6 +138,30 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
125138
this.selection = selection;
126139
}
127140

141+
showAttachInfo() {
142+
const selectedFileSystem = this.selection?.selected?.[0];
143+
144+
this.cephfsService
145+
.getFsRootDirectory(selectedFileSystem.id)
146+
.pipe(
147+
switchMap((fsData) =>
148+
this.healthService.getClusterFsid().pipe(map((data) => ({ clusterId: data, fs: fsData })))
149+
)
150+
)
151+
.subscribe({
152+
next: (val) => {
153+
this.modalRef = this.modalService.show(CephfsMountDetailsComponent, {
154+
onSubmit: () => this.modalRef.close(),
155+
mountData: {
156+
fsId: val.clusterId,
157+
fsName: selectedFileSystem?.mdsmap?.fs_name,
158+
rootPath: val.fs['path']
159+
}
160+
});
161+
}
162+
});
163+
}
164+
128165
removeVolumeModal() {
129166
const volName = this.selection.first().mdsmap['fs_name'];
130167
this.modalService.show(CriticalConfirmationModalComponent, {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<cd-modal (hide)="cancel()">
2+
<ng-container class="modal-title">
3+
<span i18n>Attach commands</span>
4+
</ng-container>
5+
<ng-container class="modal-content">
6+
<div class="modal-body">
7+
<h5 class="fw-bold"
8+
i18n>
9+
Using Mount command
10+
</h5>
11+
<cd-code-block textWrap="true"
12+
[codes]="[mount]"></cd-code-block>
13+
14+
<h5 class="fw-bold"
15+
i18n>
16+
Using FUSE command
17+
</h5>
18+
<cd-code-block textWrap="true"
19+
[codes]="[fuse]"></cd-code-block>
20+
21+
<h5 class="fw-bold"
22+
i18n>
23+
Using NFS Command
24+
</h5>
25+
<cd-code-block textWrap="true"
26+
[codes]="[nfs]"></cd-code-block>
27+
</div>
28+
<div class="modal-footer">
29+
<cd-submit-button (submitAction)="cancel()"
30+
i18n>
31+
Close
32+
</cd-submit-button>
33+
</div>
34+
</ng-container>
35+
</cd-modal>
36+
37+
38+

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.scss

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { CephfsMountDetailsComponent } from './cephfs-mount-details.component';
4+
import { HttpClientTestingModule } from '@angular/common/http/testing';
5+
import { SharedModule } from '~/app/shared/shared.module';
6+
import { ToastrModule } from 'ngx-toastr';
7+
import { RouterTestingModule } from '@angular/router/testing';
8+
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
9+
import { configureTestBed } from '~/testing/unit-test-helper';
10+
11+
describe('CephfsSnapshotscheduleListComponent', () => {
12+
let component: CephfsMountDetailsComponent;
13+
let fixture: ComponentFixture<CephfsMountDetailsComponent>;
14+
15+
configureTestBed({
16+
declarations: [CephfsMountDetailsComponent],
17+
imports: [HttpClientTestingModule, SharedModule, ToastrModule.forRoot(), RouterTestingModule],
18+
providers: [NgbActiveModal]
19+
});
20+
21+
beforeEach(() => {
22+
fixture = TestBed.createComponent(CephfsMountDetailsComponent);
23+
component = fixture.componentInstance;
24+
fixture.detectChanges();
25+
});
26+
27+
it('should create', () => {
28+
expect(component).toBeTruthy();
29+
});
30+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
2+
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
3+
4+
@Component({
5+
selector: 'cd-cephfs-mount-details',
6+
templateUrl: './cephfs-mount-details.component.html',
7+
styleUrls: ['./cephfs-mount-details.component.scss']
8+
})
9+
export class CephfsMountDetailsComponent implements OnInit, OnDestroy {
10+
@ViewChild('mountDetailsTpl', { static: true })
11+
mountDetailsTpl: any;
12+
onCancel?: Function;
13+
private canceled = false;
14+
private MOUNT_DIRECTORY = '<MOUNT_DIRECTORY>';
15+
mountData!: Record<string, any>;
16+
constructor(public activeModal: NgbActiveModal) {}
17+
mount!: string;
18+
fuse!: string;
19+
nfs!: string;
20+
21+
ngOnInit(): void {
22+
this.mount = `sudo <CLIENT_USER>@${this.mountData?.fsId}.${this.mountData?.fsName}=${this.mountData?.rootPath} ${this.MOUNT_DIRECTORY}`;
23+
this.fuse = `sudo ceph-fuse ${this.MOUNT_DIRECTORY} -r ${this.mountData?.rootPath} --client_mds_namespace=${this.mountData?.fsName}`;
24+
this.nfs = `sudo mount -t nfs -o port=<PORT> <IP of active_mds daemon>:${this.mountData?.rootPath} ${this.MOUNT_DIRECTORY}`;
25+
}
26+
27+
ngOnDestroy(): void {
28+
if (this.onCancel && this.canceled) {
29+
this.onCancel();
30+
}
31+
}
32+
33+
cancel() {
34+
this.canceled = true;
35+
this.activeModal.close();
36+
}
37+
}

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-list/cephfs-snapshotschedule-list.component.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121
class="fw-bold"
2222
[ngbTooltip]="fullpathTpl"
2323
triggers="click:blur">
24-
{{ row.path?.split?.("@")?.[0] | path }}
24+
{{ row.pathForSelection?.split?.("@")?.[0] | path }}
2525
</span>
2626

2727
<span
2828
*ngIf="row.active; else inactiveStatusTpl">
2929
<i
3030
[ngClass]="[icons.success, icons.large]"
31-
ngbTooltip="{{ row.path?.split?.('@')?.[0] }} is active"
31+
ngbTooltip="{{ row.pathForSelection?.split?.('@')?.[0] }} is active"
3232
class="text-success"
3333
></i>
3434
</span>
@@ -37,19 +37,19 @@
3737
<i
3838
[ngClass]="[icons.warning, icons.large]"
3939
class="text-warning"
40-
ngbTooltip="{{ row.path?.split?.('@')?.[0] }} has been deactivated"
40+
ngbTooltip="{{ row.pathForSelection?.split?.('@')?.[0] }} has been deactivated"
4141
></i>
4242
</ng-template>
4343

44-
<ng-template #fullpathTpl>
44+
<ng-template #fullpathForSelectionTpl>
4545
<span
4646
data-toggle="tooltip"
47-
[title]="row.path"
47+
[title]="row.pathForSelection"
4848
class="font-monospace"
49-
>{{ row.path?.split?.("@")?.[0] }}
49+
>{{ row.pathForSelection?.split?.("@")?.[0] }}
5050
<cd-copy-2-clipboard-button
51-
*ngIf="row.path"
52-
[source]="row.path?.split?.('@')?.[0]"
51+
*ngIf="row.pathForSelection"
52+
[source]="row.pathForSelection?.split?.('@')?.[0]"
5353
[byId]="false"
5454
[showIconOnly]="true"
5555
>

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-list/cephfs-snapshotschedule-list.component.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,20 @@ export class CephfsSnapshotscheduleListComponent
130130
}
131131
return this.snapshotScheduleService
132132
.getSnapshotScheduleList('/', this.fsName)
133-
.pipe(map((list) => list.map((l) => ({ ...l, path: `${l.path}@${l.schedule}` }))));
133+
.pipe(
134+
map((list) =>
135+
list.map((l) => ({ ...l, pathForSelection: `${l.path}@${l.schedule}` }))
136+
)
137+
);
134138
}),
135139
shareReplay(1)
136140
)
137141
)
138142
);
139143

140144
this.columns = [
141-
{ prop: 'path', name: $localize`Path`, flexGrow: 3, cellTemplate: this.pathTpl },
145+
{ prop: 'pathForSelection', name: $localize`Path`, flexGrow: 3, cellTemplate: this.pathTpl },
146+
{ prop: 'path', isHidden: true },
142147
{ prop: 'subvol', name: $localize`Subvolume`, cellTemplate: this.subvolTpl },
143148
{ prop: 'scheduleCopy', name: $localize`Repeat interval` },
144149
{ prop: 'schedule', isHidden: true },

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import { CdForm } from '~/app/shared/forms/cd-form';
3131
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
3232
import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
3333
import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
34+
import { CephfsMountDetailsComponent } from '../cephfs-mount-details/cephfs-mount-details.component';
35+
import { HealthService } from '~/app/shared/api/health.service';
3436

3537
@Component({
3638
selector: 'cd-cephfs-subvolume-list',
@@ -86,7 +88,8 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
8688
private modalService: ModalService,
8789
private authStorageService: AuthStorageService,
8890
private taskWrapper: TaskWrapperService,
89-
private cephfsSubvolumeGroupService: CephfsSubvolumeGroupService
91+
private cephfsSubvolumeGroupService: CephfsSubvolumeGroupService,
92+
private healthService: HealthService
9093
) {
9194
super();
9295
this.permissions = this.authStorageService.getPermissions();
@@ -149,6 +152,13 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
149152
icon: Icons.edit,
150153
click: () => this.openModal(true)
151154
},
155+
{
156+
name: this.actionLabels.ATTACH,
157+
permission: 'read',
158+
icon: Icons.bars,
159+
disable: () => !this.selection?.hasSelection,
160+
click: () => this.showAttachInfo()
161+
},
152162
{
153163
name: this.actionLabels.REMOVE,
154164
permission: 'delete',
@@ -188,6 +198,23 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
188198
this.selection = selection;
189199
}
190200

201+
showAttachInfo() {
202+
const selectedSubVolume = this.selection?.selected?.[0];
203+
204+
this.healthService.getClusterFsid().subscribe({
205+
next: (clusterId: string) => {
206+
this.modalRef = this.modalService.show(CephfsMountDetailsComponent, {
207+
onSubmit: () => this.modalRef.close(),
208+
mountData: {
209+
fsId: clusterId,
210+
fsName: this.fsName,
211+
rootPath: selectedSubVolume.info.path
212+
}
213+
});
214+
}
215+
});
216+
}
217+
191218
openModal(edit = false) {
192219
this.modalService.show(
193220
CephfsSubvolumeFormComponent,

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { CephfsSnapshotscheduleListComponent } from './cephfs-snapshotschedule-l
3030
import { DataTableModule } from '../../shared/datatable/datatable.module';
3131
import { CephfsSubvolumeSnapshotsFormComponent } from './cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-form/cephfs-subvolume-snapshots-form.component';
3232
import { CephfsSnapshotscheduleFormComponent } from './cephfs-snapshotschedule-form/cephfs-snapshotschedule-form.component';
33+
import { CephfsMountDetailsComponent } from './cephfs-mount-details/cephfs-mount-details.component';
3334

3435
@NgModule({
3536
imports: [
@@ -64,7 +65,8 @@ import { CephfsSnapshotscheduleFormComponent } from './cephfs-snapshotschedule-f
6465
CephfsSubvolumeSnapshotsListComponent,
6566
CephfsSnapshotscheduleListComponent,
6667
CephfsSnapshotscheduleFormComponent,
67-
CephfsSubvolumeSnapshotsFormComponent
68+
CephfsSubvolumeSnapshotsFormComponent,
69+
CephfsMountDetailsComponent
6870
]
6971
})
7072
export class CephfsModule {}

src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ export class CephfsService {
5050
return this.http.get(`${this.baseURL}/${id}/mds_counters`);
5151
}
5252

53+
getFsRootDirectory(id: string) {
54+
return this.http.get(`${this.baseURL}/${id}/get_root_directory`);
55+
}
56+
5357
mkSnapshot(id: number, path: string, name?: string) {
5458
let params = new HttpParams();
5559
params = params.append('path', path);

0 commit comments

Comments
 (0)