import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
+import { Observable, Subscriber } from 'rxjs';
import { RbdService } from '~/app/shared/api/rbd.service';
import { ListWithDetails } from '~/app/shared/classes/list-with-details.class';
-import { TableStatusViewCache } from '~/app/shared/classes/table-status-view-cache';
+import { TableStatus } from '~/app/shared/classes/table-status';
import { ConfirmationModalComponent } from '~/app/shared/components/confirmation-modal/confirmation-modal.component';
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { TableComponent } from '~/app/shared/datatable/table/table.component';
import { Icons } from '~/app/shared/enum/icons.enum';
-import { ViewCacheStatus } from '~/app/shared/enum/view-cache-status.enum';
import { CdTableAction } from '~/app/shared/models/cd-table-action';
import { CdTableColumn } from '~/app/shared/models/cd-table-column';
+import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
import { FinishedTask } from '~/app/shared/models/finished-task';
import { ImageSpec } from '~/app/shared/models/image-spec';
import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe';
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
+import { CdTableServerSideService } from '~/app/shared/services/cd-table-server-side.service';
import { ModalService } from '~/app/shared/services/modal.service';
import { TaskListService } from '~/app/shared/services/task-list.service';
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
import { URLBuilderService } from '~/app/shared/services/url-builder.service';
+import { RbdFormEditRequestModel } from '../rbd-form/rbd-form-edit-request.model';
import { RbdParentModel } from '../rbd-form/rbd-parent.model';
import { RbdTrashMoveModalComponent } from '../rbd-trash-move-modal/rbd-trash-move-modal.component';
import { RBDImageFormat, RbdModel } from './rbd-model';
parentTpl: TemplateRef<any>;
@ViewChild('nameTpl')
nameTpl: TemplateRef<any>;
+ @ViewChild('mirroringTpl', { static: true })
+ mirroringTpl: TemplateRef<any>;
@ViewChild('flattenTpl', { static: true })
flattenTpl: TemplateRef<any>;
@ViewChild('deleteTpl', { static: true })
images: any;
columns: CdTableColumn[];
retries: number;
- tableStatus = new TableStatusViewCache();
+ tableStatus = new TableStatus('light');
selection = new CdTableSelection();
icons = Icons;
-
+ count = 0;
+ private tableContext: CdTableFetchDataContext = null;
modalRef: NgbModalRef;
builders = {
metadata['dest_image_name']
)
};
+ remove_scheduling: boolean;
private createRbdFromTaskImageSpec(imageSpecStr: string): RbdModel {
const imageSpec = ImageSpec.fromString(imageSpecStr);
name: this.actionLabels.DELETE,
disable: (selection: CdTableSelection) => this.getDeleteDisableDesc(selection)
};
+ const resyncAction: CdTableAction = {
+ permission: 'update',
+ icon: Icons.refresh,
+ click: () => this.resyncRbdModal(),
+ name: this.actionLabels.RESYNC,
+ disable: (selection: CdTableSelection) => this.getResyncDisableDesc(selection)
+ };
const copyAction: CdTableAction = {
permission: 'create',
canBePrimary: (selection: CdTableSelection) => selection.hasSingleSelection,
this.getInvalidNameDisable(selection) ||
selection.first().image_format === RBDImageFormat.V1
};
+ const removeSchedulingAction: CdTableAction = {
+ permission: 'update',
+ icon: Icons.edit,
+ click: () => this.removeSchedulingModal(),
+ name: this.actionLabels.REMOVE_SCHEDULING,
+ disable: (selection: CdTableSelection) =>
+ this.getRemovingStatusDesc(selection) ||
+ this.getInvalidNameDisable(selection) ||
+ selection.first().schedule_info === undefined
+ };
+ const promoteAction: CdTableAction = {
+ permission: 'update',
+ icon: Icons.edit,
+ click: () => this.actionPrimary(true),
+ name: this.actionLabels.PROMOTE,
+ visible: () => this.selection.first() != null && !this.selection.first().primary
+ };
+ const demoteAction: CdTableAction = {
+ permission: 'update',
+ icon: Icons.edit,
+ click: () => this.actionPrimary(false),
+ name: this.actionLabels.DEMOTE,
+ visible: () => this.selection.first() != null && this.selection.first().primary
+ };
this.tableActions = [
addAction,
editAction,
copyAction,
flattenAction,
+ resyncAction,
deleteAction,
- moveAction
+ moveAction,
+ removeSchedulingAction,
+ promoteAction,
+ demoteAction
];
}
prop: 'size',
flexGrow: 1,
cellClass: 'text-right',
+ sortable: false,
pipe: this.dimlessBinaryPipe
},
{
prop: 'num_objs',
flexGrow: 1,
cellClass: 'text-right',
+ sortable: false,
pipe: this.dimlessPipe
},
{
prop: 'obj_size',
flexGrow: 1,
cellClass: 'text-right',
+ sortable: false,
pipe: this.dimlessBinaryPipe
},
{
cellClass: 'text-center',
flexGrow: 1,
pipe: this.dimlessBinaryPipe,
+ sortable: false,
cellTemplate: this.provisionedNotAvailableTooltipTpl
},
{
cellClass: 'text-center',
flexGrow: 1,
pipe: this.dimlessBinaryPipe,
+ sortable: false,
cellTemplate: this.totalProvisionedNotAvailableTooltipTpl
},
{
name: $localize`Parent`,
prop: 'parent',
flexGrow: 2,
+ sortable: false,
cellTemplate: this.parentTpl
+ },
+ {
+ name: $localize`Mirroring`,
+ prop: 'mirror_mode',
+ flexGrow: 3,
+ sortable: false,
+ cellTemplate: this.mirroringTpl
}
];
};
this.taskListService.init(
- () => this.rbdService.list(),
+ (context) => this.getRbdImages(context),
(resp) => this.prepareResponse(resp),
(images) => (this.images = images),
() => this.onFetchError(),
onFetchError() {
this.table.reset(); // Disable loading indicator.
- this.tableStatus = new TableStatusViewCache(ViewCacheStatus.ValueException);
+ this.tableStatus = new TableStatus('danger');
+ }
+
+ getRbdImages(context: CdTableFetchDataContext) {
+ if (context !== null) {
+ this.tableContext = context;
+ }
+ if (this.tableContext == null) {
+ this.tableContext = new CdTableFetchDataContext(() => undefined);
+ }
+ return this.rbdService.list(this.tableContext?.toParams());
}
prepareResponse(resp: any[]): any[] {
let images: any[] = [];
- const viewCacheStatusMap = {};
resp.forEach((pool) => {
- if (_.isUndefined(viewCacheStatusMap[pool.status])) {
- viewCacheStatusMap[pool.status] = [];
- }
- viewCacheStatusMap[pool.status].push(pool.pool_name);
images = images.concat(pool.value);
});
- let status: number;
- if (viewCacheStatusMap[ViewCacheStatus.ValueException]) {
- status = ViewCacheStatus.ValueException;
- } else if (viewCacheStatusMap[ViewCacheStatus.ValueStale]) {
- status = ViewCacheStatus.ValueStale;
- } else if (viewCacheStatusMap[ViewCacheStatus.ValueNone]) {
- status = ViewCacheStatus.ValueNone;
- }
-
- if (status) {
- const statusFor =
- (viewCacheStatusMap[status].length > 1 ? 'pools ' : 'pool ') +
- viewCacheStatusMap[status].join();
+ images.forEach((image) => {
+ if (image.schedule_info !== undefined) {
+ let scheduling: any[] = [];
+ const scheduleStatus = 'scheduled';
+ let nextSnapshotDate = +new Date(image.schedule_info.schedule_time);
+ const offset = new Date().getTimezoneOffset();
+ nextSnapshotDate = nextSnapshotDate + Math.abs(offset) * 60000;
+ scheduling.push(image.mirror_mode, scheduleStatus, nextSnapshotDate);
+ image.mirror_mode = scheduling;
+ scheduling = [];
+ }
+ });
- this.tableStatus = new TableStatusViewCache(status, statusFor);
+ if (images.length > 0) {
+ this.count = CdTableServerSideService.getCount(resp[0]);
} else {
- this.tableStatus = new TableStatusViewCache();
+ this.count = 0;
}
-
return images;
}
});
}
+ resyncRbdModal() {
+ const poolName = this.selection.first().pool_name;
+ const namespace = this.selection.first().namespace;
+ const imageName = this.selection.first().name;
+ const imageSpec = new ImageSpec(poolName, namespace, imageName);
+
+ this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
+ itemDescription: 'RBD',
+ itemNames: [imageSpec],
+ actionDescription: 'resync',
+ submitActionObservable: () =>
+ this.taskWrapper.wrapTaskAroundCall({
+ task: new FinishedTask('rbd/edit', {
+ image_spec: imageSpec.toString()
+ }),
+ call: this.rbdService.update(imageSpec, { resync: true })
+ })
+ });
+ }
+
trashRbdModal() {
const initialState = {
poolName: this.selection.first().pool_name,
this.modalRef = this.modalService.show(ConfirmationModalComponent, initialState);
}
+ editRequest() {
+ const request = new RbdFormEditRequestModel();
+ request.remove_scheduling = !request.remove_scheduling;
+ return request;
+ }
+
+ removeSchedulingModal() {
+ const imageName = this.selection.first().name;
+
+ const imageSpec = new ImageSpec(
+ this.selection.first().pool_name,
+ this.selection.first().namespace,
+ this.selection.first().name
+ );
+
+ this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
+ actionDescription: 'remove scheduling on',
+ itemDescription: $localize`image`,
+ itemNames: [`${imageName}`],
+ submitActionObservable: () =>
+ new Observable((observer: Subscriber<any>) => {
+ this.taskWrapper
+ .wrapTaskAroundCall({
+ task: new FinishedTask('rbd/edit', {
+ image_spec: imageSpec.toString()
+ }),
+ call: this.rbdService.update(imageSpec, this.editRequest())
+ })
+ .subscribe({
+ error: (resp) => observer.error(resp),
+ complete: () => {
+ this.modalRef.close();
+ }
+ });
+ })
+ });
+ }
+
+ actionPrimary(primary: boolean) {
+ const request = new RbdFormEditRequestModel();
+ request.primary = primary;
+ const imageSpec = new ImageSpec(
+ this.selection.first().pool_name,
+ this.selection.first().namespace,
+ this.selection.first().name
+ );
+ this.taskWrapper
+ .wrapTaskAroundCall({
+ task: new FinishedTask('rbd/edit', {
+ image_spec: imageSpec.toString()
+ }),
+ call: this.rbdService.update(imageSpec, request)
+ })
+ .subscribe();
+ }
+
hasSnapshots() {
const snapshots = this.selection.first()['snapshots'] || [];
return snapshots.length > 0;
return this.getInvalidNameDisable(selection) || this.hasClonedSnapshots(selection.first());
}
+ getResyncDisableDesc(selection: CdTableSelection): string | boolean {
+ const first = selection.first();
+
+ if (first && this.imageIsPrimary(first)) {
+ return $localize`Primary RBD images cannot be resynced`;
+ }
+
+ return this.getInvalidNameDisable(selection);
+ }
+
+ imageIsPrimary(image: object) {
+ return image['primary'];
+ }
getInvalidNameDisable(selection: CdTableSelection): string | boolean {
const first = selection.first();