]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: added cephfs mount details
authorIvo Almeida <ialmeida@redhat.com>
Mon, 12 Feb 2024 10:08:09 +0000 (10:08 +0000)
committerIvo Almeida <ialmeida@redhat.com>
Wed, 14 Feb 2024 15:51:00 +0000 (15:51 +0000)
Fixes: https://tracker.ceph.com/issues/64405
Signed-off-by: Ivo Almeida <ialmeida@redhat.com>
18 files changed:
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-list/cephfs-snapshotschedule-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-list/cephfs-snapshotschedule-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.spect.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts
src/pybind/mgr/dashboard/openapi.yaml

index 0d55845ab594912a7a1b2690fc3d195f9ea0150c..87a7fe17bd5cb86e2b599ddbf6ad9b4747b8b0a0 100644 (file)
@@ -21,6 +21,10 @@ import { ModalService } from '~/app/shared/services/modal.service';
 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
 import { FinishedTask } from '~/app/shared/models/finished-task';
 import { NotificationService } from '~/app/shared/services/notification.service';
+import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { CephfsMountDetailsComponent } from '../cephfs-mount-details/cephfs-mount-details.component';
+import { map, switchMap } from 'rxjs/operators';
+import { HealthService } from '~/app/shared/api/health.service';
 
 const BASE_URL = 'cephfs';
 
@@ -38,6 +42,7 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
   permissions: Permissions;
   icons = Icons;
   monAllowPoolDelete = false;
+  modalRef!: NgbModalRef;
 
   constructor(
     private authStorageService: AuthStorageService,
@@ -48,7 +53,8 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
     private configurationService: ConfigurationService,
     private modalService: ModalService,
     private taskWrapper: TaskWrapperService,
-    public notificationService: NotificationService
+    public notificationService: NotificationService,
+    private healthService: HealthService
   ) {
     super();
     this.permissions = this.authStorageService.getPermissions();
@@ -89,6 +95,13 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
         click: () =>
           this.router.navigate([this.urlBuilder.getEdit(this.selection.first().mdsmap.fs_name)])
       },
+      {
+        name: this.actionLabels.ATTACH,
+        permission: 'read',
+        icon: Icons.bars,
+        disable: () => !this.selection?.hasSelection,
+        click: () => this.showAttachInfo()
+      },
       {
         permission: 'delete',
         icon: Icons.destroy,
@@ -125,6 +138,30 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
     this.selection = selection;
   }
 
