You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by sa...@apache.org on 2019/07/21 09:19:53 UTC
[metron] branch feature/METRON-1856-parser-aggregation updated:
METRON-2124 [UI] Move status information and start/stop to the Aggregate
level (sardell) closes apache/metron#1418
This is an automated email from the ASF dual-hosted git repository.
sardell pushed a commit to branch feature/METRON-1856-parser-aggregation
in repository https://gitbox.apache.org/repos/asf/metron.git
The following commit(s) were added to refs/heads/feature/METRON-1856-parser-aggregation by this push:
new 9d7efa0 METRON-2124 [UI] Move status information and start/stop to the Aggregate level (sardell) closes apache/metron#1418
9d7efa0 is described below
commit 9d7efa00c906d5c31e08ae89022f073a57c8d9dc
Author: sardell <sh...@gmail.com>
AuthorDate: Sun Jul 21 11:19:36 2019 +0200
METRON-2124 [UI] Move status information and start/stop to the Aggregate level (sardell) closes apache/metron#1418
---
.../sensor-parser-list.component.html | 98 ++++++-
.../sensor-parser-list.component.spec.ts | 300 ++++++++++++---------
.../sensor-parser-list.component.ts | 40 ++-
3 files changed, 293 insertions(+), 145 deletions(-)
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.html b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.html
index 726cf77..d55618c 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.html
+++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.html
@@ -52,8 +52,8 @@
</tr>
</thead>
<tbody>
- <tr *ngFor="let sensor of sensors;" (click)="onSensorRowSelect(sensor, $event)" [ngClass]="{'active': (selectedSensors.indexOf(sensor) != -1 || selectedSensor == sensor)}">
- <td>{{ sensor.sensorName }}</td>
+ 'active': isSelected(sensor),
+ 'parent': sensor.isGroup,
<td>{{ getParserType(sensor.config) }}</td>
<td [ngClass]="{'warning-text': (sensor.status == 'Stopped' || sensor.status == 'Disabled')}">{{ sensor.status }}</td>
<td>{{ sensor.latency }}</td>
@@ -61,19 +61,93 @@
<td>{{ sensor.modifiedByDate }}</td>
<td>{{ sensor.modifiedBy }}</td>
<td class="icon-container">
- <i data-toggle="tooltip" title="Operation in progress" class="fa fa-circle-o-notch fa-spin fa-lg fa-fw" [hidden]="!sensor.config['startStopInProgress']"></i>
+ <i
+ data-toggle="tooltip"
+ title="Operation in progress"
+ class="fa fa-circle-o-notch fa-spin fa-lg fa-fw"
+ [hidden]="!sensor.startStopInProgress"
+ >
+ </i>
+ <i
+ data-toggle="tooltip"
+ title="Stop parser topology"
+ class="fa fa-stop fa-lg"
+ aria-hidden="true"
+ data-qe-id="stop-parser-button"
+ [hidden]="!isStoppable(sensor)"
+ (click)="onStopSensor(sensor, $event)"
+ >
+ </i>
+ <i
+ data-toggle="tooltip"
+ title="Start parser topology"
+ class="fa fa-play fa-lg"
+ aria-hidden="true"
+ data-qe-id="start-parser-button"
+ [hidden]="!isStartable(sensor)"
+ (click)="onStartSensor(sensor, $event)"
+ >
+ </i>
- <i data-toggle="tooltip" title="Stop parser topology" class="fa fa-stop fa-lg" aria-hidden="true" [hidden]="((sensor.status != 'Running' && sensor.status != 'Disabled') || sensor.config['startStopInProgress'])" (click)="onStopSensor(sensor, $event)"></i>
- <i data-toggle="tooltip" title="Disable parser topology" class="fa fa-ban fa-lg" aria-hidden="true" [hidden]="(sensor.status != 'Running' || sensor.config['startStopInProgress'])" (click)="onDisableSensor(sensor, $event)"></i>
+ <i
+ data-toggle="tooltip"
+ title="Disable parser topology"
+ class="fa fa-ban fa-lg"
+ aria-hidden="true"
+ data-qe-id="disable-parser-button"
+ [hidden]="!isDisableable(sensor)"
+ (click)="onDisableSensor(sensor, $event)"
+ >
+ </i>
- <i data-toggle="tooltip" title="Start parser topology" class="fa fa-play fa-lg" aria-hidden="true" [hidden]="(sensor.status != 'Stopped' || sensor.config['startStopInProgress'])" (click)="onStartSensor(sensor, $event)"></i>
- <i data-toggle="tooltip" title="Enable parser topology" class="fa fa-check-circle-o fa-lg" aria-hidden="true" [hidden]="(sensor.status != 'Disabled' || sensor.config['startStopInProgress'])" (click)="onEnableSensor(sensor, $event)"></i>
-
- <i data-toggle="tooltip" title="Edit parser topology" class="fa fa-pencil fa-lg" aria-hidden="true" (click)="navigateToSensorEdit(sensor, $event)"></i>
-
- <i data-toggle="tooltip" title="Delete parser configuration" class="fa fa-trash-o fa-lg" aria-hidden="true" (click)="deleteSensor($event, [sensor])"></i>
+ <i
+ data-toggle="tooltip"
+ title="Enable parser topology"
+ class="fa fa-check-circle-o fa-lg"
+ aria-hidden="true"
+ data-qe-id="enable-parser-button"
+ [hidden]="!isEnableable(sensor)"
+ (click)="onEnableSensor(sensor, $event)"
+ >
+ </i>
+ <i
+ data-toggle="tooltip"
+ title="Edit parser topology"
+ class="fa fa-pencil fa-lg"
+ aria-hidden="true"
+ data-qe-id="edit-parser-button"
+ [hidden]="sensor.isDeleted"
+ (click)="navigateToSensorEdit(sensor, $event)"
+ >
+ </i>
+ <i
+ data-toggle="tooltip"
+ title="Delete parser configuration"
+ class="fa fa-trash-o fa-lg"
+ aria-hidden="true"
+ data-qe-id="delete-parser-button"
+ [hidden]="sensor.isDeleted"
+ (click)="onDeleteItem(sensor, $event)"
+ >
+ </i>
+ </td>
+ <td>
+ <div data-qe-id="sensor-select" [hidden]="isDeletedOrPhantom(sensor)">
+ <input
+ id="{{ sensor.config.getName() }}"
+ class="fontawesome-checkbox"
+ type="checkbox"
+ name="{{sensor.config.getName()}}"
+ (change)="onRowSelected(sensor, $event)"
+ (click)="$event.stopPropagation()"
+ [checked]="isSelected(sensor)"
+ >
+ <label
+ attr.for="{{ sensor.config.getName() }}"
+ (click)="$event.stopPropagation()"
+ ></label>
+ </div>
</td>
- <td><input id="{{ sensor.sensorName }}" class="fontawesome-checkbox" type="checkbox" name="{{sensor.sensorName}}" (click)="onRowSelected(sensor, $event)"><label attr.for="{{ sensor.sensorName }}"></label></td>
</tr>
</tbody>
</table>
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts
index eb4dbff..755daa5 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts
@@ -22,7 +22,7 @@ import { HttpClient, HttpResponse } from '@angular/common/http';
import { DebugElement, Inject } from '@angular/core';
import { By } from '@angular/platform-browser';
import { Router, NavigationStart } from '@angular/router';
-import { Observable } from 'rxjs';
+import { Observable, of } from 'rxjs';
import { SensorParserListComponent } from './sensor-parser-list.component';
import { SensorParserConfigService } from '../../service/sensor-parser-config.service';
import { MetronAlerts } from '../../shared/metron-alerts';
@@ -150,12 +150,61 @@ describe('Component: SensorParserList', () => {
let metronAlerts: MetronAlerts;
let metronDialog: MetronDialogBox;
let dialogEl: DebugElement;
+ let sensors = [
+ {
+ config: new ParserConfigModel('TestConfigId01'),
+ status: {
+ status: 'KILLED'
+ },
+ isGroup: false,
+ isDeleted: false,
+ },
+ {
+ config: new ParserConfigModel('TestConfigId02'),
+ status: {
+ status: 'INACTIVE'
+ },
+ isGroup: false,
+ isDeleted: false,
+ },
+ {
+ config: new ParserConfigModel('TestConfigId03'),
+ status: {
+ status: 'ACTIVE'
+ },
+ isGroup: false,
+ isDeleted: false,
+ },
+ {
+ config: new ParserConfigModel('TestConfigId04'),
+ status: {
+ status: 'ACTIVE'
+ },
+ isDeleted: true
+ },
+ {
+ config: new ParserConfigModel('TestConfigId05'),
+ status: {
+ status: 'ACTIVE'
+ },
+ isPhantom: true,
+ isDeleted: false,
+ }
+ ];
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [SensorParserListModule],
providers: [
{ provide: HttpClient },
+ { provide: Store, useValue: {
+ pipe: () => {
+ return of(sensors)
+ },
+ dispatch: () => {
+
+ }
+ } },
{ provide: Location, useClass: SpyLocation },
{ provide: AuthenticationService, useClass: MockAuthenticationService },
{
@@ -639,135 +688,138 @@ describe('Component: SensorParserList', () => {
})
);
- it('sort', async(() => {
- let component: SensorParserListComponent = fixture.componentInstance;
+ it('isStoppable() should return true unless a sensor is KILLED', async(() => {
+ const component = Object.create( SensorParserListComponent.prototype );
+ const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+ let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
- component.sensors = [
- Object.assign(new SensorParserConfigHistory(), {
- sensorName: 'abc',
- config: {
- parserClassName: 'org.apache.metron.parsers.GrokParser',
- sensorTopic: 'abc'
- },
- createdBy: 'raghu',
- modifiedBy: 'abc',
- createdDate: '2016-11-25 09:09:12',
- modifiedByDate: '2016-11-25 09:09:12'
- }),
- Object.assign(new SensorParserConfigHistory(), {
- sensorName: 'plm',
- config: {
- parserClassName: 'org.apache.metron.parsers.Bro',
- sensorTopic: 'plm'
- },
- createdBy: 'raghu',
- modifiedBy: 'plm',
- createdDate: '2016-11-25 12:39:21',
- modifiedByDate: '2016-11-25 12:39:21'
- }),
- Object.assign(new SensorParserConfigHistory(), {
- sensorName: 'xyz',
- config: {
- parserClassName: 'org.apache.metron.parsers.GrokParser',
- sensorTopic: 'xyz'
- },
- createdBy: 'raghu',
- modifiedBy: 'xyz',
- createdDate: '2016-11-25 12:44:03',
- modifiedByDate: '2016-11-25 12:44:03'
- })
- ];
-
- component.onSort({ sortBy: 'sensorName', sortOrder: Sort.ASC });
- expect(component.sensors[0].sensorName).toEqual('abc');
- expect(component.sensors[1].sensorName).toEqual('plm');
- expect(component.sensors[2].sensorName).toEqual('xyz');
-
- component.onSort({ sortBy: 'sensorName', sortOrder: Sort.DSC });
- expect(component.sensors[0].sensorName).toEqual('xyz');
- expect(component.sensors[1].sensorName).toEqual('plm');
- expect(component.sensors[2].sensorName).toEqual('abc');
-
- component.onSort({ sortBy: 'parserClassName', sortOrder: Sort.ASC });
- expect(component.sensors[0].config.parserClassName).toEqual(
- 'org.apache.metron.parsers.Bro'
- );
- expect(component.sensors[1].config.parserClassName).toEqual(
- 'org.apache.metron.parsers.GrokParser'
- );
- expect(component.sensors[2].config.parserClassName).toEqual(
- 'org.apache.metron.parsers.GrokParser'
- );
+ sensor.status.status = 'KILLED';
+ expect(component.isStoppable(sensor)).toBe(false);
- component.onSort({ sortBy: 'parserClassName', sortOrder: Sort.DSC });
- expect(component.sensors[0].config.parserClassName).toEqual(
- 'org.apache.metron.parsers.GrokParser'
- );
- expect(component.sensors[1].config.parserClassName).toEqual(
- 'org.apache.metron.parsers.GrokParser'
- );
- expect(component.sensors[2].config.parserClassName).toEqual(
- 'org.apache.metron.parsers.Bro'
- );
+ sensor.status.status = 'ACTIVE';
+ expect(component.isStoppable(sensor)).toBe(true);
+
+ sensor.status.status = 'INACTIVE';
+ expect(component.isStoppable(sensor)).toBe(true);
+ }));
- component.onSort({ sortBy: 'modifiedBy', sortOrder: Sort.ASC });
- expect(component.sensors[0].modifiedBy).toEqual('abc');
- expect(component.sensors[1].modifiedBy).toEqual('plm');
- expect(component.sensors[2].modifiedBy).toEqual('xyz');
+ it('isStartable() should return true only when a parser is KILLED', async(() => {
+ const component = Object.create( SensorParserListComponent.prototype );
+ const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+ let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
- component.onSort({ sortBy: 'modifiedBy', sortOrder: Sort.DSC });
- expect(component.sensors[0].modifiedBy).toEqual('xyz');
- expect(component.sensors[1].modifiedBy).toEqual('plm');
- expect(component.sensors[2].modifiedBy).toEqual('abc');
+ sensor.status.status = 'KILLED';
+ expect(component.isStartable(sensor)).toBe(true);
+
+ sensor.status.status = 'ACTIVE';
+ expect(component.isStartable(sensor)).toBe(false);
+
+ sensor.status.status = 'INACTIVE';
+ expect(component.isStartable(sensor)).toBe(false);
}));
- it('sort', async(() => {
- let component: SensorParserListComponent = fixture.componentInstance;
+ it('isEnableable() should return true only when a parser is ACTIVE', async(() => {
+ const component = Object.create( SensorParserListComponent.prototype );
+ const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+ let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
- component.sensors = [
- Object.assign(new SensorParserConfigHistory(), {
- sensorName: 'abc',
- config: {
- parserClassName: 'org.apache.metron.parsers.GrokParser',
- sensorTopic: 'abc'
- },
- createdBy: 'raghu',
- modifiedBy: 'abc',
- createdDate: '2016-11-25 09:09:12',
- modifiedByDate: '2016-11-25 09:09:12'
- })
- ];
-
- component.sensorsStatus = [
- Object.assign(new TopologyStatus(), {
- name: 'abc',
- status: 'ACTIVE',
- latency: '10',
- throughput: '23'
- })
- ];
-
- component.updateSensorStatus();
- expect(component.sensors[0]['status']).toEqual('Running');
- expect(component.sensors[0]['latency']).toEqual('10ms');
- expect(component.sensors[0]['throughput']).toEqual('23kb/s');
-
- component.sensorsStatus[0].status = 'KILLED';
- component.updateSensorStatus();
- expect(component.sensors[0]['status']).toEqual('Stopped');
- expect(component.sensors[0]['latency']).toEqual('-');
- expect(component.sensors[0]['throughput']).toEqual('-');
-
- component.sensorsStatus[0].status = 'INACTIVE';
- component.updateSensorStatus();
- expect(component.sensors[0]['status']).toEqual('Disabled');
- expect(component.sensors[0]['latency']).toEqual('-');
- expect(component.sensors[0]['throughput']).toEqual('-');
-
- component.sensorsStatus = [];
- component.updateSensorStatus();
- expect(component.sensors[0]['status']).toEqual('Stopped');
- expect(component.sensors[0]['latency']).toEqual('-');
- expect(component.sensors[0]['throughput']).toEqual('-');
+ sensor.status.status = 'KILLED';
+ expect(component.isEnableable(sensor)).toBe(false);
+
+ sensor.status.status = 'ACTIVE';
+ expect(component.isEnableable(sensor)).toBe(false);
+
+ sensor.status.status = 'INACTIVE';
+ expect(component.isEnableable(sensor)).toBe(true);
+ }));
+
+ it('isDisableable() should return true only when a parser is INACTIVE', async(() => {
+ const component = Object.create( SensorParserListComponent.prototype );
+ const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+ let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
+
+ sensor.status.status = 'KILLED';
+ expect(component.isDisableable(sensor)).toBe(false);
+
+ sensor.status.status = 'ACTIVE';
+ expect(component.isDisableable(sensor)).toBe(true);
+
+ sensor.status.status = 'INACTIVE';
+ expect(component.isDisableable(sensor)).toBe(false);
+ }));
+
+ it('isDeletedOrPhantom() should return true if a parser is deleted or a phantom', async(() => {
+ const component = Object.create( SensorParserListComponent.prototype );
+ const sensorParserConfig1 = new ParserConfigModel('TestConfigId01');
+ let sensor: ParserMetaInfoModel = { config: sensorParserConfig1, status: new TopologyStatus() };
+
+ expect(component.isDeletedOrPhantom(sensor)).toBe(false);
+
+ sensor.isDeleted = true;
+ expect(component.isDeletedOrPhantom(sensor)).toBe(true);
+
+ sensor.isDeleted = false;
+ sensor.isPhantom = true;
+ expect(component.isDeletedOrPhantom(sensor)).toBe(true);
+ }));
+
+ it('should hide parser controls when they cannot be used', async(() => {
+ fixture.detectChanges();
+
+ const stopButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="stop-parser-button"]'));
+ const startButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="start-parser-button"]'));
+ const enableButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="enable-parser-button"]'));
+ const disableButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="disable-parser-button"]'));
+ const selectWrappers = fixture.debugElement.queryAll(By.css('[data-qe-id="sensor-select"]'));
+ const editButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="edit-parser-button"]'));
+ const deleteButtons = fixture.debugElement.queryAll(By.css('[data-qe-id="delete-parser-button"]'));
+
+ // !KILLED status should show stop button
+ expect(stopButtons[0].properties.hidden).toBe(true);
+ expect(stopButtons[1].properties.hidden).toBe(false);
+ expect(stopButtons[2].properties.hidden).toBe(false);
+ expect(stopButtons[3].properties.hidden).toBe(true);
+ expect(stopButtons[4].properties.hidden).toBe(true);
+
+ // KILLED status should only show start button
+ expect(startButtons[0].properties.hidden).toBe(false);
+ expect(startButtons[1].properties.hidden).toBe(true);
+ expect(startButtons[2].properties.hidden).toBe(true);
+ expect(startButtons[3].properties.hidden).toBe(true);
+ expect(startButtons[4].properties.hidden).toBe(true);
+
+ // ACTIVE status should hide enable buttons
+ expect(enableButtons[0].properties.hidden).toBe(true);
+ expect(enableButtons[1].properties.hidden).toBe(false);
+ expect(enableButtons[2].properties.hidden).toBe(true);
+ expect(enableButtons[3].properties.hidden).toBe(true);
+ expect(enableButtons[4].properties.hidden).toBe(true);
+
+ // INACTIVE status should hide disable buttons
+ expect(disableButtons[0].properties.hidden).toBe(true);
+ expect(disableButtons[1].properties.hidden).toBe(true);
+ expect(disableButtons[2].properties.hidden).toBe(false);
+ expect(disableButtons[3].properties.hidden).toBe(true);
+ expect(disableButtons[4].properties.hidden).toBe(true);
+
+ // Edit button should hide if a parser or group is deleted
+ expect(editButtons[0].properties.hidden).toBe(false);
+ expect(editButtons[1].properties.hidden).toBe(false);
+ expect(editButtons[2].properties.hidden).toBe(false);
+ expect(editButtons[3].properties.hidden).toBe(true);
+ expect(editButtons[4].properties.hidden).toBe(false);
+
+ expect(deleteButtons[0].properties.hidden).toBe(false);
+ expect(deleteButtons[1].properties.hidden).toBe(false);
+ expect(deleteButtons[2].properties.hidden).toBe(false);
+ expect(deleteButtons[3].properties.hidden).toBe(true);
+ expect(deleteButtons[4].properties.hidden).toBe(false);
+
+ // select checkbox should hide if parser is deleted or a phantom
+ expect(selectWrappers[0].properties.hidden).toBe(false);
+ expect(selectWrappers[1].properties.hidden).toBe(false);
+ expect(selectWrappers[2].properties.hidden).toBe(false);
+ expect(selectWrappers[3].properties.hidden).toBe(true);
+ expect(selectWrappers[4].properties.hidden).toBe(true);
}));
});
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts
index 2694ab4..fd32cc8 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts
@@ -316,6 +316,9 @@ export class SensorParserListComponent implements OnInit {
this.onDisableSensor(sensor, null);
}
}
+
+ isSelected(sensor: ParserMetaInfoModel) {
+ return this.selectedSensors.includes(sensor.config.getName());
}
onDisableSensor(sensor: SensorParserConfigHistory, event) {
@@ -330,17 +333,36 @@ export class SensorParserListComponent implements OnInit {
this.toggleStartStopInProgress(sensor);
});
- if (event !== null) {
- event.stopPropagation();
- }
+ isStoppable(sensor: ParserMetaInfoModel) {
+ return sensor.status.status && sensor.status.status !== 'KILLED'
+ && !sensor.startStopInProgress
+ && this.isRootElement(sensor)
+ && !this.isDeletedOrPhantom(sensor);
}
- onEnableSensors() {
- for (let sensor of this.selectedSensors) {
- if (sensor['status'] === 'Disabled') {
- this.onEnableSensor(sensor, null);
- }
- }
+ isStartable(sensor: ParserMetaInfoModel) {
+ return (!sensor.status.status || sensor.status.status === 'KILLED')
+ && !sensor.startStopInProgress
+ && this.isRootElement(sensor)
+ && !this.isDeletedOrPhantom(sensor);
+ }
+
+ isEnableable(sensor: ParserMetaInfoModel) {
+ return sensor.status.status === 'INACTIVE'
+ && !sensor.startStopInProgress
+ && this.isRootElement(sensor)
+ && !this.isDeletedOrPhantom(sensor);
+ }
+
+ isDisableable(sensor: ParserMetaInfoModel) {
+ return sensor.status.status === 'ACTIVE'
+ && !sensor.startStopInProgress
+ && this.isRootElement(sensor)
+ && !this.isDeletedOrPhantom(sensor);
+ }
+
+ isDeletedOrPhantom(sensor: ParserMetaInfoModel) {
+ return !!sensor.isDeleted || !!sensor.isPhantom;
}
onEnableSensor(sensor: SensorParserConfigHistory, event) {