Skip to content

Commit

Permalink
NAS-132299 / 25.04 / Implement virtualization metrics (#11066)
Browse files Browse the repository at this point in the history
* NAS-132299: Implement virtualization metrics

* NAS-132299: NAs-132299: PR Update

* NAS-132299: Implement virtualization metrics

* NAS-132299: PR update

* NAS-132299: PR Update
  • Loading branch information
AlexKarpov98 authored Nov 19, 2024
1 parent cdc9269 commit 807b9d8
Show file tree
Hide file tree
Showing 100 changed files with 706 additions and 14 deletions.
3 changes: 2 additions & 1 deletion src/app/interfaces/api/api-event-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { SmartTestProgressUpdate } from 'app/interfaces/smart-test-progress.inte
import { TrueCommandConfig } from 'app/interfaces/true-command-config.interface';
import { User } from 'app/interfaces/user.interface';
import { VirtualMachine } from 'app/interfaces/virtual-machine.interface';
import { VirtualizationGlobalConfig, VirtualizationInstance } from 'app/interfaces/virtualization.interface';
import { VirtualizationGlobalConfig, VirtualizationInstance, VirtualizationInstanceMetrics } from 'app/interfaces/virtualization.interface';
import { ZfsSnapshot } from 'app/interfaces/zfs-snapshot.interface';

export interface ApiEventDirectory {
Expand All @@ -43,6 +43,7 @@ export interface ApiEventDirectory {
'truecommand.config': { response: TrueCommandConfig };
'user.query': { response: User };
'virt.instance.query': { response: VirtualizationInstance };
'virt.instance.metrics': { response: VirtualizationInstanceMetrics };
'virt.instance.agent_running': { response: unknown }; // TODO: Fix type
'vm.query': { response: VirtualMachine };
'zfs.pool.scan': { response: PoolScan };
Expand Down
12 changes: 12 additions & 0 deletions src/app/interfaces/virtualization.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ import {
VirtualizationType,
} from 'app/enums/virtualization.enum';

export interface VirtualizationInstanceMetrics {
cpu: {
cpu_user_percentage: number;
};
mem_usage: {
mem_usage_ram_mib: number;
};
io_full_pressure: {
io_full_pressure_full_60_percentage: number;
};
}

export interface VirtualizationInstance {
id: string;
name: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ <h3 class="title">
<ix-instance-devices></ix-instance-devices>
<ix-instance-disks></ix-instance-disks>
<ix-instance-proxies></ix-instance-proxies>

<ix-instance-tools [instance]="instance()"></ix-instance-tools>
<ix-instance-metrics [instance]="instance()"></ix-instance-metrics>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import {
InstanceGeneralInfoComponent,
} from 'app/pages/virtualization/components/all-instances/instance-details/instance-general-info/instance-general-info.component';
import { InstanceMetricsComponent } from 'app/pages/virtualization/components/all-instances/instance-details/instance-metrics/instance-metrics.component';
import {
InstanceProxiesComponent,
} from 'app/pages/virtualization/components/all-instances/instance-details/instance-proxies/instance-proxies.component';
Expand All @@ -34,6 +35,7 @@ import { VirtualizationInstancesStore } from 'app/pages/virtualization/stores/vi
InstanceProxiesComponent,
InstanceDisksComponent,
InstanceToolsComponent,
InstanceMetricsComponent,
MobileBackButtonComponent,
],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<div class="wrapper">
<h4 class="title">
{{ title() | translate }}
<span>
({{ postfix() }})
</span>
</h4>
@if (isLoading()) {
<ngx-skeleton-loader
class="skeleton"
[theme]="{ width: '100%', height: '150px', background: 'var(--alt-bg2)', opacity: 0.25 }"
></ngx-skeleton-loader>
} @else {
<div class="chart">
<canvas
baseChart
type="line"
[data]="chartData()"
[options]="chartOptions()"
></canvas>
</div>
}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.wrapper {
background-color: var(--bg2);

.title {
margin-bottom: 8px;

span {
color: var(--fg2);
font-size: small;
}
}

.chart {
display: flex;
justify-content: center;
}
}

.skeleton,
.chart {
height: 150px;
margin-bottom: 15px;
padding: 0 8px;
width: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator/jest';
import { ChartData } from 'chart.js';
import { MockDirective } from 'ng-mocks';
import { BaseChartDirective } from 'ng2-charts';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { LocaleService } from 'app/services/locale.service';
import { ThemeService } from 'app/services/theme/theme.service';
import { InstanceMetricsLineChartComponent } from './instance-metrics-linechart.component';

describe('InstanceMetricsLineChartComponent', () => {
let spectator: Spectator<InstanceMetricsLineChartComponent>;
const startDate = new Date('2024-07-23').getTime();

const createComponent = createComponentFactory({
component: InstanceMetricsLineChartComponent,
imports: [NgxSkeletonLoaderModule],
declarations: [MockDirective(BaseChartDirective)],
providers: [
mockProvider(ThemeService, {
currentTheme: jest.fn(() => ({
blue: 'blue',
})),
}),
mockProvider(LocaleService, {
dateFormat: 'MM/DD/YYYY',
timeFormat: 'HH:mm:ss',
}),
],
});

beforeEach(() => {
spectator = createComponent({
props: {
title: 'Test Metrics',
data: [10, 20, 30],
labels: [startDate, startDate + 1000, startDate + 2000],
isLoading: false,
},
});
const dateNowStub = jest.fn(() => startDate);
global.Date.now = dateNowStub;
});

it('shows title', () => {
expect(spectator.query('h4')).toHaveText('Test Metrics');
});

it('shows skeleton loader when loading', () => {
spectator.setInput('isLoading', true);
expect(spectator.query('ngx-skeleton-loader')).toBeTruthy();
expect(spectator.query('canvas')).toBeNull();
});

it('renders chart when not loading', () => {
spectator.setInput('isLoading', false);
expect(spectator.query('ngx-skeleton-loader')).toBeNull();
const chart = spectator.query(BaseChartDirective);
expect(chart).not.toBeNull();
});

it('configures chart data correctly', () => {
const chart = spectator.query(BaseChartDirective);
const data = chart.data as ChartData<'line'>;

expect(data).toMatchObject({
datasets: [
{
label: 'Test Metrics',
data: [
{ x: startDate, y: 10 },
{ x: startDate + 1000, y: 20 },
{ x: startDate + 2000, y: 30 },
],
pointBackgroundColor: 'blue',
},
],
});
});

it('uses correct chart options', () => {
const chart = spectator.query(BaseChartDirective);
const options = chart.options;

expect(options).toMatchObject({
interaction: {
intersect: false,
},
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
label: expect.any(Function),
},
},
},
animation: {
duration: 0,
},
scales: {
x: {
type: 'time',
time: {
unit: 'second',
displayFormats: {
second: 'HH:mm:ss',
},
},
ticks: {
maxTicksLimit: 3,
maxRotation: 0,
},
},
y: {
type: 'linear',
beginAtZero: true,
},
},
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import {
ChangeDetectionStrategy, Component, computed, input,
} from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { ChartData, ChartOptions } from 'chart.js';
import { BaseChartDirective } from 'ng2-charts';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { LocaleService } from 'app/services/locale.service';
import { ThemeService } from 'app/services/theme/theme.service';

@Component({
selector: 'ix-instance-metrics-linechart',
templateUrl: './instance-metrics-linechart.component.html',
styleUrls: ['./instance-metrics-linechart.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
NgxSkeletonLoaderModule,
BaseChartDirective,
TranslateModule,
],
})
export class InstanceMetricsLineChartComponent {
title = input.required<string>();
data = input.required<number[]>();
labels = input.required<number[]>();
isLoading = input.required<boolean>();
postfix = input<string>();

constructor(
private themeService: ThemeService,
private localeService: LocaleService,
) {}

chartData = computed<ChartData<'line'>>(() => {
const currentTheme = this.themeService.currentTheme();

return {
datasets: [
{
label: this.title(),
data: this.data().map((y, index) => ({ x: this.labels()[index], y })),
borderColor: currentTheme.blue,
backgroundColor: currentTheme.blue,
pointBackgroundColor: currentTheme.blue,
pointRadius: 0,
tension: 0.2,
fill: false,
},
],
};
});

chartOptions = computed<ChartOptions<'line'>>(() => {
return {
interaction: {
intersect: false,
},
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
label: (item) => `${item.parsed.y} ${this.postfix() || ''}`,
},
},
},
animation: {
duration: 0,
},
transitions: {
active: {
animation: {
duration: 0,
},
},
},
scales: {
x: {
type: 'time',
time: {
unit: 'second',
displayFormats: {
second: 'HH:mm:ss',
},
tooltipFormat: `${this.localeService.dateFormat} ${this.localeService.timeFormat}`,
},
ticks: {
maxTicksLimit: 3,
maxRotation: 0,
},
},
y: {
type: 'linear',
beginAtZero: true,
},
},
};
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<mat-card class="card">
<mat-card-header>
<h3 mat-card-title>
{{ 'Metrics' | translate }}
</h3>
</mat-card-header>

<mat-card-content>
@if (instance()?.status === virtualizationStatus.Running) {
<ix-instance-metrics-linechart
postfix="%"
[title]="'CPU' | translate"
[data]="cpuData()"
[labels]="timeLabels()"
[isLoading]="isLoading()"
></ix-instance-metrics-linechart>

<ix-instance-metrics-linechart
[postfix]="'MiB' | translate"
[title]="'Memory' | translate"
[data]="memoryData()"
[labels]="timeLabels()"
[isLoading]="isLoading()"
></ix-instance-metrics-linechart>

<ix-instance-metrics-linechart
postfix="%"
[title]="'Disk I/O Full Pressure' | translate"
[data]="ioPressureData()"
[labels]="timeLabels()"
[isLoading]="isLoading()"
></ix-instance-metrics-linechart>
} @else {
{{ 'Instance is not running' | translate }}
}
</mat-card-content>
</mat-card>

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mat-card-content p {
margin: 0 0 6px;
}
Loading

0 comments on commit 807b9d8

Please sign in to comment.