Add frontend codes and unit tests.
Fixes: https://tracker.ceph.com/issues/43062
Signed-off-by: Kiefer Chang <kiefer.chang@suse.com>
</ng-template>
<ng-template #criticalConfirmationTpl
- let-safeToDestroyResult="result"
+ let-safeToPerform="safeToPerform"
+ let-message="message"
let-actionDescription="actionDescription">
- <div *ngIf="!safeToDestroyResult['is_safe_to_destroy']"
+ <div *ngIf="!safeToPerform"
class="danger">
<cd-alert-panel type="warning"
- i18n>The {selection.hasSingleSelection, select, 1 {OSD is} 0 {OSDs are}} not safe to destroy!</cd-alert-panel>
+ i18n>The {selection.hasSingleSelection, select, 1 {OSD is} 0 {OSDs are}} not safe to be {{ actionDescription }}! {{ message }}</cd-alert-panel>
</div>
<ng-container i18n><strong>OSD {{ getSelectedOsdIds() | join }}</strong> will be
<strong>{{ actionDescription }}</strong> if you proceed.</ng-container>
*/
const mockSafeToDestroy = () => {
spyOn(TestBed.get(OsdService), 'safeToDestroy').and.callFake(() =>
- of({ 'safe-to-destroy': true })
+ of({ is_safe_to_destroy: true })
+ );
+ };
+
+ const mockSafeToDelete = () => {
+ spyOn(TestBed.get(OsdService), 'safeToDelete').and.callFake(() =>
+ of({ is_safe_to_delete: true })
);
};
'Mark Down',
'Mark Lost',
'Purge',
- 'Destroy'
+ 'Destroy',
+ 'Delete'
],
primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Create' }
},
primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Create' }
},
'create,delete': {
- actions: ['Create', 'Mark Lost', 'Purge', 'Destroy'],
+ actions: ['Create', 'Mark Lost', 'Purge', 'Destroy', 'Delete'],
primary: {
multiple: 'Create',
executing: 'Mark Lost',
'Mark Down',
'Mark Lost',
'Purge',
- 'Destroy'
+ 'Destroy',
+ 'Delete'
],
primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Edit' }
},
primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Edit' }
},
delete: {
- actions: ['Mark Lost', 'Purge', 'Destroy'],
+ actions: ['Mark Lost', 'Purge', 'Destroy', 'Delete'],
primary: {
multiple: 'Mark Lost',
executing: 'Mark Lost',
expectOpensModal('Mark Lost', modalClass);
expectOpensModal('Purge', modalClass);
expectOpensModal('Destroy', modalClass);
+ mockSafeToDelete();
+ expectOpensModal('Delete', modalClass);
});
});
describe('tests if the correct methods are called on confirmation', () => {
const expectOsdServiceMethodCalled = (
actionName: string,
- osdServiceMethodName: 'markOut' | 'markIn' | 'markDown' | 'markLost' | 'purge' | 'destroy'
+ osdServiceMethodName:
+ | 'markOut'
+ | 'markIn'
+ | 'markDown'
+ | 'markLost'
+ | 'purge'
+ | 'destroy'
+ | 'delete'
): void => {
const osdServiceSpy = spyOn(osdService, osdServiceMethodName).and.callFake(() => EMPTY);
openActionModal(actionName);
expectOsdServiceMethodCalled('Mark Lost', 'markLost');
expectOsdServiceMethodCalled('Purge', 'purge');
expectOsdServiceMethodCalled('Destroy', 'destroy');
+ mockSafeToDelete();
+ expectOsdServiceMethodCalled('Delete', 'delete');
});
});
});
this.i18n('Mark'),
this.i18n('OSD lost'),
this.i18n('marked lost'),
+ (ids: number[]) => {
+ return this.osdService.safeToDestroy(JSON.stringify(ids));
+ },
+ 'is_safe_to_destroy',
this.osdService.markLost
),
disable: () => this.isNotSelectedOrInState('up'),
this.i18n('Purge'),
this.i18n('OSD'),
this.i18n('purged'),
- (id) => {
+ (ids: number[]) => {
+ return this.osdService.safeToDestroy(JSON.stringify(ids));
+ },
+ 'is_safe_to_destroy',
+ (id: number) => {
this.selection = new CdTableSelection();
return this.osdService.purge(id);
}
this.i18n('destroy'),
this.i18n('OSD'),
this.i18n('destroyed'),
- (id) => {
+ (ids: number[]) => {
+ return this.osdService.safeToDestroy(JSON.stringify(ids));
+ },
+ 'is_safe_to_destroy',
+ (id: number) => {
this.selection = new CdTableSelection();
return this.osdService.destroy(id);
}
),
disable: () => this.isNotSelectedOrInState('up'),
+ icon: Icons.destroyCircle
+ },
+ {
+ name: this.actionLabels.DELETE,
+ permission: 'delete',
+ click: () =>
+ this.showCriticalConfirmationModal(
+ this.i18n('delete'),
+ this.i18n('OSD'),
+ this.i18n('deleted'),
+ (ids: number[]) => {
+ return this.osdService.safeToDelete(JSON.stringify(ids));
+ },
+ 'is_safe_to_delete',
+ (id: number) => {
+ this.selection = new CdTableSelection();
+ return this.osdService.delete(id, true);
+ }
+ ),
+ disable: () => !this.hasOsdSelected,
icon: Icons.destroy
}
];
});
}
+ /**
+ * Perform check first and display a critical confirmation modal.
+ * @param {string} actionDescription name of the action.
+ * @param {string} itemDescription the item's name that the action operates on.
+ * @param {string} templateItemDescription the action name to be displayed in modal template.
+ * @param {Function} check the function is called to check if the action is safe.
+ * @param {string} checkKey the safe indicator's key in the check response.
+ * @param {Function} action the action function.
+ * @param {boolean} oneshot if true, action function is called with all items as parameter.
+ * Otherwise, multiple action functions with individual items are sent.
+ */
showCriticalConfirmationModal(
actionDescription: string,
itemDescription: string,
templateItemDescription: string,
- action: (id: number) => Observable<any>
+ check: (ids: number[]) => Observable<any>,
+ checkKey: string,
+ action: (id: number | number[]) => Observable<any>
): void {
- this.osdService.safeToDestroy(JSON.stringify(this.getSelectedOsdIds())).subscribe((result) => {
+ check(this.getSelectedOsdIds()).subscribe((result) => {
const modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
initialState: {
actionDescription: actionDescription,
itemDescription: itemDescription,
bodyTemplate: this.criticalConfirmationTpl,
bodyContext: {
- result: result,
+ safeToPerform: result[checkKey],
+ message: result.message,
actionDescription: templateItemDescription
},
submitAction: () => {
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { I18n } from '@ngx-translate/i18n-polyfill';
return this.http.post(`${this.path}/${id}/destroy`, null);
}
+ delete(id: number, force?: boolean) {
+ const options = force ? { params: new HttpParams().set('force', 'true') } : {};
+ options['observe'] = 'response';
+ return this.http.delete(`${this.path}/${id}`, options);
+ }
+
safeToDestroy(ids: string) {
interface SafeToDestroyResponse {
- 'safe-to-destroy': boolean;
+ is_safe_to_destroy: boolean;
message?: string;
}
return this.http.get<SafeToDestroyResponse>(`${this.path}/safe_to_destroy?ids=${ids}`);
}
+ safeToDelete(ids: string) {
+ interface SafeToDeleteResponse {
+ is_safe_to_delete: boolean;
+ message?: string;
+ }
+ return this.http.get<SafeToDeleteResponse>(`${this.path}/safe_to_delete?svc_ids=${ids}`);
+ }
+
getDevices(osdId: number) {
return this.http
.get<CdDevice[]>(`${this.path}/${osdId}/devices`)