+  showAttachInfo() {
+    const selectedFileSystem = this.selection?.selected?.[0];
+
+    this.cephfsService
+      .getFsRootDirectory(selectedFileSystem.id)
+      .pipe(
+        switchMap((fsData) =>
+          this.healthService.getClusterFsid().pipe(map((data) => ({ clusterId: data, fs: fsData })))
+        )
+      )
+      .subscribe({
+        next: (val) => {
+          this.modalRef = this.modalService.show(CephfsMountDetailsComponent, {
+            onSubmit: () => this.modalRef.close(),
+            mountData: {
+              fsId: val.clusterId,
+              fsName: selectedFileSystem?.mdsmap?.fs_name,
+              rootPath: val.fs['path']
+            }
+          });
+        }
+      });
+  }
+
   removeVolumeModal() {
     const volName = this.selection.first().mdsmap['fs_name'];
     this.modalService.show(CriticalConfirmationModalComponent, {
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.html
new file mode 100644 (file)
index 0000000..a8c30af
--- /dev/null
@@ -0,0 +1,38 @@
+<cd-modal (hide)="cancel()">
+  <ng-container class="modal-title">
+    <span i18n>Attach commands</span>
+  </ng-container>
+  <ng-container class="modal-content">
+    <div class="modal-body">
+      <h5 class="fw-bold"
+          i18n>
+        Using Mount command
+      </h5>
+      <cd-code-block textWrap="true"
+                     [codes]="[mount]"></cd-code-block>
+
+      <h5 class="fw-bold"
+          i18n>
+        Using FUSE command
+      </h5>
+      <cd-code-block textWrap="true"
+                     [codes]="[fuse]"></cd-code-block>
+
+      <h5 class="fw-bold"
+          i18n>
+          Using NFS Command
+      </h5>
+      <cd-code-block textWrap="true"
+                     [codes]="[nfs]"></cd-code-block>
+    </div>
+    <div class="modal-footer">
+      <cd-submit-button (submitAction)="cancel()"
+                        i18n>
+        Close
+      </cd-submit-button>
+    </div>
+  </ng-container>
+</cd-modal>
+
+
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.spec.ts
new file mode 100644 (file)
index 0000000..141ae42
--- /dev/null
@@ -0,0 +1,30 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CephfsMountDetailsComponent } from './cephfs-mount-details.component';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { SharedModule } from '~/app/shared/shared.module';
+import { ToastrModule } from 'ngx-toastr';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { configureTestBed } from '~/testing/unit-test-helper';
+
+describe('CephfsSnapshotscheduleListComponent', () => {
+  let component: CephfsMountDetailsComponent;
+  let fixture: ComponentFixture<CephfsMountDetailsComponent>;
+
+  configureTestBed({
+    declarations: [CephfsMountDetailsComponent],
+    imports: [HttpClientTestingModule, SharedModule, ToastrModule.forRoot(), RouterTestingModule],
+    providers: [NgbActiveModal]
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CephfsMountDetailsComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mount-details/cephfs-mount-details.component.ts
new file mode 100644 (file)
index 0000000..77a3f4a
--- /dev/null
@@ -0,0 +1,37 @@
+import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+
+@Component({
+  selector: 'cd-cephfs-mount-details',
+  templateUrl: './cephfs-mount-details.component.html',
+  styleUrls: ['./cephfs-mount-details.component.scss']
+})
+export class CephfsMountDetailsComponent implements OnInit, OnDestroy {
+  @ViewChild('mountDetailsTpl', { static: true })
+  mountDetailsTpl: any;
+  onCancel?: Function;
+  private canceled = false;
+  private MOUNT_DIRECTORY = '<MOUNT_DIRECTORY>';
+  mountData!: Record<string, any>;
+  constructor(public activeModal: NgbActiveModal) {}
+  mount!: string;
+  fuse!: string;
+  nfs!: string;
+
+  ngOnInit(): void {
+    this.mount = `sudo <CLIENT_USER>@${this.mountData?.fsId}.${this.mountData?.fsName}=${this.mountData?.rootPath} ${this.MOUNT_DIRECTORY}`;
+    this.fuse = `sudo ceph-fuse  ${this.MOUNT_DIRECTORY} -r ${this.mountData?.rootPath} --client_mds_namespace=${this.mountData?.fsName}`;
+    this.nfs = `sudo mount -t nfs -o port=<PORT> <IP of active_mds daemon>:${this.mountData?.rootPath} ${this.MOUNT_DIRECTORY}`;
+  }
+
+  ngOnDestroy(): void {
+    if (this.onCancel && this.canceled) {
+      this.onCancel();
+    }
+  }
+
+  cancel() {
+    this.canceled = true;
+    this.activeModal.close();
+  }
+}
index f26f63e755a6e58227b16d294bf6c0e1e0352e78..4142724f7cfe60e4a692cf8828e127d7ec4b6fe5 100644 (file)
     class="fw-bold"
     [ngbTooltip]="fullpathTpl"
     triggers="click:blur">
-    {{ row.path?.split?.("@")?.[0] | path }}
+    {{ row.pathForSelection?.split?.("@")?.[0] | path }}
   </span>
 
   <span
   *ngIf="row.active; else inactiveStatusTpl">
     <i
       [ngClass]="[icons.success, icons.large]"
-      ngbTooltip="{{ row.path?.split?.('@')?.[0] }} is active"
+      ngbTooltip="{{ row.pathForSelection?.split?.('@')?.[0] }} is active"
       class="text-success"
     ></i>
   </span>
     <i
       [ngClass]="[icons.warning, icons.large]"
       class="text-warning"
-      ngbTooltip="{{ row.path?.split?.('@')?.[0] }} has been deactivated"
+      ngbTooltip="{{ row.pathForSelection?.split?.('@')?.[0] }} has been deactivated"
     ></i>
   </ng-template>
 
-  <ng-template #fullpathTpl>
+  <ng-template #fullpathForSelectionTpl>
     <span
       data-toggle="tooltip"
-      [title]="row.path"
+      [title]="row.pathForSelection"
       class="font-monospace"
-      >{{ row.path?.split?.("@")?.[0] }}
+      >{{ row.pathForSelection?.split?.("@")?.[0] }}
       <cd-copy-2-clipboard-button
-        *ngIf="row.path"
-        [source]="row.path?.split?.('@')?.[0]"
+        *ngIf="row.pathForSelection"
+        [source]="row.pathForSelection?.split?.('@')?.[0]"
         [byId]="false"
         [showIconOnly]="true"
       >
index 581ee6e2fa3ae9cd2a0128bd68c276ef9e9d4e0f..ecc2036d5dd3a4743a9f1402738b89b86366ddef 100644 (file)
@@ -130,7 +130,11 @@ export class CephfsSnapshotscheduleListComponent
             }
             return this.snapshotScheduleService
               .getSnapshotScheduleList('/', this.fsName)
-              .pipe(map((list) => list.map((l) => ({ ...l, path: `${l.path}@${l.schedule}` }))));
+              .pipe(
+                map((list) =>
+                  list.map((l) => ({ ...l, pathForSelection: `${l.path}@${l.schedule}` }))
+                )
+              );
           }),
           shareReplay(1)
         )
@@ -138,7 +142,8 @@ export class CephfsSnapshotscheduleListComponent
     );
 
     this.columns = [
-      { prop: 'path', name: $localize`Path`, flexGrow: 3, cellTemplate: this.pathTpl },
+      { prop: 'pathForSelection', name: $localize`Path`, flexGrow: 3, cellTemplate: this.pathTpl },
+      { prop: 'path', isHidden: true },
       { prop: 'subvol', name: $localize`Subvolume`, cellTemplate: this.subvolTpl },
       { prop: 'scheduleCopy', name: $localize`Repeat interval` },
       { prop: 'schedule', isHidden: true },
index 92c139f8e5dd58cd812384dee8bdc0b4d23bde99..58d849c901ef57bad6a3abf55fcea7d329c812f2 100644 (file)
@@ -31,6 +31,8 @@ import { CdForm } from '~/app/shared/forms/cd-form';
 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
 import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
 import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
+import { CephfsMountDetailsComponent } from '../cephfs-mount-details/cephfs-mount-details.component';
+import { HealthService } from '~/app/shared/api/health.service';
 
 @Component({
   selector: 'cd-cephfs-subvolume-list',
@@ -86,7 +88,8 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
     private modalService: ModalService,
     private authStorageService: AuthStorageService,
     private taskWrapper: TaskWrapperService,
-    private cephfsSubvolumeGroupService: CephfsSubvolumeGroupService
+    private cephfsSubvolumeGroupService: CephfsSubvolumeGroupService,
+    private healthService: HealthService
   ) {
     super();
     this.permissions = this.authStorageService.getPermissions();
@@ -149,6 +152,13 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
         icon: Icons.edit,
         click: () => this.openModal(true)
       },
+      {
+        name: this.actionLabels.ATTACH,
+        permission: 'read',
+        icon: Icons.bars,
+        disable: () => !this.selection?.hasSelection,
+        click: () => this.showAttachInfo()
+      },
       {
         name: this.actionLabels.REMOVE,
         permission: 'delete',
@@ -188,6 +198,23 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
     this.selection = selection;
   }
 
+  showAttachInfo() {
+    const selectedSubVolume = this.selection?.selected?.[0];
+
+    this.healthService.getClusterFsid().subscribe({
+      next: (clusterId: string) => {
+        this.modalRef = this.modalService.show(CephfsMountDetailsComponent, {
+          onSubmit: () => this.modalRef.close(),
+          mountData: {
+            fsId: clusterId,
+            fsName: this.fsName,
+            rootPath: selectedSubVolume.info.path
+          }
+        });
+      }
+    });
+  }
+
   openModal(edit = false) {
     this.modalService.show(
       CephfsSubvolumeFormComponent,
index 687dd0b93ee933ac39cbd1248cb11047d424a029..14481d8382241cdd60c3f903af6434ba534dd602 100644 (file)
@@ -30,6 +30,7 @@ import { CephfsSnapshotscheduleListComponent } from './cephfs-snapshotschedule-l
 import { DataTableModule } from '../../shared/datatable/datatable.module';
 import { CephfsSubvolumeSnapshotsFormComponent } from './cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-form/cephfs-subvolume-snapshots-form.component';
 import { CephfsSnapshotscheduleFormComponent } from './cephfs-snapshotschedule-form/cephfs-snapshotschedule-form.component';
+import { CephfsMountDetailsComponent } from './cephfs-mount-details/cephfs-mount-details.component';
 
 @NgModule({
   imports: [
@@ -64,7 +65,8 @@ import { CephfsSnapshotscheduleFormComponent } from './cephfs-snapshotschedule-f
     CephfsSubvolumeSnapshotsListComponent,
     CephfsSnapshotscheduleListComponent,
     CephfsSnapshotscheduleFormComponent,
-    CephfsSubvolumeSnapshotsFormComponent
+    CephfsSubvolumeSnapshotsFormComponent,
+    CephfsMountDetailsComponent
   ]
 })
 export class CephfsModule {}
index d2dfbc0e2a763b79da80640896a6c22fde597939..ab43343f9edd4ffea0c6e82476b72cabb18ef2bc 100644 (file)
@@ -50,6 +50,10 @@ export class CephfsService {
     return this.http.get(`${this.baseURL}/${id}/mds_counters`);
   }
 
+  getFsRootDirectory(id: string) {
+    return this.http.get(`${this.baseURL}/${id}/get_root_directory`);
+  }
+
   mkSnapshot(id: number, path: string, name?: string) {
     let params = new HttpParams();
     params = params.append('path', path);
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.html
new file mode 100644 (file)
index 0000000..47eac63
--- /dev/null
@@ -0,0 +1,21 @@
+<ng-container *ngIf="codes.length > 1; else singleCodeBlock">
+  <pre id="bigCodeBlock">
+    <span *ngFor="let code of codes"
+          class="d-flex px-2 py-3 align-items-center justify-content-between text-dark">
+      <span [ngClass]="{'text-wrap': textWrap}">{{code}}</span>
+      <cd-copy-2-clipboard-button
+          [source]="code"
+          [byId]="false"></cd-copy-2-clipboard-button>
+    </span>
+  </pre>
+</ng-container>
+
+<ng-template #singleCodeBlock>
+  <pre class="d-flex px-2 py-3 align-items-center justify-content-between text-dark"
+       id="singleCodeBlock">
+    <span [ngClass]="{'text-wrap': textWrap}">{{codes}}</span>
+    <cd-copy-2-clipboard-button
+      [source]="codes"
+      [byId]="false"></cd-copy-2-clipboard-button>
+  </pre>
+</ng-template>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.scss
new file mode 100644 (file)
index 0000000..d22855f
--- /dev/null
@@ -0,0 +1,4 @@
+pre {
+  background-color: var(--gray-200);
+  border-radius: 0.5rem;
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.spect.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.spect.ts
new file mode 100644 (file)
index 0000000..bc5ad42
--- /dev/null
@@ -0,0 +1,38 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CodeBlockComponent } from './code-block.component';
+import { configureTestBed } from '~/testing/unit-test-helper';
+
+describe('CodeBlockComponent', () => {
+  let component: CodeBlockComponent;
+  let fixture: ComponentFixture<CodeBlockComponent>;
+
+  configureTestBed({
+    declarations: [CodeBlockComponent]
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CodeBlockComponent);
+    component = fixture.componentInstance;
+    component.codes = [];
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should show single codeblock if there are only one code', () => {
+    component.codes = ['code'];
+    fixture.detectChanges();
+    expect(fixture.nativeElement.querySelector('#singleCodeBlock')).not.toBeNull();
+    expect(fixture.nativeElement.querySelector('#bigCodeBlock')).toBeNull();
+  });
+
+  it('should show single codeblock if there are only one code', () => {
+    component.codes = ['code1', 'code2'];
+    fixture.detectChanges();
+    expect(fixture.nativeElement.querySelector('#bigCodeBlock')).not.toBeNull();
+    expect(fixture.nativeElement.querySelector('#singleCodeBlock')).toBeNull();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/code-block/code-block.component.ts
new file mode 100644 (file)
index 0000000..1021b8c
--- /dev/null
@@ -0,0 +1,17 @@
+import { Component, Input } from '@angular/core';
+
+@Component({
+  selector: 'cd-code-block',
+  templateUrl: './code-block.component.html',
+  styleUrls: ['./code-block.component.scss']
+})
+export class CodeBlockComponent {
+  @Input()
+  codes: string[];
+
+  @Input()
+  textWrap: boolean = false;
+
+  @Input()
+  grayBg: boolean = false;
+}
index c312cbabfca9c94fb9b7f7ec4f06ca981d1f8c6d..5b533f1cddb305c5677e0b082d8929548ece199e 100644 (file)
@@ -51,6 +51,7 @@ import { UsageBarComponent } from './usage-bar/usage-bar.component';
 import { WizardComponent } from './wizard/wizard.component';
 import { CardComponent } from './card/card.component';
 import { CardRowComponent } from './card-row/card-row.component';
+import { CodeBlockComponent } from './code-block/code-block.component';
 import { VerticalNavigationComponent } from './vertical-navigation/vertical-navigation.component';
 
 @NgModule({
@@ -107,6 +108,7 @@ import { VerticalNavigationComponent } from './vertical-navigation/vertical-navi
     ColorClassFromTextPipe,
     CardComponent,
     CardRowComponent,
+    CodeBlockComponent,
     VerticalNavigationComponent
   ],
   providers: [],
@@ -140,6 +142,7 @@ import { VerticalNavigationComponent } from './vertical-navigation/vertical-navi
     CdLabelComponent,
     CardComponent,
     CardRowComponent,
+    CodeBlockComponent,
     VerticalNavigationComponent
   ]
 })
index 80c7acbf28aeae227a012cd1ede7b5ab90dcadad..b6b8ca77e8adbcfca8a1f019404328e7cf9c8510 100644 (file)
@@ -26,7 +26,7 @@ export class Copy2ClipboardButtonComponent {
 
   private getText(): string {
     const element = document.getElementById(this.source) as HTMLInputElement;
-    return element.value;
+    return element?.value || element?.textContent;
   }
 
   @HostListener('click')
index 203c2b05d4ff6d11dde540855db40a70daad0928..4ef88170aaf3cbfbce00a3ccfb94045a484e792e 100644 (file)
@@ -143,6 +143,7 @@ export class ActionLabelsI18n {
   START_UPGRADE: string;
   ACTIVATE: string;
   DEACTIVATE: string;
+  ATTACH: string;
 
   constructor() {
     /* Create a new item */
@@ -223,6 +224,8 @@ export class ActionLabelsI18n {
 
     this.ACTIVATE = $localize`Activate`;
     this.DEACTIVATE = $localize`Deactivate`;
+
+    this.ATTACH = $localize`Attach`;
   }
 }
 
index 8d095d86c4e3bb2bcaaef483a6d8b4efed384e88..135379b127bc6bf2e2495239e144caf3df4d1583 100644 (file)
@@ -3086,6 +3086,60 @@ paths:
       - jwt: []
       tags:
       - Cephfs
+  /api/cephfs/{fs_id}/statfs:
+    get:
+      description: "\n        Get the statfs of the specified path.\n        :param\
+        \ fs_id: The filesystem identifier.\n        :param path: The path of the\
+        \ directory/file.\n        :return: Returns a dictionary containing 'bytes',\n\
+        \        'files' and 'subdirs'.\n        :rtype: dict\n        "
+      parameters:
+      - description: File System Identifier
+        in: path
+        name: fs_id
+        required: true
+        schema:
+          type: string
+      - description: File System Path
+        in: query
+        name: path
+        required: true
+        schema:
+          type: string
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              schema:
+                properties:
+                  bytes:
+                    description: ''
+                    type: integer
+                  files:
+                    description: ''
+                    type: integer
+                  subdirs:
+                    description: ''
+                    type: integer
+                required:
+                - bytes
+                - files
+                - subdirs
+                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 Cephfs statfs of the specified path
+      tags:
+      - Cephfs
   /api/cephfs/{fs_id}/tree:
     delete:
       description: "\n        Remove a directory.\n        :param fs_id: The filesystem\
@@ -3168,6 +3222,95 @@ paths:
       - jwt: []
       tags:
       - Cephfs
+  /api/cephfs/{fs_id}/unlink:
+    delete:
+      description: "\n        Removes a file, link, or symbolic link.\n        :param\
+        \ fs_id: The filesystem identifier.\n        :param path: The path of the\
+        \ file or link to unlink.\n        "
+      parameters:
+      - in: path
+        name: fs_id
+        required: true
+        schema:
+          type: string
+      - in: query
+        name: path
+        required: true
+        schema:
+          type: string
+      responses:
+        '202':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Operation is still executing. Please check the task queue.
+        '204':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Resource deleted.
+        '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:
+      - Cephfs
+  /api/cephfs/{fs_id}/write_to_file:
+    post:
+      description: "\n        Write some data to the specified path.\n        :param\
+        \ fs_id: The filesystem identifier.\n        :param path: The path of the\
+        \ file to write.\n        :param buf: The str to write to the buf.\n     \
+        \   "
+      parameters:
+      - in: path
+        name: fs_id
+        required: true
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              properties:
+                buf:
+                  type: string
+                path:
+                  type: string
+              required:
+              - path
+              - buf
+              type: object
+      responses:
+        '201':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Resource created.
+        '202':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Operation is still executing. Please check the task queue.
+        '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:
+      - Cephfs
   /api/cluster:
     get:
       parameters: []