Skip to content

Commit

Permalink
mgr/dashboard Adds multiple selection to osd table
Browse files Browse the repository at this point in the history
Fixes: https://tracker.ceph.com/issues/38091

Signed-off-by: Pooja <pooja.gautam@ts.fujitsu.com>
  • Loading branch information
Pooja committed Sep 3, 2019
1 parent db98646 commit 4bdeadf
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 67 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<tabset>
<tab i18n-heading
heading="OSDs List">

<cd-table [data]="osds"
(fetchData)="getOsdList()"
[columns]="columns"
selectionType="single"
selectionType="multi"
(updateSelection)="updateSelection($event)"
[updateSelectionOnRefresh]="'never'">

<div class="table-actions btn-toolbar">
<cd-helper i18n>Press and hold control button to select multiple rows to execute a common action on.</cd-helper>
<cd-table-actions [permission]="permissions.osd"
[selection]="selection"
class="btn-group"
Expand Down Expand Up @@ -57,7 +60,7 @@

<ng-template #markOsdConfirmationTpl
let-markActionDescription="markActionDescription">
<ng-container i18n><strong>OSD {{ selection.first().id }}</strong> will be marked
<ng-container i18n><strong>OSD(s) {{ getSelectedIds() | list }}</strong> will be marked
<strong>{{ markActionDescription }}</strong> if you proceed.</ng-container>
</ng-template>

Expand All @@ -66,8 +69,8 @@
let-actionDescription="actionDescription">
<div *ngIf="!safeToDestroyResult['is_safe_to_destroy']"
class="danger">
<cd-warning-panel i18n>The OSD is not safe to destroy!</cd-warning-panel>
<cd-warning-panel i18n>The OSD(s) is/are not safe to destroy!</cd-warning-panel>
</div>
<ng-container i18n><strong>OSD {{ selection.first().id }}</strong> will be
<ng-container i18n><strong>OSD {{ getSelectedIds() | list }}</strong> will be
<strong>{{ actionDescription }}</strong> if you proceed.</ng-container>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';

import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { Observable } from 'rxjs';

Expand All @@ -16,6 +17,7 @@ import { CdTableColumn } from '../../../../shared/models/cd-table-column';
import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { Permissions } from '../../../../shared/models/permissions';
import { DimlessBinaryPipe } from '../../../../shared/pipes/dimless-binary.pipe';
import { ListPipe } from '../../../../shared/pipes/list.pipe';
import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
import { OsdFlagsModalComponent } from '../osd-flags-modal/osd-flags-modal.component';
import { OsdPgScrubModalComponent } from '../osd-pg-scrub-modal/osd-pg-scrub-modal.component';
Expand Down Expand Up @@ -51,8 +53,8 @@ export class OsdListComponent implements OnInit {
clusterWideActions: CdTableAction[];
icons = Icons;

osds = [];
selection = new CdTableSelection();
osds = [];

protected static collectStates(osd) {
return [osd['in'] ? 'in' : 'out', osd['up'] ? 'up' : 'down'];
Expand All @@ -62,6 +64,7 @@ export class OsdListComponent implements OnInit {
private authStorageService: AuthStorageService,
private osdService: OsdService,
private dimlessBinaryPipe: DimlessBinaryPipe,
private listPipe: ListPipe,
private modalService: BsModalService,
private i18n: I18n,
public actionLabels: ActionLabelsI18n
Expand All @@ -86,7 +89,7 @@ export class OsdListComponent implements OnInit {
name: this.actionLabels.REWEIGHT,
permission: 'update',
click: () => this.reweight(),
disable: () => !this.hasOsdSelected,
disable: () => !this.hasOsdSelected || !this.selection.hasSingleSelection,
icon: Icons.reweight
},
{
Expand Down Expand Up @@ -206,11 +209,17 @@ export class OsdListComponent implements OnInit {
];
}

getSelectedIds() {
return this.selection.selected.map((row) => row.id);
}

get hasOsdSelected() {
const validOsds = [];
if (this.selection.hasSelection) {
const osdId = this.selection.first().id;
const osd = this.osds.filter((o) => o.id === osdId).pop();
return !!osd;
for (const osdId of this.getSelectedIds()) {
validOsds.push(this.osds.filter((o) => o.id === osdId).pop());
}
return !!validOsds;
}
return false;
}
Expand All @@ -228,23 +237,31 @@ export class OsdListComponent implements OnInit {
return true;
}

const osdId = this.selection.first().id;
const osd = this.osds.filter((o) => o.id === osdId).pop();
const validOsds = [];
if (this.selection.hasSelection) {
for (const osdId of this.getSelectedIds()) {
validOsds.push(this.osds.filter((o) => o.id === osdId).pop());
}
}

if (!osd) {
if (!this.hasOsdSelected) {
return true;
}

if (!validOsds) {
// `osd` is undefined if the selected OSD has been removed.
return true;
}

switch (state) {
case 'in':
return osd.in === 1;
return validOsds.some((osd) => osd.in === 1);
case 'out':
return osd.in !== 1;
return validOsds.some((osd) => osd.in !== 1);
case 'down':
return osd.up !== 1;
return validOsds.some((osd) => osd.up !== 1);
case 'up':
return osd.up === 1;
return validOsds.some((osd) => osd.up === 1);
}
}

Expand All @@ -267,7 +284,7 @@ export class OsdListComponent implements OnInit {
}

const initialState = {
selected: this.tableComponent.selection.selected,
selected: this.getSelectedIds(),
deep: deep
};

Expand All @@ -288,9 +305,9 @@ export class OsdListComponent implements OnInit {
markActionDescription: markAction
},
onSubmit: () => {
onSubmit
.call(this.osdService, this.selection.first().id)
.subscribe(() => this.bsModalRef.hide());
for (const id of this.getSelectedIds()) {
onSubmit.call(this.osdService, id).subscribe(() => this.bsModalRef.hide());
}
}
}
});
Expand All @@ -312,24 +329,26 @@ export class OsdListComponent implements OnInit {
templateItemDescription: string,
action: (id: number) => Observable<any>
): void {
this.osdService.safeToDestroy(this.selection.first().id).subscribe((result) => {
const modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
initialState: {
actionDescription: actionDescription,
itemDescription: itemDescription,
bodyTemplate: this.criticalConfirmationTpl,
bodyContext: {
result: result,
actionDescription: templateItemDescription
},
submitAction: () => {
action
.call(this.osdService, this.selection.first().id)
.subscribe(() => modalRef.hide());
this.osdService
.safeToDestroy(this.listPipe.transform(this.getSelectedIds()))
.subscribe((result) => {
const modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
initialState: {
actionDescription: actionDescription,
itemDescription: itemDescription,
bodyTemplate: this.criticalConfirmationTpl,
bodyContext: {
result: result,
actionDescription: templateItemDescription
},
submitAction: () => {
for (const id of this.getSelectedIds()) {
action.call(this.osdService, id).subscribe(() => modalRef.hide());
}
}
}
}
});
});
});
}

