Repository: metron Updated Branches: refs/heads/master 6ffff0299 -> d69101a8d
METRON-1161 Add ability to edit parser command line options in the management UI (merrimanr) closes apache/metron#737 Project: http://git-wip-us.apache.org/repos/asf/metron/repo Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/d69101a8 Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/d69101a8 Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/d69101a8 Branch: refs/heads/master Commit: d69101a8d10620b04e90ef7f9c1885847bf0111e Parents: 6ffff02 Author: merrimanr <[email protected]> Authored: Wed Oct 4 08:48:51 2017 -0500 Committer: merrimanr <[email protected]> Committed: Wed Oct 4 08:48:51 2017 -0500 ---------------------------------------------------------------------- .../src/app/model/sensor-parser-config.ts | 12 ++ ...sensor-parser-config-readonly.component.html | 1 + ...sor-parser-config-readonly.component.spec.ts | 3 +- .../sensor-parser-config-readonly.component.ts | 11 ++ .../sensor-parser-config.component.html | 17 ++ .../sensor-parser-config.component.ts | 18 +- .../sensor-parser-config.module.ts | 3 +- .../sensor-raw-json.component.ts | 19 +-- .../sensor-storm-settings.component.html | 104 ++++++++++++ .../sensor-storm-settings.component.scss | 36 ++++ .../sensor-storm-settings.component.spec.ts | 168 +++++++++++++++++++ .../sensor-storm-settings.component.ts | 95 +++++++++++ .../sensor-storm-settings.module.ts | 29 ++++ 13 files changed, 487 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/model/sensor-parser-config.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/model/sensor-parser-config.ts b/metron-interface/metron-config/src/app/model/sensor-parser-config.ts index c0d907d..a2cd4ae 100644 --- a/metron-interface/metron-config/src/app/model/sensor-parser-config.ts +++ b/metron-interface/metron-config/src/app/model/sensor-parser-config.ts @@ -25,9 +25,21 @@ export class SensorParserConfig { invalidWriterClassName: string; parserConfig: {}; fieldTransformations: FieldTransformer[]; + numWorkers: number; + numAckers: number; + spoutParallelism: number; + spoutNumTasks: number; + parserParallelism: number; + parserNumTasks: number; + errorWriterParallelism: number; + errorWriterNumTasks: number; + spoutConfig: {}; + stormConfig: {}; constructor() { this.parserConfig = {}; this.fieldTransformations = []; + this.spoutConfig = {}; + this.stormConfig = {}; } } http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.html ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.html b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.html index d988dd1..365a8af 100644 --- a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.html +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.html @@ -39,6 +39,7 @@ <div *ngSwitchDefault class="row"> <div *ngIf="item.label!=''" class="col-xs-6 form-label" [ngClass]="{'form-value font-weight-bold': item.boldTitle}">{{ item.label }}</div> + <div *ngIf="item.model == 'sensorParserConfig'" class="col-xs-6 px-0 pull-left form-value">{{ sensorParserConfig[item.value] ? sensorParserConfig[item.value] : "-" }}</div> <div *ngIf="item.model == 'sensorParserConfigHistory'" class="col-xs-6 px-0 pull-left form-value">{{ sensorParserConfigHistory[item.value] ? sensorParserConfigHistory[item.value] : "-" }}</div> <div *ngIf="item.model == 'kafkaTopic'" class="col-xs-6 px-0 pull-left form-value">{{ kafkaTopic[item.value] ? kafkaTopic[item.value] : "-" }}</div> <div *ngIf="item.model == 'topologyStatus'" class="col-xs-6 px-0 pull-left form-value">{{ getTopologyStatus(item.value) }}</div> http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts index dbbec12..5afa953 100644 --- a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts @@ -301,7 +301,8 @@ describe('Component: SensorParserConfigReadonly', () => { })); it('should have metadata defined ', async(() => { - expect(component.editViewMetaData.length).toEqual(24); + // the expected value refers to the number of fields that should be visible in the readonly view + expect(component.editViewMetaData.length).toEqual(32); })); it('should have sensorsService with parserName and grokPattern defined and kafkaService defined', async(() => { http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.ts index b2cfef1..f28a206 100644 --- a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.ts +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.ts @@ -31,6 +31,7 @@ import {RiskLevelRule} from '../../model/risk-level-rule'; import {HdfsService} from '../../service/hdfs.service'; import {RestError} from '../../model/rest-error'; import {GrokValidationService} from '../../service/grok-validation.service'; +import {SensorParserConfig} from '../../model/sensor-parser-config'; @Component({ selector: 'metron-config-sensor-parser-readonly', @@ -43,6 +44,7 @@ export class SensorParserConfigReadonlyComponent implements OnInit { startStopInProgress: boolean = false; kafkaTopic: KafkaTopic = new KafkaTopic(); sensorParserConfigHistory: SensorParserConfigHistory = new SensorParserConfigHistory(); + sensorParserConfig: SensorParserConfig = new SensorParserConfig(); topologyStatus: TopologyStatus = new TopologyStatus(); sensorEnrichmentConfig: SensorEnrichmentConfig = new SensorEnrichmentConfig(); grokStatement: string = ''; @@ -68,6 +70,14 @@ export class SensorParserConfigReadonlyComponent implements OnInit { {label: 'THROUGHPUT', model: 'topologyStatus', value: 'throughput'}, {label: 'EMITTED(10 MIN)', model: 'topologyStatus', value: 'emitted'}, {label: 'ACKED(10 MIN)', model: 'topologyStatus', value: 'acked'}, + {label: 'NUM WORKERS', model: 'sensorParserConfig', value: 'numWorkers'}, + {label: 'NUM ACKERS', model: 'sensorParserConfig', value: 'numAckers'}, + {label: 'SPOUT PARALLELISM', model: 'sensorParserConfig', value: 'spoutParallelism'}, + {label: 'SPOUT NUM TASKS', model: 'sensorParserConfig', value: 'spoutNumTasks'}, + {label: 'PARSER PARALLELISM', model: 'sensorParserConfig', value: 'parserParallelism'}, + {label: 'PARSER NUM TASKS', model: 'sensorParserConfig', value: 'parserNumTasks'}, + {label: 'ERROR WRITER PARALLELISM', model: 'sensorParserConfig', value: 'errorWriterParallelism'}, + {label: 'ERROR NUM TASKS', model: 'sensorParserConfig', value: 'errorWriterNumTasks'}, {type: 'SPACER', model: '', value: ''}, @@ -102,6 +112,7 @@ export class SensorParserConfigReadonlyComponent implements OnInit { this.sensorParserConfigHistoryService.get(this.selectedSensorName).subscribe( (results: SensorParserConfigHistory) => { this.sensorParserConfigHistory = results; + this.sensorParserConfig = this.sensorParserConfigHistory.config; this.setGrokStatement(); this.setTransformsConfigKeys(); http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.html ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.html b/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.html index 33e8fd5..186e221 100644 --- a/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.html +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.html @@ -34,6 +34,10 @@ [(sensorEnrichmentConfig)]="sensorEnrichmentConfig" (hideThreatTriage)="hidePane(pane.THREATTRIAGE)"></metron-config-sensor-threat-triage> + <metron-config-sensor-storm-settings [hidden]="!showStormSettings" [showStormSettings]="showStormSettings" (hideStormSettings)="hidePane(pane.STORMSETTINGS)" + [(sensorParserConfig)]="sensorParserConfig" + (onStormSettingsChanged)="onStormSettingsChanged()"></metron-config-sensor-storm-settings> + <div class="metron-slider-pane-edit fill load-right-to-left dialog1x" style="overflow: auto" > <div style="height:100%"> @@ -105,6 +109,19 @@ </div> </div> + <div class="form-group" [ngClass]="{'panel-selected': showStormSettings }"> + <label attr.for="stellar">STORM SETTINGS</label> + <div class="input-group"> + <div class="form-control" style="resize: none;" readonly>Select</div> + <span class="input-group-btn"> + <button class="btn btn-default" type="button" (click)="showPane(pane.STORMSETTINGS)" readonly> + <i class="fa fa-columns" aria-hidden="true"></i> + <i class="fa fa-angle-double-right" style="padding-left: 3px" aria-hidden="true"></i> + </button> + </span> + </div> + </div> + <div [hidden]="!showAdvancedParserConfiguration"> <div class="form-group"> <div class="form-seperator-edit"></div> http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.ts index a4192f1..679d057 100644 --- a/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.ts +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.component.ts @@ -34,7 +34,7 @@ import {HdfsService} from '../../service/hdfs.service'; import {GrokValidationService} from '../../service/grok-validation.service'; export enum Pane { - GROK, RAWJSON, FIELDSCHEMA, THREATTRIAGE + GROK, RAWJSON, FIELDSCHEMA, THREATTRIAGE, STORMSETTINGS } export enum KafkaStatus { @@ -62,6 +62,7 @@ export class SensorParserConfigComponent implements OnInit { showRawJson: boolean = false; showFieldSchema: boolean = false; showThreatTriage: boolean = false; + showStormSettings: boolean = false; configValid = false; sensorNameValid = false; @@ -291,13 +292,6 @@ export class SensorParserConfigComponent implements OnInit { } onSave() { - let sensorParserConfigSave: SensorParserConfig = new SensorParserConfig(); - sensorParserConfigSave.parserConfig = {}; - sensorParserConfigSave.sensorTopic = this.sensorParserConfig.sensorTopic; - sensorParserConfigSave.parserClassName = this.sensorParserConfig.parserClassName; - sensorParserConfigSave.parserConfig = this.sensorParserConfig.parserConfig; - sensorParserConfigSave.fieldTransformations = this.sensorParserConfig.fieldTransformations; - if (!this.indexingConfigurations.hdfs.index) { this.indexingConfigurations.hdfs.index = this.sensorParserConfig.sensorTopic; } @@ -307,7 +301,7 @@ export class SensorParserConfigComponent implements OnInit { if (!this.indexingConfigurations.solr.index) { this.indexingConfigurations.solr.index = this.sensorParserConfig.sensorTopic; } - this.sensorParserConfigService.post(sensorParserConfigSave).subscribe( + this.sensorParserConfigService.post(this.sensorParserConfig).subscribe( sensorParserConfig => { if (this.isGrokParser(sensorParserConfig)) { this.hdfsService.post(this.sensorParserConfig.parserConfig['grokPath'], this.grokStatement).subscribe( @@ -326,7 +320,7 @@ export class SensorParserConfigComponent implements OnInit { this.metronAlerts.showErrorMessage(this.getMessagePrefix() + msg + error.message); }); this.metronAlerts.showSuccessMessage(this.getMessagePrefix() + ' Sensor ' + sensorParserConfig.sensorTopic); - this.sensorParserConfigService.dataChangedSource.next([sensorParserConfigSave]); + this.sensorParserConfigService.dataChangedSource.next([this.sensorParserConfig]); this.goBack(); }, (error: RestError) => { this.metronAlerts.showErrorMessage('Unable to save sensor config: ' + error.message); @@ -409,12 +403,16 @@ export class SensorParserConfigComponent implements OnInit { this.showFieldSchema = (pane === Pane.FIELDSCHEMA) ? visibilty : false; this.showRawJson = (pane === Pane.RAWJSON) ? visibilty : false; this.showThreatTriage = (pane === Pane.THREATTRIAGE) ? visibilty : false; + this.showStormSettings = (pane === Pane.STORMSETTINGS) ? visibilty : false; } onRawJsonChanged(): void { this.sensorFieldSchema.createFieldSchemaRows(); } + onStormSettingsChanged(): void { + } + onAdvancedConfigFormClose(): void { this.showAdvancedParserConfiguration = false; } http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.module.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.module.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.module.ts index 68e22f8..54e431e 100644 --- a/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.module.ts +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-config/sensor-parser-config.module.ts @@ -26,10 +26,11 @@ import {SensorGrokModule} from '../sensor-grok/sensor-grok.module'; import {SensorFieldSchemaModule} from '../sensor-field-schema/sensor-field-schema.module'; import {SensorRawJsonModule} from '../sensor-raw-json/sensor-raw-json.module'; import {SensorThreatTriageModule} from '../sensor-threat-triage/sensor-threat-triage.module'; +import {SensorStormSettingsModule} from '../sensor-storm-settings/sensor-storm-settings.module'; @NgModule ({ imports: [ routing, ReactiveFormsModule, SharedModule, NumberSpinnerModule, AdvancedConfigFormModule, - SensorGrokModule, SensorFieldSchemaModule, SensorRawJsonModule, SensorThreatTriageModule ], + SensorGrokModule, SensorFieldSchemaModule, SensorRawJsonModule, SensorThreatTriageModule, SensorStormSettingsModule ], declarations: [ SensorParserConfigComponent ] }) export class SensorParserConfigModule { } http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.ts index 38d9e90..4b56b62 100644 --- a/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.ts +++ b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.ts @@ -64,23 +64,8 @@ export class SensorRawJsonComponent implements OnChanges { onSave() { let newParsedSensorParserConfig = JSON.parse(this.newSensorParserConfig); - this.sensorParserConfig.sensorTopic = newParsedSensorParserConfig.sensorTopic; - this.sensorParserConfig.parserConfig = newParsedSensorParserConfig.parserConfig; - this.sensorParserConfig.parserClassName = newParsedSensorParserConfig.parserClassName; - this.sensorParserConfig.fieldTransformations = newParsedSensorParserConfig.fieldTransformations; - - if (newParsedSensorParserConfig.writerClassName != null) { - this.sensorParserConfig.writerClassName = newParsedSensorParserConfig.writerClassName; - } - if (newParsedSensorParserConfig.errorWriterClassName != null) { - this.sensorParserConfig.errorWriterClassName = newParsedSensorParserConfig.errorWriterClassName; - } - if (newParsedSensorParserConfig.filterClassName != null) { - this.sensorParserConfig.filterClassName = newParsedSensorParserConfig.filterClassName; - } - if (newParsedSensorParserConfig.invalidWriterClassName != null) { - this.sensorParserConfig.invalidWriterClassName = newParsedSensorParserConfig.invalidWriterClassName; - } + Object.keys(newParsedSensorParserConfig).filter(key => newParsedSensorParserConfig[key]) + .forEach(key => this.sensorParserConfig[key] = newParsedSensorParserConfig[key]); let newParsedSensorEnrichmentConfig = JSON.parse(this.newSensorEnrichmentConfig); this.sensorEnrichmentConfig.enrichment = Object.assign(new EnrichmentConfig(), newParsedSensorEnrichmentConfig.enrichment); http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.html ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.html b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.html new file mode 100644 index 0000000..0e33324 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.html @@ -0,0 +1,104 @@ +<!-- + Licensed to the Apache Software + Foundation (ASF) under one or more contributor license agreements. See the + NOTICE file distributed with this work for additional information regarding + copyright ownership. The ASF licenses this file to You under the Apache License, + Version 2.0 (the "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed + under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + OR CONDITIONS OF ANY KIND, either express or implied. See the License for + the specific language governing permissions and limitations under the License. + --> +<div class="metron-slider-pane-edit fill load-left-to-right dialog2x"> + + <div class="form-group"> + <div class="form-title">Configure Storm Settings</div> + <i class="fa fa-times pull-right close-button" aria-hidden="true" (click)="onCancel()"></i> + </div> + + + <form role="form" class="enrichments-form"> + <div class="form-group"> + <label attr.for="numWorkers">NUM WORKERS</label> + <div style="width: 25%"> + <metron-config-number-spinner name="numWorkers" [(ngModel)]="newSensorParserConfig.numWorkers" [min]="1"> </metron-config-number-spinner> + </div> + <div><label *ngIf="!newSensorParserConfig.numWorkers"><span class="warning-text"> This uses Storm defaults if not set </span></label></div> + <label *ngIf="newSensorParserConfig.numWorkers !== sensorParserConfig.numWorkers"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + <div class="form-group"> + <label attr.for="numAckers">NUM ACKERS</label> + <div style="width: 25%"> + <metron-config-number-spinner name="numAckers" [(ngModel)]="newSensorParserConfig.numAckers" [min]="1"> </metron-config-number-spinner> + </div> + <div><label *ngIf="!newSensorParserConfig.numAckers"><span class="warning-text"> This uses Storm defaults if not set </span></label></div> + <label *ngIf="newSensorParserConfig.numAckers !== sensorParserConfig.numAckers"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + <div class="form-group"> + <label attr.for="spoutParallelism">SPOUT PARALLELISM</label> + <div style="width: 25%"> + <metron-config-number-spinner name="spoutParallelism" [(ngModel)]="newSensorParserConfig.spoutParallelism" [min]="1"> </metron-config-number-spinner> + </div> + <label *ngIf="newSensorParserConfig.spoutParallelism !== sensorParserConfig.spoutParallelism"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + <div class="form-group"> + <label attr.for="spoutNumTasks">SPOUT NUM TASKS</label> + <div style="width: 25%"> + <metron-config-number-spinner name="spoutNumTasks" [(ngModel)]="newSensorParserConfig.spoutNumTasks" [min]="1"> </metron-config-number-spinner> + </div> + <label *ngIf="newSensorParserConfig.spoutNumTasks !== sensorParserConfig.spoutNumTasks"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + <div class="form-group"> + <label attr.for="parserParallelism">PARSER PARALLELISM</label> + <div style="width: 25%"> + <metron-config-number-spinner name="parserParallelism" [(ngModel)]="newSensorParserConfig.parserParallelism" [min]="1"> </metron-config-number-spinner> + </div> + <label *ngIf="newSensorParserConfig.parserParallelism !== sensorParserConfig.parserParallelism"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + <div class="form-group"> + <label attr.for="parserNumTasks">PARSER NUM TASKS</label> + <div style="width: 25%"> + <metron-config-number-spinner name="parserNumTasks" [(ngModel)]="newSensorParserConfig.parserNumTasks" [min]="1"> </metron-config-number-spinner> + </div> + <label *ngIf="newSensorParserConfig.parserNumTasks !== sensorParserConfig.parserNumTasks"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + <div class="form-group"> + <label attr.for="errorWriterParallelism">ERROR WRITER PARALLELISM</label> + <div style="width: 25%"> + <metron-config-number-spinner name="errorWriterParallelism" [(ngModel)]="newSensorParserConfig.errorWriterParallelism" [min]="1"> </metron-config-number-spinner> + </div> + <label *ngIf="newSensorParserConfig.errorWriterParallelism !== sensorParserConfig.errorWriterParallelism"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + <div class="form-group"> + <label attr.for="errorWriterNumTasks">ERROR WRITER NUM TASKS</label> + <div style="width: 25%"> + <metron-config-number-spinner name="errorWriterNumTasks" [(ngModel)]="newSensorParserConfig.errorWriterNumTasks" [min]="1"> </metron-config-number-spinner> + </div> + <label *ngIf="newSensorParserConfig.errorWriterNumTasks !== sensorParserConfig.errorWriterNumTasks"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + <div class="form-group"> + <label attr.for="newSpoutConfig">SPOUT CONFIG</label> + <metron-config-ace-editor [(ngModel)]="newSpoutConfig" [ngModelOptions]="{standalone: true}" [type]="'JSON'" [placeHolder]="'Enter Spout Config'"> </metron-config-ace-editor> + <label *ngIf="hasSpoutConfigChanged()"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + <div class="form-group"> + <label attr.for="newStormConfig">STORM CONFIG</label> + <metron-config-ace-editor [(ngModel)]="newStormConfig" [ngModelOptions]="{standalone: true}" [type]="'JSON'" [placeHolder]="'Enter Storm Config'"> </metron-config-ace-editor> + <label *ngIf="hasStormConfigChanged()"><span class="warning-text"> This change requires a parser topology restart </span></label> + </div> + + <div class="form-group"> + <div class="form-seperator-edit"></div> + <div class="button-row"> + <button type="submit" class="btn form-enable-disable-button" (click)="onSave()">SAVE</button> + <button class="btn form-enable-disable-button" (click)="onCancel()" >CANCEL</button> + </div> + </div> + + </form> + +</div> http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.scss ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.scss b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.scss new file mode 100644 index 0000000..3a9f5e3 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.scss @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +textarea +{ + height: auto; +} + +.form-group +{ + padding-left: 25px; + padding-right: 20px; + padding-bottom: 10px; +} + +.button-row { + padding-top: 10px; +} + +.form-enable-disable-button { + width: 16%; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.spec.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.spec.ts new file mode 100644 index 0000000..02f1fd9 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.spec.ts @@ -0,0 +1,168 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {async, TestBed, ComponentFixture} from '@angular/core/testing'; +import {SensorStormSettingsComponent} from './sensor-storm-settings.component'; +import {SharedModule} from '../../shared/shared.module'; +import {SimpleChanges, SimpleChange} from '@angular/core'; +import {SensorParserConfig} from '../../model/sensor-parser-config'; +import {SensorStormSettingsModule} from './sensor-storm-settings.module'; +import '../../rxjs-operators'; + +describe('Component: SensorStormSettingsComponent', () => { + + let fixture: ComponentFixture<SensorStormSettingsComponent>; + let component: SensorStormSettingsComponent; + let sensorParserConfig: SensorParserConfig = new SensorParserConfig(); + sensorParserConfig.sensorTopic = 'bro'; + sensorParserConfig.parserClassName = 'org.apache.metron.parsers.bro.BasicBroParser'; + sensorParserConfig.parserConfig = {}; + sensorParserConfig.numWorkers = 2; + sensorParserConfig.numAckers = 2; + sensorParserConfig.spoutParallelism = 2; + sensorParserConfig.spoutNumTasks = 2; + sensorParserConfig.parserParallelism = 2; + sensorParserConfig.parserNumTasks = 2; + sensorParserConfig.errorWriterParallelism = 2; + sensorParserConfig.errorWriterNumTasks = 2; + sensorParserConfig.spoutConfig = {'spoutConfigProp': 'spoutConfigValue1'}; + sensorParserConfig.stormConfig = {'stormConfigProp': 'stormConfigValue1'}; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [SharedModule, SensorStormSettingsModule], + }); + + fixture = TestBed.createComponent(SensorStormSettingsComponent); + component = fixture.componentInstance; + })); + + it('should create an instance', () => { + expect(component).toBeDefined(); + }); + + it('should create an instance', () => { + spyOn(component, 'init'); + let changes: SimpleChanges = {'showStormSettings': new SimpleChange(false, true)}; + + component.ngOnChanges(changes); + expect(component.init).toHaveBeenCalled(); + + changes = {'showStormSettings': new SimpleChange(true, false)}; + component.ngOnChanges(changes); + expect(component.init['calls'].count()).toEqual(1); + + fixture.destroy(); + }); + + it('should initialise the fields', () => { + + component.init(); + expect(component.newSensorParserConfig).toEqual(new SensorParserConfig()); + + component.sensorParserConfig = sensorParserConfig; + component.init(); + expect(component.newSensorParserConfig).toEqual(sensorParserConfig); + expect(component.newSpoutConfig).toEqual('{\n\t"spoutConfigProp": "spoutConfigValue1"\n}'); + expect(component.newStormConfig).toEqual('{\n\t"stormConfigProp": "stormConfigValue1"\n}'); + + fixture.destroy(); + }); + + it('should save the fields', () => { + spyOn(component.hideStormSettings, 'emit'); + spyOn(component.onStormSettingsChanged, 'emit'); + component.sensorParserConfig = sensorParserConfig; + component.init(); + component.newSensorParserConfig.numWorkers = 3; + component.newSensorParserConfig.numAckers = 3; + component.newSensorParserConfig.spoutParallelism = 3; + component.newSensorParserConfig.spoutNumTasks = 3; + component.newSensorParserConfig.parserParallelism = 3; + component.newSensorParserConfig.parserNumTasks = 3; + component.newSensorParserConfig.errorWriterParallelism = 3; + component.newSensorParserConfig.errorWriterNumTasks = 3; + component.newSpoutConfig = '{"spoutConfigProp": "spoutConfigValue2"}'; + component.newStormConfig = '{"stormConfigProp": "stormConfigValue2"}'; + component.onSave(); + expect(component.sensorParserConfig.numWorkers).toEqual(3); + expect(component.sensorParserConfig.numAckers).toEqual(3); + expect(component.sensorParserConfig.spoutParallelism).toEqual(3); + expect(component.sensorParserConfig.spoutNumTasks).toEqual(3); + expect(component.sensorParserConfig.parserParallelism).toEqual(3); + expect(component.sensorParserConfig.parserNumTasks).toEqual(3); + expect(component.sensorParserConfig.errorWriterParallelism).toEqual(3); + expect(component.sensorParserConfig.errorWriterNumTasks).toEqual(3); + expect(component.sensorParserConfig.spoutConfig).toEqual({'spoutConfigProp': 'spoutConfigValue2'}); + expect(component.sensorParserConfig.stormConfig).toEqual({'stormConfigProp': 'stormConfigValue2'}); + expect(component.hideStormSettings.emit).toHaveBeenCalled(); + expect(component.onStormSettingsChanged.emit).toHaveBeenCalled(); + }); + + it('hasSpoutConfigChanged should properly detect changes', () => { + let sensorParserConfigWithSpoutConfig = new SensorParserConfig(); + sensorParserConfigWithSpoutConfig.spoutConfig = {}; + component.sensorParserConfig = sensorParserConfigWithSpoutConfig; + component.newSpoutConfig = '{}'; + expect(component.hasSpoutConfigChanged()).toEqual(false); + + sensorParserConfigWithSpoutConfig.spoutConfig = {'field': 'value'}; + component.sensorParserConfig = sensorParserConfigWithSpoutConfig; + component.newSpoutConfig = '{ "field" : "value" }'; + expect(component.hasSpoutConfigChanged()).toEqual(false); + + sensorParserConfigWithSpoutConfig.spoutConfig = {'field': 'value'}; + component.sensorParserConfig = sensorParserConfigWithSpoutConfig; + component.newSpoutConfig = '{"field": "value2"}'; + expect(component.hasSpoutConfigChanged()).toEqual(true); + + component.newSpoutConfig = '{"field": "value2", }'; + expect(component.hasSpoutConfigChanged()).toEqual(true); + }); + + it('hasStormConfigChanged should properly detect changes', () => { + let sensorParserConfigWithStormConfig = new SensorParserConfig(); + sensorParserConfigWithStormConfig.stormConfig = {}; + component.sensorParserConfig = sensorParserConfigWithStormConfig; + component.newStormConfig = '{}'; + expect(component.hasStormConfigChanged()).toEqual(false); + + sensorParserConfigWithStormConfig.stormConfig = {'field': 'value'}; + component.sensorParserConfig = sensorParserConfigWithStormConfig; + component.newStormConfig = '{ "field" : "value" }'; + expect(component.hasStormConfigChanged()).toEqual(false); + + sensorParserConfigWithStormConfig.stormConfig = {'field': 'value'}; + component.sensorParserConfig = sensorParserConfigWithStormConfig; + component.newStormConfig = '{"field": "value2"}'; + expect(component.hasStormConfigChanged()).toEqual(true); + + component.newSpoutConfig = '{"field": "value2", }'; + expect(component.hasStormConfigChanged()).toEqual(true); + }); + + it('should hide panel', () => { + spyOn(component.hideStormSettings, 'emit'); + + component.onCancel(); + + expect(component.hideStormSettings.emit).toHaveBeenCalled(); + + fixture.destroy(); + }); +}); http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.ts new file mode 100644 index 0000000..a393da8 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.component.ts @@ -0,0 +1,95 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {Component, Input, EventEmitter, Output, OnChanges, SimpleChanges} from '@angular/core'; +import {SensorParserConfig} from '../../model/sensor-parser-config'; + +declare var ace: any; + +@Component({ + selector: 'metron-config-sensor-storm-settings', + templateUrl: './sensor-storm-settings.component.html', + styleUrls: ['./sensor-storm-settings.component.scss'] +}) + +export class SensorStormSettingsComponent implements OnChanges { + + @Input() showStormSettings: boolean; + @Input() sensorParserConfig: SensorParserConfig; + + @Output() hideStormSettings: EventEmitter<boolean> = new EventEmitter<boolean>(); + @Output() onStormSettingsChanged: EventEmitter<boolean> = new EventEmitter<boolean>(); + + newSensorParserConfig: SensorParserConfig = new SensorParserConfig(); + newSpoutConfig: string = '{}'; + newStormConfig: string = '{}'; + + ngOnChanges(changes: SimpleChanges) { + if (changes['showStormSettings'] && changes['showStormSettings'].currentValue) { + this.init(); + } + } + + init(): void { + if (this.sensorParserConfig) { + this.newSensorParserConfig = Object.assign(new SensorParserConfig(), this.sensorParserConfig); + this.newSpoutConfig = JSON.stringify(this.sensorParserConfig.spoutConfig, null, '\t'); + this.newStormConfig = JSON.stringify(this.sensorParserConfig.stormConfig, null, '\t'); + } + } + + onSave() { + this.sensorParserConfig.numWorkers = this.newSensorParserConfig.numWorkers; + this.sensorParserConfig.numAckers = this.newSensorParserConfig.numAckers; + this.sensorParserConfig.spoutParallelism = this.newSensorParserConfig.spoutParallelism; + this.sensorParserConfig.spoutNumTasks = this.newSensorParserConfig.spoutNumTasks; + this.sensorParserConfig.parserParallelism = this.newSensorParserConfig.parserParallelism; + this.sensorParserConfig.parserNumTasks = this.newSensorParserConfig.parserNumTasks; + this.sensorParserConfig.errorWriterParallelism = this.newSensorParserConfig.errorWriterParallelism; + this.sensorParserConfig.errorWriterNumTasks = this.newSensorParserConfig.errorWriterNumTasks; + this.sensorParserConfig.spoutConfig = JSON.parse(this.newSpoutConfig); + this.sensorParserConfig.stormConfig = JSON.parse(this.newStormConfig); + this.hideStormSettings.emit(true); + this.onStormSettingsChanged.emit(true); + } + + onCancel(): void { + this.hideStormSettings.emit(true); + } + + hasSpoutConfigChanged(): boolean { + try { + // serialize/deserialize to ignore formatting differences + return JSON.stringify(JSON.parse(this.newSpoutConfig), null, '\t') !== + JSON.stringify(this.sensorParserConfig.spoutConfig, null, '\t'); + } catch (err) { + // malformed json means it is being edited + return true; + } + } + + hasStormConfigChanged(): boolean { + try { + // serialize/deserialize to ignore formatting differences + return JSON.stringify(JSON.parse(this.newStormConfig), null, '\t') !== + JSON.stringify(this.sensorParserConfig.stormConfig, null, '\t'); + } catch (err) { + // malformed json means it is being edited + return true; + } + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/d69101a8/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.module.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.module.ts b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.module.ts new file mode 100644 index 0000000..910e3aa --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-storm-settings/sensor-storm-settings.module.ts @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { NgModule } from '@angular/core'; +import {SharedModule} from '../../shared/shared.module'; +import {SensorStormSettingsComponent} from './sensor-storm-settings.component'; +import {AceEditorModule} from '../../shared/ace-editor/ace-editor.module'; +import {NumberSpinnerModule} from '../../shared/number-spinner/number-spinner.module'; + +@NgModule ({ + imports: [ SharedModule, AceEditorModule, NumberSpinnerModule ], + declarations: [ SensorStormSettingsComponent ], + exports: [ SensorStormSettingsComponent ] +}) +export class SensorStormSettingsModule {}
