]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Edit ceph authx users 51367/head
authorPedro Gonzalez Gomez <pegonzal@redhat.com>
Mon, 20 Feb 2023 13:37:00 +0000 (14:37 +0100)
committerPedro Gonzalez Gomez <pegonzal@redhat.com>
Mon, 8 May 2023 07:00:56 +0000 (09:00 +0200)
Signed-off-by: Pedro Gonzalez Gomez <pegonzal@redhat.com>
Signed-off-by: Pere Diaz Bou <pdiazbou@redhat.com>
(cherry picked from commit 8177a748bd831568417df5c687109fbbbd9b981d)
(cherry picked from commit bc73c1aec686282547d6b920ef7ef239d0231f40)

13 files changed:
src/pybind/mgr/dashboard/controllers/_crud.py
src/pybind/mgr/dashboard/controllers/ceph_users.py
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/crud-table/crud-table.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/forms/crud-form/crud-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/forms/crud-form/crud-form.model.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/crud-form-adapter.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/crud-form-adapter.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/data-gateway.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/data-gateway.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts
src/pybind/mgr/dashboard/openapi.yaml

index 7b2f13defac3cb8bb71aaff70ed8f66ccb6e46b1..20152e900da30b6ecb12047390800af5c2fea6af 100644 (file)
@@ -91,6 +91,7 @@ class Icon(Enum):
     DESTROY = 'fa fa-times'
     IMPORT = 'fa fa-upload'
     EXPORT = 'fa fa-download'
+    EDIT = 'fa fa-pencil'
 
 
 class Validator(Enum):
@@ -102,7 +103,7 @@ class Validator(Enum):
 
 class FormField(NamedTuple):
     """
-    The key of a FromField is then used to send the data related to that key into the
+    The key of a FormField is then used to send the data related to that key into the
     POST and PUT endpoints. It is imperative for the developer to map keys of fields and containers
     to the input of the POST and PUT endpoints.
     """
@@ -111,6 +112,7 @@ class FormField(NamedTuple):
     field_type: Any = str
     default_value: Optional[Any] = None
     optional: bool = False
+    readonly: bool = False
     help: str = ''
     validators: List[Validator] = []
 
@@ -133,11 +135,12 @@ class FormField(NamedTuple):
 
 class Container:
     def __init__(self, name: str, key: str, fields: List[Union[FormField, "Container"]],
-                 optional: bool = False, min_items=1):
+                 optional: bool = False, readonly: bool = False, min_items=1):
         self.name = name
         self.key = key
         self.fields = fields
         self.optional = optional
+        self.readonly = readonly
         self.min_items = min_items
 
     def layout_type(self):
@@ -215,6 +218,7 @@ class Container:
                 properties[field.key]['type'] = _type
                 properties[field.key]['title'] = field.name
                 field_ui_schema['key'] = field_key
+                field_ui_schema['readonly'] = field.readonly
                 field_ui_schema['help'] = f'{field.help}'
                 field_ui_schema['validators'] = [i.value for i in field.validators]
                 items.append(field_ui_schema)
@@ -272,16 +276,21 @@ class FormTaskInfo:
 
 
 class Form:
-    def __init__(self, path, root_container,
-                 task_info: FormTaskInfo = FormTaskInfo("Unknown task", [])):
+    def __init__(self, path, root_container, method_type='',
+                 task_info: FormTaskInfo = FormTaskInfo("Unknown task", []),
+                 model_callback=None):
         self.path = path
         self.root_container: Container = root_container
+        self.method_type = method_type
         self.task_info = task_info
+        self.model_callback = model_callback
 
     def to_dict(self):
         res = self.root_container.to_dict()
+        res['method_type'] = self.method_type
         res['task_info'] = self.task_info.to_dict()
         res['path'] = self.path
+        res['ask'] = self.path
         return res
 
 
@@ -319,9 +328,10 @@ class CRUDEndpoint:
                  meta: CRUDMeta = CRUDMeta(), get_all: Optional[CRUDCollectionMethod] = None,
                  create: Optional[CRUDCollectionMethod] = None,
                  delete: Optional[CRUDCollectionMethod] = None,
-                 detail_columns: Optional[List[str]] = None,
                  selection_type: SelectionType = SelectionType.SINGLE,
