From: Naman Munet Date: Fri, 7 Jun 2024 10:49:23 +0000 (+0530) Subject: mgr/dashboard: Expand Cluster improvements X-Git-Tag: v20.0.0~1677^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=aa4d6b58459079a3b59ffdd1c9913c0a07a1549a;p=ceph.git mgr/dashboard: Expand Cluster improvements worked on expand cluster screen hide/show and persisting osd form values Fixes: https://tracker.ceph.com/issues/66344 Signed-off-by: Naman Munet --- diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/cluster/create-cluster.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/cluster/create-cluster.po.ts index 300eddbcc3de8..2ec31869daf7e 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/e2e/cluster/create-cluster.po.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/cluster/create-cluster.po.ts @@ -4,7 +4,7 @@ import { HostsPageHelper } from './hosts.po'; import { ServicesPageHelper } from './services.po'; const pages = { - index: { url: '#/expand-cluster', id: 'cd-create-cluster' } + index: { url: '#/expand-cluster?welcome=true', id: 'cd-create-cluster' } }; export class CreateClusterWizardHelper extends PageHelper { pages = pages; @@ -28,7 +28,7 @@ export class CreateClusterWizardHelper extends PageHelper { export class CreateClusterHostPageHelper extends HostsPageHelper { pages = { - index: { url: '#/expand-cluster', id: 'cd-wizard' }, + index: { url: '#/expand-cluster?welcome=true', id: 'cd-wizard' }, add: { url: '', id: 'cd-host-form' } }; @@ -42,7 +42,7 @@ export class CreateClusterHostPageHelper extends HostsPageHelper { export class CreateClusterServicePageHelper extends ServicesPageHelper { pages = { - index: { url: '#/expand-cluster', id: 'cd-wizard' }, + index: { url: '#/expand-cluster?welcome=true', id: 'cd-wizard' }, create: { url: '', id: 'cd-service-form' } }; diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/cluster/osds.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/cluster/osds.po.ts index cd812f474fb89..e96518bceb70c 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/e2e/cluster/osds.po.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/cluster/osds.po.ts @@ -15,6 +15,11 @@ export class OSDsPageHelper extends PageHelper { create(deviceType: 'hdd' | 'ssd', hostname?: string, expandCluster = false) { cy.get('[aria-label="toggle advanced mode"]').click(); + cy.get('[aria-label="toggle advanced mode"]').then(($button) => { + if ($button.hasClass('collapsed')) { + cy.wrap($button).click(); + } + }); // Click Primary devices Add button cy.get('cd-osd-devices-selection-groups[name="Primary"]').as('primaryGroups'); cy.get('@primaryGroups').find('button').click(); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/common/urls.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/common/urls.po.ts index 6f7316f98f59e..d888388cad35d 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/e2e/common/urls.po.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/common/urls.po.ts @@ -3,7 +3,7 @@ import { PageHelper } from '../page-helper.po'; export class UrlsCollection extends PageHelper { pages = { // Cluster expansion - welcome: { url: '#/expand-cluster', id: 'cd-create-cluster' }, + welcome: { url: '#/expand-cluster?welcome=true', id: 'cd-create-cluster' }, // Landing page dashboard: { url: '#/dashboard', id: 'cd-dashboard' }, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.html index a2ae23b2c2bf4..df61fd40a9536 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.html @@ -9,20 +9,20 @@ class="bold">Hosts {{ hostsCount }} - + -
-
-

Storage Capacity

-
-
-

Number of devices

-
-
-

Raw capacity

-
-
- +
+
+

Storage Capacity

+
+
+

Number of devices

+
+
+

Raw capacity

+
+
+

{{ totalDevices }}

{{ totalCapacity | dimlessBinary }}

@@ -40,13 +40,28 @@ -
+
Host Details - -
+ [showGeneralActionsOnly]="true" + [showExpandClusterBtn]="false"> + +
+ + + +
+
+

Storage Capacity

+
+
+

{{deploymentDescText}}

