From 2e31ba54273732db5d93a843d22345b676fcecf7 Mon Sep 17 00:00:00 2001 From: Avan Thakkar Date: Fri, 9 Jul 2021 18:57:35 +0530 Subject: [PATCH] mgr/dashboard: Review Section for the Create Cluster Workflow Fixes: https://tracker.ceph.com/issues/50566 Signed-off-by: Avan Thakkar --- .../src/app/ceph/cluster/cluster.module.ts | 4 +- .../create-cluster-review.component.html | 31 +++++++ .../create-cluster-review.component.scss | 0 .../create-cluster-review.component.spec.ts | 61 +++++++++++++ .../create-cluster-review.component.ts | 86 +++++++++++++++++++ .../create-cluster.component.html | 5 +- .../create-cluster.component.spec.ts | 4 +- 7 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.ts diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts index 185c34b27504d..610bb79baebc7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts @@ -22,6 +22,7 @@ import { CephSharedModule } from '../shared/ceph-shared.module'; import { ConfigurationDetailsComponent } from './configuration/configuration-details/configuration-details.component'; import { ConfigurationFormComponent } from './configuration/configuration-form/configuration-form.component'; import { ConfigurationComponent } from './configuration/configuration.component'; +import { CreateClusterReviewComponent } from './create-cluster/create-cluster-review.component'; import { CreateClusterComponent } from './create-cluster/create-cluster.component'; import { CrushmapComponent } from './crushmap/crushmap.component'; import { HostDetailsComponent } from './hosts/host-details/host-details.component'; @@ -114,7 +115,8 @@ import { TelemetryComponent } from './telemetry/telemetry.component'; ServiceFormComponent, OsdFlagsIndivModalComponent, PlacementPipe, - CreateClusterComponent + CreateClusterComponent, + CreateClusterReviewComponent ], providers: [NgbActiveModal] }) 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 new file mode 100644 index 0000000000000..fa010fdcd8ede --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.html @@ -0,0 +1,31 @@ +
+
+
+ Cluster Resources + + + + + +
Hosts{{ hostsCount }}
+
+
+ +
+ Hosts by Label + + + + Host Details + + +
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.scss new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.spec.ts new file mode 100644 index 0000000000000..e823932c09732 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.spec.ts @@ -0,0 +1,61 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import _ from 'lodash'; +import { of } from 'rxjs'; + +import { CephModule } from '~/app/ceph/ceph.module'; +import { CoreModule } from '~/app/core/core.module'; +import { HostService } from '~/app/shared/api/host.service'; +import { SharedModule } from '~/app/shared/shared.module'; +import { configureTestBed } from '~/testing/unit-test-helper'; +import { CreateClusterReviewComponent } from './create-cluster-review.component'; + +describe('CreateClusterReviewComponent', () => { + let component: CreateClusterReviewComponent; + let fixture: ComponentFixture; + let hostService: HostService; + let hostListSpy: jasmine.Spy; + + configureTestBed({ + imports: [HttpClientTestingModule, SharedModule, CoreModule, CephModule] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateClusterReviewComponent); + component = fixture.componentInstance; + hostService = TestBed.inject(HostService); + hostListSpy = spyOn(hostService, 'list'); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should verify host metadata calculations', () => { + const hostnames = ['ceph.test1', 'ceph.test2']; + const payload = [ + { + hostname: hostnames[0], + ceph_version: 'ceph version Development', + labels: ['foo', 'bar'] + }, + { + hostname: hostnames[1], + ceph_version: 'ceph version Development', + labels: ['foo1', 'bar1'] + } + ]; + hostListSpy.and.callFake(() => of(payload)); + fixture.detectChanges(); + expect(hostListSpy).toHaveBeenCalled(); + + expect(component.hostsCount).toBe(2); + expect(component.uniqueLabels.size).toBe(4); + const labels = ['foo', 'bar', 'foo1', 'bar1']; + + labels.forEach((label) => { + expect(component.labelOccurrences[label]).toBe(1); + }); + }); +}); 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 new file mode 100644 index 0000000000000..c78e8f910ece6 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster-review.component.ts @@ -0,0 +1,86 @@ +import { Component, OnInit } from '@angular/core'; + +import _ from 'lodash'; + +import { HostService } from '~/app/shared/api/host.service'; +import { CellTemplate } from '~/app/shared/enum/cell-template.enum'; + +@Component({ + selector: 'cd-create-cluster-review', + templateUrl: './create-cluster-review.component.html', + styleUrls: ['./create-cluster-review.component.scss'] +}) +export class CreateClusterReviewComponent implements OnInit { + hosts: object[] = []; + hostsDetails: object; + hostsByLabel: object; + hostsCount: number; + labelOccurrences = {}; + hostsCountPerLabel: object[] = []; + uniqueLabels: Set = new Set(); + + constructor(private hostService: HostService) {} + + ngOnInit() { + this.hostsDetails = { + columns: [ + { + prop: 'hostname', + name: $localize`Host Name`, + flexGrow: 2 + }, + { + name: $localize`Labels`, + prop: 'labels', + flexGrow: 1, + cellTransformation: CellTemplate.badge, + customTemplateConfig: { + class: 'badge-dark' + } + } + ] + }; + + this.hostsByLabel = { + columns: [ + { + prop: 'label', + name: $localize`Labels`, + flexGrow: 1, + cellTransformation: CellTemplate.badge, + customTemplateConfig: { + class: 'badge-dark' + } + }, + { + name: $localize`Number of Hosts`, + prop: 'hosts_per_label', + flexGrow: 1 + } + ] + }; + + this.hostService.list().subscribe((resp: object[]) => { + this.hosts = resp; + this.hostsCount = this.hosts.length; + + _.forEach(this.hosts, (hostKey) => { + const labels = hostKey['labels']; + _.forEach(labels, (label) => { + this.labelOccurrences[label] = (this.labelOccurrences[label] || 0) + 1; + this.uniqueLabels.add(label); + }); + }); + + this.uniqueLabels.forEach((label) => { + this.hostsCountPerLabel.push({ + label: label, + hosts_per_label: this.labelOccurrences[label] + }); + }); + + this.hostsByLabel['data'] = [...this.hostsCountPerLabel]; + this.hostsDetails['data'] = [...this.hosts]; + }); + } +} 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 fb006ec1beb10..38887328ec3ab 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 @@ -44,10 +44,7 @@
-

Review

-
-

To be implemented

+
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 1ebdfb3a59d2f..b0564703840d4 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 @@ -90,10 +90,8 @@ describe('CreateClusterComponent', () => { fixture.detectChanges(); component.onNextStep(); fixture.detectChanges(); - const heading = fixture.debugElement.query(By.css('.title')).nativeElement; expect(wizardStepServiceSpy).toHaveBeenCalledTimes(1); - expect(hostServiceSpy).toBeCalledTimes(1); - expect(heading.innerHTML).toBe('Review'); + expect(hostServiceSpy).toBeCalledTimes(2); }); it('should show the button labels correctly', () => { -- 2.39.5