-                 extra_endpoints: Optional[List[Tuple[str, CRUDCollectionMethod]]] = None):
+                 extra_endpoints: Optional[List[Tuple[str, CRUDCollectionMethod]]] = None,
+                 edit: Optional[CRUDCollectionMethod] = None,
+                 detail_columns: Optional[List[str]] = None):
         self.router = router
         self.doc = doc
         self.set_column = set_column
@@ -331,6 +341,7 @@ class CRUDEndpoint:
         self.get_all = get_all
         self.create = create
         self.delete = delete
+        self.edit = edit
         self.permissions = permissions if permissions is not None else []
         self.column_key = column_key if column_key is not None else ''
         self.detail_columns = detail_columns if detail_columns is not None else []
@@ -372,6 +383,13 @@ class CRUDEndpoint:
                 return outer_self.delete.func(self, *args, **kwargs)  # type: ignore
             funcs['delete'] = delete
 
+        if self.edit:
+            @self.edit.doc
+            @wraps(self.edit.func)
+            def singleton_set(self, *args, **kwargs):
+                return outer_self.edit.func(self, *args, **kwargs)  # type: ignore
+            funcs['singleton_set'] = singleton_set
+
         for extra_endpoint in self.extra_endpoints:
             funcs[extra_endpoint[0]] = extra_endpoint[1].doc(extra_endpoint[1].func)
 
@@ -386,10 +404,10 @@ class CRUDEndpoint:
         cls.CRUDClass = crud_class
 
     def create_meta_class(self, cls):
-        def _list(self):
+        def _list(self, model_key: str = ''):
             self.update_columns()
             self.generate_actions()
-            self.generate_forms()
+            self.generate_forms(model_key)
             self.set_permissions()
             self.set_column_key()
             self.get_detail_columns()
@@ -424,13 +442,20 @@ class CRUDEndpoint:
             for action in self.__class__.outer_self.actions:
                 self.__class__.outer_self.meta.actions.append(action._asdict())
 
-        def generate_forms(self):
+        def generate_forms(self, model_key):
             self.__class__.outer_self.meta.forms.clear()
 
             for form in self.__class__.outer_self.forms:
-                self.__class__.outer_self.meta.forms.append(form.to_dict())
+                form_as_dict = form.to_dict()
+                model = {}
+                if form.model_callback and model_key:
+                    model = form.model_callback(model_key)
+                    form_as_dict['model'] = model
+                self.__class__.outer_self.meta.forms.append(form_as_dict)
 
         def set_permissions(self):
+            self.__class__.outer_self.meta.permissions.clear()
+
             if self.__class__.outer_self.permissions:
                 self.outer_self.meta.permissions.extend(self.__class__.outer_self.permissions)
 
index 77e67dc7ff8404679d0549dc0a3cc82203da1e4f..a4593144739dfa7aa6635407817ecade22336134 100644 (file)
@@ -1,4 +1,5 @@
 import logging
+from enum import Enum
 from errno import EINVAL
 from typing import List, NamedTuple, Optional
 
@@ -26,6 +27,11 @@ class Cap(NamedTuple):
     cap: str
 
 
+class MethodType(Enum):
+    POST = 'post'
+    PUT = 'put'
+
+
 class CephUserEndpoints:
     @staticmethod
     def _run_auth_command(command: str, *args, **kwargs):
@@ -75,16 +81,10 @@ class CephUserEndpoints:
     def user_delete(_, user_entity: str):
         """
         Delete a ceph user and it's defined capabilities.
-        :param user_entity: Entity to dlelete
+        :param user_entity: Entity to delete
         """
         logger.debug("Sending command 'auth del' of entity '%s'", user_entity)
-        try:
-            CephUserEndpoints._run_auth_command('auth del', entity=user_entity)
-        except SendCommandError as ex:
-            msg = f'{ex} in command {ex.prefix}'
-            if ex.errno == -EINVAL:
-                raise DashboardException(msg, code=400)
-            raise DashboardException(msg, code=500)
+        CephUserEndpoints._run_auth_command('auth del', entity=user_entity)
         return f"Successfully deleted user '{user_entity}'"
 
     @staticmethod
@@ -95,8 +95,35 @@ class CephUserEndpoints:
             export_string += f'{out}\n'
         return export_string
 
