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';
ServiceFormComponent,
OsdFlagsIndivModalComponent,
PlacementPipe,
- CreateClusterComponent
+ CreateClusterComponent,
+ CreateClusterReviewComponent
],
providers: [NgbActiveModal]
})
--- /dev/null
+<div class="row">
+ <div class="col-lg-4">
+ <fieldset>
+ <legend class="cd-header"
+ i18n>Cluster Resources</legend>
+ <table class="table table-striped">
+ <tr>
+ <td i18n
+ class="bold">Hosts</td>
+ <td>{{ hostsCount }}</td>
+ </tr>
+ </table>
+ </fieldset>
+ </div>
+
+ <div class="col-lg-8">
+ <legend i18n
+ class="cd-header">Hosts by Label</legend>
+ <cd-table [data]="hostsByLabel['data']"
+ [columns]="hostsByLabel['columns']"
+ [toolHeader]="false">
+ </cd-table>
+
+ <legend i18n
+ class="cd-header">Host Details</legend>
+ <cd-table [data]="hostsDetails['data']"
+ [columns]="hostsDetails['columns']"
+ [toolHeader]="false">
+ </cd-table>
+ </div>
+</div>
--- /dev/null
+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<CreateClusterReviewComponent>;
+ 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);
+ });
+ });
+});
--- /dev/null
+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<string> = 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];
+ });
+ }
+}
</div>
<div *ngSwitchCase="'2'"
class="ml-5">
- <h4 class="title"
- i18n>Review</h4>
- <br>
- <p>To be implemented</p>
+ <cd-create-cluster-review></cd-create-cluster-review>
</div>
</ng-container>
</div>
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', () => {