configureQosParamsAction() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<cd-modal [modalRef]="bsModalRef">
<ng-container class="modal-title"
i18n>Reweight OSD</ng-container>
i18n>Reweight OSD: {{ osdId }}</ng-container>

<ng-container class="modal-content">
<form [formGroup]="reweightForm">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@
[formGroup]="scrubForm"
novalidate>
<div class="modal-body">
<div *ngIf="selected.length === 1">
<p i18n>You are about to apply a {deep, select, 1 {deep }}scrub to
the OSD <strong>{{ selected[0].id }}</strong>.</p>
</div>
<p i18n>You are about to apply a {deep, select, 1 {deep }}scrub to
the OSD(s): <strong>{{ selected | list }}</strong>.</p>
</div>

<div class="modal-footer">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { BsModalRef } from 'ngx-bootstrap/modal';

import { OsdService } from '../../../../shared/api/osd.service';
import { NotificationType } from '../../../../shared/enum/notification-type.enum';
import { ListPipe } from '../../../../shared/pipes/list.pipe';
import { NotificationService } from '../../../../shared/services/notification.service';

@Component({
Expand All @@ -15,40 +16,40 @@ import { NotificationService } from '../../../../shared/services/notification.se
})
export class OsdScrubModalComponent implements OnInit {
deep: boolean;
selected = [];
scrubForm: FormGroup;
selected = [];

constructor(
public bsModalRef: BsModalRef,
private osdService: OsdService,
private notificationService: NotificationService,
private i18n: I18n
private i18n: I18n,
private listPipe: ListPipe
) {}

ngOnInit() {
this.scrubForm = new FormGroup({});
}

scrub() {
const id = this.selected[0].id;

this.osdService.scrub(id, this.deep).subscribe(
() => {
const operation = this.deep ? 'Deep scrub' : 'Scrub';

this.notificationService.show(
NotificationType.success,
this.i18n('{{operation}} was initialized in the following OSD: {{id}}', {
operation: operation,
id: id
})
);

this.bsModalRef.hide();
},
() => {
this.bsModalRef.hide();
}
);
for (const id of this.selected) {
this.osdService.scrub(id, this.deep).subscribe(
() => {
const operation = this.deep ? 'Deep scrub' : 'Scrub';

this.notificationService.show(
NotificationType.success,
this.i18n('{{operation}} was initialized in the following OSD(s): {{id}}', {
operation: operation,
id: this.listPipe.transform(this.selected)
})
);
this.bsModalRef.hide();
},
() => {
this.bsModalRef.hide();
}
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ describe('OsdService', () => {
});

it('should return if it is safe to destroy an OSD', () => {
service.safeToDestroy(1).subscribe();
const req = httpTesting.expectOne('api/osd/1/safe_to_destroy');
service.safeToDestroy('0,1').subscribe();
const req = httpTesting.expectOne('api/osd/0,1/safe_to_destroy');
expect(req.request.method).toBe('GET');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ export class OsdService {
return this.http.post(`${this.path}/${id}/destroy`, null);
}

safeToDestroy(id: number) {
safeToDestroy(ids: string) {
interface SafeToDestroyResponse {
'safe-to-destroy': boolean;
message?: string;
}
return this.http.get<SafeToDestroyResponse>(`${this.path}/${id}/safe_to_destroy`);
return this.http.get<SafeToDestroyResponse>(`${this.path}/${ids}/safe_to_destroy`);
}
}

0 comments on commit 4bdeadf

Please sign in to comment.