Merge pull request #52700 from cloudbehl/utilization-improvements

mgr/dashboard: Adding legends and fixing queries

Reviewed-by: Aashish Sharma <aasharma@redhat.com>
This commit is contained in:
Aashish Sharma 2023-08-22 18:32:28 +05:30 committed by GitHub
commit 267a9351ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 130 additions and 101 deletions

View File

@ -39,7 +39,7 @@ describe('Dashboard-v3 Main Page', () => {
it('should verify that cards exist on dashboard in proper order', () => {
// Ensures that cards are all displayed on the dashboard tab while being in the proper
// order, checks for card title and position via indexing into a list of all cards.
const order = ['Details', 'Status', 'Capacity', 'Inventory', 'Cluster utilization'];
const order = ['Details', 'Status', 'Capacity', 'Inventory', 'Cluster Utilization'];
for (let i = 0; i < order.length; i++) {
dashboard.card(i).should('contain.text', order[i]);

View File

@ -1,17 +1,33 @@
<div class="row">
<div class="col-3 center-text">
<div class="row mt-2">
<div class="col-3 d-flex flex-column align-self-center">
<br>
<b class="chartTitle"
<b class="chartTitle pb-2"
i18n>{{ chartTitle }}</b>
<br>
<span [ngbTooltip]="label"
i18n>{{currentData}} {{ currentDataUnits }}</span>
<br>
<span [ngbTooltip]="label2"
i18n>{{currentData2}} {{ currentDataUnits2 }}</span>
<div
i18n>
<div class="d-inline-flex align-items-center gap-1">
<div *ngIf="!maxValue"
class="blue-box">
</div>
<div *ngIf="label2">{{ label }}:
</div>
{{ currentData || 'N/A' }} {{ currentDataUnits }}
<div *ngIf="maxValue && currentData"> used of
{{ maxConvertedValue }} {{ maxConvertedValueUnits }}
</div>
</div>
</div>
<div *ngIf="label2"
i18n>
<div class="d-inline-flex align-items-center gap-1">
<div class="yellow-box"></div>
<div *ngIf="label2 !== chartTitle" >{{ label2 }}: </div>
<div>{{ currentData2 || 'N/A' }} {{ currentDataUnits2 }}</div>
</div>
</div>
</div>
<div class="col-9">
<div class="chart">
<div class="col-9 d-flex flex-column">
<div class="chart mt-3">
<canvas baseChart
[datasets]="chartData.dataset"
[options]="options"

View File

@ -1,9 +1,19 @@
.center-text {
margin-top: 1.2vw;
position: relative;
}
@use './src/styles/vendor/variables' as vv;
.chart {
height: 8vh;
margin-top: 15px;
height: 9vh;
}
.blue-box {
background-color: vv.$chart-color-strong-blue;
border: 2px double vv.$chart-color-light-gray;
height: 13px;
width: 13px;
}
.yellow-box {
background-color: vv.$chart-color-orange;
border: 2px double vv.$chart-color-light-gray;
height: 13px;
width: 13px;
}

View File

@ -37,26 +37,29 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
currentData: number;
currentDataUnits2?: string;
currentData2?: number;
maxConvertedValue?: number;
maxConvertedValueUnits?: string;
chartDataUnits: string;
chartData: any = {
dataset: [
{
label: '',
data: [{ x: 0, y: 0 }],
tension: 0,
tension: 0.2,
pointBackgroundColor: this.cssHelper.propertyValue('chart-color-strong-blue'),
backgroundColor: this.cssHelper.propertyValue('chart-color-translucent-blue'),
borderColor: this.cssHelper.propertyValue('chart-color-strong-blue')
borderColor: this.cssHelper.propertyValue('chart-color-strong-blue'),
borderWidth: 1
},
{
label: '',
data: [],
tension: 0,
tension: 0.2,
pointBackgroundColor: this.cssHelper.propertyValue('chart-color-orange'),
backgroundColor: this.cssHelper.propertyValue('chart-color-yellow'),
borderColor: this.cssHelper.propertyValue('chart-color-orange')
backgroundColor: this.cssHelper.propertyValue('chart-color-translucent-yellow'),
borderColor: this.cssHelper.propertyValue('chart-color-orange'),
borderWidth: 1
}
]
};
@ -64,6 +67,7 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
options: any = {
responsive: true,
maintainAspectRatio: false,
animation: false,
elements: {
point: {
radius: 0
@ -73,6 +77,7 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
display: false
},
tooltips: {
mode: 'index',
custom: function (tooltipModel: { x: number; y: number }) {
tooltipModel.x = 10;
tooltipModel.y = 0;
@ -83,6 +88,16 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
callbacks: {
title: function (tooltipItem: any): any {
return tooltipItem[0].xLabel;
},
label: (tooltipItems: any, data: any) => {
return (
' ' +
data.datasets[tooltipItems.datasetIndex].label +
' - ' +
tooltipItems.value +
' ' +
this.chartDataUnits
);
}
}
},
@ -98,7 +113,7 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
display: false
},
time: {
tooltipFormat: 'YYYY/MM/DD hh:mm:ss'
tooltipFormat: 'DD/MM/YYYY - HH:mm:ss'
}
}
],
@ -109,7 +124,7 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
},
ticks: {
beginAtZero: true,
maxTicksLimit: 3,
maxTicksLimit: 4,
callback: (value: any) => {
if (value === 0) {
return null;
@ -168,17 +183,20 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
}
private updateChartData(): void {
this.chartData.dataset[0].label = this.label;
this.chartData.dataset[1].label = this.label2;
this.setChartTicks();
if (this.data) {
this.setChartTicks();
this.chartData.dataset[0].data = this.formatData(this.data);
this.chartData.dataset[0].label = this.label;
[this.currentData, this.currentDataUnits] = this.convertUnits(
this.data[this.data.length - 1][1]
).split(' ');
[this.maxConvertedValue, this.maxConvertedValueUnits] = this.convertUnits(
this.maxValue
).split(' ');
}
if (this.data2) {
this.chartData.dataset[1].data = this.formatData(this.data2);
this.chartData.dataset[1].label = this.label2;
[this.currentData2, this.currentDataUnits2] = this.convertUnits(
this.data2[this.data2.length - 1][1]
).split(' ');
@ -204,13 +222,15 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
dataWithUnits = this.numberFormatter.formatBytesFromTo(
data,
this.dataUnits,
this.chartDataUnits
this.chartDataUnits,
this.decimals
);
} else if (this.dataUnits === 'B/s') {
dataWithUnits = this.numberFormatter.formatBytesPerSecondFromTo(
data,
this.dataUnits,
this.chartDataUnits
this.chartDataUnits,
this.decimals
);
} else if (this.dataUnits === 'ms') {
dataWithUnits = this.numberFormatter.formatSecondsFromTo(
@ -223,7 +243,8 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
dataWithUnits = this.numberFormatter.formatUnitlessFromTo(
data,
this.dataUnits,
this.chartDataUnits
this.chartDataUnits,
this.decimals
);
}
}
@ -233,13 +254,13 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
private convertUnits(data: any): any {
let dataWithUnits: string = '';
if (this.dataUnits === 'B') {
dataWithUnits = this.dimlessBinary.transform(data);
dataWithUnits = this.dimlessBinary.transform(data, this.decimals);
} else if (this.dataUnits === 'B/s') {
dataWithUnits = this.dimlessBinaryPerSecond.transform(data);
dataWithUnits = this.dimlessBinaryPerSecond.transform(data, this.decimals);
} else if (this.dataUnits === 'ms') {
dataWithUnits = this.formatter.format_number(data, 1000, ['ms', 's'], this.decimals);
} else {
dataWithUnits = this.dimlessPipe.transform(data);
dataWithUnits = this.dimlessPipe.transform(data, this.decimals);
}
return dataWithUnits;
}
@ -265,11 +286,7 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
let maxValueDataUnits = '';
let extraRoom = 1.2;
if (this.maxValue) {
extraRoom = 1.0;
[maxValue, maxValueDataUnits] = this.convertUnits(this.maxValue).split(' ');
} else if (this.data) {
extraRoom = 1.2;
if (this.data) {
let maxValueData = Math.max(...this.data.map((values: any) => values[1]));
if (this.data2) {
let maxValueData2 = Math.max(...this.data2.map((values: any) => values[1]));
@ -283,7 +300,6 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
const yAxesTicks = this.chart.chart.options.scales.yAxes[0].ticks;
yAxesTicks.suggestedMax = maxValue * extraRoom;
yAxesTicks.suggestedMin = 0;
yAxesTicks.stepSize = Number((yAxesTicks.suggestedMax / 2).toFixed(0));
yAxesTicks.callback = (value: any) => {
if (value === 0) {
return null;

View File

@ -1,7 +1,3 @@
select#timepicker {
border: 0;
}
.timeSelector {
position: absolute;
right: 18px;

View File

@ -26,35 +26,27 @@ export class DashboardTimeSelectorComponent {
},
{
name: $localize`Last 30 minutes`,
value: this.timeToDate(30 * 60, 6)
value: this.timeToDate(30 * 60, 7)
},
{
name: $localize`Last 1 hour`,
value: this.timeToDate(3600, 12)
value: this.timeToDate(3600, 14)
},
{
name: $localize`Last 3 hours`,
value: this.timeToDate(3 * 3600, 36)
value: this.timeToDate(3 * 3600, 42)
},
{
name: $localize`Last 6 hours`,
value: this.timeToDate(6 * 3600, 72)
value: this.timeToDate(6 * 3600, 84)
},
{
name: $localize`Last 12 hours`,
value: this.timeToDate(12 * 3600, 144)
value: this.timeToDate(12 * 3600, 168)
},
{
name: $localize`Last 24 hours`,
value: this.timeToDate(24 * 3600, 288)
},
{
name: $localize`Last 2 days`,
value: this.timeToDate(48 * 3600, 576)
},
{
name: $localize`Last 7 days`,
value: this.timeToDate(168 * 3600, 2016)
value: this.timeToDate(24 * 3600, 336)
}
];
this.time = this.times[3].value;
@ -64,7 +56,7 @@ export class DashboardTimeSelectorComponent {
this.selectedTime.emit(this.timeToDate(this.time.end - this.time.start, this.time.step));
}
private timeToDate(secondsAgo: number, step: number): any {
public timeToDate(secondsAgo: number, step: number): any {
const date: number = moment().unix() - secondsAgo;
const dateNow: number = moment().unix();
const formattedDate: any = {

View File

@ -227,7 +227,7 @@
</li>
</cd-card>
<cd-card cardTitle="Cluster utilization"
<cd-card cardTitle="Cluster Utilization"
i18n-title
class="col-sm-9 px-3 d-flex"
aria-label="Cluster utilization card">
@ -235,7 +235,7 @@
<cd-dashboard-time-selector (selectedTime)="getPrometheusData($event)">
</cd-dashboard-time-selector>
<ng-container *ngIf="capacity">
<cd-dashboard-area-chart chartTitle="Used Capacity"
<cd-dashboard-area-chart chartTitle="Used Capacity (RAW)"
[maxValue]="capacity.total_bytes"
dataUnits="B"
label="Used Capacity"
@ -244,28 +244,31 @@
</ng-container>
<cd-dashboard-area-chart chartTitle="IOPS"
dataUnits=""
label="OPS"
label2="IPS"
[data]="queriesResults.OPS"
[data2]="queriesResults.IPS">
decimals="0"
label="Reads"
label2="Writes"
[data]="queriesResults.READIOPS"
[data2]="queriesResults.WRITEIOPS">
</cd-dashboard-area-chart>
<cd-dashboard-area-chart chartTitle="Latency"
<cd-dashboard-area-chart chartTitle="OSD Latencies"
dataUnits="ms"
decimals="3"
label="Read"
label2="Write"
decimals="2"
label="Apply"
label2="Commit"
[data]="queriesResults.READLATENCY"
[data2]="queriesResults.WRITELATENCY">
</cd-dashboard-area-chart>
<cd-dashboard-area-chart chartTitle="Client Throughput"
dataUnits="B/s"
label="Read"
label2="Write"
decimals="2"
label="Reads"
label2="Writes"
[data]="queriesResults.READCLIENTTHROUGHPUT"
[data2]="queriesResults.WRITECLIENTTHROUGHPUT">
</cd-dashboard-area-chart>
<cd-dashboard-area-chart chartTitle="Recovery Throughput"
dataUnits="B/s"
decimals="2"
label="Recovery Throughput"
[data]="queriesResults.RECOVERYBYTES">
</cd-dashboard-area-chart>

View File

@ -73,7 +73,7 @@ export class DashboardV3Component extends PrometheusListHelper implements OnInit
readonly lastHourDateObject = {
start: moment().unix() - 3600,
end: moment().unix(),
step: 12
step: 14
};
origin = window.location.origin;

View File

@ -1,7 +1,7 @@
export enum Promqls {
USEDCAPACITY = 'ceph_cluster_total_used_bytes',
IPS = 'sum(rate(ceph_osd_op_w_in_bytes[1m]))',
OPS = 'sum(rate(ceph_osd_op_r_out_bytes[1m]))',
WRITEIOPS = 'sum(rate(ceph_pool_wr[1m]))',
READIOPS = 'sum(rate(ceph_pool_rd[1m]))',
READLATENCY = 'avg_over_time(ceph_osd_apply_latency_ms[1m])',
WRITELATENCY = 'avg_over_time(ceph_osd_commit_latency_ms[1m])',
READCLIENTTHROUGHPUT = 'sum(rate(ceph_pool_rd_bytes[1m]))',

View File

@ -8,17 +8,12 @@ import { FormatterService } from '../services/formatter.service';
export class DimlessBinaryPerSecondPipe implements PipeTransform {
constructor(private formatter: FormatterService) {}
transform(value: any): any {
return this.formatter.format_number(value, 1024, [
'B/s',
'KiB/s',
'MiB/s',
'GiB/s',
'TiB/s',
'PiB/s',
'EiB/s',
'ZiB/s',
'YiB/s'
]);
transform(value: any, decimals: number = 1): any {
return this.formatter.format_number(
value,
1024,
['B/s', 'KiB/s', 'MiB/s', 'GiB/s', 'TiB/s', 'PiB/s', 'EiB/s', 'ZiB/s', 'YiB/s'],
decimals
);
}
}

View File

@ -8,17 +8,12 @@ import { FormatterService } from '../services/formatter.service';
export class DimlessBinaryPipe implements PipeTransform {
constructor(private formatter: FormatterService) {}
transform(value: any): any {
return this.formatter.format_number(value, 1024, [
'B',
'KiB',
'MiB',
'GiB',
'TiB',
'PiB',
'EiB',
'ZiB',
'YiB'
]);
transform(value: any, decimals: number = 1): any {
return this.formatter.format_number(
value,
1024,
['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'],
decimals
);
}
}

View File

@ -8,7 +8,12 @@ import { FormatterService } from '../services/formatter.service';
export class DimlessPipe implements PipeTransform {
constructor(private formatter: FormatterService) {}
transform(value: any): any {
return this.formatter.format_number(value, 1000, ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']);
transform(value: any, decimals: number = 1): any {
return this.formatter.format_number(
value,
1000,
['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'],
decimals
);
}
}

View File

@ -94,6 +94,7 @@ $chart-danger: #c9190b !default;
$chart-color-strong-blue: #0078c8 !default;
$chart-color-translucent-blue: #0096dc80 !default;
$chart-color-border: #00000020 !default;
$chart-color-translucent-yellow: #ef923472 !default;
// Typography