You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by rm...@apache.org on 2017/10/04 13:49:07 UTC

metron git commit: METRON-1161 Add ability to edit parser command line options in the management UI (merrimanr) closes apache/metron#737

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 <me...@gmail.com>
Authored: Wed Oct 4 08:48:51 2017 -0500
Committer: merrimanr <me...@apache.org>
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 {}