+    @staticmethod
+    def user_edit(_, user_entity: str = '', capabilities: List[Cap] = None):
+        """
+        Change the ceph user capabilities.
+        Setting new capabilities will overwrite current ones.
+        :param user_entity: Entity to change
+        :param capabilities: List of updated capabilities to user_entity
+        """
+        caps = []
+        for cap in capabilities:
+            caps.append(cap['entity'])
+            caps.append(cap['cap'])
+
+        logger.debug("Sending command 'auth caps' of entity '%s' with caps '%s'",
+                     user_entity, str(caps))
+        CephUserEndpoints._run_auth_command('auth caps', entity=user_entity, caps=caps)
+        return f"Successfully edited user '{user_entity}'"
+
+    @staticmethod
+    def model(user_entity: str):
+        user_data = CephUserEndpoints._run_auth_command('auth get', entity=user_entity)[0]
+        model = {'user_entity': '', 'capabilities': []}
+        model['user_entity'] = user_data['entity']
+        for entity, cap in user_data['caps'].items():
+            model['capabilities'].append({'entity': entity, 'cap': cap})
+        return model
+
 
-create_cap_container = ArrayHorizontalContainer('Capabilities', 'capabilities', fields=[
+cap_container = ArrayHorizontalContainer('Capabilities', 'capabilities', fields=[
     FormField('Entity', 'entity',
               field_type=str),
     FormField('Entity Capabilities',
@@ -105,12 +132,19 @@ create_cap_container = ArrayHorizontalContainer('Capabilities', 'capabilities',
 create_container = VerticalContainer('Create User', 'create_user', fields=[
     FormField('User entity', 'user_entity',
               field_type=str),
-    create_cap_container,
+    cap_container,
+])
+
+edit_container = VerticalContainer('Edit User', 'edit_user', fields=[
+    FormField('User entity', 'user_entity',
+              field_type=str, readonly=True),
+    cap_container,
 ])
 
 create_form = Form(path='/cluster/user/create',
                    root_container=create_container,
-                   task_info=FormTaskInfo("Ceph user '{user_entity}' created successfully",
+                   method_type=MethodType.POST.value,
+                   task_info=FormTaskInfo("Ceph user '{user_entity}' successfully",
                                           ['user_entity']))
 
 # pylint: disable=C0301
@@ -127,7 +161,15 @@ import_container = VerticalContainer('Import User', 'import_user', fields=[
 
 import_user_form = Form(path='/cluster/user/import',
                         root_container=import_container,
-                        task_info=FormTaskInfo("User imported successfully", []))
+                        task_info=FormTaskInfo("successfully", []),
+                        method_type=MethodType.POST.value)
+
+edit_form = Form(path='/cluster/user/edit',
+                 root_container=edit_container,
+                 method_type=MethodType.PUT.value,
+                 task_info=FormTaskInfo("Ceph user '{user_entity}' successfully",
+                                        ['user_entity']),
+                 model_callback=CephUserEndpoints.model)
 
 
 @CRUDEndpoint(
@@ -137,16 +179,18 @@ import_user_form = Form(path='/cluster/user/import',
     actions=[
         TableAction(name='Create', permission='create', icon=Icon.ADD.value,
                     routerLink='/cluster/user/create'),
+        TableAction(name='Edit', permission='update', icon=Icon.EDIT.value,
+                    click='edit'),
         TableAction(name='Delete', permission='delete', icon=Icon.DESTROY.value,
                     click='delete', disable=True),
         TableAction(name='Import', permission='create', icon=Icon.IMPORT.value,
                     routerLink='/cluster/user/import'),
         TableAction(name='Export', permission='read', icon=Icon.EXPORT.value,
-                    click='authExport', disable=True),
+                    click='authExport', disable=True)
     ],
-    column_key='entity',
     permissions=[Scope.CONFIG_OPT],
-    forms=[create_form, import_user_form],
+    forms=[create_form, edit_form, import_user_form],
+    column_key='entity',
     get_all=CRUDCollectionMethod(
         func=CephUserEndpoints.user_list,
         doc=EndpointDoc("Get Ceph Users")
@@ -155,6 +199,10 @@ import_user_form = Form(path='/cluster/user/import',
         func=CephUserEndpoints.user_create,
         doc=EndpointDoc("Create Ceph User")
     ),
+    edit=CRUDCollectionMethod(
+        func=CephUserEndpoints.user_edit,
+        doc=EndpointDoc("Edit Ceph User")
+    ),
     delete=CRUDCollectionMethod(
         func=CephUserEndpoints.user_delete,
         doc=EndpointDoc("Delete Ceph User")
index c053d51ef0bf9dacdbed68435102705b5a7a928f..fed34a3d2459a283e13b41c1d861b90adc59861e 100644 (file)
@@ -141,6 +141,14 @@ const routes: Routes = [
           resource: 'api.cluster.user@1.0'
         }
       },
+      {
+        path: 'cluster/user/edit',
+        component: CrudFormComponent,
+        data: {
+          breadcrumbs: 'Cluster/Users',
+          resource: 'api.cluster.user@1.0'
+        }
+      },
       {
         path: 'monitor',
         component: MonitorComponent,
index 22e23d845eb953cfa8addb370694ec8865475f60..595fb667b764b7985b858c08ad52fa27621b2cdb 100644 (file)
@@ -74,7 +74,7 @@ export class SubmitButtonComponent implements OnInit {
       (<FormGroupDirective>this.form).onSubmit($event);
     }
 
-    if (this.form.invalid) {
+    if (this.form?.invalid) {
       this.focusInvalid();
       return;
     }
index bdb3b44bd73b1312581ab259b2367e41df2b1489..750152161c246e0a4d88b6c8d824d83bdaa14fa2 100644 (file)
@@ -1,5 +1,5 @@
 import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
 
 import _ from 'lodash';
@@ -52,7 +52,8 @@ export class CRUDTableComponent implements OnInit {
     private taskWrapper: TaskWrapperService,
     private cephUserService: CephUserService,
     private activatedRoute: ActivatedRoute,
-    private modalService: ModalService
+    private modalService: ModalService,
+    private router: Router
   ) {
     this.permissions = this.authStorageService.getPermissions();
   }
@@ -147,6 +148,14 @@ export class CRUDTableComponent implements OnInit {
     this.expandedRow = event;
   }
 
+  edit() {
+    let key = '';
+    if (this.selection.hasSelection) {
+      key = this.selection.first()[this.meta.columnKey];
+    }
+    this.router.navigate(['/cluster/user/edit'], { queryParams: { key: key } });
+  }
+
   authExport() {
     let entities: string[] = [];
     this.selection.selected.forEach((row) => entities.push(row.entity));
index 14ddccbc1957943d09c3deede32c3805aa62dc33..9f03ec4a6de8dabb5676d61f39a113b0452c632f 100644 (file)
@@ -1,5 +1,5 @@
 import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { DataGatewayService } from '~/app/shared/services/data-gateway.service';
 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
 import { FinishedTask } from '~/app/shared/models/finished-task';
@@ -9,6 +9,7 @@ import { mergeMap } from 'rxjs/operators';
 import { CrudTaskInfo, JsonFormUISchema } from './crud-form.model';
 import { Observable } from 'rxjs';
 import _ from 'lodash';
+import { CdTableSelection } from '../../models/cd-table-selection';
 
 @Component({
   selector: 'cd-crud-form',
@@ -21,22 +22,40 @@ export class CrudFormComponent implements OnInit {
   task: { message: string; id: string } = { message: '', id: '' };
   form = new FormGroup({});
   formUISchema$: Observable<JsonFormUISchema>;
+  methodType: string;
+  urlFormName: string;
+  key: string = '';
+  selected: CdTableSelection;
 
   constructor(
     private dataGatewayService: DataGatewayService,
     private activatedRoute: ActivatedRoute,
     private taskWrapper: TaskWrapperService,
-    private location: Location
+    private location: Location,
+    private router: Router
   ) {}
 
   ngOnInit(): void {
-    this.formUISchema$ = this.activatedRoute.data.pipe(
-      mergeMap((data: any) => {
-        this.resource = data.resource;
-        const url = '/' + this.activatedRoute.snapshot.url.join('/');
-        return this.dataGatewayService.form(`ui-${this.resource}`, url);
-      })
-    );
+    this.activatedRoute.queryParamMap.subscribe((paramMap) => {
+      this.formUISchema$ = this.activatedRoute.data.pipe(
+        mergeMap((data: any) => {
+          this.resource = data.resource || this.resource;
+          const url = '/' + this.activatedRoute.snapshot.url.join('/');
+          const key = paramMap.get('key') || '';
+          return this.dataGatewayService.form(`ui-${this.resource}`, url, key);
+        })
+      );
+      this.formUISchema$.subscribe((data: any) => {
+        this.methodType = data.methodType;
+        this.model = data.model;
+      });
+      this.urlFormName = this.router.url.split('/').pop();
+      // remove optional arguments
+      const paramIndex = this.urlFormName.indexOf('?');
+      if (paramIndex > 0) {
+        this.urlFormName = this.urlFormName.substring(0, paramIndex);
+      }
+    });
   }
 
   async readFileAsText(file: File): Promise<string> {
@@ -72,8 +91,8 @@ export class CrudFormComponent implements OnInit {
       await this.preSubmit(data);
       this.taskWrapper
         .wrapTaskAroundCall({
-          task: new FinishedTask('crud-component', taskMetadata),
-          call: this.dataGatewayService.create(this.resource, data)
+          task: new FinishedTask(`crud-component/${this.urlFormName}`, taskMetadata),
+          call: this.dataGatewayService.submit(this.resource, data, this.methodType)
         })
         .subscribe({
           complete: () => {
index fbded8f24bc222cde8c20746872c97ac4846cad7..b0fcdfb60082fc87931fdc311f2f8b9f9b3c0556 100644 (file)
@@ -10,4 +10,6 @@ export interface JsonFormUISchema {
   controlSchema: FormlyFieldConfig[];
   uiSchema: any;
   taskInfo: CrudTaskInfo;
+  methodType: string;
+  model: any;
 }
index 7905a11e11aa2118367faf51874eeaeabac2d2ac..bdefdd64180f02fd7bfed9ded872c42fbb130d24 100644 (file)
@@ -1,12 +1,15 @@
 import { TestBed } from '@angular/core/testing';
 
 import { CrudFormAdapterService } from './crud-form-adapter.service';
+import { RouterTestingModule } from '@angular/router/testing';
 
 describe('CrudFormAdapterService', () => {
   let service: CrudFormAdapterService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    TestBed.configureTestingModule({
+      imports: [RouterTestingModule]
+    });
     service = TestBed.inject(CrudFormAdapterService);
   });
 
index ab0b7a6d6e64f58ff167b9fd42b9c9656c1add81..49c8fc9417b83ccbc8705ac1e79ef8aa1baa7a32 100644 (file)
@@ -26,6 +26,7 @@ export class CrudFormAdapterService {
       for (let j = 0; j < uiSchema.length; j++) {
         if (controlSchema[i].key == uiSchema[j].key) {
           controlSchema[i].props.templateOptions = uiSchema[j].templateOptions;
+          controlSchema[i].props.readonly = uiSchema[j].readonly;
           setupValidators(controlSchema[i], uiSchema);
         }
       }
@@ -34,6 +35,8 @@ export class CrudFormAdapterService {
       metadataFields: response.forms[form].task_info.metadataFields,
       message: response.forms[form].task_info.message
     };
-    return { title, uiSchema, controlSchema, taskInfo };
+    const methodType = response.forms[form].method_type;
+    const model = response.forms[form].model || {};
+    return { title, uiSchema, controlSchema, taskInfo, methodType, model };
   }
 }
index 1eb7ccbc4249220e154a756dab6a6b07a1c5a74d..96095dfe6fd4b20a7ca1c1b09ea57d0f1392b0da 100644 (file)
@@ -4,11 +4,12 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { inject, TestBed } from '@angular/core/testing';
 
 import { DataGatewayService } from './data-gateway.service';
+import { RouterTestingModule } from '@angular/router/testing';
 
 describe('Service: DataGateway', () => {
   beforeEach(() => {
     TestBed.configureTestingModule({
-      imports: [HttpClientTestingModule],
+      imports: [HttpClientTestingModule, RouterTestingModule],
       providers: [DataGatewayService]
     });
   });
index 15045d6d831984191b064a8d38eae3ba22176bab..c4a223e31b3e982d2862a31d2f6df4cb7d8da5f3 100644 (file)
@@ -1,4 +1,4 @@
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpParams } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 
 import { Observable } from 'rxjs';
@@ -11,6 +11,7 @@ import { CrudFormAdapterService } from './crud-form-adapter.service';
 })
 export class DataGatewayService {
   cache: { [keys: string]: Observable<any> } = {};
+  selected: any;
 
   constructor(private http: HttpClient, private crudFormAdapater: CrudFormAdapterService) {}
 
@@ -27,10 +28,10 @@ export class DataGatewayService {
     return this.cache[cacheable];
   }
 
-  create(dataPath: string, data: any): Observable<any> {
+  submit(dataPath: string, data: any, methodType: string): Observable<any> {
     const { url, version } = this.getUrlAndVersion(dataPath);
 
-    return this.http.post<any>(url, data, {
+    return this.http[methodType]<any>(url, data, {
       headers: { Accept: `application/vnd.ceph.api.v${version}+json` }
     });
   }
@@ -44,13 +45,15 @@ export class DataGatewayService {
     });
   }
 
-  form(dataPath: string, formPath: string): Observable<JsonFormUISchema> {
-    const cacheable = this.getCacheable(dataPath, 'get');
+  form(dataPath: string, formPath: string, modelKey: string = ''): Observable<JsonFormUISchema> {
+    const cacheable = this.getCacheable(dataPath, 'get', modelKey);
+    const params = { model_key: modelKey };
     if (this.cache[cacheable] === undefined) {
       const { url, version } = this.getUrlAndVersion(dataPath);
 
       this.cache[cacheable] = this.http.get<any>(url, {
-        headers: { Accept: `application/vnd.ceph.api.v${version}+json` }
+        headers: { Accept: `application/vnd.ceph.api.v${version}+json` },
+        params: params
       });
     }
     return this.cache[cacheable].pipe(
@@ -60,8 +63,21 @@ export class DataGatewayService {
     );
   }
 
-  getCacheable(dataPath: string, method: string) {
-    return dataPath + method;
+  model(dataPath: string, params: HttpParams): Observable<any> {
+    const cacheable = this.getCacheable(dataPath, 'get');
+    if (this.cache[cacheable] === undefined) {
+      const { url, version } = this.getUrlAndVersion(dataPath);
+
+      this.cache[cacheable] = this.http.get<any>(`${url}/model`, {
+        headers: { Accept: `application/vnd.ceph.api.v${version}+json` },
+        params: params
+      });
+    }
+    return this.cache[cacheable];
+  }
+
+  getCacheable(dataPath: string, method: string, key: string = '') {
+    return dataPath + method + key;
   }
 
   getUrlAndVersion(dataPath: string) {
index 195f3b1374ab3134a348417d0757df91e2c3e3a2..bc11a0be39cac07b4d5dc7ce9a9deb38f2e2d98c 100644 (file)
@@ -341,7 +341,13 @@ export class TaskMessageService {
     'service/delete': this.newTaskMessage(this.commonOperations.delete, (metadata) =>
       this.service(metadata)
     ),
-    'crud-component': this.newTaskMessage(this.commonOperations.create, (metadata) =>
+    'crud-component/create': this.newTaskMessage(this.commonOperations.create, (metadata) =>
+      this.crudMessage(metadata)
+    ),
+    'crud-component/edit': this.newTaskMessage(this.commonOperations.update, (metadata) =>
+      this.crudMessage(metadata)
+    ),
+    'crud-component/import': this.newTaskMessage(this.commonOperations.import, (metadata) =>
       this.crudMessage(metadata)
     ),
     'crud-component/id': this.newTaskMessage(this.commonOperations.delete, (id) =>
index 21be14568fd4e30af79e9824fdf1e519432c795a..9dcbdeb35aadc49a144d9725e84062d895a0daf6 100644 (file)
@@ -2143,6 +2143,28 @@ paths:
       summary: Update the cluster status
       tags:
       - Cluster
+  /api/cluster/capacity:
+    get:
+      parameters: []
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              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:
+      - Cluster
   /api/cluster/user:
     get:
       description: "\n        Get list of ceph users and its respective data\n   \
@@ -2212,6 +2234,48 @@ paths:
       summary: Create Ceph User
       tags:
       - Cluster
+    put:
+      description: "\n        Change the ceph user capabilities.\n        Setting\
+        \ new capabilities will overwrite current ones.\n        :param user_entity:\
+        \ Entity to change\n        :param capabilities: List of updated capabilities\
+        \ to user_entity\n        "
+      parameters: []
+      requestBody:
+        content:
+          application/json:
+            schema:
+              properties:
+                capabilities:
+                  type: string
+                user_entity:
+                  default: ''
+                  type: string
+              type: object
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Resource updated.
+        '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: []
+      summary: Edit Ceph User
+      tags:
+      - Cluster
   /api/cluster/user/export:
     post:
       parameters: []
@@ -2253,7 +2317,7 @@ paths:
   /api/cluster/user/{user_entity}:
     delete:
       description: "\n        Delete a ceph user and it's defined capabilities.\n\
-        \        :param user_entity: Entity to dlelete\n        "
+        \        :param user_entity: Entity to delete\n        "
       parameters:
       - in: path
         name: user_entity