]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: fix cephfs forms validations 53596/head
authorNizamudeen A <nia@redhat.com>
Fri, 22 Sep 2023 12:37:39 +0000 (18:07 +0530)
committerNizamudeen A <nia@redhat.com>
Fri, 22 Sep 2023 12:37:39 +0000 (18:07 +0530)
1. CephFS Edit Form didnt had any validation for name eventhough the
   create had. So reused the Create form to display the Edit as well

2. Add Name Validations to Subvoume and Subvolume group forms

3. Removed the datePipe from the cephfs list template since we are using
   the relativeDate.

Fixes: https://tracker.ceph.com/issues/62939
Signed-off-by: Nizamudeen A <nia@redhat.com>
src/pybind/mgr/dashboard/frontend/cypress/e2e/filesystems/filesystems.e2e-spec.feature
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.ts
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-subvolume-form/cephfs-subvolume-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-form/cephfs-subvolume-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts

index 79685299b93579c921eb71fedfbe2d5b2f8efa30..2c08fb56eff132b3513441ad6e0ee7acac919937 100644 (file)
@@ -16,7 +16,7 @@ Feature: CephFS Management
         Given I am on the "cephfs" page
         And I select a row "test_cephfs"
         And I click on "Edit" button
-        And enter "name" "test_cephfs_edit" in the modal
+        And enter "name" "test_cephfs_edit"
         And I click on "Edit File System" button
         Then I should see a row with "test_cephfs_edit"
 
index 43ebb5964d3117b0b72d23fb45f7cbea5cd3216a..794bf1809562cb0a1a5219e876314bdacd49a948 100644 (file)
@@ -359,6 +359,11 @@ const routes: Routes = [
             path: URLVerbs.CREATE,
             component: CephfsVolumeFormComponent,
             data: { breadcrumbs: ActionLabels.CREATE }
+          },
+          {
+            path: `${URLVerbs.EDIT}/:name`,
+            component: CephfsVolumeFormComponent,
+            data: { breadcrumbs: ActionLabels.EDIT }
           }
         ]
       },
index 8da9a74a50664486f16a2f23101e69013f98d7f8..c05bee1d0ae50a31347955a3021c7cc119fdac09 100644 (file)
@@ -12,7 +12,8 @@
         <cd-alert-panel type="info"
                         class="m-3"
                         spacingClass="mt-3"
-                        i18n>Orchestrator is not configured. Deploy MDS daemons manually after creating the volume.</cd-alert-panel>
+                        i18n
+                        *ngIf="!editing">Orchestrator is not configured. Deploy MDS daemons manually after creating the volume.</cd-alert-panel>
       </ng-container>
       <div class="card-body">
         <!-- Name -->
@@ -40,7 +41,8 @@
 
         <ng-container *ngIf="orchStatus.available">
           <!-- Placement -->
-          <div class="form-group row">
+          <div class="form-group row"
+               *ngIf="!editing">
             <label class="cd-col-form-label"
                    for="placement"
                    i18n>Placement</label>
index 0e607e9a02b94df6dcf97807787b264e5d190fbd..82f3c4684dd86291989137dbae84ce1a1e2fd82d 100644 (file)
@@ -1,6 +1,6 @@
 import { Component, OnInit, ViewChild } from '@angular/core';
 import { FormControl, Validators } from '@angular/forms';
