Improve scan all page

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
Will Sun 2020-12-24 17:36:03 +08:00 committed by GitHub
parent 923a538570
commit 75da08303a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 63 additions and 103 deletions

View File

@ -179,6 +179,11 @@ export class ScanningMetrics {
[key: string]: number; [key: string]: number;
}; };
requester?: string; requester?: string;
isScheduled?: boolean; trigger?: string;
ongoing: boolean; ongoing: boolean;
} }
export enum Triggers {
MANUAL= 'Manual',
SCHEDULE = 'Schedule',
EVENT = 'Event'
}

View File

@ -51,12 +51,7 @@ export class ScanAllRepoService {
return this.scanApiRepository.putSchedule(param); return this.scanApiRepository.putSchedule(param);
} }
getScheduleMetrics(): Observable<ScanningMetrics> { getMetrics(): Observable<ScanningMetrics> {
return this.http.get(CURRENT_BASE_HREF + '/scans/schedule/metrics')
.pipe(catchError(error => observableThrowError(error)))
.pipe(map(response => response as ScanningMetrics));
}
getManualMetrics(): Observable<ScanningMetrics> {
return this.http.get(CURRENT_BASE_HREF + '/scans/all/metrics') return this.http.get(CURRENT_BASE_HREF + '/scans/all/metrics')
.pipe(catchError(error => observableThrowError(error))) .pipe(catchError(error => observableThrowError(error)))
.pipe(map(response => response as ScanningMetrics)); .pipe(map(response => response as ScanningMetrics));

View File

@ -23,8 +23,8 @@
</div> </div>
<div class="clr-col" *ngIf="scanningMetrics && scanningMetrics.total"> <div class="clr-col" *ngIf="scanningMetrics && scanningMetrics.total">
<div class="total" [style.width]="totalWidth+'px'"> <div class="total" [style.width]="totalWidth+'px'">
<span class="float-left" *ngIf="!scanningMetrics?.isScheduled">{{ 'CONFIG.SCANNING.MANUAL' | translate }}</span> <span class="float-left" *ngIf="isManual()">{{ 'CONFIG.SCANNING.MANUAL' | translate }}</span>
<span class="float-left" *ngIf="scanningMetrics?.isScheduled">{{ 'CONFIG.SCANNING.SCHEDULED' | translate }}</span> <span class="float-left" *ngIf="isSchedule()">{{ 'CONFIG.SCANNING.SCHEDULED' | translate }}</span>
<span>{{ 'SCANNER.TOTAL' | translate }}</span> <span>{{ 'SCANNER.TOTAL' | translate }}</span>
<span class="margin-left-5">{{scanningMetrics?.total}}</span> <span class="margin-left-5">{{scanningMetrics?.total}}</span>
</div> </div>
@ -32,13 +32,15 @@
<div class="error h-100" [style.width]="errorWidth()"></div> <div class="error h-100" [style.width]="errorWidth()"></div>
<div class="finished h-100" [style.width]="finishedWidth()"></div> <div class="finished h-100" [style.width]="finishedWidth()"></div>
<div class="in-progress h-100" [style.width]="runningWidth()"></div> <div class="in-progress h-100" [style.width]="runningWidth()"></div>
<div class="abort h-100" [style.width]="abortWidth()"></div>
</div> </div>
<div class="state-container" [style.width]="totalWidth+'px'"> <div class="state-container" [style.width]="totalWidth+'px'">
<div class="clr-row m-0" *ngFor="let item of scanningMetrics?.metrics | keyvalue"> <div class="clr-row m-0" *ngFor="let item of scanningMetrics?.metrics | keyvalue">
<div class="state"> <div class="state">
<span class="badge" [ngClass]="{ error: isError(item?.key), <span class="badge" [ngClass]="{ error: isError(item?.key),
finished: isFinished(item?.key), finished: isFinished(item?.key),
'in-progress': isInProgress(item?.key)}">{{item?.value}}</span> 'in-progress': isInProgress(item?.key),
abort: isAborted(item?.key)}">{{item?.value}}</span>
</div> </div>
<div class="value">{{getI18nKey(item?.key)|translate}}</div> <div class="value">{{getI18nKey(item?.key)|translate}}</div>
</div> </div>

View File

@ -103,6 +103,9 @@
.in-progress { .in-progress {
background-color: #0079b8; background-color: #0079b8;
} }
.abort {
background-color: #cccc15;
}
.badge { .badge {
min-width: 30px; min-width: 30px;
} }

View File

@ -4,7 +4,7 @@ import { ScanAllRepoService } from "./scanAll.service";
import { of } from "rxjs"; import { of } from "rxjs";
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { IServiceConfig, SERVICE_CONFIG } from "../../../entities/service.config"; import { IServiceConfig, SERVICE_CONFIG } from "../../../entities/service.config";
import { ScanningMetrics } from "../config"; import { ScanningMetrics, Triggers } from "../config";
import { SharedModule } from "../../../utils/shared/shared.module"; import { SharedModule } from "../../../utils/shared/shared.module";
import { ErrorHandler } from "../../../utils/error-handler"; import { ErrorHandler } from "../../../utils/error-handler";
import { CURRENT_BASE_HREF } from "../../../utils/utils"; import { CURRENT_BASE_HREF } from "../../../utils/utils";
@ -23,17 +23,8 @@ let mockedScheduledMetrics: ScanningMetrics = {
"Success": 20, "Success": 20,
"Error": 30, "Error": 30,
}, },
ongoing: false ongoing: false,
}; trigger: Triggers.SCHEDULE
let mockedManualMetrics: ScanningMetrics = {
total: 100,
completed: 20,
metrics: {
"Error": 10,
"Success": 20,
"Running": 70
},
ongoing: true
}; };
const mockedScanner: Scanner = { const mockedScanner: Scanner = {
"uuid": "ca3c27f3-72f3-11ea-9e46-0242ac170004", "uuid": "ca3c27f3-72f3-11ea-9e46-0242ac170004",
@ -52,12 +43,9 @@ let fakedScanAllRepoService = {
getSchedule() { getSchedule() {
return of(mockedSchedule); return of(mockedSchedule);
}, },
getScheduleMetrics() { getMetrics() {
return of(mockedScheduledMetrics); return of(mockedScheduledMetrics);
}, },
getManualMetrics() {
return of(mockedManualMetrics);
},
manualScan() { manualScan() {
return of(true); return of(true);
}, },
@ -105,10 +93,9 @@ describe('VulnerabilityConfigComponent', () => {
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
const ele = fixture.nativeElement.querySelector('.finished'); const ele = fixture.nativeElement.querySelector('.finished');
expect(ele.style.width).toEqual('40px'); expect(ele.style.width).toEqual('80px');
}); });
it('should loop scheduled metrics if scheduled scanning is on going', () => { it('should loop metrics if scheduled scanning is on going', () => {
component.scanningMetrics.isScheduled = true;
component.scanningMetrics.ongoing = true; component.scanningMetrics.ongoing = true;
expect(component.scanAvailable).toBeFalsy(); expect(component.scanAvailable).toBeFalsy();
}); });
@ -116,10 +103,10 @@ describe('VulnerabilityConfigComponent', () => {
const button = fixture.nativeElement.querySelector('#scan-now'); const button = fixture.nativeElement.querySelector('#scan-now');
button.click(); button.click();
const ele = fixture.nativeElement.querySelector('.finished'); const ele = fixture.nativeElement.querySelector('.finished');
expect(ele.style.width).toEqual('40px'); expect(ele.style.width).toEqual('80px');
}); });
it('should stop looping manual metrics if manual scanning is finished', () => { it('should stop looping manual metrics if manual scanning is finished', () => {
component.scanningMetrics.isScheduled = false; component.scanningMetrics.trigger = Triggers.MANUAL;
component.scanningMetrics.ongoing = false; component.scanningMetrics.ongoing = false;
component.hasDefaultScanner = true; component.hasDefaultScanner = true;
expect(component.scanAvailable).toBeTruthy(); expect(component.scanAvailable).toBeTruthy();

View File

@ -1,11 +1,11 @@
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
import { finalize } from "rxjs/operators"; import { finalize } from "rxjs/operators";
import { ScanningMetrics } from '../config'; import { ScanningMetrics, Triggers } from '../config';
import { ErrorHandler } from '../../../utils/error-handler'; import { ErrorHandler } from '../../../utils/error-handler';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ScanAllRepoService } from './scanAll.service'; import { ScanAllRepoService } from './scanAll.service';
import { OriginCron } from '../../../services/interface'; import { OriginCron } from '../../../services';
import { CronScheduleComponent } from "../../cron-schedule/cron-schedule.component"; import { CronScheduleComponent } from "../../cron-schedule";
import { DATABASE_UPDATED_PROPERTY, VULNERABILITY_SCAN_STATUS } from "../../../utils/utils"; import { DATABASE_UPDATED_PROPERTY, VULNERABILITY_SCAN_STATUS } from "../../../utils/utils";
import { errorHandler as errorHandFn} from "../../../utils/shared/shared.utils"; import { errorHandler as errorHandFn} from "../../../utils/shared/shared.utils";
import { DatePipe } from "@angular/common"; import { DatePipe } from "@angular/common";
@ -37,8 +37,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
"Success": "CONFIG.SCANNING.STATUS.SUCCESS", "Success": "CONFIG.SCANNING.STATUS.SUCCESS",
"Scheduled": "CONFIG.SCANNING.STATUS.SCHEDULED" "Scheduled": "CONFIG.SCANNING.STATUS.SCHEDULED"
}; };
private _loopScheduleInterval; private _loopInterval;
private _loopManualInterval;
updatedTimeStr: string; updatedTimeStr: string;
onGettingUpdatedTimeStr: boolean; onGettingUpdatedTimeStr: boolean;
hasDefaultScanner: boolean = false; hasDefaultScanner: boolean = false;
@ -52,34 +51,19 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
return this._scanningMetrics; return this._scanningMetrics;
} }
set scanningMetrics(metrics: ScanningMetrics) { set scanningMetrics(metrics: ScanningMetrics) {
// start looping scheduled metrics // start looping metrics
if (metrics && metrics.ongoing && metrics.isScheduled) { if (metrics && metrics.ongoing) {
if (!this._loopScheduleInterval) { if (!this._loopInterval) {
this._loopScheduleInterval = setInterval(() => { this._loopInterval = setInterval(() => {
this.getScheduleMetrics(); this.getMetrics();
}, 5000); }, 5000);
} }
} }
// stop looping scheduled metrics // stop looping metrics
if (metrics && !metrics.ongoing && metrics.isScheduled) { if (metrics && !metrics.ongoing) {
if (this._loopScheduleInterval) { if (this._loopInterval) {
clearInterval(this._loopScheduleInterval); clearInterval(this._loopInterval);
this._loopScheduleInterval = null; this._loopInterval = null;
}
}
// start looping manual metrics
if (metrics && metrics.ongoing && !metrics.isScheduled) {
if (!this._loopManualInterval) {
this._loopManualInterval = setInterval(() => {
this.getManualMetrics();
}, 5000);
}
}
// stop looping manual metrics
if (metrics && !metrics.ongoing && !metrics.isScheduled) {
if (this._loopManualInterval) {
clearInterval(this._loopManualInterval);
this._loopManualInterval = null;
} }
} }
this._scanningMetrics = metrics; this._scanningMetrics = metrics;
@ -117,7 +101,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
scanners.forEach(scanner => { scanners.forEach(scanner => {
if (scanner.is_default) { if (scanner.is_default) {
flag = true; flag = true;
this.initMetrics(); this.getMetrics();
this.getSchedule(); this.getSchedule();
this.getScannerMetadata(scanner.uuid); this.getScannerMetadata(scanner.uuid);
} }
@ -169,60 +153,25 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
} }
ngOnDestroy() { ngOnDestroy() {
if (this._loopScheduleInterval) { if (this._loopInterval) {
clearInterval(this._loopScheduleInterval); clearInterval(this._loopInterval);
this._loopScheduleInterval = null; this._loopInterval = null;
}
if (this._loopManualInterval) {
clearInterval(this._loopManualInterval);
this._loopManualInterval = null;
} }
} }
isOnScanning(): boolean { isOnScanning(): boolean {
return this.scanningMetrics return this.scanningMetrics
&& this.scanningMetrics.ongoing; && this.scanningMetrics.ongoing;
} }
getScheduleMetrics() { getMetrics() {
this.gettingMetrics = true; this.gettingMetrics = true;
this.scanningService.getScheduleMetrics() this.scanningService.getMetrics()
.pipe(finalize(() => this.gettingMetrics = false)) .pipe(finalize(() => this.gettingMetrics = false))
.subscribe(response => { .subscribe(response => {
if (response) { if (response) {
response.isScheduled = true;
this.scanningMetrics = response; this.scanningMetrics = response;
} }
}); });
} }
getManualMetrics() {
this.gettingMetrics = true;
this.scanningService.getManualMetrics()
.pipe(finalize(() => this.gettingMetrics = false))
.subscribe(response => {
if (response) {
response.isScheduled = false;
this.scanningMetrics = response;
}
});
}
initMetrics() {
// get scheduled metrics first
this.scanningService.getScheduleMetrics()
.pipe(finalize(() => this.gettingMetrics = false))
.subscribe(response => {
// if scheduled scanning is on going
if (response && response.ongoing) {
response.isScheduled = true;
this.scanningMetrics = response;
} else {
this.getManualMetrics();
}
},
error => {
this.errorHandler.error(error);
// if error, get manual metrics
this.getManualMetrics();
});
}
getI18nKey(str: string): string { getI18nKey(str: string): string {
if (str && this.i18nKeyMap[str]) { if (str && this.i18nKeyMap[str]) {
return this.i18nKeyMap[str]; return this.i18nKeyMap[str];
@ -259,6 +208,16 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
} }
return '0'; return '0';
} }
abortWidth() {
if (this.scanningMetrics
&& this.scanningMetrics.metrics
&& this.scanningMetrics.total
&& this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.STOPPED]) {
return this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.STOPPED] /
this.scanningMetrics.total * this.totalWidth + 'px';
}
return '0';
}
scanNow(): void { scanNow(): void {
if (this.onSubmitting) { if (this.onSubmitting) {
return; // Aoid duplicated submitting return; // Aoid duplicated submitting
@ -277,7 +236,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
}); });
// reset metrics and then get new metrics // reset metrics and then get new metrics
this.scanningMetrics = null; this.scanningMetrics = null;
this.getManualMetrics(); this.getMetrics();
} }
, error => { , error => {
if (error && error.status && error.status === 412) { if (error && error.status && error.status === 412) {
@ -341,4 +300,13 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
isInProgress(status: string): boolean { isInProgress(status: string): boolean {
return status === VULNERABILITY_SCAN_STATUS.RUNNING; return status === VULNERABILITY_SCAN_STATUS.RUNNING;
} }
isAborted(status: string): boolean {
return status === VULNERABILITY_SCAN_STATUS.STOPPED;
}
isManual() {
return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.MANUAL;
}
isSchedule() {
return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.SCHEDULE;
}
} }