+
+
+ + +
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.ts index 964fd7594e79c..ed60ddf805ada 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.ts @@ -23,6 +23,8 @@ export class CreateClusterReviewComponent implements OnInit { services: Array = []; totalCPUs = 0; totalMemory = 0; + deploymentDescText: string; + isSimpleDeployment = true; constructor( public wizardStepsService: WizardStepsService, @@ -40,6 +42,7 @@ export class CreateClusterReviewComponent implements OnInit { let dbDevices = 0; let dbDeviceCapacity = 0; + this.isSimpleDeployment = this.osdService.isDeployementModeSimple; const hostContext = new CdTableFetchDataContext(() => undefined); this.hostService.list(hostContext.toParams(), 'true').subscribe((resp: object[]) => { this.hosts = resp; @@ -67,6 +70,21 @@ export class CreateClusterReviewComponent implements OnInit { dbDeviceCapacity = this.osdService.osdDevices['db']['capacity']; } + if (this.isSimpleDeployment) { + this.osdService.getDeploymentOptions().subscribe((optionsObj) => { + if (!_.isEmpty(optionsObj)) { + Object.keys(optionsObj.options).forEach((option) => { + if ( + this.osdService.selectedFormValues && + this.osdService.selectedFormValues.get('deploymentOption').value === option + ) { + this.deploymentDescText = optionsObj.options[option].desc; + } + }); + } + }); + } + this.totalDevices = dataDevices + walDevices + dbDevices; this.osdService.osdDevices['totalDevices'] = this.totalDevices; this.totalCapacity = dataDeviceCapacity + walDeviceCapacity + dbDeviceCapacity; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.html index 272b5b0b91617..930c6b42ac9c7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.html @@ -1,5 +1,5 @@
+ *ngIf="startClusterCreation">
@@ -30,7 +30,7 @@
+ *ngIf="!startClusterCreation">
Expand Cluster
@@ -45,7 +45,8 @@ + [showGeneralActionsOnly]="true" + [showExpandClusterBtn]="false">
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.spec.ts index ca3435536067b..943d5c8ff1627 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.spec.ts @@ -59,6 +59,8 @@ describe('CreateClusterComponent', () => { }); it('should have project name as heading in welcome screen', () => { + component.startClusterCreation = true; + fixture.detectChanges(); const heading = fixture.debugElement.query(By.css('h3')).nativeElement; expect(heading.innerHTML).toBe(`Welcome to ${projectConstants.projectName}`); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.ts index 670a3e00dfe5b..25d8717513064 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.ts @@ -7,7 +7,7 @@ import { TemplateRef, ViewChild } from '@angular/core'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import _ from 'lodash'; @@ -68,7 +68,8 @@ export class CreateClusterComponent implements OnInit, OnDestroy { private clusterService: ClusterService, private modalService: ModalService, private taskWrapper: TaskWrapperService, - private osdService: OsdService + private osdService: OsdService, + private route: ActivatedRoute ) { this.permissions = this.authStorageService.getPermissions(); this.currentStepSub = this.wizardStepsService @@ -80,6 +81,14 @@ export class CreateClusterComponent implements OnInit, OnDestroy { } ngOnInit(): void { + this.route.queryParams.subscribe((params) => { + // reading 'welcome' value true/false to toggle expand-cluster wizand view and welcome view + const showWelcomeScreen = params['welcome']; + if (showWelcomeScreen) { + this.startClusterCreation = showWelcomeScreen; + } + }); + this.osdService.getDeploymentOptions().subscribe((options) => { this.deploymentOption = options; this.selectedOption = { option: options.recommended_option, encrypted: false }; @@ -91,7 +100,7 @@ export class CreateClusterComponent implements OnInit, OnDestroy { } createCluster() { - this.startClusterCreation = true; + this.startClusterCreation = false; } skipClusterCreation() { @@ -244,5 +253,6 @@ export class CreateClusterComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.currentStepSub.unsubscribe(); + this.osdService.selectedFormValues = null; } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.html index c02b29d101f6a..43d41c8ce7f99 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.html @@ -26,6 +26,12 @@ id="host-actions" [tableActions]="tableActions"> + +
= []; hosts: Array = []; isLoadingHosts = false; cdParams = { fromLink: '/hosts' }; tableActions: CdTableAction[]; + expandClusterActions: CdTableAction[]; selection = new CdTableSelection(); modalRef: NgbModalRef; isExecuting = false; @@ -125,6 +129,16 @@ export class HostsComponent extends ListWithDetails implements OnDestroy, OnInit ) { super(); this.permissions = this.authStorageService.getPermissions(); + this.expandClusterActions = [ + { + name: this.actionLabels.EXPAND_CLUSTER, + permission: 'create', + icon: Icons.expand, + routerLink: '/expand-cluster', + disable: (selection: CdTableSelection) => this.getDisable('add', selection), + visible: () => this.showExpandClusterBtn + } + ]; this.tableActions = [ { name: this.actionLabels.ADD, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.spec.ts index 725fc953fbb6c..162a429f690d4 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.spec.ts @@ -237,6 +237,7 @@ describe('OsdFormComponent', () => { describe('without data devices selected', () => { it('should disable preview button', () => { + component.simpleDeployment = false; expectPreviewButton(false); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts index 00a162dac1e2f..16b223b9cbc06 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts @@ -1,4 +1,12 @@ -import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { + Component, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, + ViewChild +} from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { Router } from '@angular/router'; @@ -34,7 +42,7 @@ import { OsdFeature } from './osd-feature.interface'; templateUrl: './osd-form.component.html', styleUrls: ['./osd-form.component.scss'] }) -export class OsdFormComponent extends CdForm implements OnInit { +export class OsdFormComponent extends CdForm implements OnInit, OnDestroy { @ViewChild('dataDeviceSelectionGroups') dataDeviceSelectionGroups: OsdDevicesSelectionGroupsComponent; @@ -121,12 +129,23 @@ export class OsdFormComponent extends CdForm implements OnInit { this.osdService.getDeploymentOptions().subscribe((options) => { this.deploymentOptions = options; - this.form.get('deploymentOption').setValue(this.deploymentOptions?.recommended_option); + if (!this.osdService.selectedFormValues) { + this.form.get('deploymentOption').setValue(this.deploymentOptions?.recommended_option); + } if (this.deploymentOptions?.recommended_option) { this.enableFeatures(); } }); + + // restoring form value on back/next + if (this.osdService.selectedFormValues) { + this.form = _.cloneDeep(this.osdService.selectedFormValues); + this.form + .get('deploymentOption') + .setValue(this.osdService.selectedFormValues.value?.deploymentOption); + } + this.simpleDeployment = this.osdService.isDeployementModeSimple; this.form.get('walSlots').valueChanges.subscribe((value) => this.setSlots('wal', value)); this.form.get('dbSlots').valueChanges.subscribe((value) => this.setSlots('db', value)); _.each(this.features, (feature) => { @@ -283,4 +302,9 @@ export class OsdFormComponent extends CdForm implements OnInit { this.previewButtonPanel.submitButton.loading = false; } } + + ngOnDestroy() { + this.osdService.selectedFormValues = _.cloneDeep(this.form); + this.osdService.isDeployementModeSimple = this.dataDeviceSelectionGroups?.devices?.length === 0; + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.spec.ts index fc02e9bdeeefb..3b9e62c482998 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.spec.ts @@ -53,6 +53,8 @@ describe('LoginComponent', () => { component.login(); expect(routerNavigateSpy).toHaveBeenCalledTimes(1); - expect(routerNavigateSpy).toHaveBeenCalledWith(['/expand-cluster']); + expect(routerNavigateSpy).toHaveBeenCalledWith(['/expand-cluster'], { + queryParams: { welcome: true } + }); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.ts index 57039c0f6d0c4..8bfda90c9e71b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.ts @@ -71,7 +71,11 @@ export class LoginComponent implements OnInit { if (!this.postInstalled && this.route.snapshot.queryParams['returnUrl'] === '/dashboard') { url = '/expand-cluster'; } - this.router.navigate([url]); + if (url == '/expand-cluster') { + this.router.navigate([url], { queryParams: { welcome: true } }); + } else { + this.router.navigate([url]); + } }); } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.ts index 34461bf631493..f2ed4d7cc9e76 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.ts @@ -11,6 +11,7 @@ import { DeploymentOptions } from '../models/osd-deployment-options'; import { OsdSettings } from '../models/osd-settings'; import { SmartDataResponseV1 } from '../models/smart'; import { DeviceService } from '../services/device.service'; +import { CdFormGroup } from '../forms/cd-form-group'; @Injectable({ providedIn: 'root' @@ -20,6 +21,8 @@ export class OsdService { private uiPath = 'ui-api/osd'; osdDevices: InventoryDeviceType[] = []; + selectedFormValues: CdFormGroup; + isDeployementModeSimple: boolean = true; osdRecvSpeedModalPriorities = { KNOWN_PRIORITIES: [ diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts index 185c778bc1bdf..24915507fd9ae 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts @@ -148,6 +148,7 @@ export class ActionLabelsI18n { DISCONNECT: string; RECONNECT: string; AUTHORIZE: string; + EXPAND_CLUSTER: string; constructor() { /* Create a new item */ @@ -234,6 +235,7 @@ export class ActionLabelsI18n { this.CONNECT = $localize`Connect`; this.DISCONNECT = $localize`Disconnect`; this.RECONNECT = $localize`Reconnect`; + this.EXPAND_CLUSTER = $localize`Expand Cluster`; } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts index be454076b8621..8f90a51cf8673 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts @@ -27,7 +27,7 @@ export enum Icons { right = 'fa fa-arrow-right', // Mark in down = 'fa fa-arrow-down', // Mark Down erase = 'fa fa-eraser', // Purge color: bd.$white; - + expand = 'fa fa-expand', // Expand cluster user = 'fa fa-user', // User, Initiators users = 'fa fa-users', // Users, Groups share = 'fa fa-share-alt', // share