-import { Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import _ from 'lodash';
 
 import { NgbNav, NgbTooltip, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap';
@@ -50,6 +50,7 @@ export class CephfsVolumeFormComponent extends CdForm implements OnInit {
   hosts: any;
   labels: string[];
   hasOrchestrator: boolean;
+  currentVolumeName: string;
 
   constructor(
     private router: Router,
@@ -58,10 +59,11 @@ export class CephfsVolumeFormComponent extends CdForm implements OnInit {
     private formBuilder: CdFormBuilder,
     public actionLabels: ActionLabelsI18n,
     private hostService: HostService,
-    private cephfsService: CephfsService
+    private cephfsService: CephfsService,
+    private route: ActivatedRoute
   ) {
     super();
-    this.editing = this.router.url.startsWith(`/pool/${URLVerbs.EDIT}`);
+    this.editing = this.router.url.startsWith(`/cephfs/${URLVerbs.EDIT}`);
     this.action = this.editing ? this.actionLabels.EDIT : this.actionLabels.CREATE;
     this.resource = $localize`File System`;
     this.hosts = {
@@ -98,20 +100,27 @@ export class CephfsVolumeFormComponent extends CdForm implements OnInit {
   }
 
   ngOnInit() {
-    const hostContext = new CdTableFetchDataContext(() => undefined);
-    this.hostService.list(hostContext.toParams(), '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);
-        }
+    if (this.editing) {
+      this.route.params.subscribe((params: { name: string }) => {
+        this.currentVolumeName = params.name;
+        this.form.get('name').setValue(this.currentVolumeName);
       });
-      this.hosts.options = [...options];
-    });
-    this.hostService.getLabels().subscribe((resp: string[]) => {
-      this.labels = resp;
-    });
+    } else {
+      const hostContext = new CdTableFetchDataContext(() => undefined);
+      this.hostService.list(hostContext.toParams(), '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();
   }
 
@@ -130,39 +139,59 @@ export class CephfsVolumeFormComponent extends CdForm implements OnInit {
   };
 
   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']);
-        }
-      });
+    const BASE_URL = 'cephfs';
+
+    if (this.editing) {
+      this.taskWrapperService
+        .wrapTaskAroundCall({
+          task: new FinishedTask(`${BASE_URL}/${URLVerbs.EDIT}`, {
+            volumeName: volumeName
+          }),
+          call: this.cephfsService.rename(this.currentVolumeName, volumeName)
+        })
+        .subscribe({
+          error: () => {
+            this.form.setErrors({ cdSubmitButton: true });
+          },
+          complete: () => {
+            this.router.navigate([BASE_URL]);
+          }
+        });
+    } else {
+      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 self = this;
+      let taskUrl = `${BASE_URL}/${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([BASE_URL]);
+          }
+        });
+    }
   }
 }
index 280189ece620c929ded78c261f38873a3ece50b3..0d55845ab594912a7a1b2690fc3d195f9ea0150c 100644 (file)
@@ -11,13 +11,10 @@ import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
 import { Icons } from '~/app/shared/enum/icons.enum';
 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
-import { NotificationType } from '~/app/shared/enum/notification-type.enum';
-import { FormModalComponent } from '~/app/shared/components/form-modal/form-modal.component';
 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';
 import { ModalService } from '~/app/shared/services/modal.service';
@@ -45,7 +42,6 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
   constructor(
     private authStorageService: AuthStorageService,
     private cephfsService: CephfsService,
-    private cdDatePipe: CdDatePipe,
     public actionLabels: ActionLabelsI18n,
     private router: Router,
     private urlBuilder: URLBuilderService,
@@ -75,7 +71,6 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
         name: $localize`Created`,
         prop: 'mdsmap.created',
         flexGrow: 1,
-        pipe: this.cdDatePipe,
         cellTransformation: CellTemplate.timeAgo
       }
     ];
@@ -91,7 +86,8 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
         name: this.actionLabels.EDIT,
         permission: 'update',
         icon: Icons.edit,
-        click: () => this.editAction()
+        click: () =>
+          this.router.navigate([this.urlBuilder.getEdit(this.selection.first().mdsmap.fs_name)])
       },
       {
         permission: 'delete',
@@ -154,30 +150,4 @@ export class CephfsListComponent extends ListWithDetails implements OnInit {
 
     return true;
   }
-
-  editAction() {
-    const selectedVolume = this.selection.first().mdsmap['fs_name'];
-
-    this.modalService.show(FormModalComponent, {
-      titleText: $localize`Edit File System: ${selectedVolume}`,
-      fields: [
-        {
-          type: 'text',
-          name: 'name',
-          value: selectedVolume,
-          label: $localize`Name`,
-          required: true
-        }
-      ],
-      submitButtonText: $localize`Edit File System`,
-      onSubmit: (values: any) => {
-        this.cephfsService.rename(selectedVolume, values.name).subscribe(() => {
-          this.notificationService.show(
-            NotificationType.success,
-            $localize`Updated File System '${selectedVolume}'`
-          );
-        });
-      }
-    });
-  }
 }
index ad6f414486d6d5f0d52c34ef093124f2235d89ec..a810b7e5d2fc6403e2e539c70dcface292929156 100644 (file)
@@ -27,6 +27,9 @@
             <span class="invalid-feedback"
                   *ngIf="subvolumeForm.showError('subvolumeName', formDir, 'notUnique')"
                   i18n>The subvolume already exists.</span>
+            <span *ngIf="subvolumeForm.showError('subvolumeName', formDir, 'pattern')"
+                  class="invalid-feedback"
+                  i18n>Subvolume name can only contain letters, numbers, '.', '-' or '_'</span>
           </div>
         </div>
 
index b85c268326a5996d0ff020ba5b4f72c324d1db0d..2c2fe8f9fa0adbc7ca8f5a966c445703d1b7cf92 100644 (file)
@@ -101,7 +101,7 @@ export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
     this.subvolumeForm = new CdFormGroup({
       volumeName: new FormControl({ value: this.fsName, disabled: true }),
       subvolumeName: new FormControl('', {
-        validators: [Validators.required],
+        validators: [Validators.required, Validators.pattern(/^[.A-Za-z0-9_-]+$/)],
         asyncValidators: [
           CdValidators.unique(
             this.cephFsSubvolumeService.exists,
index 1b451dfbb379b7359ede7723df047e2307c6336c..58bb86021bdb7d5c789667219d4ac851c281d64e 100644 (file)
@@ -27,6 +27,9 @@
             <span class="invalid-feedback"
                   *ngIf="subvolumegroupForm.showError('subvolumegroupName', formDir, 'notUnique')"
                   i18n>The subvolume group already exists.</span>
+            <span *ngIf="subvolumegroupForm.showError('subvolumegroupName', formDir, 'pattern')"
+                  class="invalid-feedback"
+                  i18n>Subvolume name can only contain letters, numbers, '.', '-' or '_'</span>
           </div>
         </div>
 
index f7d5c4802a4d3637fc74154ebf54da89c01daf60..8ecf1eafa8c9f54dd8f1e32924072c91a0908bce 100644 (file)
@@ -93,7 +93,7 @@ export class CephfsSubvolumegroupFormComponent extends CdForm implements OnInit
     this.subvolumegroupForm = new CdFormGroup({
       volumeName: new FormControl({ value: this.fsName, disabled: true }),
       subvolumegroupName: new FormControl('', {
-        validators: [Validators.required],
+        validators: [Validators.required, Validators.pattern(/^[.A-Za-z0-9_-]+$/)],
         asyncValidators: [
           CdValidators.unique(
             this.cephfsSubvolumeGroupService.exists,
index 023a1529fa746e4f147cb985d0893fa866217ba9..f6969c2e8e1b48a627f0c933b26f2b8b34289145 100644 (file)
@@ -356,6 +356,9 @@ export class TaskMessageService {
     'cephfs/create': this.newTaskMessage(this.commonOperations.create, (metadata) =>
       this.volume(metadata)
     ),
+    'cephfs/edit': this.newTaskMessage(this.commonOperations.update, (metadata) =>
+      this.volume(metadata)
+    ),
     'cephfs/remove': this.newTaskMessage(this.commonOperations.remove, (metadata) =>
       this.volume(metadata)
     ),