mirror of
https://github.com/ceph/ceph
synced 2025-01-21 02:31:19 +00:00
mgr/dashboard_v2: Auto reload cd-table
Now every used cd-table with a valid "fetch" function will auto reload itself by default every 10 seconds. You can also identify the unique property of the row now, by default the unique identifier is "id". Signed-off-by: Stephan Müller <smueller@suse.com>
This commit is contained in:
parent
1f5051ab8a
commit
772609ffd4
@ -10,6 +10,7 @@
|
||||
|
||||
<legend i18n>Daemons</legend>
|
||||
<cd-table [data]="daemons"
|
||||
(fetchData)="refresh()"
|
||||
[columns]="daemonsColumns">
|
||||
</cd-table>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { CephShortVersionPipe } from '../../../shared/pipes/ceph-short-version.pipe';
|
||||
import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
|
||||
@ -12,13 +12,12 @@ import { TcmuIscsiService } from '../../../shared/services/tcmu-iscsi.service';
|
||||
templateUrl: './iscsi.component.html',
|
||||
styleUrls: ['./iscsi.component.scss']
|
||||
})
|
||||
export class IscsiComponent implements OnInit, OnDestroy {
|
||||
export class IscsiComponent {
|
||||
|
||||
daemons = [];
|
||||
daemonsColumns: any;
|
||||
images = [];
|
||||
imagesColumns: any;
|
||||
interval: any;
|
||||
|
||||
constructor(private tcmuIscsiService: TcmuIscsiService,
|
||||
cephShortVersionPipe: CephShortVersionPipe,
|
||||
@ -93,18 +92,6 @@ export class IscsiComponent implements OnInit, OnDestroy {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.refresh();
|
||||
|
||||
this.interval = setInterval(() => {
|
||||
this.refresh();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.tcmuIscsiService.tcmuiscsi().then((resp) => {
|
||||
this.daemons = resp.daemons;
|
||||
|
@ -16,6 +16,7 @@
|
||||
<cd-table [data]="daemons.data"
|
||||
columnMode="flex"
|
||||
[columns]="daemons.columns"
|
||||
[autoReload]="30000"
|
||||
(fetchData)="refresh()">
|
||||
</cd-table>
|
||||
</fieldset>
|
||||
@ -27,8 +28,9 @@
|
||||
|
||||
<cd-table [data]="pools.data"
|
||||
columnMode="flex"
|
||||
[columns]="pools.columns"
|
||||
(fetchData)="refresh()">
|
||||
[autoReload]="0"
|
||||
(fetchData)="refresh()"
|
||||
[columns]="pools.columns">
|
||||
</cd-table>
|
||||
</fieldset>
|
||||
</div>
|
||||
@ -42,22 +44,25 @@
|
||||
<tab heading="Issues" i18n-heading>
|
||||
<cd-table [data]="image_error.data"
|
||||
columnMode="flex"
|
||||
[columns]="image_error.columns"
|
||||
(fetchData)="refresh()">
|
||||
[autoReload]="0"
|
||||
(fetchData)="refresh()"
|
||||
[columns]="image_error.columns">
|
||||
</cd-table>
|
||||
</tab>
|
||||
<tab heading="Syncing" i18n-heading>
|
||||
<cd-table [data]="image_syncing.data"
|
||||
columnMode="flex"
|
||||
[columns]="image_syncing.columns"
|
||||
(fetchData)="refresh()">
|
||||
[autoReload]="0"
|
||||
(fetchData)="refresh()"
|
||||
[columns]="image_syncing.columns">
|
||||
</cd-table>
|
||||
</tab>
|
||||
<tab heading="Ready" i18n-heading>
|
||||
<cd-table [data]="image_ready.data"
|
||||
columnMode="flex"
|
||||
[columns]="image_ready.columns"
|
||||
(fetchData)="refresh()">
|
||||
[autoReload]="0"
|
||||
(fetchData)="refresh()"
|
||||
[columns]="image_ready.columns">
|
||||
</cd-table>
|
||||
</tab>
|
||||
</tabset>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
|
||||
import * as _ from 'lodash';
|
||||
|
||||
@ -12,14 +12,13 @@ import { RbdMirroringService } from '../../../shared/services/rbd-mirroring.serv
|
||||
templateUrl: './mirroring.component.html',
|
||||
styleUrls: ['./mirroring.component.scss']
|
||||
})
|
||||
export class MirroringComponent implements OnInit, OnDestroy {
|
||||
export class MirroringComponent implements OnInit {
|
||||
@ViewChild('healthTmpl') healthTmpl: TemplateRef<any>;
|
||||
@ViewChild('stateTmpl') stateTmpl: TemplateRef<any>;
|
||||
@ViewChild('syncTmpl') syncTmpl: TemplateRef<any>;
|
||||
@ViewChild('progressTmpl') progressTmpl: TemplateRef<any>;
|
||||
|
||||
contentData: any;
|
||||
interval: any;
|
||||
|
||||
status: ViewCacheStatus;
|
||||
daemons = {
|
||||
@ -122,14 +121,6 @@ export class MirroringComponent implements OnInit, OnDestroy {
|
||||
flexGrow: 1
|
||||
}
|
||||
];
|
||||
|
||||
setTimeout(() => {
|
||||
this.interval = this.refresh();
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
|
@ -5,7 +5,6 @@ import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
|
||||
import { CdTableColumn } from '../../../shared/models/cd-table-column';
|
||||
import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
|
||||
import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
|
||||
import { FormatterService } from '../../../shared/services/formatter.service';
|
||||
import { PoolService } from '../../../shared/services/pool.service';
|
||||
|
||||
@Component({
|
||||
@ -18,10 +17,8 @@ export class PoolDetailComponent implements OnInit, OnDestroy {
|
||||
images: any;
|
||||
columns: CdTableColumn[];
|
||||
retries: number;
|
||||
maxRetries = 5;
|
||||
routeParamsSubscribe: any;
|
||||
viewCacheStatus: ViewCacheStatus;
|
||||
interval: any;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@ -75,15 +72,10 @@ export class PoolDetailComponent implements OnInit, OnDestroy {
|
||||
this.images = [];
|
||||
this.retries = 0;
|
||||
});
|
||||
|
||||
this.interval = setInterval(() => {
|
||||
this.loadImages();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.routeParamsSubscribe.unsubscribe();
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
loadImages() {
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
<cd-table [data]="ranks.data"
|
||||
[columns]="ranks.columns"
|
||||
(fetchData)="refresh()"
|
||||
toolHeader="false">
|
||||
</cd-table>
|
||||
</fieldset>
|
||||
|
@ -3,7 +3,6 @@ import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
|
||||
import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
|
||||
import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
|
||||
import { CephfsService } from '../cephfs.service';
|
||||
@ -95,19 +94,10 @@ export class CephfsComponent implements OnInit, OnDestroy {
|
||||
this.pools.data = [];
|
||||
this.standbys = [];
|
||||
this.mdsCounters = {};
|
||||
|
||||
this.refresh();
|
||||
this.draw_chart();
|
||||
});
|
||||
|
||||
this.interval = setInterval(() => {
|
||||
this.refresh();
|
||||
this.draw_chart();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearInterval(this.interval);
|
||||
this.routeParamsSubscribe.unsubscribe();
|
||||
}
|
||||
|
||||
@ -123,6 +113,7 @@ export class CephfsComponent implements OnInit, OnDestroy {
|
||||
];
|
||||
this.name = data.cephfs.name;
|
||||
this.clientCount = data.cephfs.client_count;
|
||||
this.draw_chart();
|
||||
});
|
||||
}
|
||||
|
||||
@ -130,9 +121,6 @@ export class CephfsComponent implements OnInit, OnDestroy {
|
||||
this.cephfsService.getMdsCounters(this.id).subscribe(data => {
|
||||
const topChart = true;
|
||||
|
||||
const oldKeys = Object.keys(this.mdsCounters);
|
||||
const newKeys = Object.keys(data);
|
||||
|
||||
_.each(this.mdsCounters, (value, key) => {
|
||||
if (data[key] === undefined) {
|
||||
delete this.mdsCounters[key];
|
||||
@ -144,7 +132,7 @@ export class CephfsComponent implements OnInit, OnDestroy {
|
||||
const rhsData = this.delta_timeseries(mdsData[this.rhsCounter]);
|
||||
|
||||
if (this.mdsCounters[mdsName] === undefined) {
|
||||
const elem = {
|
||||
this.mdsCounters[mdsName] = {
|
||||
datasets: [
|
||||
{
|
||||
label: this.lhsCounter,
|
||||
@ -197,8 +185,6 @@ export class CephfsComponent implements OnInit, OnDestroy {
|
||||
},
|
||||
chartType: 'line'
|
||||
};
|
||||
|
||||
this.mdsCounters[mdsName] = elem;
|
||||
} else {
|
||||
this.mdsCounters[mdsName].datasets[0].data = lhsData;
|
||||
this.mdsCounters[mdsName].datasets[1].data = rhsData;
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
<cd-table [data]="clients.data"
|
||||
[columns]="clients.columns"
|
||||
(fetchData)="refresh()"
|
||||
[header]="false">
|
||||
</cd-table>
|
||||
</fieldset>
|
||||
|
@ -17,8 +17,6 @@ export class ClientsComponent implements OnInit, OnDestroy {
|
||||
clients: any;
|
||||
viewCacheStatus: ViewCacheStatus;
|
||||
|
||||
interval: any;
|
||||
|
||||
constructor(private route: ActivatedRoute, private cephfsService: CephfsService) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -42,17 +40,10 @@ export class ClientsComponent implements OnInit, OnDestroy {
|
||||
this.cephfsService.getCephfs(this.id).subscribe((data: any) => {
|
||||
this.name = data.cephfs.name;
|
||||
});
|
||||
|
||||
this.refresh();
|
||||
});
|
||||
|
||||
this.interval = setInterval(() => {
|
||||
this.refresh();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearInterval(this.interval);
|
||||
this.routeParamsSubscribe.unsubscribe();
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
|
||||
import { CdTableColumn } from '../../../shared/models/cd-table-column';
|
||||
import { CephShortVersionPipe } from '../../../shared/pipes/ceph-short-version.pipe';
|
||||
@ -9,11 +9,10 @@ import { HostService } from '../../../shared/services/host.service';
|
||||
templateUrl: './hosts.component.html',
|
||||
styleUrls: ['./hosts.component.scss']
|
||||
})
|
||||
export class HostsComponent implements OnInit, OnDestroy {
|
||||
export class HostsComponent implements OnInit {
|
||||
|
||||
columns: Array<CdTableColumn> = [];
|
||||
hosts: Array<object> = [];
|
||||
interval: any;
|
||||
isLoadingHosts = false;
|
||||
|
||||
@ViewChild('servicesTpl') public servicesTpl: TemplateRef<any>;
|
||||
@ -41,13 +40,6 @@ export class HostsComponent implements OnInit, OnDestroy {
|
||||
pipe: this.cephShortVersionPipe
|
||||
}
|
||||
];
|
||||
this.interval = setInterval(() => {
|
||||
this.getHosts();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
getHosts() {
|
||||
|
@ -64,6 +64,7 @@
|
||||
<legend i18n
|
||||
class="in-quorum">Not In Quorum</legend>
|
||||
<cd-table [data]="notInQuorum.data"
|
||||
(fetchData)="refresh()"
|
||||
[columns]="notInQuorum.columns">
|
||||
</cd-table>
|
||||
</fieldset>
|
||||
|
@ -1,6 +1,4 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { CellTemplate } from '../../../shared/enum/cell-template.enum';
|
||||
import { MonitorService } from '../monitor.service';
|
||||
@ -10,7 +8,7 @@ import { MonitorService } from '../monitor.service';
|
||||
templateUrl: './monitor.component.html',
|
||||
styleUrls: ['./monitor.component.scss']
|
||||
})
|
||||
export class MonitorComponent implements OnInit, OnDestroy {
|
||||
export class MonitorComponent {
|
||||
|
||||
mon_status: any;
|
||||
inQuorum: any;
|
||||
@ -22,9 +20,7 @@ export class MonitorComponent implements OnInit, OnDestroy {
|
||||
width: '50%'
|
||||
};
|
||||
|
||||
constructor(private monitorService: MonitorService) {}
|
||||
|
||||
ngOnInit() {
|
||||
constructor(private monitorService: MonitorService) {
|
||||
this.inQuorum = {
|
||||
columns: [
|
||||
{ prop: 'name', name: 'Name', cellTransformation: CellTemplate.routerLink },
|
||||
@ -47,16 +43,6 @@ export class MonitorComponent implements OnInit, OnDestroy {
|
||||
],
|
||||
data: []
|
||||
};
|
||||
|
||||
this.refresh();
|
||||
|
||||
this.interval = setInterval(() => {
|
||||
this.refresh();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
@ -6,14 +6,12 @@ import { ActivatedRoute } from '@angular/router';
|
||||
templateUrl: './performance-counter.component.html',
|
||||
styleUrls: ['./performance-counter.component.scss']
|
||||
})
|
||||
export class PerformanceCounterComponent implements OnInit, OnDestroy {
|
||||
export class PerformanceCounterComponent implements OnDestroy {
|
||||
serviceId: string;
|
||||
serviceType: string;
|
||||
routeParamsSubscribe: any;
|
||||
|
||||
constructor(private route: ActivatedRoute) { }
|
||||
|
||||
ngOnInit() {
|
||||
constructor(private route: ActivatedRoute) {
|
||||
this.routeParamsSubscribe = this.route.params.subscribe(
|
||||
(params: { type: string; id: string }) => {
|
||||
this.serviceId = params.id;
|
||||
|
@ -41,7 +41,7 @@
|
||||
|
||||
<!-- refresh button -->
|
||||
<div class="widget-toolbar tc_refreshBtn">
|
||||
<a (click)="reloadData()">
|
||||
<a (click)="refreshBtn()">
|
||||
<i class="fa fa-lg fa-refresh"></i>
|
||||
</a>
|
||||
</div>
|
||||
@ -62,6 +62,7 @@
|
||||
[footerHeight]="footer ? 'auto' : 0"
|
||||
[limit]="limit > 0 ? limit : undefined"
|
||||
[loadingIndicator]="loadingIndicator"
|
||||
[rowIdentity]="rowIdentity()"
|
||||
[rowHeight]="'auto'">
|
||||
<!-- Row Detail Template -->
|
||||
<ngx-datatable-row-detail (toggle)="updateDetailView()">
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
TemplateRef,
|
||||
@ -13,7 +14,10 @@ import {
|
||||
} from '@angular/core';
|
||||
|
||||
import { DatatableComponent, SortDirection, SortPropDir } from '@swimlane/ngx-datatable';
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import 'rxjs/add/observable/timer';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { CdTableColumn } from '../../models/cd-table-column';
|
||||
import { TableDetailsDirective } from '../table-details.directive';
|
||||
@ -23,7 +27,7 @@ import { TableDetailsDirective } from '../table-details.directive';
|
||||
templateUrl: './table.component.html',
|
||||
styleUrls: ['./table.component.scss']
|
||||
})
|
||||
export class TableComponent implements AfterContentChecked, OnInit, OnChanges {
|
||||
export class TableComponent implements AfterContentChecked, OnInit, OnChanges, OnDestroy {
|
||||
@ViewChild(DatatableComponent) table: DatatableComponent;
|
||||
@ViewChild(TableDetailsDirective) detailTemplate: TableDetailsDirective;
|
||||
@ViewChild('tableCellBoldTpl') tableCellBoldTpl: TemplateRef<any>;
|
||||
@ -32,7 +36,7 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges {
|
||||
@ViewChild('perSecondTpl') perSecondTpl: TemplateRef<any>;
|
||||
|
||||
// This is the array with the items to be shown.
|
||||
@Input() data: any[] = [];
|
||||
@Input() data: any[];
|
||||
// Each item -> { prop: 'attribute name', name: 'display name' }
|
||||
@Input() columns: CdTableColumn[];
|
||||
// Each item -> { prop: 'attribute name', dir: 'asc'||'desc'}
|
||||
@ -54,7 +58,25 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges {
|
||||
// the details page, return false.
|
||||
@Input() beforeShowDetails: Function;
|
||||
|
||||
// Should be the function that will update the input data.
|
||||
/**
|
||||
* Auto reload time in ms - per default every 5s
|
||||
* You can set it to 0, undefined or false to disable the auto reload feature in order to
|
||||
* trigger 'fetchData' if the reload button is clicked.
|
||||
*/
|
||||
@Input() autoReload: any = 5000;
|
||||
|
||||
// Which row property is unique for a row
|
||||
@Input() identifier = 'id';
|
||||
|
||||
/**
|
||||
* Should be a function to update the input data if undefined nothing will be triggered
|
||||
*
|
||||
* Sometimes it's useful to only define fetchData once.
|
||||
* Example:
|
||||
* Usage of multiple tables with data which is updated by the same function
|
||||
* What happens:
|
||||
* The function is triggered through one table and all tables will update
|
||||
*/
|
||||
@Output() fetchData = new EventEmitter();
|
||||
|
||||
cellTemplates: {
|
||||
@ -64,13 +86,15 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges {
|
||||
search = '';
|
||||
rows = [];
|
||||
selected = [];
|
||||
loadingIndicator = false;
|
||||
loadingIndicator = true;
|
||||
paginationClasses = {
|
||||
pagerLeftArrow: 'i fa fa-angle-double-left',
|
||||
pagerRightArrow: 'i fa fa-angle-double-right',
|
||||
pagerPrevious: 'i fa fa-angle-left',
|
||||
pagerNext: 'i fa fa-angle-right'
|
||||
};
|
||||
private subscriber;
|
||||
private updating = false;
|
||||
|
||||
// Internal variable to check if it is necessary to recalculate the
|
||||
// table columns after the browser window has been resized.
|
||||
@ -86,18 +110,31 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges {
|
||||
}
|
||||
return column;
|
||||
});
|
||||
this.reloadData();
|
||||
if (this.detailsComponent) {
|
||||
this.selectionType = 'multi';
|
||||
}
|
||||
if (!this.sorts) {
|
||||
const sortProp = this.columns.some((c) => c.prop === this.identifier) ?
|
||||
this.identifier :
|
||||
this.columns[0].prop;
|
||||
this.sorts = [
|
||||
{
|
||||
prop: this.columns[0].prop,
|
||||
prop: sortProp,
|
||||
dir: SortDirection.asc
|
||||
}
|
||||
];
|
||||
}
|
||||
if (this.autoReload) { // Also if nothing is bound to fetchData nothing will be triggered
|
||||
this.subscriber = Observable.timer(0, this.autoReload).subscribe(x => {
|
||||
return this.reloadData();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subscriber) {
|
||||
this.subscriber.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterContentChecked() {
|
||||
@ -131,11 +168,25 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges {
|
||||
}
|
||||
|
||||
reloadData() {
|
||||
if (this.loadingIndicator) {
|
||||
return;
|
||||
if (!this.updating) {
|
||||
this.fetchData.emit();
|
||||
this.updating = true;
|
||||
}
|
||||
}
|
||||
|
||||
refreshBtn () {
|
||||
this.loadingIndicator = true;
|
||||
this.fetchData.emit();
|
||||
this.reloadData();
|
||||
}
|
||||
|
||||
rowIdentity() {
|
||||
return (row) => {
|
||||
const id = row[this.identifier];
|
||||
if (_.isUndefined(id)) {
|
||||
throw new Error(`Wrong identifier "${this.identifier}" -> "${id}"`);
|
||||
}
|
||||
return id;
|
||||
};
|
||||
}
|
||||
|
||||
useData() {
|
||||
@ -143,7 +194,11 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges {
|
||||
return; // Wait for data
|
||||
}
|
||||
this.rows = [...this.data];
|
||||
if (this.search.length > 0) {
|
||||
this.updateFilter(true);
|
||||
}
|
||||
this.loadingIndicator = false;
|
||||
this.updating = false;
|
||||
}
|
||||
|
||||
toggleExpandRow() {
|
||||
|
Loading…
Reference in New Issue
Block a user