From: Volker Theile Date: Thu, 5 Nov 2020 15:12:51 +0000 (+0100) Subject: mgr/dashboard: display placement column in service table X-Git-Tag: v16.1.0~446^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=1c722aa89ec1efbf5cc76ea968a1f9a725a86e57;p=ceph.git mgr/dashboard: display placement column in service table Fixes: https://tracker.ceph.com/issues/44404 Signed-off-by: Volker Theile --- 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 62464cadefe9..916f831f8bf3 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 @@ -47,6 +47,7 @@ import { RulesListComponent } from './prometheus/rules-list/rules-list.component import { SilenceFormComponent } from './prometheus/silence-form/silence-form.component'; import { SilenceListComponent } from './prometheus/silence-list/silence-list.component'; import { SilenceMatcherModalComponent } from './prometheus/silence-matcher-modal/silence-matcher-modal.component'; +import { PlacementPipe } from './services/placement.pipe'; import { ServiceDaemonListComponent } from './services/service-daemon-list/service-daemon-list.component'; import { ServiceDetailsComponent } from './services/service-details/service-details.component'; import { ServiceFormComponent } from './services/service-form/service-form.component'; @@ -108,7 +109,8 @@ import { TelemetryComponent } from './telemetry/telemetry.component'; TelemetryComponent, PrometheusTabsComponent, ServiceFormComponent, - OsdFlagsIndivModalComponent + OsdFlagsIndivModalComponent, + PlacementPipe ] }) export class ClusterModule {} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/placement.pipe.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/placement.pipe.spec.ts new file mode 100644 index 000000000000..6aef3c364cbe --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/placement.pipe.spec.ts @@ -0,0 +1,78 @@ +import { PlacementPipe } from './placement.pipe'; + +describe('PlacementPipe', () => { + const pipe = new PlacementPipe(); + + it('create an instance', () => { + expect(pipe).toBeTruthy(); + }); + + it('transforms to no spec', () => { + expect(pipe.transform(undefined)).toBe('no spec'); + }); + + it('transforms to unmanaged', () => { + expect(pipe.transform({ unmanaged: true })).toBe('unmanaged'); + }); + + it('transforms placement (1)', () => { + expect( + pipe.transform({ + placement: { + hosts: ['mon0'] + } + }) + ).toBe('mon0'); + }); + + it('transforms placement (2)', () => { + expect( + pipe.transform({ + placement: { + hosts: ['mon0', 'mgr0'] + } + }) + ).toBe('mon0;mgr0'); + }); + + it('transforms placement (3)', () => { + expect( + pipe.transform({ + placement: { + count: 1 + } + }) + ).toBe('count:1'); + }); + + it('transforms placement (4)', () => { + expect( + pipe.transform({ + placement: { + label: 'foo' + } + }) + ).toBe('label:foo'); + }); + + it('transforms placement (5)', () => { + expect( + pipe.transform({ + placement: { + host_pattern: '*' + } + }) + ).toBe('*'); + }); + + it('transforms placement (6)', () => { + expect( + pipe.transform({ + placement: { + count: 2, + hosts: ['mon0', 'mgr0'] + } + }) + ).toBe('mon0;mgr0;count:2'); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/placement.pipe.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/placement.pipe.ts new file mode 100644 index 000000000000..bd461bceb2f9 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/placement.pipe.ts @@ -0,0 +1,41 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +import _ from 'lodash'; + +@Pipe({ + name: 'placement' +}) +export class PlacementPipe implements PipeTransform { + /** + * Convert the placement configuration into human readable form. + * The output is equal to the column 'PLACEMENT' in 'ceph orch ls'. + * @param serviceSpec The service specification to process. + * @return The placement configuration as human readable string. + */ + transform(serviceSpec: object | undefined): string { + if (_.isUndefined(serviceSpec)) { + return $localize`no spec`; + } + if (_.get(serviceSpec, 'unmanaged', false)) { + return $localize`unmanaged`; + } + const kv: Array = []; + const hosts: Array = _.get(serviceSpec, 'placement.hosts'); + const count: number = _.get(serviceSpec, 'placement.count'); + const label: string = _.get(serviceSpec, 'placement.label'); + const hostPattern: string = _.get(serviceSpec, 'placement.host_pattern'); + if (_.isArray(hosts)) { + kv.push(...hosts); + } + if (_.isNumber(count)) { + kv.push($localize`count:${count}`); + } + if (_.isString(label)) { + kv.push($localize`label:${label}`); + } + if (_.isString(hostPattern)) { + kv.push(...hostPattern); + } + return kv.join(';'); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.spec.ts index c3631d50e530..a6e56ce2282d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.spec.ts @@ -82,7 +82,10 @@ describe('ServicesComponent', () => { it('should have columns that are sortable', () => { expect( component.columns + // Filter the 'Expand/Collapse Row' column. .filter((column) => !(column.cellClass === 'cd-datatable-expand-collapse')) + // Filter the 'Placement' column. + .filter((column) => !(column.prop === '')) .every((column) => Boolean(column.prop)) ).toBeTruthy(); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.ts index 8306f7ddf0fd..db4f51c56d56 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.ts @@ -24,6 +24,7 @@ import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; import { ModalService } from '~/app/shared/services/modal.service'; import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; import { URLBuilderService } from '~/app/shared/services/url-builder.service'; +import { PlacementPipe } from './placement.pipe'; const BASE_URL = 'services'; @@ -108,6 +109,12 @@ export class ServicesComponent extends ListWithDetails implements OnChanges, OnI length: 12 } }, + { + name: $localize`Placement`, + prop: '', + pipe: new PlacementPipe(), + flexGrow: 1 + }, { name: $localize`Running`, prop: 'status.running',