From 14c37754868906a7b3d3d92a9227ced2b1710fab Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Tue, 18 Jul 2023 21:30:17 +0200 Subject: [PATCH] mgr/dashboard: cephfs volume creation form Fixes: https://tracker.ceph.com/issues/62085 Signed-off-by: Pere Diaz Bou (cherry picked from commit fa35ea07e937d9307c3f20c08b9bc45f0a2285e0) Conflicts: src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts - Remove the UpgradeComponent import --- .../mgr/dashboard/controllers/cephfs.py | 20 +++ .../frontend/src/app/app-routing.module.ts | 12 +- .../cephfs-directories.component.ts | 5 + .../cephfs-form/cephfs-form.component.html | 103 +++++++++++ .../cephfs-form/cephfs-form.component.scss | 0 .../cephfs-form/cephfs-form.component.spec.ts | 32 ++++ .../cephfs-form/cephfs-form.component.ts | 166 ++++++++++++++++++ .../cephfs-list/cephfs-list.component.html | 8 + .../cephfs-list/cephfs-list.component.spec.ts | 6 +- .../cephfs-list/cephfs-list.component.ts | 33 +++- .../src/app/ceph/cephfs/cephfs.module.ts | 17 +- .../src/app/shared/api/cephfs.service.ts | 10 ++ .../shared/services/task-message.service.ts | 7 + src/pybind/mgr/dashboard/openapi.yaml | 39 ++++ 14 files changed, 450 insertions(+), 8 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.ts diff --git a/src/pybind/mgr/dashboard/controllers/cephfs.py b/src/pybind/mgr/dashboard/controllers/cephfs.py index 7b7589d423abd..5061317e135cb 100644 --- a/src/pybind/mgr/dashboard/controllers/cephfs.py +++ b/src/pybind/mgr/dashboard/controllers/cephfs.py @@ -2,6 +2,7 @@ import logging import os from collections import defaultdict +from typing import Any, Dict import cephfs import cherrypy @@ -23,6 +24,7 @@ GET_QUOTAS_SCHEMA = { logger = logging.getLogger("controllers.rgw") +# pylint: disable=R0904 @APIRouter('/cephfs', Scope.CEPHFS) @APIDoc("Cephfs Management API", "Cephfs") class CephFS(RESTController): @@ -37,6 +39,24 @@ class CephFS(RESTController): fsmap = mgr.get("fs_map") return fsmap['filesystems'] + def create(self, name: str, service_spec: Dict[str, Any]): + service_spec_str = '1 ' + if 'labels' in service_spec['placement']: + for label in service_spec['placement']['labels']: + service_spec_str += f'label:{label},' + service_spec_str = service_spec_str[:-1] + if 'hosts' in service_spec['placement']: + for host in service_spec['placement']['hosts']: + service_spec_str += f'{host},' + service_spec_str = service_spec_str[:-1] + + error_code, _, err = mgr.remote('volumes', '_cmd_fs_volume_create', None, + {'name': name, 'placement': service_spec_str}) + if error_code != 0: + raise RuntimeError( + f'Error creating volume {name} with placement {str(service_spec)}: {err}') + return f'Volume {name} created successfully' + def get(self, fs_id): fs_id = self.fs_id_to_int(fs_id) return self.fs_status(fs_id) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts index f15a6b2faf901..5e8ca7e29c869 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts @@ -45,6 +45,7 @@ import { ChangePasswordGuardService } from './shared/services/change-password-gu import { FeatureTogglesGuardService } from './shared/services/feature-toggles-guard.service'; import { ModuleStatusGuardService } from './shared/services/module-status-guard.service'; import { NoSsoGuardService } from './shared/services/no-sso-guard.service'; +import { CephfsVolumeFormComponent } from './ceph/cephfs/cephfs-form/cephfs-form.component'; @Injectable() export class PerformanceCounterBreadcrumbsResolver extends BreadcrumbsResolver { @@ -322,9 +323,16 @@ const routes: Routes = [ // File Systems { path: 'cephfs', - component: CephfsListComponent, canActivate: [FeatureTogglesGuardService], - data: { breadcrumbs: 'File Systems' } + data: { breadcrumbs: 'File Systems' }, + children: [ + { path: '', component: CephfsListComponent }, + { + path: URLVerbs.CREATE, + component: CephfsVolumeFormComponent, + data: { breadcrumbs: ActionLabels.CREATE } + } + ] }, // Object Gateway { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-directories/cephfs-directories.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-directories/cephfs-directories.component.ts index 4ae8a159a0559..841d635b1a095 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-directories/cephfs-directories.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-directories/cephfs-directories.component.ts @@ -208,6 +208,11 @@ export class CephfsDirectoriesComponent implements OnInit, OnChanges { name: $localize`Created`, flexGrow: 1, pipe: this.cdDatePipe + }, + { + prop: 'created', + name: $localize`Capacity`, + flexGrow: 1 } ], selection: new CdTableSelection(), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.html new file mode 100644 index 0000000000000..dc2b1fd0bbc31 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.html @@ -0,0 +1,103 @@ +
+
+
+
{{ action | titlecase }} {{ resource | upperFirst }}
+ + + Orchestrator is not configured. Deploy MDS daemons manually after creating the volume. + +
+ +
+ +
+ + This field is required! + Volume name can only contain letters, numbers, '.', '-', '_' or '/'. +
+
+ + + +
+ +
+ +
+
+ + +
+ +
+ + This field is required. +
+
+ + +
+ +
+ + +
+
+
+
+ +
+
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.scss new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.spec.ts new file mode 100644 index 0000000000000..cf85a2128d770 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.spec.ts @@ -0,0 +1,32 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; +import { CephfsVolumeFormComponent } from './cephfs-form.component'; +import { configureTestBed } from '~/testing/unit-test-helper'; +import { SharedModule } from '~/app/shared/shared.module'; +import { ToastrModule } from 'ngx-toastr'; +import { ReactiveFormsModule } from '@angular/forms'; +describe('CephfsVolumeFormComponent', () => { + let component: CephfsVolumeFormComponent; + let fixture: ComponentFixture; + configureTestBed({ + imports: [ + BrowserAnimationsModule, + SharedModule, + HttpClientTestingModule, + RouterTestingModule, + ReactiveFormsModule, + ToastrModule.forRoot() + ], + declarations: [CephfsVolumeFormComponent] + }); + beforeEach(() => { + fixture = TestBed.createComponent(CephfsVolumeFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.ts new file mode 100644 index 0000000000000..fd77a4c0b1419 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.ts @@ -0,0 +1,166 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { FormControl, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import _ from 'lodash'; + +import { NgbNav, NgbTooltip, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; +import { merge, Observable, Subject } from 'rxjs'; +import { debounceTime, distinctUntilChanged, filter, map } from 'rxjs/operators'; + +import { CephfsService } from '~/app/shared/api/cephfs.service'; +import { HostService } from '~/app/shared/api/host.service'; +import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; +import { SelectMessages } from '~/app/shared/components/select/select-messages.model'; +import { SelectOption } from '~/app/shared/components/select/select-option.model'; +import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants'; +import { Icons } from '~/app/shared/enum/icons.enum'; +import { CdForm } from '~/app/shared/forms/cd-form'; +import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder'; +import { CdFormGroup } from '~/app/shared/forms/cd-form-group'; +import { CdValidators } from '~/app/shared/forms/cd-validators'; +import { FinishedTask } from '~/app/shared/models/finished-task'; +import { Permission } from '~/app/shared/models/permissions'; +import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; + +@Component({ + selector: 'cd-cephfs-form', + templateUrl: './cephfs-form.component.html', + styleUrls: ['./cephfs-form.component.scss'] +}) +export class CephfsVolumeFormComponent extends CdForm implements OnInit { + @ViewChild('crushInfoTabs') crushInfoTabs: NgbNav; + @ViewChild('crushDeletionBtn') crushDeletionBtn: NgbTooltip; + @ViewChild('ecpInfoTabs') ecpInfoTabs: NgbNav; + @ViewChild('ecpDeletionBtn') ecpDeletionBtn: NgbTooltip; + @ViewChild(NgbTypeahead, { static: false }) + typeahead: NgbTypeahead; + + labelFocus = new Subject(); + labelClick = new Subject(); + + orchStatus$: Observable; + + permission: Permission; + form: CdFormGroup; + action: string; + resource: string; + editing: boolean; + icons = Icons; + hosts: any; + labels: string[]; + hasOrchestrator: boolean; + + constructor( + private router: Router, + private taskWrapperService: TaskWrapperService, + private orchService: OrchestratorService, + private formBuilder: CdFormBuilder, + public actionLabels: ActionLabelsI18n, + private hostService: HostService, + private cephfsService: CephfsService + ) { + super(); + this.editing = this.router.url.startsWith(`/pool/${URLVerbs.EDIT}`); + this.action = this.editing ? this.actionLabels.EDIT : this.actionLabels.CREATE; + this.resource = $localize`volume`; + this.hosts = { + options: [], + messages: new SelectMessages({ + empty: $localize`There are no hosts.`, + filter: $localize`Filter hosts` + }) + }; + this.createForm(); + } + + private createForm() { + this.orchService.status().subscribe((status) => { + this.hasOrchestrator = status.available; + }); + this.form = this.formBuilder.group({ + name: new FormControl('', { + validators: [Validators.pattern(/^[.A-Za-z0-9_/-]+$/), Validators.required] + }), + placement: ['hosts'], + hosts: [[]], + label: [ + null, + [ + CdValidators.requiredIf({ + placement: 'label', + unmanaged: false + }) + ] + ], + unmanaged: [false] + }); + } + + ngOnInit() { + this.hostService.list('false').subscribe((resp: object[]) => { + const options: SelectOption[] = []; + _.forEach(resp, (host: object) => { + if (_.get(host, 'sources.orchestrator', false)) { + const option = new SelectOption(false, _.get(host, 'hostname'), ''); + options.push(option); + } + }); + this.hosts.options = [...options]; + }); + this.hostService.getLabels().subscribe((resp: string[]) => { + this.labels = resp; + }); + this.orchStatus$ = this.orchService.status(); + } + + searchLabels = (text$: Observable) => { + return merge( + text$.pipe(debounceTime(200), distinctUntilChanged()), + this.labelFocus, + this.labelClick.pipe(filter(() => !this.typeahead.isPopupOpen())) + ).pipe( + map((value) => + this.labels + .filter((label: string) => label.toLowerCase().indexOf(value.toLowerCase()) > -1) + .slice(0, 10) + ) + ); + }; + + submit() { + let values = this.form.getRawValue(); + const serviceSpec: object = { + placement: {}, + unmanaged: values['unmanaged'] + }; + switch (values['placement']) { + case 'hosts': + if (values['hosts'].length > 0) { + serviceSpec['placement']['hosts'] = values['hosts']; + } + break; + case 'label': + serviceSpec['placement']['label'] = values['label']; + break; + } + + const volumeName = this.form.get('name').value; + const self = this; + let taskUrl = `cephfs/${URLVerbs.CREATE}`; + this.taskWrapperService + .wrapTaskAroundCall({ + task: new FinishedTask(taskUrl, { + volumeName: volumeName + }), + call: this.cephfsService.create(this.form.get('name').value, serviceSpec) + }) + .subscribe({ + error() { + self.form.setErrors({ cdSubmitButton: true }); + }, + complete: () => { + this.router.navigate(['cephfs']); + } + }); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.html index 05960e87fa197..cf5c0a51c633d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.html @@ -11,4 +11,12 @@ +
+ + +
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.spec.ts index 793651081dc89..47923d5e0d3ac 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.spec.ts @@ -2,10 +2,12 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { Component, Input } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; import { SharedModule } from '~/app/shared/shared.module'; import { configureTestBed } from '~/testing/unit-test-helper'; +import { CephfsVolumeFormComponent } from '../cephfs-form/cephfs-form.component'; import { CephfsListComponent } from './cephfs-list.component'; @Component({ selector: 'cd-cephfs-tabs', template: '' }) @@ -19,8 +21,8 @@ describe('CephfsListComponent', () => { let fixture: ComponentFixture; configureTestBed({ - imports: [BrowserAnimationsModule, SharedModule, HttpClientTestingModule], - declarations: [CephfsListComponent, CephfsTabsStubComponent] + imports: [BrowserAnimationsModule, SharedModule, HttpClientTestingModule, RouterTestingModule], + declarations: [CephfsListComponent, CephfsTabsStubComponent, CephfsVolumeFormComponent] }); beforeEach(() => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.ts index 8d19d394c3455..c752a9c58e4b1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.ts @@ -1,25 +1,45 @@ import { Component, OnInit } from '@angular/core'; +import { Permissions } from '~/app/shared/models/permissions'; +import { Router } from '@angular/router'; import { CephfsService } from '~/app/shared/api/cephfs.service'; import { ListWithDetails } from '~/app/shared/classes/list-with-details.class'; +import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; import { CellTemplate } from '~/app/shared/enum/cell-template.enum'; +import { Icons } from '~/app/shared/enum/icons.enum'; +import { CdTableAction } from '~/app/shared/models/cd-table-action'; import { CdTableColumn } from '~/app/shared/models/cd-table-column'; import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context'; import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe'; +import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; +import { URLBuilderService } from '~/app/shared/services/url-builder.service'; + +const BASE_URL = 'cephfs'; @Component({ selector: 'cd-cephfs-list', templateUrl: './cephfs-list.component.html', - styleUrls: ['./cephfs-list.component.scss'] + styleUrls: ['./cephfs-list.component.scss'], + providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }] }) export class CephfsListComponent extends ListWithDetails implements OnInit { columns: CdTableColumn[]; filesystems: any = []; selection = new CdTableSelection(); + tableActions: CdTableAction[]; + permissions: Permissions; - constructor(private cephfsService: CephfsService, private cdDatePipe: CdDatePipe) { + constructor( + private authStorageService: AuthStorageService, + private cephfsService: CephfsService, + private cdDatePipe: CdDatePipe, + public actionLabels: ActionLabelsI18n, + private router: Router, + private urlBuilder: URLBuilderService + ) { super(); + this.permissions = this.authStorageService.getPermissions(); } ngOnInit() { @@ -42,6 +62,15 @@ export class CephfsListComponent extends ListWithDetails implements OnInit { cellTransformation: CellTemplate.checkIcon } ]; + this.tableActions = [ + { + name: this.actionLabels.CREATE, + permission: 'create', + icon: Icons.add, + click: () => this.router.navigate([this.urlBuilder.getCreate()]), + canBePrimary: (selection: CdTableSelection) => !selection.hasSelection + } + ]; } loadFilesystems(context: CdTableFetchDataContext) { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts index 41b58a0a36b12..31398666c53cf 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts @@ -1,8 +1,9 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TreeModule } from '@circlon/angular-tree-component'; -import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbNavModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; import { ChartsModule } from 'ng2-charts'; import { AppRoutingModule } from '~/app/app-routing.module'; @@ -11,17 +12,29 @@ import { CephfsChartComponent } from './cephfs-chart/cephfs-chart.component'; import { CephfsClientsComponent } from './cephfs-clients/cephfs-clients.component'; import { CephfsDetailComponent } from './cephfs-detail/cephfs-detail.component'; import { CephfsDirectoriesComponent } from './cephfs-directories/cephfs-directories.component'; +import { CephfsVolumeFormComponent } from './cephfs-form/cephfs-form.component'; import { CephfsListComponent } from './cephfs-list/cephfs-list.component'; import { CephfsTabsComponent } from './cephfs-tabs/cephfs-tabs.component'; @NgModule({ - imports: [CommonModule, SharedModule, AppRoutingModule, ChartsModule, TreeModule, NgbNavModule], + imports: [ + CommonModule, + SharedModule, + AppRoutingModule, + ChartsModule, + TreeModule, + NgbNavModule, + FormsModule, + ReactiveFormsModule, + NgbTypeaheadModule + ], declarations: [ CephfsDetailComponent, CephfsClientsComponent, CephfsChartComponent, CephfsListComponent, CephfsTabsComponent, + CephfsVolumeFormComponent, CephfsDirectoriesComponent ] }) 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 02f31ca7b56b8..4e212adeba994 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 @@ -73,4 +73,14 @@ export class CephfsService { params }); } + + create(name: string, serviceSpec: object) { + return this.http.post( + this.baseURL, + { name: name, service_spec: serviceSpec }, + { + observe: 'response' + } + ); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts index bc11a0be39cac..bf8f189b4e497 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts @@ -352,6 +352,9 @@ export class TaskMessageService { ), 'crud-component/id': this.newTaskMessage(this.commonOperations.delete, (id) => this.crudMessageId(id) + ), + 'cephfs/create': this.newTaskMessage(this.commonOperations.create, (metadata) => + this.volume(metadata) ) }; @@ -408,6 +411,10 @@ export class TaskMessageService { return $localize`${message}`; } + volume(metadata: any) { + return $localize`'${metadata.volumeName}'`; + } + crudMessageId(id: string) { return $localize`${id}`; } diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 3729c87331d82..a8c57491ae07b 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -1619,6 +1619,45 @@ paths: - jwt: [] tags: - Cephfs + post: + parameters: [] + requestBody: + content: + application/json: + schema: + properties: + name: + type: string + service_spec: + type: string + required: + - name + - service_spec + 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/cephfs/{fs_id}: get: parameters: -- 2.39.5