You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by ni...@apache.org on 2018/08/31 19:19:57 UTC

[01/50] [abbrv] metron git commit: METRON-1560 Update MPack to support Pcap panel (merrimanr) closes apache/metron#1124

Repository: metron
Updated Branches:
  refs/remotes/apache/feature/METRON-1699-create-batch-profiler 113ac0d3a -> f5eaef3c2


METRON-1560 Update MPack to support Pcap panel (merrimanr) closes apache/metron#1124


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/e78bc530
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/e78bc530
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/e78bc530

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: e78bc530260aa622e8263cda2b94be99194dffb5
Parents: 8763fba
Author: merrimanr <me...@gmail.com>
Authored: Mon Jul 23 09:08:12 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Mon Jul 23 09:08:12 2018 -0500

----------------------------------------------------------------------
 .../CURRENT/configuration/metron-rest-env.xml   | 25 ++++++++++++++++++++
 .../package/scripts/params/params_linux.py      |  5 +++-
 .../CURRENT/package/scripts/rest_commands.py    | 18 ++++++++++++--
 .../METRON/CURRENT/package/templates/metron.j2  |  4 ++++
 .../METRON/CURRENT/themes/metron_theme.json     | 10 ++++++++
 .../src/main/config/rest_application.yml        |  6 ++++-
 .../src/main/config/pcap.properties             |  2 +-
 7 files changed, 65 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/e78bc530/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
index 066b527..20f9767 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
@@ -168,4 +168,29 @@
         <description>The field name where the threat triage score can be found in the search indices. This setting primarily affects the Alerts UI.</description>
         <value>threat:triage:score</value>
     </property>
+    <property>
+        <name>pcap_base_path</name>
+        <display-name>Pcap Base Path</display-name>
+        <description>The HDFS path where pcap files are stored.  These files are the input to pcap queries.</description>
+        <value>/apps/metron/pcap/input</value>
+    </property>
+    <property>
+        <name>pcap_base_interim_result_path</name>
+        <display-name>Pcap Base Interim Result Path</display-name>
+        <description>The HDFS path where interim results of a pcap query are stored, before they are separated into pages.</description>
+        <value>/apps/metron/pcap/interim</value>
+    </property>
+    <property>
+        <name>pcap_final_output_path</name>
+        <display-name>Pcap Final Output Path</display-name>
+        <description>The HDFS path where the final results of a pcap query are stored.</description>
+        <value>/apps/metron/pcap/output</value>
+    </property>
+    <property>
+        <name>pcap_page_size</name>
+        <display-name>Pcap Page Size</display-name>
+        <description>The number of pcaps written to a page/file as a result of a pcap query.</description>
+        <value>10</value>
+    </property>
+
 </configuration>

http://git-wip-us.apache.org/repos/asf/metron/blob/e78bc530/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
index b8d6123..73d3469 100755
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
@@ -386,7 +386,10 @@ bolt_hdfs_rotation_policy_units = config['configurations']['metron-indexing-env'
 bolt_hdfs_rotation_policy_count = config['configurations']['metron-indexing-env']['bolt_hdfs_rotation_policy_count']
 
 # Pcap
-pcap_hdfs_dir = format("{metron_apps_hdfs_dir}/pcap")
+pcap_base_path = config['configurations']['metron-rest-env']['pcap_base_path']
+pcap_base_interim_result_path = config['configurations']['metron-rest-env']['pcap_base_interim_result_path']
+pcap_final_output_path = config['configurations']['metron-rest-env']['pcap_final_output_path']
+pcap_page_size = config['configurations']['metron-rest-env']['pcap_page_size']
 pcap_configured_flag_file = status_params.pcap_configured_flag_file
 
 # MapReduce

http://git-wip-us.apache.org/repos/asf/metron/blob/e78bc530/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py
index 2fd3f0d..463dca1 100755
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py
@@ -117,8 +117,22 @@ class RestCommands:
         metron_service.init_kafka_acl_groups(self.__params, groups)
 
     def init_pcap(self):
-        Logger.info("Creating HDFS location for Pcap")
-        self.__params.HdfsResource(self.__params.pcap_hdfs_dir,
+        Logger.info("Creating HDFS locations for Pcap")
+        self.__params.HdfsResource(self.__params.pcap_base_path,
+                                   type="directory",
+                                   action="create_on_execute",
+                                   owner=self.__params.metron_user,
+                                   group=self.__params.metron_group,
+                                   mode=0755,
+                                   )
+        self.__params.HdfsResource(self.__params.pcap_base_interim_result_path,
+                                   type="directory",
+                                   action="create_on_execute",
+                                   owner=self.__params.metron_user,
+                                   group=self.__params.metron_group,
+                                   mode=0755,
+                                   )
+        self.__params.HdfsResource(self.__params.pcap_final_output_path,
                                    type="directory",
                                    action="create_on_execute",
                                    owner=self.__params.metron_user,

http://git-wip-us.apache.org/repos/asf/metron/blob/e78bc530/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2 b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
index 049b6ee..278d6f8 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
@@ -40,3 +40,7 @@ SECURITY_ENABLED={{security_enabled|lower}}
 KAFKA_SECURITY_PROTOCOL="{{kafka_security_protocol}}"
 PARSER_TOPOLOGY_OPTIONS="/home/{{metron_user}}/.storm/storm.config"
 METRON_ESCALATION_TOPIC="{{metron_escalation_topic}}"
+PCAP_BASE_PATH="{{pcap_base_path}}"
+PCAP_BASE_INTERIM_RESULT_PATH="{{pcap_base_interim_result_path}}"
+PCAP_FINAL_OUTPUT_PATH="{{pcap_final_output_path}}"
+PCAP_PAGE_SIZE="{{pcap_page_size}}"

http://git-wip-us.apache.org/repos/asf/metron/blob/e78bc530/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
index 17b4460..9f5b04e 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
@@ -820,6 +820,10 @@
           "subsection-name": "subsection-rest"
         },
         {
+          "config": "metron-rest-env/pcap_page_size",
+          "subsection-name": "subsection-rest"
+        },
+        {
           "config": "metron-management-ui-env/metron_management_ui_port",
           "subsection-name": "subsection-management-ui"
         },
@@ -1421,6 +1425,12 @@
         }
       },
       {
+        "config": "metron-rest-env/pcap_page_size",
+        "widget": {
+          "type": "text-field"
+        }
+      },
+      {
         "config": "metron-management-ui-env/metron_management_ui_port",
         "widget": {
           "type": "text-field"

http://git-wip-us.apache.org/repos/asf/metron/blob/e78bc530/metron-interface/metron-rest/src/main/config/rest_application.yml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/config/rest_application.yml b/metron-interface/metron-rest/src/main/config/rest_application.yml
index 3999393..7486112 100644
--- a/metron-interface/metron-rest/src/main/config/rest_application.yml
+++ b/metron-interface/metron-rest/src/main/config/rest_application.yml
@@ -55,4 +55,8 @@ kerberos:
   keytab: ${METRON_SERVICE_KEYTAB}
 
 pcap:
-  pdml.script.path: ${METRON_HOME}/bin/pcap_to_pdml.sh
\ No newline at end of file
+  pdml.script.path: ${METRON_HOME}/bin/pcap_to_pdml.sh
+  base.path: ${PCAP_BASE_PATH}
+  base.interim.result.path: ${PCAP_BASE_INTERIM_RESULT_PATH}
+  final.output.path: ${PCAP_FINAL_OUTPUT_PATH}
+  page.size: ${PCAP_PAGE_SIZE}

http://git-wip-us.apache.org/repos/asf/metron/blob/e78bc530/metron-platform/metron-pcap-backend/src/main/config/pcap.properties
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/config/pcap.properties b/metron-platform/metron-pcap-backend/src/main/config/pcap.properties
index 7160178..848b588 100644
--- a/metron-platform/metron-pcap-backend/src/main/config/pcap.properties
+++ b/metron-platform/metron-pcap-backend/src/main/config/pcap.properties
@@ -27,6 +27,6 @@ kafka.pcap.start=UNCOMMITTED_EARLIEST
 kafka.pcap.numPackets=1000
 kafka.pcap.maxTimeMS=300000
 kafka.pcap.ts_scheme=FROM_KEY
-kafka.pcap.out=/apps/metron/pcap
+kafka.pcap.out=/apps/metron/pcap/input
 kafka.pcap.ts_granularity=MICROSECONDS
 kafka.spout.parallelism=1


[24/50] [abbrv] metron git commit: METRON-1712 PCAP UI - Input validation (tiborm via merrimanr) closes apache/metron#1142

Posted by ni...@apache.org.
METRON-1712 PCAP UI - Input validation (tiborm via merrimanr) closes apache/metron#1142


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/52de126f
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/52de126f
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/52de126f

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 52de126fc746181d6aac8ce002d6b814fd1e6cb5
Parents: afb9607
Author: tiborm <ti...@gmail.com>
Authored: Wed Aug 8 16:36:35 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Wed Aug 8 16:36:35 2018 -0500

----------------------------------------------------------------------
 .../src/app/pcap/model/pcap.mock.ts             |   4 +-
 .../src/app/pcap/model/pcap.request.ts          |   4 +-
 .../pcap-filters/pcap-filters.component.html    |  12 +-
 .../pcap-filters/pcap-filters.component.spec.ts | 228 ++++++++++++++++++-
 .../pcap/pcap-filters/pcap-filters.component.ts |   5 +
 5 files changed, 232 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
index c867fe9..bf02da8 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
@@ -22,9 +22,9 @@ export const fakePcapRequest = {
   startTimeMs: 0,
   endTimeMs: 0,
   ipSrcAddr: '0.0.0.0',
-  ipSrcPort: '80',
+  ipSrcPort: 80,
   ipDstAddr: '0.0.0.0',
-  ipDstPort: '80',
+  ipDstPort: 80,
   protocol: '*',
   packetFilter: '*',
   includeReverse: false

http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
index 91c2287..d00a6ac 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
@@ -20,9 +20,9 @@ export class PcapRequest {
   startTimeMs: number = 0;
   endTimeMs: number = 150000000000000000;
   ipSrcAddr: string = '';
-  ipSrcPort: string = '';
+  ipSrcPort: number = 0;
   ipDstAddr: string = '';
-  ipDstPort: string = '';
+  ipDstPort: number = 0;
   protocol: string = '';
   packetFilter: string = '';
   includeReverse: boolean = false;

http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
index f4133df..039307a 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
@@ -24,21 +24,21 @@
 
   <div class="form-group">
     <label for="ipSrcAddr">IP Source Address</label>
-    <input name="ipSrcAddr" #ipSrcAddr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipSrcAddr">
+    <input name="ipSrcAddr" #ipSrcAddr="ngModel" class="form-control" pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}$" [(ngModel)]="model.ipSrcAddr" data-qe-id="ip-src-addr">
   </div>
-  
+
   <div class="form-group">
     <label for="ipSrcPort">IP Source Port</label>
-    <input name="ipSrcPort" #ipSrcPort="ngModel" class="form-control" type="number" [(ngModel)]="model.ipSrcPort">
+    <input name="ipSrcPort" class="form-control" pattern="^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$" [(ngModel)]="ipSrcPort" data-qe-id="ip-src-port">
   </div>
 
   <div class="form-group"><label for="ipDstAddr">IP Dest Address</label>
-    <input name="ipDstAddr" #ipDstAddr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipDstAddr">
+    <input name="ipDstAddr" #ipDstAddr="ngModel" class="form-control" pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}$" [(ngModel)]="model.ipDstAddr" data-qe-id="ip-dst-addr">
   </div>
 
   <div class="form-group">
     <label for="ipDstPort">IP Dest Port</label>
-    <input id="ipDstPort" name="ipDstPort" #ipDstPort="ngModel" class="form-control" type="number" [(ngModel)]="model.ipDstPort">
+    <input id="ipDstPort" name="ipDstPort" class="form-control" pattern="^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$" [(ngModel)]="ipDstPort" data-qe-id="ip-dest-port">
   </div>
 
     <div class="form-group">
@@ -57,6 +57,6 @@
   </div>
 
   <div class="form-group">
-    <button type="submit" [ngClass]="{'disabled':!f.form.valid || queryRunning}" class="btn btn-primary btn-search" [disabled]="!f.form.valid || queryRunning"></button>
+    <button type="submit" [ngClass]="{'disabled':!f.form.valid || queryRunning}" class="btn btn-primary btn-search" [disabled]="!f.form.valid || queryRunning" data-qe-id="submit-button"></button>
   </div>
 </form>

http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
index c0f9c3b..4336b22 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
@@ -16,20 +16,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 import { By } from '@angular/platform-browser';
 
 import { PcapFiltersComponent } from './pcap-filters.component';
 import { FormsModule } from '../../../../node_modules/@angular/forms';
-import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { Component, Input, Output, EventEmitter, DebugElement } from '@angular/core';
 import { PcapRequest } from '../model/pcap.request';
-import { emit } from 'cluster';
 
 @Component({
   selector: 'app-date-picker',
   template: '<input type="text" [(value)]="date">',
 })
-class FakeDatePicker {
+class FakeDatePickerComponent {
   @Input() date: string;
   @Output() dateChange = new EventEmitter<string>();
 }
@@ -44,7 +43,7 @@ describe('PcapFiltersComponent', () => {
         FormsModule
       ],
       declarations: [
-        FakeDatePicker,
+        FakeDatePickerComponent,
         PcapFiltersComponent,
       ]
     })
@@ -86,13 +85,21 @@ describe('PcapFiltersComponent', () => {
     expect(component.model.ipSrcAddr).toBe('192.168.0.1');
   });
 
-  it('IP Source Port should be bound to the model', () => {
+  it('IP Source Port should be bound to the property', () => {
     let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipSrcPort"]');
     input.value = '9345';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.model.ipSrcPort).toBe(9345);
+    expect(component.ipSrcPort).toBe('9345');
+  });
+
+  it('IP Source Port should be converted to number on submit', () => {
+    component.ipSrcPort = '42';
+    component.search.emit = (model: PcapRequest) => {
+      expect(model.ipSrcPort).toBe(42);
+    };
+    component.onSubmit();
   });
 
   it('IP Dest Address should be bound to the model', () => {
@@ -104,13 +111,21 @@ describe('PcapFiltersComponent', () => {
     expect(component.model.ipDstAddr).toBe('256.0.0.7');
   });
 
-  it('IP Dest Port should be bound to the model', () => {
+  it('IP Dest Port should be bound to the property', () => {
     let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipDstPort"]');
     input.value = '8989';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.model.ipDstPort).toBe(8989);
+    expect(component.ipDstPort).toBe('8989');
+  });
+
+  it('IP Dest Port should be converted to number on submit', () => {
+    component.ipDstPort = '42';
+    component.search.emit = (model: PcapRequest) => {
+      expect(model.ipDstPort).toBe(42);
+    };
+    component.onSubmit();
   });
 
   it('Protocol should be bound to the model', () => {
@@ -144,7 +159,7 @@ describe('PcapFiltersComponent', () => {
     component.startTimeStr = '2220-12-12 12:12:12';
     component.search.emit = (model: PcapRequest) => {
       expect(model.startTimeMs).toBe(new Date(component.startTimeStr).getTime());
-    }
+    };
     component.onSubmit();
   });
 
@@ -152,7 +167,7 @@ describe('PcapFiltersComponent', () => {
     component.endTimeStr = '2320-03-13 13:13:13';
     component.search.emit = (model: PcapRequest) => {
       expect(model.endTimeMs).toBe(new Date(component.endTimeStr).getTime());
-    }
+    };
     component.onSubmit();
   });
 
@@ -187,4 +202,195 @@ describe('PcapFiltersComponent', () => {
     expect(fixture.componentInstance.model.hasOwnProperty('includeReverse')).toBeTruthy();
   });
 
+  describe('Filter validation', () => {
+
+    function setup() {
+      component.queryRunning = false;
+      fixture.detectChanges();
+    }
+
+    function getFieldWithSubmit(fieldId: string): { field: DebugElement, submit: DebugElement } {
+      const field = fixture.debugElement.query(By.css('[data-qe-id="' + fieldId  + '"]'));
+      const submit = fixture.debugElement.query(By.css('[data-qe-id="submit-button"]'));
+      return {
+        field,
+        submit
+      };
+    }
+
+    function setFieldValue(field: DebugElement, value: any) {
+      field.nativeElement.value = value;
+      field.nativeElement.dispatchEvent(new Event('input'));
+      fixture.detectChanges();
+    }
+
+    function isSubmitDisabled(submit: DebugElement): boolean {
+      return submit.classes['disabled'] && submit.nativeElement.disabled;
+    }
+
+    function isFieldInvalid(field: DebugElement): boolean {
+      return field.classes['ng-invalid'];
+    }
+
+    function tearDown(field: DebugElement) {
+      setFieldValue(field, '');
+    };
+
+    beforeEach(setup);
+
+    it('should disable the form if the ip source port is invalid', () => {
+      const invalidValues = [
+        '-42',
+        '-1',
+        'foobar',
+        '.',
+        '-',
+        '+',
+        'e',
+        'E',
+        '3.14',
+        '123456',
+        '65536',
+        '99999',
+        '2352363474576',
+        '1e3',
+      ];
+
+      invalidValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-src-port');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(true, 'the field should be invalid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(true, 'the submit button should be disabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+    it('should keep the form enabled if the ip source port is valid', () => {
+      const validValues = [
+        '8080',
+        '1024',
+        '3000',
+        '1',
+        '0',
+        '12345',
+        '65535',
+      ];
+
+      validValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-src-port');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+    it('should disable the form if the ip destination port is invalid', () => {
+      const invalidValues = [
+        '-42',
+        '-1',
+        'foobar',
+        '.',
+        '-',
+        '+',
+        'e',
+        'E',
+        '3.14',
+        '123456',
+        '65536',
+        '99999',
+        '2352363474576',
+        '1e3',
+      ];
+
+      invalidValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-dest-port');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(true, 'the field should be invalid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(true, 'the submit button should be disabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+    it('should keep the form enabled if the ip destination port is valid', () => {
+      const validValues = [
+        '8080',
+        '1024',
+        '3000',
+        '1',
+        '0',
+        '12345',
+        '65535',
+      ];
+
+      validValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-dest-port');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+
+    it('should disable the form if the ip source field is invalid', () => {
+      const invalidValues = [
+        'tst',
+        0o0,
+        0,
+        '111.111.111',
+        '222.222.222.222.222',
+        '333.333.333.333',
+      ];
+
+      invalidValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-src-addr');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(true, 'the field should be invalid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(true, 'the submit button should be disabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+    it('should keep the form enabled if the ip source field is valid', () => {
+      const validValues = [
+        '0.0.0.0',
+        '222.222.222.222',
+        '255.255.255.255',
+      ];
+
+      validValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-src-addr');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(false, 'tthe field should be valid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+  });
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
index 4f6a3dd..5bbdb67 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
@@ -33,6 +33,8 @@ export class PcapFiltersComponent implements OnInit {
 
   startTimeStr: string;
   endTimeStr: string;
+  ipSrcPort: string = '';
+  ipDstPort: string = '';
 
   model = new PcapRequest();
 
@@ -49,6 +51,9 @@ export class PcapFiltersComponent implements OnInit {
   onSubmit() {
     this.model.startTimeMs = new Date(this.startTimeStr).getTime();
     this.model.endTimeMs = new Date(this.endTimeStr).getTime();
+    this.model.ipSrcPort = +this.ipSrcPort;
+    this.model.ipDstPort = +this.ipDstPort;
+
     this.search.emit(this.model);
   }
 }


[32/50] [abbrv] metron git commit: METRON-1731: PCAP - Escape colons in output dir names (mmiklavc via mmiklavc) closes apache/metron#1155

Posted by ni...@apache.org.
METRON-1731: PCAP - Escape colons in output dir names (mmiklavc via mmiklavc) closes apache/metron#1155


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/73dc63e6
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/73dc63e6
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/73dc63e6

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 73dc63e671b55d22d251f4be1c217259f4f5dc71
Parents: 05316a4
Author: mmiklavc <mi...@gmail.com>
Authored: Fri Aug 10 12:42:47 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Fri Aug 10 12:42:47 2018 -0600

----------------------------------------------------------------------
 .../apache/metron/pcap/FixedPcapFilterTest.java | 286 ------------------
 .../org/apache/metron/pcap/PcapJobTest.java     | 290 -------------------
 .../apache/metron/pcap/QueryPcapFilterTest.java | 228 ---------------
 .../pcap/filter/fixed/FixedPcapFilter.java      |  14 +-
 .../pcap/filter/query/QueryPcapFilter.java      |  17 +-
 .../metron/pcap/mr/OutputDirFormatter.java      |  37 +++
 .../java/org/apache/metron/pcap/mr/PcapJob.java |   5 +-
 .../pcap/filter/fixed/FixedPcapFilterTest.java  | 271 ++++++++++++++++-
 .../pcap/filter/query/QueryPcapFilterTest.java  | 207 ++++++++++++-
 .../metron/pcap/mr/OutputDirFormatterTest.java  |  62 ++++
 .../org/apache/metron/pcap/mr/PcapJobTest.java  | 290 +++++++++++++++++++
 11 files changed, 877 insertions(+), 830 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/FixedPcapFilterTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/FixedPcapFilterTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/FixedPcapFilterTest.java
deleted file mode 100644
index 84969d3..0000000
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/FixedPcapFilterTest.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/**
- * 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.
- */
-
-package org.apache.metron.pcap;
-
-import org.apache.hadoop.conf.Configuration;
-import org.apache.metron.common.Constants;
-import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class FixedPcapFilterTest {
-  @Test
-  public void testTrivialEquality() throws Exception {
-    Configuration config = new Configuration();
-    final Map<String, String> fields = new HashMap<String, String>() {{
-      put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-      put(Constants.Fields.SRC_PORT.getName(), "0");
-      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-      put(Constants.Fields.DST_PORT.getName(), "1");
-      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
-    }};
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected Map<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-  }
-
-  @Test
-  public void testReverseTraffic() throws Exception {
-    Configuration config = new Configuration();
-    final Map<String, String> fields = new HashMap<String, String>() {{
-      put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-      put(Constants.Fields.SRC_PORT.getName(), "0");
-      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-      put(Constants.Fields.DST_PORT.getName(), "1");
-      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "true");
-    }};
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected Map<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected Map<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 1);
-            put(Constants.Fields.DST_ADDR.getName(), "src_ip");
-            put(Constants.Fields.DST_PORT.getName(), 0);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected Map<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "src_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertFalse(filter.test(null));
-    }
-  }
-@Test
-public void testMissingDstAddr() throws Exception {
-  Configuration config = new Configuration();
-  final HashMap<String, String> fields = new HashMap<String, String>() {{
-    put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-    put(Constants.Fields.SRC_PORT.getName(), "0");
-    put(Constants.Fields.DST_PORT.getName(), "1");
-    put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
-  }};
-  new FixedPcapFilter.Configurator().addToConfig(fields, config);
-  {
-    FixedPcapFilter filter = new FixedPcapFilter() {
-      @Override
-      protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-        return new HashMap<String, Object>() {{
-          put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-          put(Constants.Fields.SRC_PORT.getName(), 0);
-          put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-          put(Constants.Fields.DST_PORT.getName(), 1);
-        }};
-      }
-    };
-    filter.configure(config);
-    Assert.assertTrue(filter.test(null));
-  }
-  new FixedPcapFilter.Configurator().addToConfig(fields, config);
-  {
-    FixedPcapFilter filter = new FixedPcapFilter() {
-      @Override
-      protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-        return new HashMap<String, Object>() {{
-          put(Constants.Fields.SRC_ADDR.getName(), "src_ip1");
-          put(Constants.Fields.SRC_PORT.getName(), 0);
-          put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-          put(Constants.Fields.DST_PORT.getName(), 1);
-        }};
-      }
-    };
-    filter.configure(config);
-    Assert.assertFalse(filter.test(null));
-  }
-}
-  @Test
-  public void testMissingDstPort() throws Exception {
-    Configuration config = new Configuration();
-    final HashMap<String, String> fields = new HashMap<String, String>() {{
-      put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-      put(Constants.Fields.SRC_PORT.getName(), "0");
-      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
-    }};
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 100);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 100);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 100);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertFalse(filter.test(null));
-    }
-  }
-  @Test
-  public void testMissingSrcAddr() throws Exception {
-    Configuration config = new Configuration();
-    final HashMap<String, String> fields = new HashMap<String, String>() {{
-      put(Constants.Fields.SRC_PORT.getName(), "0");
-      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-      put(Constants.Fields.DST_PORT.getName(), "1");
-      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
-    }};
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-  }
-  @Test
-  public void testMissingSrcPort() throws Exception {
-    Configuration config = new Configuration();
-    final HashMap<String, String> fields = new HashMap<String, String>() {{
-      put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-      put(Constants.Fields.DST_PORT.getName(), "1");
-      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
-    }};
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-    new FixedPcapFilter.Configurator().addToConfig(fields, config);
-    {
-      FixedPcapFilter filter = new FixedPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 100);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
deleted file mode 100644
index 796c8a5..0000000
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/**
- * 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.
- */
-
-package org.apache.metron.pcap;
-
-import static java.lang.Long.toUnsignedString;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Timer;
-import java.util.TimerTask;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.FileSystem;
-import org.apache.hadoop.fs.Path;
-import org.apache.hadoop.io.BytesWritable;
-import org.apache.hadoop.io.LongWritable;
-import org.apache.hadoop.mapreduce.Job;
-import org.apache.hadoop.mapreduce.JobID;
-import org.apache.metron.common.utils.timestamp.TimestampConverters;
-import org.apache.metron.job.Finalizer;
-import org.apache.metron.job.JobStatus;
-import org.apache.metron.job.JobStatus.State;
-import org.apache.metron.job.Pageable;
-import org.apache.metron.job.Statusable;
-import org.apache.metron.pcap.config.FixedPcapConfig;
-import org.apache.metron.pcap.config.PcapOptions;
-import org.apache.metron.pcap.filter.PcapFilterConfigurator;
-import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
-import org.apache.metron.pcap.mr.PcapJob;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-public class PcapJobTest {
-
-  @Mock
-  private Job mrJob;
-  @Mock
-  private org.apache.hadoop.mapreduce.JobStatus mrStatus;
-  @Mock
-  private JobID jobId;
-  @Mock
-  private Finalizer<Path> finalizer;
-  private TestTimer timer;
-  private Pageable<Path> pageableResult;
-  private FixedPcapConfig config;
-  private Configuration hadoopConfig;
-  private FileSystem fileSystem;
-  private String jobIdVal = "job_abc_123";
-  private Path basePath;
-  private Path baseOutPath;
-  private long startTime;
-  private long endTime;
-  private int numReducers;
-  private int numRecordsPerFile;
-  private Path finalOutputPath;
-  private Map<String, String> fixedFields;
-  private PcapJob<Map<String, String>> testJob;
-
-  @Before
-  public void setup() throws IOException {
-    MockitoAnnotations.initMocks(this);
-    basePath = new Path("basepath");
-    baseOutPath = new Path("outpath");
-    startTime = 100;
-    endTime = 200;
-    numReducers = 5;
-    numRecordsPerFile = 5;
-    fixedFields = new HashMap<>();
-    fixedFields.put("ip_src_addr", "192.168.1.1");
-    hadoopConfig = new Configuration();
-    fileSystem = FileSystem.get(hadoopConfig);
-    finalOutputPath = new Path("finaloutpath");
-    when(jobId.toString()).thenReturn(jobIdVal);
-    when(mrStatus.getJobID()).thenReturn(jobId);
-    when(mrJob.getJobID()).thenReturn(jobId);
-    pageableResult = new PcapPages();
-    timer = new TestTimer();
-    // handles setting the file name prefix under the hood
-    config = new FixedPcapConfig(clock -> "clockprefix");
-    PcapOptions.HADOOP_CONF.put(config, hadoopConfig);
-    PcapOptions.FILESYSTEM.put(config, FileSystem.get(hadoopConfig));
-    PcapOptions.BASE_PATH.put(config, basePath);
-    PcapOptions.BASE_INTERIM_RESULT_PATH.put(config, baseOutPath);
-    PcapOptions.START_TIME_NS.put(config, startTime);
-    PcapOptions.END_TIME_NS.put(config, endTime);
-    PcapOptions.NUM_REDUCERS.put(config, numReducers);
-    PcapOptions.FIELDS.put(config, fixedFields);
-    PcapOptions.FILTER_IMPL.put(config, new FixedPcapFilter.Configurator());
-    PcapOptions.NUM_RECORDS_PER_FILE.put(config, numRecordsPerFile);
-    PcapOptions.FINAL_OUTPUT_PATH.put(config, finalOutputPath);
-    testJob = new TestJob<>(mrJob);
-    testJob.setStatusInterval(1);
-    testJob.setCompleteCheckInterval(1);
-    testJob.setTimer(timer);
-  }
-
-  private class TestJob<T> extends PcapJob<T> {
-
-    private final Job mrJob;
-
-    public TestJob(Job mrJob) {
-      this.mrJob = mrJob;
-    }
-
-    @Override
-    public Job createJob(Optional<String> jobName,
-        Path basePath,
-        Path outputPath,
-        long beginNS,
-        long endNS,
-        int numReducers,
-        T fields,
-        Configuration conf,
-        FileSystem fs,
-        PcapFilterConfigurator<T> filterImpl) throws IOException {
-      return mrJob;
-    }
-  }
-
-  private class TestTimer extends Timer {
-
-    private TimerTask task;
-
-    @Override
-    public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
-      this.task = task;
-    }
-
-    public void updateJobStatus() {
-      task.run();
-    }
-
-  }
-
-  @Test
-  public void partition_gives_value_in_range() throws Exception {
-    long start = 1473897600000000000L;
-    long end = TimestampConverters.MILLISECONDS.toNanoseconds(1473995927455L);
-    Configuration conf = new Configuration();
-    conf.set(PcapJob.START_TS_CONF, toUnsignedString(start));
-    conf.set(PcapJob.END_TS_CONF, toUnsignedString(end));
-    conf.set(PcapJob.WIDTH_CONF, "" + PcapJob.findWidth(start, end, 10));
-    PcapJob.PcapPartitioner partitioner = new PcapJob.PcapPartitioner();
-    partitioner.setConf(conf);
-    Assert.assertThat("Partition not in range",
-        partitioner.getPartition(new LongWritable(1473978789181189000L), new BytesWritable(), 10),
-        equalTo(8));
-  }
-
-  @Test
-  public void job_succeeds_synchronously() throws Exception {
-    pageableResult = new PcapPages(
-        Arrays.asList(new Path("1.txt"), new Path("2.txt"), new Path("3.txt")));
-    when(finalizer.finalizeJob(any())).thenReturn(pageableResult);
-    when(mrJob.isComplete()).thenReturn(true);
-    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
-    when(mrJob.getStatus()).thenReturn(mrStatus);
-    Statusable<Path> statusable = testJob.submit(finalizer, config);
-    timer.updateJobStatus();
-    Pageable<Path> results = statusable.get();
-    Assert.assertThat(results.getSize(), equalTo(3));
-    JobStatus status = statusable.getStatus();
-    Assert.assertThat(status.getState(), equalTo(State.SUCCEEDED));
-    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
-    Assert.assertThat(status.getJobId(), equalTo(jobIdVal));
-  }
-
-  @Test
-  public void job_fails_synchronously() throws Exception {
-    when(mrJob.isComplete()).thenReturn(true);
-    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.FAILED);
-    when(mrJob.getStatus()).thenReturn(mrStatus);
-    Statusable<Path> statusable = testJob.submit(finalizer, config);
-    timer.updateJobStatus();
-    Pageable<Path> results = statusable.get();
-    JobStatus status = statusable.getStatus();
-    Assert.assertThat(status.getState(), equalTo(State.FAILED));
-    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
-    Assert.assertThat(results.getSize(), equalTo(0));
-  }
-
-  @Test
-  public void job_fails_with_killed_status_synchronously() throws Exception {
-    when(mrJob.isComplete()).thenReturn(true);
-    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.KILLED);
-    when(mrJob.getStatus()).thenReturn(mrStatus);
-    Statusable<Path> statusable = testJob.submit(finalizer, config);
-    timer.updateJobStatus();
-    Pageable<Path> results = statusable.get();
-    JobStatus status = statusable.getStatus();
-    Assert.assertThat(status.getState(), equalTo(State.KILLED));
-    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
-    Assert.assertThat(results.getSize(), equalTo(0));
-  }
-
-  @Test
-  public void job_succeeds_asynchronously() throws Exception {
-    when(mrJob.isComplete()).thenReturn(true);
-    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
-    when(mrJob.getStatus()).thenReturn(mrStatus);
-    Statusable<Path> statusable = testJob.submit(finalizer, config);
-    timer.updateJobStatus();
-    JobStatus status = statusable.getStatus();
-    Assert.assertThat(status.getState(), equalTo(State.SUCCEEDED));
-    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
-  }
-
-  @Test
-  public void job_reports_percent_complete() throws Exception {
-    when(mrJob.isComplete()).thenReturn(false);
-    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.RUNNING);
-    when(mrJob.getStatus()).thenReturn(mrStatus);
-    when(mrJob.mapProgress()).thenReturn(0.5f);
-    when(mrJob.reduceProgress()).thenReturn(0f);
-    Statusable<Path> statusable = testJob.submit(finalizer, config);
-    timer.updateJobStatus();
-    JobStatus status = statusable.getStatus();
-    Assert.assertThat(status.getState(), equalTo(State.RUNNING));
-    Assert.assertThat(status.getDescription(), equalTo("map: 50.0%, reduce: 0.0%"));
-    Assert.assertThat(status.getPercentComplete(), equalTo(25.0));
-    when(mrJob.mapProgress()).thenReturn(1.0f);
-    when(mrJob.reduceProgress()).thenReturn(0.5f);
-    timer.updateJobStatus();
-    status = statusable.getStatus();
-    Assert.assertThat(status.getDescription(), equalTo("map: 100.0%, reduce: 50.0%"));
-    Assert.assertThat(status.getPercentComplete(), equalTo(75.0));
-  }
-
-  @Test
-  public void killing_job_causes_status_to_return_KILLED_state() throws Exception {
-    when(mrJob.isComplete()).thenReturn(false);
-    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.RUNNING);
-    when(mrJob.getStatus()).thenReturn(mrStatus);
-    Statusable<Path> statusable = testJob.submit(finalizer, config);
-    statusable.kill();
-    when(mrJob.isComplete()).thenReturn(true);
-    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.KILLED);
-    timer.updateJobStatus();
-    JobStatus status = statusable.getStatus();
-    Assert.assertThat(status.getState(), equalTo(State.KILLED));
-  }
-
-  @Test
-  public void handles_null_values_with_defaults() throws Exception {
-    PcapOptions.START_TIME_NS.put(config, null);
-    PcapOptions.END_TIME_NS.put(config, null);
-    PcapOptions.NUM_REDUCERS.put(config, null);
-    PcapOptions.NUM_RECORDS_PER_FILE.put(config, null);
-
-    pageableResult = new PcapPages(
-        Arrays.asList(new Path("1.txt"), new Path("2.txt"), new Path("3.txt")));
-    when(finalizer.finalizeJob(any())).thenReturn(pageableResult);
-    when(mrJob.isComplete()).thenReturn(true);
-    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
-    when(mrJob.getStatus()).thenReturn(mrStatus);
-    Statusable<Path> statusable = testJob.submit(finalizer, config);
-    timer.updateJobStatus();
-    Pageable<Path> results = statusable.get();
-    Assert.assertThat(results.getSize(), equalTo(3));
-    JobStatus status = statusable.getStatus();
-    Assert.assertThat(status.getState(), equalTo(State.SUCCEEDED));
-    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
-    Assert.assertThat(status.getJobId(), equalTo(jobIdVal));
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/QueryPcapFilterTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/QueryPcapFilterTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/QueryPcapFilterTest.java
deleted file mode 100644
index 7e3d55c..0000000
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/QueryPcapFilterTest.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/**
- * 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.
- */
-
-package org.apache.metron.pcap;
-
-import org.apache.hadoop.conf.Configuration;
-import org.apache.metron.common.Constants;
-import org.apache.metron.pcap.filter.PcapFilter;
-import org.apache.metron.pcap.filter.query.QueryPcapFilter;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.EnumMap;
-import java.util.HashMap;
-
-public class QueryPcapFilterTest {
-
-  @Test
-  public void testEmptyQueryFilter() throws Exception {
-    Configuration config = new Configuration();
-    String query = "";
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      PcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-  }
-
-  @Test
-  public void testTrivialEquality() throws Exception {
-    Configuration config = new Configuration();
-    String query = "ip_src_addr == 'src_ip' and ip_src_port == 0 and ip_dst_addr == 'dst_ip' and ip_dst_port == 1";
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      PcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-  }
-
-  @Test
-  public void testMissingDstAddr() throws Exception {
-    Configuration config = new Configuration();
-    String query = "ip_src_addr == 'src_ip' and ip_src_port == 0 and ip_dst_port == 1";
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      QueryPcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      QueryPcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip_no_match");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertFalse(filter.test(null));
-    }
-  }
-
-  @Test
-  public void testMissingDstPort() throws Exception {
-    Configuration config = new Configuration();
-    String query = "ip_src_addr == 'src_ip' and ip_src_port == 0 and ip_dst_addr == 'dst_ip'";
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      QueryPcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      QueryPcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 100);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      QueryPcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 100);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 100);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertFalse(filter.test(null));
-    }
-  }
-
-  @Test
-  public void testMissingSrcAddr() throws Exception {
-    Configuration config = new Configuration();
-    String query = "ip_src_port == 0 and ip_dst_addr == 'dst_ip' and ip_dst_port == 1";
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      QueryPcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-  }
-
-  @Test
-  public void testMissingSrcPort() throws Exception {
-    Configuration config = new Configuration();
-    String query = "ip_src_addr == 'src_ip' and ip_dst_addr == 'dst_ip' and ip_dst_port == 1";
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      QueryPcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 0);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-    new QueryPcapFilter.Configurator().addToConfig(query, config);
-    {
-      QueryPcapFilter filter = new QueryPcapFilter() {
-        @Override
-        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
-          return new HashMap<String, Object>() {{
-            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
-            put(Constants.Fields.SRC_PORT.getName(), 100);
-            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
-            put(Constants.Fields.DST_PORT.getName(), 1);
-          }};
-        }
-      };
-      filter.configure(config);
-      Assert.assertTrue(filter.test(null));
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilter.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilter.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilter.java
index 1954f1a..314bd85 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilter.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilter.java
@@ -19,20 +19,19 @@
 package org.apache.metron.pcap.filter.fixed;
 
 import com.google.common.base.Joiner;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import javax.xml.bind.DatatypeConverter;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.metron.common.Constants;
-import org.apache.metron.stellar.dsl.MapVariableResolver;
-import org.apache.metron.stellar.dsl.VariableResolver;
 import org.apache.metron.pcap.PacketInfo;
 import org.apache.metron.pcap.PcapHelper;
 import org.apache.metron.pcap.filter.PcapFilter;
 import org.apache.metron.pcap.filter.PcapFilterConfigurator;
 import org.apache.metron.pcap.filter.PcapFilters;
 import org.apache.metron.pcap.pattern.ByteArrayMatchingUtil;
-
-import javax.xml.bind.DatatypeConverter;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
+import org.apache.metron.stellar.dsl.MapVariableResolver;
+import org.apache.metron.stellar.dsl.VariableResolver;
 
 
 public class FixedPcapFilter implements PcapFilter {
@@ -48,7 +47,8 @@ public class FixedPcapFilter implements PcapFilter {
 
     @Override
     public String queryToString(Map<String, String> fields) {
-      return (fields == null ? "" : Joiner.on("_").join(fields.values()));
+      return (fields == null ? "" : Joiner.on("_").join(fields.values()).replaceAll("\\s", "_")
+      );
     }
   }
 

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/query/QueryPcapFilter.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/query/QueryPcapFilter.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/query/QueryPcapFilter.java
index 552a5ae..e7fff16 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/query/QueryPcapFilter.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/filter/query/QueryPcapFilter.java
@@ -18,19 +18,18 @@
 
 package org.apache.metron.pcap.filter.query;
 
+import java.util.Map;
 import org.apache.hadoop.conf.Configuration;
-import org.apache.metron.stellar.dsl.Context;
-import org.apache.metron.stellar.dsl.MapVariableResolver;
-import org.apache.metron.stellar.dsl.StellarFunctions;
-import org.apache.metron.stellar.common.StellarPredicateProcessor;
-import org.apache.metron.stellar.dsl.VariableResolver;
 import org.apache.metron.pcap.PacketInfo;
 import org.apache.metron.pcap.PcapHelper;
 import org.apache.metron.pcap.filter.PcapFilter;
 import org.apache.metron.pcap.filter.PcapFilterConfigurator;
 import org.apache.metron.pcap.filter.PcapFilters;
-
-import java.util.Map;
+import org.apache.metron.stellar.common.StellarPredicateProcessor;
+import org.apache.metron.stellar.dsl.Context;
+import org.apache.metron.stellar.dsl.MapVariableResolver;
+import org.apache.metron.stellar.dsl.StellarFunctions;
+import org.apache.metron.stellar.dsl.VariableResolver;
 
 public class QueryPcapFilter implements PcapFilter {
   public static final String QUERY_STR_CONFIG = "mql";
@@ -45,9 +44,7 @@ public class QueryPcapFilter implements PcapFilter {
     @Override
     public String queryToString(String fields) {
       return (fields == null ? "" :
-              fields.trim().replaceAll("\\s", "_")
-                      .replace(".", "-")
-                      .replace("'", "")
+          fields.trim().replaceAll("\\s", "_")
       );
     }
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/OutputDirFormatter.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/OutputDirFormatter.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/OutputDirFormatter.java
new file mode 100644
index 0000000..0d464d5
--- /dev/null
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/OutputDirFormatter.java
@@ -0,0 +1,37 @@
+/**
+ * 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.
+ */
+
+package org.apache.metron.pcap.mr;
+
+import com.google.common.base.Joiner;
+import java.util.UUID;
+
+public class OutputDirFormatter {
+
+  public String format(long beginNS, long endNS, String query) {
+    return sanitize(Joiner.on("_").join(beginNS, endNS, query, UUID.randomUUID().toString()));
+  }
+
+  private String sanitize(String path) {
+    return path
+        .replace(".", "-")
+        .replace("'", "")
+        .replace(":", "");
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
index 10f31b4..0f5ad4d 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
@@ -35,7 +35,6 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Timer;
 import java.util.TimerTask;
-import java.util.UUID;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.conf.Configurable;
@@ -83,6 +82,7 @@ public class PcapJob<T> implements Statusable<Path> {
   public static final String WIDTH_CONF = "width";
   private static final long THREE_SECONDS = 3000;
   private static final long ONE_SECOND = 1000;
+  private final OutputDirFormatter outputDirFormatter;
   private volatile Job mrJob; // store a running MR job reference for async status check
   private volatile JobStatus jobStatus; // overall job status, including finalization step
   private Finalizer<Path> finalizer;
@@ -187,6 +187,7 @@ public class PcapJob<T> implements Statusable<Path> {
   public PcapJob() {
     jobStatus = new JobStatus();
     finalResults = new PcapPages();
+    outputDirFormatter = new OutputDirFormatter();
     timer = new Timer();
     statusInterval = THREE_SECONDS;
     completeCheckInterval = ONE_SECOND;
@@ -271,7 +272,7 @@ public class PcapJob<T> implements Statusable<Path> {
       FileSystem fs,
       PcapFilterConfigurator<T> filterImpl)
       throws IOException, ClassNotFoundException, InterruptedException {
-    String outputDirName = Joiner.on("_").join(beginNS, endNS, filterImpl.queryToString(fields), UUID.randomUUID().toString());
+    String outputDirName = outputDirFormatter.format(beginNS, endNS, filterImpl.queryToString(fields));
     if(LOG.isDebugEnabled()) {
       DateFormat format = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.LONG
           , SimpleDateFormat.LONG

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilterTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilterTest.java b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilterTest.java
index af2afd3..b32f23f 100644
--- a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilterTest.java
+++ b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/fixed/FixedPcapFilterTest.java
@@ -18,14 +18,17 @@
 
 package org.apache.metron.pcap.filter.fixed;
 
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.metron.common.Constants;
+import org.apache.metron.pcap.PacketInfo;
 import org.junit.Assert;
 import org.junit.Test;
 
-import java.util.LinkedHashMap;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-
 public class FixedPcapFilterTest {
 
   @Test
@@ -66,4 +69,264 @@ public class FixedPcapFilterTest {
     }
   }
 
+  @Test
+  public void testTrivialEquality() throws Exception {
+    Configuration config = new Configuration();
+    final Map<String, String> fields = new HashMap<String, String>() {{
+      put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+      put(Constants.Fields.SRC_PORT.getName(), "0");
+      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+      put(Constants.Fields.DST_PORT.getName(), "1");
+      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
+    }};
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected Map<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testReverseTraffic() throws Exception {
+    Configuration config = new Configuration();
+    final Map<String, String> fields = new HashMap<String, String>() {{
+      put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+      put(Constants.Fields.SRC_PORT.getName(), "0");
+      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+      put(Constants.Fields.DST_PORT.getName(), "1");
+      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "true");
+    }};
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected Map<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected Map<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 1);
+            put(Constants.Fields.DST_ADDR.getName(), "src_ip");
+            put(Constants.Fields.DST_PORT.getName(), 0);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected Map<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "src_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertFalse(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testMissingDstAddr() throws Exception {
+    Configuration config = new Configuration();
+    final HashMap<String, String> fields = new HashMap<String, String>() {{
+      put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+      put(Constants.Fields.SRC_PORT.getName(), "0");
+      put(Constants.Fields.DST_PORT.getName(), "1");
+      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
+    }};
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip1");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertFalse(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testMissingDstPort() throws Exception {
+    Configuration config = new Configuration();
+    final HashMap<String, String> fields = new HashMap<String, String>() {{
+      put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+      put(Constants.Fields.SRC_PORT.getName(), "0");
+      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
+    }};
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 100);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 100);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 100);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertFalse(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testMissingSrcAddr() throws Exception {
+    Configuration config = new Configuration();
+    final HashMap<String, String> fields = new HashMap<String, String>() {{
+      put(Constants.Fields.SRC_PORT.getName(), "0");
+      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+      put(Constants.Fields.DST_PORT.getName(), "1");
+      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
+    }};
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testMissingSrcPort() throws Exception {
+    Configuration config = new Configuration();
+    final HashMap<String, String> fields = new HashMap<String, String>() {{
+      put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+      put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+      put(Constants.Fields.DST_PORT.getName(), "1");
+      put(Constants.Fields.INCLUDES_REVERSE_TRAFFIC.getName(), "false");
+    }};
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new FixedPcapFilter.Configurator().addToConfig(fields, config);
+    {
+      FixedPcapFilter filter = new FixedPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 100);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/query/QueryPcapFilterTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/query/QueryPcapFilterTest.java b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/query/QueryPcapFilterTest.java
index 061066e..2724e06 100644
--- a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/query/QueryPcapFilterTest.java
+++ b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/filter/query/QueryPcapFilterTest.java
@@ -18,18 +18,23 @@
 
 package org.apache.metron.pcap.filter.query;
 
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import java.util.HashMap;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.metron.common.Constants;
+import org.apache.metron.pcap.PacketInfo;
+import org.apache.metron.pcap.filter.PcapFilter;
 import org.junit.Assert;
 import org.junit.Test;
 
-import static org.hamcrest.CoreMatchers.equalTo;
-
 public class QueryPcapFilterTest {
 
   @Test
   public void string_representation_of_query_gets_formatted() throws Exception {
     String query = "ip_src_addr == 'srcIp' and ip_src_port == '80' and ip_dst_addr == 'dstIp' and ip_dst_port == '100' and protocol == 'protocol'";
     String actual = new QueryPcapFilter.Configurator().queryToString(query);
-    String expected = "ip_src_addr_==_srcIp_and_ip_src_port_==_80_and_ip_dst_addr_==_dstIp_and_ip_dst_port_==_100_and_protocol_==_protocol";
+    String expected = "ip_src_addr_==_'srcIp'_and_ip_src_port_==_'80'_and_ip_dst_addr_==_'dstIp'_and_ip_dst_port_==_'100'_and_protocol_==_'protocol'";
     Assert.assertThat("string representation did not match", actual, equalTo(expected));
   }
 
@@ -55,4 +60,200 @@ public class QueryPcapFilterTest {
     }
   }
 
+  @Test
+  public void testEmptyQueryFilter() throws Exception {
+    Configuration config = new Configuration();
+    String query = "";
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      PcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testTrivialEquality() throws Exception {
+    Configuration config = new Configuration();
+    String query = "ip_src_addr == 'src_ip' and ip_src_port == 0 and ip_dst_addr == 'dst_ip' and ip_dst_port == 1";
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      PcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testMissingDstAddr() throws Exception {
+    Configuration config = new Configuration();
+    String query = "ip_src_addr == 'src_ip' and ip_src_port == 0 and ip_dst_port == 1";
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      QueryPcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      QueryPcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip_no_match");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertFalse(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testMissingDstPort() throws Exception {
+    Configuration config = new Configuration();
+    String query = "ip_src_addr == 'src_ip' and ip_src_port == 0 and ip_dst_addr == 'dst_ip'";
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      QueryPcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      QueryPcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 100);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      QueryPcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 100);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 100);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertFalse(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testMissingSrcAddr() throws Exception {
+    Configuration config = new Configuration();
+    String query = "ip_src_port == 0 and ip_dst_addr == 'dst_ip' and ip_dst_port == 1";
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      QueryPcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+  }
+
+  @Test
+  public void testMissingSrcPort() throws Exception {
+    Configuration config = new Configuration();
+    String query = "ip_src_addr == 'src_ip' and ip_dst_addr == 'dst_ip' and ip_dst_port == 1";
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      QueryPcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 0);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+    new QueryPcapFilter.Configurator().addToConfig(query, config);
+    {
+      QueryPcapFilter filter = new QueryPcapFilter() {
+        @Override
+        protected HashMap<String, Object> packetToFields(PacketInfo pi) {
+          return new HashMap<String, Object>() {{
+            put(Constants.Fields.SRC_ADDR.getName(), "src_ip");
+            put(Constants.Fields.SRC_PORT.getName(), 100);
+            put(Constants.Fields.DST_ADDR.getName(), "dst_ip");
+            put(Constants.Fields.DST_PORT.getName(), 1);
+          }};
+        }
+      };
+      filter.configure(config);
+      Assert.assertTrue(filter.test(null));
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/OutputDirFormatterTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/OutputDirFormatterTest.java b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/OutputDirFormatterTest.java
new file mode 100644
index 0000000..ae1cda4
--- /dev/null
+++ b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/OutputDirFormatterTest.java
@@ -0,0 +1,62 @@
+/**
+ * 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.
+ */
+
+package org.apache.metron.pcap.mr;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.hadoop.fs.Path;
+import org.apache.metron.common.utils.timestamp.TimestampConverters;
+import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
+import org.apache.metron.pcap.filter.query.QueryPcapFilter;
+import org.junit.Test;
+
+public class OutputDirFormatterTest {
+
+  @Test
+  public void formats_directory_name_for_query_filter_types() throws Exception {
+    long beginNS = TimestampConverters.MILLISECONDS.toNanoseconds(System.currentTimeMillis());
+    long endNS = TimestampConverters.MILLISECONDS.toNanoseconds(System.currentTimeMillis());
+    String query = "ip_dst_addr == '207.28.210.1' and protocol == 'PROTOCOL: ICMP(1)";
+    String queryFilterString = new QueryPcapFilter.Configurator().queryToString(query);
+    OutputDirFormatter formatter = new OutputDirFormatter();
+    String actual = formatter.format(beginNS, endNS, queryFilterString);
+    assertThat("Formatted directory names did not match.", actual, containsString("_ip_dst_addr_==_207-28-210-1_and_protocol_==_PROTOCOL_ICMP(1)_"));
+    // no URI exception should be thrown with dir name
+    new Path(actual);
+  }
+
+  @Test
+  public void formats_directory_name_for_fixed_filter_types() throws Exception {
+    long beginNS = TimestampConverters.MILLISECONDS.toNanoseconds(System.currentTimeMillis());
+    long endNS = TimestampConverters.MILLISECONDS.toNanoseconds(System.currentTimeMillis());
+    Map<String, String> fields = new HashMap<>();
+    fields.put("ip_src_address", "207.28.210.1");
+    fields.put("protocol", "PROTOCOL: ICMP(1)");
+    String fixedFilterString = new FixedPcapFilter.Configurator().queryToString(fields);
+    OutputDirFormatter formatter = new OutputDirFormatter();
+    String actual = formatter.format(beginNS, endNS, fixedFilterString);
+    assertThat("Formatted directory names did not match.", actual, containsString("PROTOCOL_ICMP(1)_207-28-210-1"));
+    // no URI exception should be thrown with dir name
+    new Path(actual);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/73dc63e6/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
new file mode 100644
index 0000000..0f555d0
--- /dev/null
+++ b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
@@ -0,0 +1,290 @@
+/**
+ * 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.
+ */
+
+package org.apache.metron.pcap.mr;
+
+import static java.lang.Long.toUnsignedString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Timer;
+import java.util.TimerTask;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.io.BytesWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.mapreduce.Job;
+import org.apache.hadoop.mapreduce.JobID;
+import org.apache.metron.common.utils.timestamp.TimestampConverters;
+import org.apache.metron.job.Finalizer;
+import org.apache.metron.job.JobStatus;
+import org.apache.metron.job.JobStatus.State;
+import org.apache.metron.job.Pageable;
+import org.apache.metron.job.Statusable;
+import org.apache.metron.pcap.PcapPages;
+import org.apache.metron.pcap.config.FixedPcapConfig;
+import org.apache.metron.pcap.config.PcapOptions;
+import org.apache.metron.pcap.filter.PcapFilterConfigurator;
+import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class PcapJobTest {
+
+  @Mock
+  private Job mrJob;
+  @Mock
+  private org.apache.hadoop.mapreduce.JobStatus mrStatus;
+  @Mock
+  private JobID jobId;
+  @Mock
+  private Finalizer<Path> finalizer;
+  private TestTimer timer;
+  private Pageable<Path> pageableResult;
+  private FixedPcapConfig config;
+  private Configuration hadoopConfig;
+  private FileSystem fileSystem;
+  private String jobIdVal = "job_abc_123";
+  private Path basePath;
+  private Path baseOutPath;
+  private long startTime;
+  private long endTime;
+  private int numReducers;
+  private int numRecordsPerFile;
+  private Path finalOutputPath;
+  private Map<String, String> fixedFields;
+  private PcapJob<Map<String, String>> testJob;
+
+  @Before
+  public void setup() throws IOException {
+    MockitoAnnotations.initMocks(this);
+    basePath = new Path("basepath");
+    baseOutPath = new Path("outpath");
+    startTime = 100;
+    endTime = 200;
+    numReducers = 5;
+    numRecordsPerFile = 5;
+    fixedFields = new HashMap<>();
+    fixedFields.put("ip_src_addr", "192.168.1.1");
+    hadoopConfig = new Configuration();
+    fileSystem = FileSystem.get(hadoopConfig);
+    finalOutputPath = new Path("finaloutpath");
+    when(jobId.toString()).thenReturn(jobIdVal);
+    when(mrStatus.getJobID()).thenReturn(jobId);
+    when(mrJob.getJobID()).thenReturn(jobId);
+    pageableResult = new PcapPages();
+    timer = new TestTimer();
+    // handles setting the file name prefix under the hood
+    config = new FixedPcapConfig(clock -> "clockprefix");
+    PcapOptions.HADOOP_CONF.put(config, hadoopConfig);
+    PcapOptions.FILESYSTEM.put(config, FileSystem.get(hadoopConfig));
+    PcapOptions.BASE_PATH.put(config, basePath);
+    PcapOptions.BASE_INTERIM_RESULT_PATH.put(config, baseOutPath);
+    PcapOptions.START_TIME_NS.put(config, startTime);
+    PcapOptions.END_TIME_NS.put(config, endTime);
+    PcapOptions.NUM_REDUCERS.put(config, numReducers);
+    PcapOptions.FIELDS.put(config, fixedFields);
+    PcapOptions.FILTER_IMPL.put(config, new FixedPcapFilter.Configurator());
+    PcapOptions.NUM_RECORDS_PER_FILE.put(config, numRecordsPerFile);
+    PcapOptions.FINAL_OUTPUT_PATH.put(config, finalOutputPath);
+    testJob = new TestJob<>(mrJob);
+    testJob.setStatusInterval(1);
+    testJob.setCompleteCheckInterval(1);
+    testJob.setTimer(timer);
+  }
+
+  private class TestJob<T> extends PcapJob<T> {
+
+    private final Job mrJob;
+
+    public TestJob(Job mrJob) {
+      this.mrJob = mrJob;
+    }
+
+    @Override
+    public Job createJob(Optional<String> jobName,
+        Path basePath,
+        Path outputPath,
+        long beginNS,
+        long endNS,
+        int numReducers,
+        T fields,
+        Configuration conf,
+        FileSystem fs,
+        PcapFilterConfigurator<T> filterImpl) throws IOException {
+      return mrJob;
+    }
+  }
+
+  private class TestTimer extends Timer {
+
+    private TimerTask task;
+
+    @Override
+    public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
+      this.task = task;
+    }
+
+    public void updateJobStatus() {
+      task.run();
+    }
+
+  }
+
+  @Test
+  public void partition_gives_value_in_range() throws Exception {
+    long start = 1473897600000000000L;
+    long end = TimestampConverters.MILLISECONDS.toNanoseconds(1473995927455L);
+    Configuration conf = new Configuration();
+    conf.set(PcapJob.START_TS_CONF, toUnsignedString(start));
+    conf.set(PcapJob.END_TS_CONF, toUnsignedString(end));
+    conf.set(PcapJob.WIDTH_CONF, "" + PcapJob.findWidth(start, end, 10));
+    PcapJob.PcapPartitioner partitioner = new PcapJob.PcapPartitioner();
+    partitioner.setConf(conf);
+    Assert.assertThat("Partition not in range",
+        partitioner.getPartition(new LongWritable(1473978789181189000L), new BytesWritable(), 10),
+        equalTo(8));
+  }
+
+  @Test
+  public void job_succeeds_synchronously() throws Exception {
+    pageableResult = new PcapPages(
+        Arrays.asList(new Path("1.txt"), new Path("2.txt"), new Path("3.txt")));
+    when(finalizer.finalizeJob(any())).thenReturn(pageableResult);
+    when(mrJob.isComplete()).thenReturn(true);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
+    when(mrJob.getStatus()).thenReturn(mrStatus);
+    Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
+    Pageable<Path> results = statusable.get();
+    Assert.assertThat(results.getSize(), equalTo(3));
+    JobStatus status = statusable.getStatus();
+    Assert.assertThat(status.getState(), equalTo(State.SUCCEEDED));
+    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
+    Assert.assertThat(status.getJobId(), equalTo(jobIdVal));
+  }
+
+  @Test
+  public void job_fails_synchronously() throws Exception {
+    when(mrJob.isComplete()).thenReturn(true);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.FAILED);
+    when(mrJob.getStatus()).thenReturn(mrStatus);
+    Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
+    Pageable<Path> results = statusable.get();
+    JobStatus status = statusable.getStatus();
+    Assert.assertThat(status.getState(), equalTo(State.FAILED));
+    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
+    Assert.assertThat(results.getSize(), equalTo(0));
+  }
+
+  @Test
+  public void job_fails_with_killed_status_synchronously() throws Exception {
+    when(mrJob.isComplete()).thenReturn(true);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.KILLED);
+    when(mrJob.getStatus()).thenReturn(mrStatus);
+    Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
+    Pageable<Path> results = statusable.get();
+    JobStatus status = statusable.getStatus();
+    Assert.assertThat(status.getState(), equalTo(State.KILLED));
+    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
+    Assert.assertThat(results.getSize(), equalTo(0));
+  }
+
+  @Test
+  public void job_succeeds_asynchronously() throws Exception {
+    when(mrJob.isComplete()).thenReturn(true);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
+    when(mrJob.getStatus()).thenReturn(mrStatus);
+    Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
+    JobStatus status = statusable.getStatus();
+    Assert.assertThat(status.getState(), equalTo(State.SUCCEEDED));
+    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
+  }
+
+  @Test
+  public void job_reports_percent_complete() throws Exception {
+    when(mrJob.isComplete()).thenReturn(false);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.RUNNING);
+    when(mrJob.getStatus()).thenReturn(mrStatus);
+    when(mrJob.mapProgress()).thenReturn(0.5f);
+    when(mrJob.reduceProgress()).thenReturn(0f);
+    Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
+    JobStatus status = statusable.getStatus();
+    Assert.assertThat(status.getState(), equalTo(State.RUNNING));
+    Assert.assertThat(status.getDescription(), equalTo("map: 50.0%, reduce: 0.0%"));
+    Assert.assertThat(status.getPercentComplete(), equalTo(25.0));
+    when(mrJob.mapProgress()).thenReturn(1.0f);
+    when(mrJob.reduceProgress()).thenReturn(0.5f);
+    timer.updateJobStatus();
+    status = statusable.getStatus();
+    Assert.assertThat(status.getDescription(), equalTo("map: 100.0%, reduce: 50.0%"));
+    Assert.assertThat(status.getPercentComplete(), equalTo(75.0));
+  }
+
+  @Test
+  public void killing_job_causes_status_to_return_KILLED_state() throws Exception {
+    when(mrJob.isComplete()).thenReturn(false);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.RUNNING);
+    when(mrJob.getStatus()).thenReturn(mrStatus);
+    Statusable<Path> statusable = testJob.submit(finalizer, config);
+    statusable.kill();
+    when(mrJob.isComplete()).thenReturn(true);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.KILLED);
+    timer.updateJobStatus();
+    JobStatus status = statusable.getStatus();
+    Assert.assertThat(status.getState(), equalTo(State.KILLED));
+  }
+
+  @Test
+  public void handles_null_values_with_defaults() throws Exception {
+    PcapOptions.START_TIME_NS.put(config, null);
+    PcapOptions.END_TIME_NS.put(config, null);
+    PcapOptions.NUM_REDUCERS.put(config, null);
+    PcapOptions.NUM_RECORDS_PER_FILE.put(config, null);
+
+    pageableResult = new PcapPages(
+        Arrays.asList(new Path("1.txt"), new Path("2.txt"), new Path("3.txt")));
+    when(finalizer.finalizeJob(any())).thenReturn(pageableResult);
+    when(mrJob.isComplete()).thenReturn(true);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
+    when(mrJob.getStatus()).thenReturn(mrStatus);
+    Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
+    Pageable<Path> results = statusable.get();
+    Assert.assertThat(results.getSize(), equalTo(3));
+    JobStatus status = statusable.getStatus();
+    Assert.assertThat(status.getState(), equalTo(State.SUCCEEDED));
+    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
+    Assert.assertThat(status.getJobId(), equalTo(jobIdVal));
+  }
+
+}


[15/50] [abbrv] metron git commit: METRON-1676 PCAP UI - Add data range selector to the filter bar (tiborm via merrimanr) closes apache/metron#1119

Posted by ni...@apache.org.
METRON-1676 PCAP UI - Add data range selector to the filter bar (tiborm via merrimanr) closes apache/metron#1119


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/09a7f189
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/09a7f189
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/09a7f189

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 09a7f18994dc8254ca158139c44258cb7f1107ec
Parents: b445bfe
Author: tiborm <ti...@gmail.com>
Authored: Mon Aug 6 09:14:54 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Mon Aug 6 09:14:54 2018 -0500

----------------------------------------------------------------------
 .../pcap-filters/pcap-filters.component.html    |  30 ++--
 .../pcap-filters/pcap-filters.component.scss    |   2 +-
 .../pcap-filters/pcap-filters.component.spec.ts | 156 ++++++++++++++++++-
 .../pcap/pcap-filters/pcap-filters.component.ts |  19 ++-
 .../metron-alerts/src/app/pcap/pcap.module.ts   |   6 +-
 .../src/app/pcap/service/pcap.service.ts        |   4 +-
 .../shared/directives/alert-search.directive.ts |   2 +-
 7 files changed, 195 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
index abc4cf0..f4133df 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
@@ -12,23 +12,33 @@
   the specific language governing permissions and limitations under the License.
   -->
 <form (ngSubmit)="onSubmit()" #f="ngForm" class="form-inline pcap-search">
+
+  <div class="form-group">
+    <label>From</label>
+    <app-date-picker id="startTime" [(date)]="startTimeStr"> </app-date-picker>
+  </div>
+  <div class="form-group">
+    <label>To</label>
+    <app-date-picker id="endTime" [(date)]="endTimeStr"> </app-date-picker>
+  </div>
+
   <div class="form-group">
-    <label for="ip_src_addr">IP Source Address</label>
-    <input name="ip_src_addr" #ip_src_addr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipSrcAddr">
+    <label for="ipSrcAddr">IP Source Address</label>
+    <input name="ipSrcAddr" #ipSrcAddr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipSrcAddr">
   </div>
   
   <div class="form-group">
-    <label for="ip_src_port">IP Source Port</label>
-    <input name="ip_src_port" #ip_src_port="ngModel" class="form-control" type="number" [(ngModel)]="model.ipSrcPort">
+    <label for="ipSrcPort">IP Source Port</label>
+    <input name="ipSrcPort" #ipSrcPort="ngModel" class="form-control" type="number" [(ngModel)]="model.ipSrcPort">
   </div>
 
-  <div class="form-group"><label for="ip_dest_addr">IP Dest Address</label>
-    <input name="ip_dest_addr" #ip_dest_addr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipDstAddr">
+  <div class="form-group"><label for="ipDstAddr">IP Dest Address</label>
+    <input name="ipDstAddr" #ipDstAddr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipDstAddr">
   </div>
 
   <div class="form-group">
-    <label for="ip_dest_port">IP Dest Port</label>
-    <input id="ip_dest_port" name="ip_dest_port" #ip_dest_port="ngModel" class="form-control" type="number" [(ngModel)]="model.ipDstPort">
+    <label for="ipDstPort">IP Dest Port</label>
+    <input id="ipDstPort" name="ipDstPort" #ipDstPort="ngModel" class="form-control" type="number" [(ngModel)]="model.ipDstPort">
   </div>
 
     <div class="form-group">
@@ -37,8 +47,8 @@
     </div>
 
     <div class="form-group">
-        <label for="includeReverseTraffic">Include Reverse Traffic</label>
-        <input id="includeReverseTraffic" name="includeReverseTraffic" #includeReverseTraffic="ngModel" class="form-control" type="checkbox" [(ngModel)]="model.includeReverse">
+        <label for="includeReverse">Include Reverse Traffic</label>
+        <input id="includeReverse" name="includeReverse" #includeReverse="ngModel" class="form-control" type="checkbox" [(ngModel)]="model.includeReverse">
     </div>
 
   <div class="form-group">

http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
index 80bac08..b33e804 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
@@ -28,7 +28,7 @@
 }
 
 .form-group {
-  padding-right: 0.75em;
+  padding: 0 0.75em 0.75em 0;
   label {
     margin-right: 1em;
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
index 8134f1d..c0f9c3b 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
@@ -1,3 +1,4 @@
+
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -15,10 +16,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
 
 import { PcapFiltersComponent } from './pcap-filters.component';
 import { FormsModule } from '../../../../node_modules/@angular/forms';
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { PcapRequest } from '../model/pcap.request';
+import { emit } from 'cluster';
+
+@Component({
+  selector: 'app-date-picker',
+  template: '<input type="text" [(value)]="date">',
+})
+class FakeDatePicker {
+  @Input() date: string;
+  @Output() dateChange = new EventEmitter<string>();
+}
 
 describe('PcapFiltersComponent', () => {
   let component: PcapFiltersComponent;
@@ -29,18 +43,148 @@ describe('PcapFiltersComponent', () => {
       imports: [
         FormsModule
       ],
-      declarations: [ PcapFiltersComponent ]
+      declarations: [
+        FakeDatePicker,
+        PcapFiltersComponent,
+      ]
     })
     .compileComponents();
-  }));
 
-  beforeEach(() => {
     fixture = TestBed.createComponent(PcapFiltersComponent);
     component = fixture.componentInstance;
     fixture.detectChanges();
-  });
+  }));
 
-  it('should create', () => {
+  it('should be created', () => {
     expect(component).toBeTruthy();
   });
+
+  it('From date should be bound to the component', () => {
+    let input = fixture.debugElement.query(By.css('#startTime'));
+    const dateString = '2020-11-11 11:11:11';
+    input.componentInstance.dateChange.emit(dateString);
+    fixture.detectChanges();
+
+    expect(component.startTimeStr).toBe(dateString);
+  });
+
+  it('To date should be bound to the component', () => {
+    let input = fixture.debugElement.query(By.css('#endTime'));
+    const dateString = '2030-11-11 11:11:11';
+    input.componentInstance.dateChange.emit(dateString);
+    fixture.detectChanges();
+
+    expect(component.endTimeStr).toBe(dateString);
+  });
+
+  it('IP Source Address should be bound to the model', () => {
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipSrcAddr"]');
+    input.value = '192.168.0.1';
+    input.dispatchEvent(new Event('input'));
+    fixture.detectChanges();
+
+    expect(component.model.ipSrcAddr).toBe('192.168.0.1');
+  });
+
+  it('IP Source Port should be bound to the model', () => {
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipSrcPort"]');
+    input.value = '9345';
+    input.dispatchEvent(new Event('input'));
+    fixture.detectChanges();
+
+    expect(component.model.ipSrcPort).toBe(9345);
+  });
+
+  it('IP Dest Address should be bound to the model', () => {
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipDstAddr"]');
+    input.value = '256.0.0.7';
+    input.dispatchEvent(new Event('input'));
+    fixture.detectChanges();
+
+    expect(component.model.ipDstAddr).toBe('256.0.0.7');
+  });
+
+  it('IP Dest Port should be bound to the model', () => {
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipDstPort"]');
+    input.value = '8989';
+    input.dispatchEvent(new Event('input'));
+    fixture.detectChanges();
+
+    expect(component.model.ipDstPort).toBe(8989);
+  });
+
+  it('Protocol should be bound to the model', () => {
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="protocol"]');
+    input.value = 'TCP';
+    input.dispatchEvent(new Event('input'));
+    fixture.detectChanges();
+
+    expect(component.model.protocol).toBe('TCP');
+  });
+
+  it('Include Reverse Traffic should be bound to the model', () => {
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="includeReverse"]');
+    input.click();
+    input.dispatchEvent(new Event('input'));
+    fixture.detectChanges();
+
+    expect(component.model.includeReverse).toBe(true);
+  });
+
+  it('Text filter should be bound to the model', () => {
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="protocol"]');
+    input.value = 'TestStringFilter';
+    input.dispatchEvent(new Event('input'));
+    fixture.detectChanges();
+
+    expect(component.model.protocol).toBe('TestStringFilter');
+  });
+
+  it('From date should be converted to timestamp on submit', () => {
+    component.startTimeStr = '2220-12-12 12:12:12';
+    component.search.emit = (model: PcapRequest) => {
+      expect(model.startTimeMs).toBe(new Date(component.startTimeStr).getTime());
+    }
+    component.onSubmit();
+  });
+
+  it('To date should be converted to timestamp on submit', () => {
+    component.endTimeStr = '2320-03-13 13:13:13';
+    component.search.emit = (model: PcapRequest) => {
+      expect(model.endTimeMs).toBe(new Date(component.endTimeStr).getTime());
+    }
+    component.onSubmit();
+  });
+
+  it('Filter should have an output called search', () => {
+    component.search.subscribe((filterModel) => {
+      expect(filterModel).toBeDefined();
+    });
+    component.onSubmit();
+  });
+
+  it('Filter should emit search event on submit', () => {
+    spyOn(component.search, 'emit');
+    component.onSubmit();
+    expect(component.search.emit).toHaveBeenCalled();
+  });
+
+  it('Search event should contains the filter model', () => {
+    spyOn(component.search, 'emit');
+    component.onSubmit();
+    expect(component.search.emit).toHaveBeenCalledWith(component.model);
+  });
+
+  it('Filter model structure aka PcapRequest', () => {
+    expect(fixture.componentInstance.model.hasOwnProperty('startTimeMs')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('endTimeMs')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('ipSrcAddr')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('ipSrcPort')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('ipDstAddr')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('ipDstPort')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('protocol')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('packetFilter')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('includeReverse')).toBeTruthy();
+  });
+
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
index e16d71b..4f6a3dd 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
@@ -15,8 +15,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
-import { PcapRequest } from '../model/pcap.request'
+import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
+import * as moment from 'moment/moment';
+import { DEFAULT_TIMESTAMP_FORMAT } from '../../utils/constants';
+
+import { PcapRequest } from '../model/pcap.request';
 
 @Component({
   selector: 'app-pcap-filters',
@@ -28,14 +31,24 @@ export class PcapFiltersComponent implements OnInit {
   @Input() queryRunning: boolean = true;
   @Output() search: EventEmitter<PcapRequest> = new EventEmitter<PcapRequest>();
 
+  startTimeStr: string;
+  endTimeStr: string;
+
   model = new PcapRequest();
 
   constructor() { }
 
   ngOnInit() {
+    const endTime = new Date();
+    const startTime = new Date().setDate(endTime.getDate() - 5);
+
+    this.startTimeStr = moment(startTime).format(DEFAULT_TIMESTAMP_FORMAT);
+    this.endTimeStr = moment(endTime).format(DEFAULT_TIMESTAMP_FORMAT);
   }
 
   onSubmit() {
-    this.search.emit(this.model)
+    this.model.startTimeMs = new Date(this.startTimeStr).getTime();
+    this.model.endTimeMs = new Date(this.endTimeStr).getTime();
+    this.search.emit(this.model);
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
index c66b965..ef5c6c0 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
@@ -22,6 +22,8 @@ import { HttpModule } from '@angular/http';
 
 import { routing } from './pcap.routing';
 
+import { DatePickerModule } from '../shared/date-picker/date-picker.module';
+
 import { PcapListComponent } from './pcap-list/pcap-list.component';
 import { PcapPacketComponent } from './pcap-packet/pcap-packet.component';
 import { PcapFiltersComponent } from './pcap-filters/pcap-filters.component';
@@ -29,13 +31,15 @@ import { PcapPanelComponent } from './pcap-panel/pcap-panel.component';
 import { PcapPacketLineComponent } from './pcap-packet-line/pcap-packet-line.component';
 
 import { PcapService } from './service/pcap.service'
+
  
 @NgModule({
   imports: [
     routing,
     CommonModule,
     FormsModule,
-    HttpModule    
+    HttpModule,
+    DatePickerModule,
   ],
   declarations: [
     PcapListComponent,

http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
index ba209c4..6fd3e62 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
@@ -67,7 +67,7 @@ export class PcapService {
             .onErrorResumeNext();
     }
 
-    public getDownloadUrl(id: string, pageNo: number) {
-      return `/api/v1/pcap/${id}/raw?page=${pageNo}`;
+    public getDownloadUrl(id: string, pageId: number) {
+      return `/api/v1/pcap/${id}/raw?page=${pageId}`;
     }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts b/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts
index c512233..eb69b56 100644
--- a/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts
+++ b/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts
@@ -130,7 +130,7 @@ export class AlertSearchDirective implements AfterViewInit, OnChanges {
 
   private handleMouseEvent (callback: Function) {
     clearTimeout(this.mouseEventTimer);
-    this.mouseEventTimer = setTimeout(() => { callback(); }, 100);
+    this.mouseEventTimer = window.setTimeout(() => { callback(); }, 100);
   }
 
   private mouseover($event) {


[35/50] [abbrv] metron git commit: METRON-1733 PCAP UI - PCAP queries don' t work on Safari (sardell via merrimanr) closes apache/metron#1158

Posted by ni...@apache.org.
METRON-1733 PCAP UI - PCAP queries don&apos;t work on Safari (sardell via merrimanr) closes apache/metron#1158


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/bce9b903
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/bce9b903
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/bce9b903

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: bce9b903fbd3436ea0c41eb8d75da279e16ce922
Parents: ba0a609
Author: sardell <sa...@hortonworks.com>
Authored: Mon Aug 13 16:04:45 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Mon Aug 13 16:04:45 2018 -0500

----------------------------------------------------------------------
 .../src/app/pcap/pcap-filters/pcap-filters.component.ts          | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/bce9b903/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
index df91ae3..b23a2e2 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
@@ -59,8 +59,8 @@ export class PcapFiltersComponent implements OnInit, OnChanges {
   }
 
   onSubmit() {
-    this.model.startTimeMs = new Date(this.startTimeStr).getTime();
-    this.model.endTimeMs = new Date(this.endTimeStr).getTime();
+    this.model.startTimeMs = moment(this.startTimeStr, DEFAULT_TIMESTAMP_FORMAT).valueOf();
+    this.model.endTimeMs = moment(this.endTimeStr, DEFAULT_TIMESTAMP_FORMAT).valueOf();
     if (this.ipSrcPort !== '') {
       this.model.ipSrcPort = +this.ipSrcPort;
     } else {


[26/50] [abbrv] metron git commit: METRON-1713 PCAP UI - Add a way to kill a pcap job (tiborm via merrimanr) closes apache/metron#1143

Posted by ni...@apache.org.
METRON-1713 PCAP UI - Add a way to kill a pcap job (tiborm via merrimanr) closes apache/metron#1143


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/14dcb2d9
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/14dcb2d9
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/14dcb2d9

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 14dcb2d90581835d8206c65918c24e8cb04bfd06
Parents: 5b3e2c3
Author: tiborm <ti...@gmail.com>
Authored: Thu Aug 9 08:33:21 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Thu Aug 9 08:33:21 2018 -0500

----------------------------------------------------------------------
 .../pcap/pcap-panel/pcap-panel.component.html   |   7 +-
 .../pcap/pcap-panel/pcap-panel.component.scss   |  33 +++++
 .../pcap-panel/pcap-panel.component.spec.ts     | 130 +++++++++++++++++++
 .../app/pcap/pcap-panel/pcap-panel.component.ts |  42 +++++-
 .../src/app/pcap/service/pcap.service.ts        |  10 +-
 5 files changed, 215 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/14dcb2d9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
index 950f49c..0dda268 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
@@ -15,8 +15,11 @@
   <div class="panel-header">
     <app-pcap-filters [queryRunning]="queryRunning" (search)="onSearch($event)"></app-pcap-filters>
   </div>
-  <div *ngIf="queryRunning" class="progress pcap-progress-background">
-    <div class="progress-bar progress-bar-animated pcap-progress" role="progressbar" attr.aria-valuenow="{{progressWidth}}" aria-valuemin="0" aria-valuemax="100" [ngStyle]="{'width': progressWidth + '%'}">{{progressWidth}}%</div>
+  <div *ngIf="queryRunning" class="pcap-progress-wrapper">
+    <div class="progress pcap-progress-background">
+      <div class="progress-bar progress-bar-animated pcap-progress" role="progressbar" attr.aria-valuenow="{{progressWidth}}" aria-valuemin="0" aria-valuemax="100" [ngStyle]="{'width': progressWidth + '%'}">{{progressWidth}}%</div>
+    </div>
+    <button data-qe-id="pcap-cancel-query-button" class="pcap-cancel-query-button btn btn-primary btn-sm" (click)="cancelQuery()" [disabled]="!queryId"></button>
   </div>
   <div *ngIf="errorMsg" class="alert alert-danger" role="alert" data-qe-id="error">
     {{ errorMsg }}

http://git-wip-us.apache.org/repos/asf/metron/blob/14dcb2d9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
index 8989bf9..523f5ce 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
@@ -31,4 +31,37 @@
 
 .progress-bar {
   width: 0;
+  height: 34px;
+  line-height: 34px;
+  vertical-align: middle;
+  font-size: 0.875rem;
+}
+
+.pcap-progress-wrapper {
+  position: relative;
+  padding-right: 55px;
+}
+
+.pcap-cancel-query-button {
+  position: absolute;
+  top: 0;
+  right: 0;
+  padding-top: 6px;
+  padding-bottom: 6px;
+  background: $icon-button-background;
+  min-width: 42px;
+  padding-left: 0;
+  padding-right: 0;
+  border: 1px solid $blue-chill !important;
+  cursor: pointer;
+
+  &:focus {
+    box-shadow: none;
+  }
+
+  &::before {
+    font-family: "FontAwesome";
+    content: '\f00d';
+    color: $piction-blue;
+  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/14dcb2d9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
index 0804b79..9dacc7f 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
@@ -50,6 +50,9 @@ class FakePcapService {
     return '';
   }
   submitRequest() {}
+  cancelQuery() {
+    return defer(() => Promise.resolve());
+  }
 }
 
 describe('PcapPanelComponent', () => {
@@ -327,6 +330,133 @@ describe('PcapPanelComponent', () => {
     expect(fixture.debugElement.query(By.css('app-pcap-list'))).toBeDefined();
   }));
 
+  it('should render a cancel button only if a query runs', () => {
+    component.queryRunning = false;
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]'))).toBeFalsy();
+
+    component.queryRunning = true;
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]'))).toBeDefined();
+  });
+
+  it('should hide the progress bar if the user clicks on the cancel button', fakeAsync(() => {
+    component.queryRunning = true;
+    component.queryId = 'testid';
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeDefined();
+
+    const cancelBtn = fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]'));
+    const cancelBtnEl = cancelBtn.nativeElement;
+
+    cancelBtnEl.click();
+    tick();
+    fixture.detectChanges();
+
+    expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeFalsy();
+  }));
+
+  it('should hide the progress bar if the cancellation request fails', fakeAsync(() => {
+    const restError = new RestError();
+    pcapService.cancelQuery = jasmine.createSpy('cancelQuery').and.returnValue(
+      defer(() => Promise.reject(restError))
+    );
+
+    component.queryRunning = true;
+    component.queryId = 'testid';
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeDefined();
+
+    const cancelBtn = fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]'));
+    const cancelBtnEl = cancelBtn.nativeElement;
+
+    cancelBtnEl.click();
+    tick();
+    fixture.detectChanges();
+
+    expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeFalsy();
+  }));
+
+  it('should show an error message if the cancellation request fails', fakeAsync(() => {
+    const restError = new RestError();
+    restError.message = 'cancellation error';
+    pcapService.cancelQuery = jasmine.createSpy('cancelQuery').and.returnValue(
+      defer(() => Promise.reject(restError))
+    );
+
+    component.queryRunning = true;
+    component.queryId = 'testid';
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="error"]'))).toBeFalsy();
+
+    const cancelBtn = fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]'));
+    const cancelBtnEl = cancelBtn.nativeElement;
+
+    cancelBtnEl.click();
+    tick();
+    fixture.detectChanges();
+
+    expect(
+      fixture.debugElement.query(By.css('[data-qe-id="error"]'))
+        .nativeElement
+        .textContent.trim()
+    ).toBe(`Response message: ${restError.message}. Something went wrong with the cancellation!`);
+  }));
+
+  it('cancel button should be disabled till we get back a queryId', fakeAsync(() => {
+    component.queryRunning = true;
+    component.queryId = '';
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]')).nativeElement.disabled).toBeTruthy();
+  }));
+
+  it('cancel button should be enabled when we have a queryId', fakeAsync(() => {
+    component.queryRunning = true;
+    component.queryId = 'testid';
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]')).nativeElement.disabled).toBeFalsy();
+  }));
+
+  it('queryId should be emptied if the cancellation request fails', fakeAsync(() => {
+    const restError = new RestError();
+    restError.message = 'cancellation error';
+    pcapService.cancelQuery = jasmine.createSpy('cancelQuery').and.returnValue(
+      defer(() => Promise.reject(restError))
+    );
+
+    component.queryRunning = true;
+    component.queryId = 'testid';
+    fixture.detectChanges();
+
+    const cancelBtn = fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]'));
+    const cancelBtnEl = cancelBtn.nativeElement;
+
+    cancelBtnEl.click();
+    tick();
+    fixture.detectChanges();
+
+    expect(component.queryId).toBeFalsy();
+  }));
+
+  it('queryId should be emptied if the cancellation success', fakeAsync(() => {
+    pcapService.cancelQuery = jasmine.createSpy('cancelQuery').and.returnValue(
+      defer(() => Promise.resolve())
+    );
+
+    component.queryRunning = true;
+    component.queryId = 'testid';
+    fixture.detectChanges();
+
+    const cancelBtn = fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]'));
+    const cancelBtnEl = cancelBtn.nativeElement;
+
+    cancelBtnEl.click();
+    tick();
+    fixture.detectChanges();
+
+    expect(component.queryId).toBeFalsy();
+  }));
+
   it('should handle get packet 404', fakeAsync(() => {
     const searchResponse = new PcapStatusResponse();
     searchResponse.jobId = '42';

http://git-wip-us.apache.org/repos/asf/metron/blob/14dcb2d9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
index 8e4ced0..b11d9df 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
@@ -15,7 +15,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, Input } from '@angular/core';
+import { Component, Input, OnDestroy } from '@angular/core';
 
 import { PcapService } from '../service/pcap.service';
 import { PcapStatusResponse } from '../model/pcap-status-response';
@@ -30,13 +30,16 @@ import {RestError} from "../../model/rest-error";
   templateUrl: './pcap-panel.component.html',
   styleUrls: ['./pcap-panel.component.scss']
 })
-export class PcapPanelComponent {
+export class PcapPanelComponent implements OnDestroy {
 
   @Input() pdml: Pdml = null;
   @Input() pcapRequest: PcapRequest;
   @Input() resetPaginationForSearch: boolean;
 
   statusSubscription: Subscription;
+  cancelSubscription: Subscription;
+  submitSubscription: Subscription;
+  getSubscription: Subscription;
   queryRunning: boolean = false;
   queryId: string;
   progressWidth: number = 0;
@@ -55,12 +58,13 @@ export class PcapPanelComponent {
 
   onSearch(pcapRequest) {
     this.queryRunning = true;
+    this.queryId = '';
     this.savedPcapRequest = pcapRequest;
     this.pagination.selectedPage = 1;
     this.pdml = null;
     this.progressWidth = 0;
     this.errorMsg = null;
-    this.pcapService.submitRequest(pcapRequest).subscribe((submitResponse: PcapStatusResponse) => {
+    this.submitSubscription = this.pcapService.submitRequest(pcapRequest).subscribe((submitResponse: PcapStatusResponse) => {
       let id = submitResponse.jobId;
       if (!id) {
         this.errorMsg = submitResponse.description;
@@ -104,4 +108,34 @@ export class PcapPanelComponent {
   getDownloadUrl() {
     return this.pcapService.getDownloadUrl(this.queryId, this.pagination.selectedPage);
   }
-}
\ No newline at end of file
+
+  unsubscribeAll() {
+    if (this.cancelSubscription) {
+      this.cancelSubscription.unsubscribe();
+    }
+    if (this.statusSubscription) {
+      this.statusSubscription.unsubscribe();
+    }
+    if (this.submitSubscription) {
+      this.submitSubscription.unsubscribe();
+    }
+  }
+
+  cancelQuery() {
+    this.cancelSubscription = this.pcapService.cancelQuery(this.queryId)
+      .subscribe(() => {
+        this.unsubscribeAll();
+        this.queryId = '';
+        this.queryRunning = false;
+      }, (error: any) => {
+        this.cancelSubscription.unsubscribe();
+        this.queryId = '';
+        this.errorMsg = `Response message: ${error.message}. Something went wrong with the cancellation!`;
+        this.queryRunning = false;
+      });
+  }
+
+  ngOnDestroy() {
+    this.unsubscribeAll();
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/14dcb2d9/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
index 518cc92..85ed9cc 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
@@ -52,7 +52,7 @@ export class PcapService {
           new RequestOptions({headers: new Headers(this.defaultHeaders)}))
           .map(HttpUtil.extractData)
           .catch(HttpUtil.handleError);
-  }
+    }
     public getPackets(id: string, pageId: number): Observable<Pdml> {
         return this.http.get(`/api/v1/pcap/${id}/pdml?page=${pageId}`, new RequestOptions({headers: new Headers(this.defaultHeaders)}))
             .map(HttpUtil.extractData)
@@ -62,4 +62,12 @@ export class PcapService {
     public getDownloadUrl(id: string, pageId: number) {
       return `/api/v1/pcap/${id}/raw?page=${pageId}`;
     }
+
+    public cancelQuery(queryId: string) {
+      return this.http
+        .delete(`/api/v1/pcap/kill/${queryId}`, new RequestOptions({
+          headers: new Headers(this.defaultHeaders),
+        }))
+        .catch(HttpUtil.handleError);
+    }
 }
\ No newline at end of file


[17/50] [abbrv] metron git commit: METRON-1721 New default input path is wrong in pcap CLI (merrimanr) closes apache/metron#1137

Posted by ni...@apache.org.
METRON-1721 New default input path is wrong in pcap CLI (merrimanr) closes apache/metron#1137


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/756ceedc
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/756ceedc
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/756ceedc

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 756ceedc3d3567f3508478eaa5ed29f12c22688a
Parents: 8972665
Author: merrimanr <me...@gmail.com>
Authored: Mon Aug 6 13:36:40 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Mon Aug 6 13:36:40 2018 -0500

----------------------------------------------------------------------
 .../src/main/java/org/apache/metron/pcap/query/CliParser.java      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/756ceedc/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
index e6534c5..4ad6ffa 100644
--- a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
+++ b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
@@ -25,7 +25,7 @@ import org.apache.metron.pcap.config.PcapConfig;
  * Provides commmon required fields for the PCAP filter jobs
  */
 public class CliParser {
-  public static final String BASE_PATH_DEFAULT = "/apps/metron/pcap";
+  public static final String BASE_PATH_DEFAULT = "/apps/metron/pcap/input";
   public static final String BASE_INTERIM_OUTPUT_PATH_DEFAULT = "/tmp";
   public static final int NUM_REDUCERS_DEFAULT = 10;
   public static final int NUM_RECORDS_PER_FILE_DEFAULT = 10000;


[11/50] [abbrv] metron git commit: METRON-1671 Create PCAP UI (tiborm via merrimanr) closes apache/metron#1103

Posted by ni...@apache.org.
http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/package-lock.json
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/package-lock.json b/metron-interface/metron-alerts/package-lock.json
index 3ace2b7..7d3ad99 100644
--- a/metron-interface/metron-alerts/package-lock.json
+++ b/metron-interface/metron-alerts/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "metron-alerts",
-  "version": "0.4.3",
+  "version": "0.5.1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -12,66 +12,66 @@
       "requires": {
         "@ngtools/json-schema": "1.1.0",
         "@ngtools/webpack": "1.5.3",
-        "autoprefixer": "6.7.7",
-        "chalk": "2.3.0",
-        "circular-dependency-plugin": "3.0.0",
-        "common-tags": "1.5.1",
-        "core-object": "3.1.5",
-        "css-loader": "0.28.7",
-        "cssnano": "3.10.0",
-        "denodeify": "1.2.1",
-        "diff": "3.2.0",
-        "ember-cli-normalize-entity-name": "1.0.0",
-        "ember-cli-string-utils": "1.1.0",
-        "exports-loader": "0.6.4",
-        "extract-text-webpack-plugin": "2.1.2",
-        "file-loader": "0.10.1",
-        "fs-extra": "4.0.2",
-        "get-caller-file": "1.0.2",
-        "glob": "7.1.2",
-        "heimdalljs": "0.2.5",
-        "heimdalljs-logger": "0.1.9",
-        "html-webpack-plugin": "2.30.1",
-        "inflection": "1.12.0",
-        "inquirer": "3.3.0",
-        "isbinaryfile": "3.0.2",
-        "istanbul-instrumenter-loader": "2.0.0",
-        "json-loader": "0.5.7",
-        "karma-source-map-support": "1.2.0",
-        "less": "2.7.3",
-        "less-loader": "4.0.5",
-        "license-webpack-plugin": "0.4.3",
-        "lodash": "4.17.4",
-        "memory-fs": "0.4.1",
-        "minimatch": "3.0.4",
-        "node-modules-path": "1.0.1",
-        "node-sass": "4.5.3",
-        "nopt": "4.0.1",
-        "opn": "5.1.0",
-        "portfinder": "1.0.13",
-        "postcss-loader": "1.3.3",
-        "postcss-url": "5.1.2",
-        "raw-loader": "0.5.1",
-        "resolve": "1.3.3",
-        "rsvp": "3.6.2",
-        "rxjs": "5.4.0",
-        "sass-loader": "6.0.6",
-        "script-loader": "0.7.2",
-        "semver": "5.3.0",
-        "silent-error": "1.1.0",
-        "source-map-loader": "0.2.3",
-        "style-loader": "0.13.2",
-        "stylus": "0.54.5",
-        "stylus-loader": "3.0.1",
+        "autoprefixer": "^6.5.3",
+        "chalk": "^2.0.1",
+        "circular-dependency-plugin": "^3.0.0",
+        "common-tags": "^1.3.1",
+        "core-object": "^3.1.0",
+        "css-loader": "^0.28.1",
+        "cssnano": "^3.10.0",
+        "denodeify": "^1.2.1",
+        "diff": "^3.1.0",
+        "ember-cli-normalize-entity-name": "^1.0.0",
+        "ember-cli-string-utils": "^1.0.0",
+        "exports-loader": "^0.6.3",
+        "extract-text-webpack-plugin": "^2.1.0",
+        "file-loader": "^0.10.0",
+        "fs-extra": "^4.0.0",
+        "get-caller-file": "^1.0.0",
+        "glob": "^7.0.3",
+        "heimdalljs": "^0.2.4",
+        "heimdalljs-logger": "^0.1.9",
+        "html-webpack-plugin": "^2.19.0",
+        "inflection": "^1.7.0",
+        "inquirer": "^3.0.0",
+        "isbinaryfile": "^3.0.0",
+        "istanbul-instrumenter-loader": "^2.0.0",
+        "json-loader": "^0.5.4",
+        "karma-source-map-support": "^1.2.0",
+        "less": "^2.7.2",
+        "less-loader": "^4.0.2",
+        "license-webpack-plugin": "^0.4.2",
+        "lodash": "^4.11.1",
+        "memory-fs": "^0.4.1",
+        "minimatch": "^3.0.3",
+        "node-modules-path": "^1.0.0",
+        "node-sass": "^4.3.0",
+        "nopt": "^4.0.1",
+        "opn": "~5.1.0",
+        "portfinder": "~1.0.12",
+        "postcss-loader": "^1.3.3",
+        "postcss-url": "^5.1.2",
+        "raw-loader": "^0.5.1",
+        "resolve": "^1.1.7",
+        "rsvp": "^3.0.17",
+        "rxjs": "^5.0.1",
+        "sass-loader": "^6.0.3",
+        "script-loader": "^0.7.0",
+        "semver": "^5.1.0",
+        "silent-error": "^1.0.0",
+        "source-map-loader": "^0.2.0",
+        "style-loader": "^0.13.1",
+        "stylus": "^0.54.5",
+        "stylus-loader": "^3.0.1",
         "temp": "0.8.3",
-        "typescript": "2.2.2",
-        "url-loader": "0.5.9",
-        "walk-sync": "0.3.2",
-        "webpack": "2.4.1",
-        "webpack-dev-middleware": "1.12.2",
-        "webpack-dev-server": "2.4.5",
-        "webpack-merge": "2.6.1",
-        "zone.js": "0.8.18"
+        "typescript": ">=2.0.0 <2.4.0",
+        "url-loader": "^0.5.7",
+        "walk-sync": "^0.3.1",
+        "webpack": "~2.4.0",
+        "webpack-dev-middleware": "^1.10.2",
+        "webpack-dev-server": "~2.4.5",
+        "webpack-merge": "^2.4.0",
+        "zone.js": "^0.8.14"
       },
       "dependencies": {
         "ansi-styles": {
@@ -80,7 +80,7 @@
           "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
           "dev": true,
           "requires": {
-            "color-convert": "1.9.1"
+            "color-convert": "^1.9.0"
           }
         },
         "chalk": {
@@ -89,9 +89,9 @@
           "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
           "dev": true,
           "requires": {
-            "ansi-styles": "3.2.0",
-            "escape-string-regexp": "1.0.5",
-            "supports-color": "4.5.0"
+            "ansi-styles": "^3.1.0",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^4.0.0"
           }
         },
         "has-flag": {
@@ -106,7 +106,7 @@
           "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
           "dev": true,
           "requires": {
-            "has-flag": "2.0.0"
+            "has-flag": "^2.0.0"
           }
         },
         "zone.js": {
@@ -134,8 +134,8 @@
       "dev": true,
       "requires": {
         "@angular/tsc-wrapped": "4.1.3",
-        "minimist": "1.2.0",
-        "reflect-metadata": "0.1.10"
+        "minimist": "^1.2.0",
+        "reflect-metadata": "^0.1.2"
       },
       "dependencies": {
         "minimist": {
@@ -182,7 +182,7 @@
       "integrity": "sha1-LWNyyRh78WIerNlguUs5xPlSk80=",
       "dev": true,
       "requires": {
-        "tsickle": "0.21.6"
+        "tsickle": "^0.21.0"
       }
     },
     "@ngtools/json-schema": {
@@ -198,9 +198,9 @@
       "dev": true,
       "requires": {
         "enhanced-resolve": "3.3.0",
-        "loader-utils": "1.1.0",
-        "magic-string": "0.22.4",
-        "source-map": "0.5.6"
+        "loader-utils": "^1.0.2",
+        "magic-string": "^0.22.3",
+        "source-map": "^0.5.6"
       }
     },
     "@types/ace": {
@@ -227,7 +227,7 @@
       "integrity": "sha1-YE69GJvDvDShVIaJQE5hoqSqyJY=",
       "dev": true,
       "requires": {
-        "moment": "2.19.2"
+        "moment": "*"
       }
     },
     "@types/node": {
@@ -242,7 +242,7 @@
       "integrity": "sha1-040HYaJnOzjAjgULvQieYcw8LRc=",
       "dev": true,
       "requires": {
-        "moment": "2.19.2"
+        "moment": ">=2.14.0"
       }
     },
     "@types/q": {
@@ -269,7 +269,7 @@
       "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=",
       "dev": true,
       "requires": {
-        "mime-types": "2.1.15",
+        "mime-types": "~2.1.11",
         "negotiator": "0.6.1"
       }
     },
@@ -290,7 +290,7 @@
       "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=",
       "dev": true,
       "requires": {
-        "acorn": "4.0.13"
+        "acorn": "^4.0.3"
       },
       "dependencies": {
         "acorn": {
@@ -319,8 +319,8 @@
       "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=",
       "dev": true,
       "requires": {
-        "extend": "3.0.1",
-        "semver": "5.0.3"
+        "extend": "~3.0.0",
+        "semver": "~5.0.1"
       },
       "dependencies": {
         "semver": {
@@ -337,8 +337,8 @@
       "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
       "dev": true,
       "requires": {
-        "co": "4.6.0",
-        "json-stable-stringify": "1.0.1"
+        "co": "^4.6.0",
+        "json-stable-stringify": "^1.0.1"
       }
     },
     "ajv-keywords": {
@@ -353,9 +353,9 @@
       "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
       "dev": true,
       "requires": {
-        "kind-of": "3.2.2",
-        "longest": "1.0.1",
-        "repeat-string": "1.6.1"
+        "kind-of": "^3.0.2",
+        "longest": "^1.0.1",
+        "repeat-string": "^1.5.2"
       }
     },
     "alphanum-sort": {
@@ -376,7 +376,7 @@
       "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
       "dev": true,
       "requires": {
-        "string-width": "2.0.0"
+        "string-width": "^2.0.0"
       }
     },
     "ansi-escapes": {
@@ -415,8 +415,8 @@
       "integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=",
       "dev": true,
       "requires": {
-        "arrify": "1.0.1",
-        "micromatch": "2.3.11"
+        "arrify": "^1.0.0",
+        "micromatch": "^2.1.5"
       }
     },
     "app-root-path": {
@@ -431,7 +431,7 @@
       "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=",
       "dev": true,
       "requires": {
-        "default-require-extensions": "1.0.0"
+        "default-require-extensions": "^1.0.0"
       }
     },
     "aproba": {
@@ -446,8 +446,8 @@
       "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
       "dev": true,
       "requires": {
-        "delegates": "1.0.0",
-        "readable-stream": "2.2.11"
+        "delegates": "^1.0.0",
+        "readable-stream": "^2.0.6"
       }
     },
     "argparse": {
@@ -456,7 +456,7 @@
       "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
       "dev": true,
       "requires": {
-        "sprintf-js": "1.0.3"
+        "sprintf-js": "~1.0.2"
       }
     },
     "arr-diff": {
@@ -465,7 +465,7 @@
       "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
       "dev": true,
       "requires": {
-        "arr-flatten": "1.0.3"
+        "arr-flatten": "^1.0.1"
       }
     },
     "arr-flatten": {
@@ -498,7 +498,7 @@
       "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
       "dev": true,
       "requires": {
-        "array-uniq": "1.0.3"
+        "array-uniq": "^1.0.1"
       }
     },
     "array-uniq": {
@@ -544,9 +544,9 @@
       "integrity": "sha1-gRfvT37YfNj4kES1v/l6wkOhbJo=",
       "dev": true,
       "requires": {
-        "bn.js": "4.11.8",
-        "inherits": "2.0.3",
-        "minimalistic-assert": "1.0.0"
+        "bn.js": "^4.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0"
       }
     },
     "assert": {
@@ -570,7 +570,7 @@
       "integrity": "sha1-YqVrJ5yYoR0JhwlqAcw+6463u9c=",
       "dev": true,
       "requires": {
-        "lodash": "4.17.4"
+        "lodash": "^4.14.0"
       }
     },
     "async-each": {
@@ -585,6 +585,12 @@
       "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
       "dev": true
     },
+    "async-limiter": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
+      "dev": true
+    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -602,12 +608,12 @@
       "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=",
       "dev": true,
       "requires": {
-        "browserslist": "1.7.7",
-        "caniuse-db": "1.0.30000772",
-        "normalize-range": "0.1.2",
-        "num2fraction": "1.2.2",
-        "postcss": "5.2.18",
-        "postcss-value-parser": "3.3.0"
+        "browserslist": "^1.7.6",
+        "caniuse-db": "^1.0.30000634",
+        "normalize-range": "^0.1.2",
+        "num2fraction": "^1.2.2",
+        "postcss": "^5.2.16",
+        "postcss-value-parser": "^3.2.3"
       }
     },
     "aws-sign2": {
@@ -628,9 +634,9 @@
       "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=",
       "dev": true,
       "requires": {
-        "chalk": "1.1.3",
-        "esutils": "2.0.2",
-        "js-tokens": "3.0.1"
+        "chalk": "^1.1.0",
+        "esutils": "^2.0.2",
+        "js-tokens": "^3.0.0"
       }
     },
     "babel-generator": {
@@ -639,14 +645,14 @@
       "integrity": "sha1-5xX0hsWN7SVknYiJRNUqoHxdlJc=",
       "dev": true,
       "requires": {
-        "babel-messages": "6.23.0",
-        "babel-runtime": "6.23.0",
-        "babel-types": "6.24.1",
-        "detect-indent": "4.0.0",
-        "jsesc": "1.3.0",
-        "lodash": "4.17.4",
-        "source-map": "0.5.6",
-        "trim-right": "1.0.1"
+        "babel-messages": "^6.23.0",
+        "babel-runtime": "^6.22.0",
+        "babel-types": "^6.24.1",
+        "detect-indent": "^4.0.0",
+        "jsesc": "^1.3.0",
+        "lodash": "^4.2.0",
+        "source-map": "^0.5.0",
+        "trim-right": "^1.0.1"
       },
       "dependencies": {
         "jsesc": {
@@ -663,7 +669,7 @@
       "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
       "dev": true,
       "requires": {
-        "babel-runtime": "6.23.0"
+        "babel-runtime": "^6.22.0"
       }
     },
     "babel-runtime": {
@@ -672,8 +678,8 @@
       "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=",
       "dev": true,
       "requires": {
-        "core-js": "2.4.1",
-        "regenerator-runtime": "0.10.5"
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.10.0"
       }
     },
     "babel-template": {
@@ -682,11 +688,11 @@
       "integrity": "sha1-BK5RTx+Ts6JTfyoPYKWkX7gwgzM=",
       "dev": true,
       "requires": {
-        "babel-runtime": "6.23.0",
-        "babel-traverse": "6.24.1",
-        "babel-types": "6.24.1",
-        "babylon": "6.17.2",
-        "lodash": "4.17.4"
+        "babel-runtime": "^6.22.0",
+        "babel-traverse": "^6.24.1",
+        "babel-types": "^6.24.1",
+        "babylon": "^6.11.0",
+        "lodash": "^4.2.0"
       }
     },
     "babel-traverse": {
@@ -695,15 +701,15 @@
       "integrity": "sha1-qzZnP9NW+aCUhlnnszjV/q2zFpU=",
       "dev": true,
       "requires": {
-        "babel-code-frame": "6.22.0",
-        "babel-messages": "6.23.0",
-        "babel-runtime": "6.23.0",
-        "babel-types": "6.24.1",
-        "babylon": "6.17.2",
-        "debug": "2.6.8",
-        "globals": "9.18.0",
-        "invariant": "2.2.2",
-        "lodash": "4.17.4"
+        "babel-code-frame": "^6.22.0",
+        "babel-messages": "^6.23.0",
+        "babel-runtime": "^6.22.0",
+        "babel-types": "^6.24.1",
+        "babylon": "^6.15.0",
+        "debug": "^2.2.0",
+        "globals": "^9.0.0",
+        "invariant": "^2.2.0",
+        "lodash": "^4.2.0"
       }
     },
     "babel-types": {
@@ -712,10 +718,10 @@
       "integrity": "sha1-oTaHncFbNga9oNkMH8dDBML/CXU=",
       "dev": true,
       "requires": {
-        "babel-runtime": "6.23.0",
-        "esutils": "2.0.2",
-        "lodash": "4.17.4",
-        "to-fast-properties": "1.0.3"
+        "babel-runtime": "^6.22.0",
+        "esutils": "^2.0.2",
+        "lodash": "^4.2.0",
+        "to-fast-properties": "^1.0.1"
       }
     },
     "babylon": {
@@ -767,7 +773,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "tweetnacl": "0.14.5"
+        "tweetnacl": "^0.14.3"
       }
     },
     "better-assert": {
@@ -803,7 +809,7 @@
       "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
       "dev": true,
       "requires": {
-        "inherits": "2.0.3"
+        "inherits": "~2.0.0"
       }
     },
     "blocking-proxy": {
@@ -812,7 +818,7 @@
       "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
       "dev": true,
       "requires": {
-        "minimist": "1.2.0"
+        "minimist": "^1.2.0"
       },
       "dependencies": {
         "minimist": {
@@ -842,15 +848,15 @@
       "dev": true,
       "requires": {
         "bytes": "2.4.0",
-        "content-type": "1.0.2",
+        "content-type": "~1.0.2",
         "debug": "2.6.7",
-        "depd": "1.1.0",
-        "http-errors": "1.6.1",
+        "depd": "~1.1.0",
+        "http-errors": "~1.6.1",
         "iconv-lite": "0.4.15",
-        "on-finished": "2.3.0",
+        "on-finished": "~2.3.0",
         "qs": "6.4.0",
-        "raw-body": "2.2.0",
-        "type-is": "1.6.15"
+        "raw-body": "~2.2.0",
+        "type-is": "~1.6.15"
       },
       "dependencies": {
         "bytes": {
@@ -888,7 +894,7 @@
       "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
       "dev": true,
       "requires": {
-        "hoek": "2.16.3"
+        "hoek": "2.x.x"
       }
     },
     "bootstrap": {
@@ -896,8 +902,8 @@
       "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.0.0-alpha.6.tgz",
       "integrity": "sha1-T1TdM6wN6sOyhAe8LffsYIhpycg=",
       "requires": {
-        "jquery": "3.2.1",
-        "tether": "1.4.0"
+        "jquery": ">=1.9.1",
+        "tether": "^1.4.0"
       }
     },
     "boxen": {
@@ -906,13 +912,13 @@
       "integrity": "sha1-sbad1SIwXoB6md7ud329blFnsQI=",
       "dev": true,
       "requires": {
-        "ansi-align": "2.0.0",
-        "camelcase": "4.1.0",
-        "chalk": "1.1.3",
-        "cli-boxes": "1.0.0",
-        "string-width": "2.0.0",
-        "term-size": "0.1.1",
-        "widest-line": "1.0.0"
+        "ansi-align": "^2.0.0",
+        "camelcase": "^4.0.0",
+        "chalk": "^1.1.1",
+        "cli-boxes": "^1.0.0",
+        "string-width": "^2.0.0",
+        "term-size": "^0.1.0",
+        "widest-line": "^1.0.0"
       },
       "dependencies": {
         "camelcase": {
@@ -929,7 +935,7 @@
       "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=",
       "dev": true,
       "requires": {
-        "balanced-match": "0.4.2",
+        "balanced-match": "^0.4.1",
         "concat-map": "0.0.1"
       }
     },
@@ -939,9 +945,9 @@
       "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
       "dev": true,
       "requires": {
-        "expand-range": "1.8.2",
-        "preserve": "0.2.0",
-        "repeat-element": "1.1.2"
+        "expand-range": "^1.8.1",
+        "preserve": "^0.2.0",
+        "repeat-element": "^1.1.2"
       }
     },
     "brorand": {
@@ -956,12 +962,12 @@
       "integrity": "sha1-OLerVe24Bv8tzaGn8WIHc6R3xJ8=",
       "dev": true,
       "requires": {
-        "buffer-xor": "1.0.3",
-        "cipher-base": "1.0.4",
-        "create-hash": "1.1.3",
-        "evp_bytestokey": "1.0.3",
-        "inherits": "2.0.3",
-        "safe-buffer": "5.0.1"
+        "buffer-xor": "^1.0.3",
+        "cipher-base": "^1.0.0",
+        "create-hash": "^1.1.0",
+        "evp_bytestokey": "^1.0.3",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
       }
     },
     "browserify-cipher": {
@@ -970,9 +976,9 @@
       "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=",
       "dev": true,
       "requires": {
-        "browserify-aes": "1.1.1",
-        "browserify-des": "1.0.0",
-        "evp_bytestokey": "1.0.3"
+        "browserify-aes": "^1.0.4",
+        "browserify-des": "^1.0.0",
+        "evp_bytestokey": "^1.0.0"
       }
     },
     "browserify-des": {
@@ -981,9 +987,9 @@
       "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=",
       "dev": true,
       "requires": {
-        "cipher-base": "1.0.4",
-        "des.js": "1.0.0",
-        "inherits": "2.0.3"
+        "cipher-base": "^1.0.1",
+        "des.js": "^1.0.0",
+        "inherits": "^2.0.1"
       }
     },
     "browserify-rsa": {
@@ -992,8 +998,8 @@
       "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
       "dev": true,
       "requires": {
-        "bn.js": "4.11.8",
-        "randombytes": "2.0.5"
+        "bn.js": "^4.1.0",
+        "randombytes": "^2.0.1"
       }
     },
     "browserify-sign": {
@@ -1002,13 +1008,13 @@
       "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
       "dev": true,
       "requires": {
-        "bn.js": "4.11.8",
-        "browserify-rsa": "4.0.1",
-        "create-hash": "1.1.3",
-        "create-hmac": "1.1.6",
-        "elliptic": "6.4.0",
-        "inherits": "2.0.3",
-        "parse-asn1": "5.1.0"
+        "bn.js": "^4.1.1",
+        "browserify-rsa": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "create-hmac": "^1.1.2",
+        "elliptic": "^6.0.0",
+        "inherits": "^2.0.1",
+        "parse-asn1": "^5.0.0"
       }
     },
     "browserify-zlib": {
@@ -1017,7 +1023,7 @@
       "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=",
       "dev": true,
       "requires": {
-        "pako": "1.0.6"
+        "pako": "~1.0.5"
       }
     },
     "browserslist": {
@@ -1026,8 +1032,8 @@
       "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
       "dev": true,
       "requires": {
-        "caniuse-db": "1.0.30000772",
-        "electron-to-chromium": "1.3.27"
+        "caniuse-db": "^1.0.30000639",
+        "electron-to-chromium": "^1.2.7"
       }
     },
     "buffer": {
@@ -1036,11 +1042,17 @@
       "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
       "dev": true,
       "requires": {
-        "base64-js": "1.2.1",
-        "ieee754": "1.1.8",
-        "isarray": "1.0.0"
+        "base64-js": "^1.0.2",
+        "ieee754": "^1.1.4",
+        "isarray": "^1.0.0"
       }
     },
+    "buffer-from": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz",
+      "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==",
+      "dev": true
+    },
     "buffer-xor": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
@@ -1077,8 +1089,8 @@
       "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
       "dev": true,
       "requires": {
-        "no-case": "2.3.2",
-        "upper-case": "1.1.3"
+        "no-case": "^2.2.0",
+        "upper-case": "^1.1.1"
       }
     },
     "camelcase": {
@@ -1093,8 +1105,8 @@
       "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
       "dev": true,
       "requires": {
-        "camelcase": "2.1.1",
-        "map-obj": "1.0.1"
+        "camelcase": "^2.0.0",
+        "map-obj": "^1.0.0"
       }
     },
     "caniuse-api": {
@@ -1103,10 +1115,10 @@
       "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=",
       "dev": true,
       "requires": {
-        "browserslist": "1.7.7",
-        "caniuse-db": "1.0.30000772",
-        "lodash.memoize": "4.1.2",
-        "lodash.uniq": "4.5.0"
+        "browserslist": "^1.3.6",
+        "caniuse-db": "^1.0.30000529",
+        "lodash.memoize": "^4.1.2",
+        "lodash.uniq": "^4.5.0"
       }
     },
     "caniuse-db": {
@@ -1133,8 +1145,8 @@
       "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
       "dev": true,
       "requires": {
-        "align-text": "0.1.4",
-        "lazy-cache": "1.0.4"
+        "align-text": "^0.1.3",
+        "lazy-cache": "^1.0.3"
       }
     },
     "chalk": {
@@ -1143,11 +1155,11 @@
       "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
       "dev": true,
       "requires": {
-        "ansi-styles": "2.2.1",
-        "escape-string-regexp": "1.0.5",
-        "has-ansi": "2.0.0",
-        "strip-ansi": "3.0.1",
-        "supports-color": "2.0.0"
+        "ansi-styles": "^2.2.1",
+        "escape-string-regexp": "^1.0.2",
+        "has-ansi": "^2.0.0",
+        "strip-ansi": "^3.0.0",
+        "supports-color": "^2.0.0"
       },
       "dependencies": {
         "supports-color": {
@@ -1170,15 +1182,15 @@
       "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
       "dev": true,
       "requires": {
-        "anymatch": "1.3.0",
-        "async-each": "1.0.1",
-        "fsevents": "1.1.1",
-        "glob-parent": "2.0.0",
-        "inherits": "2.0.3",
-        "is-binary-path": "1.0.1",
-        "is-glob": "2.0.1",
-        "path-is-absolute": "1.0.1",
-        "readdirp": "2.1.0"
+        "anymatch": "^1.3.0",
+        "async-each": "^1.0.0",
+        "fsevents": "^1.0.0",
+        "glob-parent": "^2.0.0",
+        "inherits": "^2.0.1",
+        "is-binary-path": "^1.0.0",
+        "is-glob": "^2.0.0",
+        "path-is-absolute": "^1.0.0",
+        "readdirp": "^2.0.0"
       }
     },
     "cipher-base": {
@@ -1187,8 +1199,8 @@
       "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=",
       "dev": true,
       "requires": {
-        "inherits": "2.0.3",
-        "safe-buffer": "5.0.1"
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
       }
     },
     "circular-dependency-plugin": {
@@ -1203,7 +1215,7 @@
       "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=",
       "dev": true,
       "requires": {
-        "chalk": "1.1.3"
+        "chalk": "^1.1.3"
       }
     },
     "clean-css": {
@@ -1212,7 +1224,7 @@
       "integrity": "sha1-Nc7ornaHpJuYA09w3gDE7dOCYwE=",
       "dev": true,
       "requires": {
-        "source-map": "0.5.6"
+        "source-map": "0.5.x"
       }
     },
     "cli-boxes": {
@@ -1227,7 +1239,7 @@
       "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
       "dev": true,
       "requires": {
-        "restore-cursor": "2.0.0"
+        "restore-cursor": "^2.0.0"
       }
     },
     "cli-width": {
@@ -1242,9 +1254,9 @@
       "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
       "dev": true,
       "requires": {
-        "string-width": "1.0.2",
-        "strip-ansi": "3.0.1",
-        "wrap-ansi": "2.1.0"
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wrap-ansi": "^2.0.0"
       },
       "dependencies": {
         "is-fullwidth-code-point": {
@@ -1253,7 +1265,7 @@
           "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
           "dev": true,
           "requires": {
-            "number-is-nan": "1.0.1"
+            "number-is-nan": "^1.0.0"
           }
         },
         "string-width": {
@@ -1262,9 +1274,9 @@
           "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
           "dev": true,
           "requires": {
-            "code-point-at": "1.1.0",
-            "is-fullwidth-code-point": "1.0.0",
-            "strip-ansi": "3.0.1"
+            "code-point-at": "^1.0.0",
+            "is-fullwidth-code-point": "^1.0.0",
+            "strip-ansi": "^3.0.0"
           }
         }
       }
@@ -1281,10 +1293,10 @@
       "integrity": "sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg=",
       "dev": true,
       "requires": {
-        "for-own": "1.0.0",
-        "is-plain-object": "2.0.4",
-        "kind-of": "3.2.2",
-        "shallow-clone": "0.1.2"
+        "for-own": "^1.0.0",
+        "is-plain-object": "^2.0.1",
+        "kind-of": "^3.2.2",
+        "shallow-clone": "^0.1.2"
       },
       "dependencies": {
         "for-own": {
@@ -1293,7 +1305,7 @@
           "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
           "dev": true,
           "requires": {
-            "for-in": "1.0.2"
+            "for-in": "^1.0.1"
           }
         }
       }
@@ -1310,7 +1322,7 @@
       "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=",
       "dev": true,
       "requires": {
-        "q": "1.5.0"
+        "q": "^1.1.2"
       }
     },
     "code-point-at": {
@@ -1325,12 +1337,12 @@
       "integrity": "sha1-0PcSH2eoQkyS0h07MfNkC4Pe+e0=",
       "dev": true,
       "requires": {
-        "app-root-path": "2.0.1",
-        "css-selector-tokenizer": "0.7.0",
-        "cssauron": "1.4.0",
-        "semver-dsl": "1.0.1",
-        "source-map": "0.5.6",
-        "sprintf-js": "1.0.3"
+        "app-root-path": "^2.0.1",
+        "css-selector-tokenizer": "^0.7.0",
+        "cssauron": "^1.4.0",
+        "semver-dsl": "^1.0.1",
+        "source-map": "^0.5.6",
+        "sprintf-js": "^1.0.3"
       }
     },
     "color": {
@@ -1339,9 +1351,9 @@
       "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=",
       "dev": true,
       "requires": {
-        "clone": "1.0.3",
-        "color-convert": "1.9.1",
-        "color-string": "0.3.0"
+        "clone": "^1.0.2",
+        "color-convert": "^1.3.0",
+        "color-string": "^0.3.0"
       }
     },
     "color-convert": {
@@ -1350,7 +1362,7 @@
       "integrity": "sha1-wSYRB66y8pTr/+ye2eytUppgl+0=",
       "dev": true,
       "requires": {
-        "color-name": "1.1.3"
+        "color-name": "^1.1.1"
       }
     },
     "color-name": {
@@ -1365,7 +1377,7 @@
       "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=",
       "dev": true,
       "requires": {
-        "color-name": "1.1.3"
+        "color-name": "^1.0.0"
       }
     },
     "colormin": {
@@ -1374,9 +1386,9 @@
       "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=",
       "dev": true,
       "requires": {
-        "color": "0.11.4",
+        "color": "^0.11.0",
         "css-color-names": "0.0.4",
-        "has": "1.0.1"
+        "has": "^1.0.1"
       }
     },
     "colors": {
@@ -1391,7 +1403,7 @@
       "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=",
       "dev": true,
       "requires": {
-        "lodash": "4.17.4"
+        "lodash": "^4.5.0"
       }
     },
     "combined-stream": {
@@ -1400,7 +1412,7 @@
       "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
       "dev": true,
       "requires": {
-        "delayed-stream": "1.0.0"
+        "delayed-stream": "~1.0.0"
       }
     },
     "commander": {
@@ -1415,7 +1427,7 @@
       "integrity": "sha1-4uOZMaATzQIlPe/u2Joa1hWifwc=",
       "dev": true,
       "requires": {
-        "babel-runtime": "6.26.0"
+        "babel-runtime": "^6.26.0"
       },
       "dependencies": {
         "babel-runtime": {
@@ -1424,8 +1436,8 @@
           "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
           "dev": true,
           "requires": {
-            "core-js": "2.4.1",
-            "regenerator-runtime": "0.11.0"
+            "core-js": "^2.4.0",
+            "regenerator-runtime": "^0.11.0"
           }
         },
         "regenerator-runtime": {
@@ -1460,7 +1472,7 @@
       "integrity": "sha1-/tocf3YXkScyspv4zyYlKiC57s0=",
       "dev": true,
       "requires": {
-        "mime-db": "1.27.0"
+        "mime-db": ">= 1.27.0 < 2"
       }
     },
     "compression": {
@@ -1469,12 +1481,12 @@
       "integrity": "sha1-zOsSHsydCcUtetDDNQ6pPd1AK8M=",
       "dev": true,
       "requires": {
-        "accepts": "1.3.3",
+        "accepts": "~1.3.3",
         "bytes": "2.3.0",
-        "compressible": "2.0.10",
-        "debug": "2.2.0",
-        "on-headers": "1.0.1",
-        "vary": "1.1.1"
+        "compressible": "~2.0.8",
+        "debug": "~2.2.0",
+        "on-headers": "~1.0.1",
+        "vary": "~1.1.0"
       },
       "dependencies": {
         "debug": {
@@ -1500,18 +1512,30 @@
       "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
       "dev": true
     },
+    "concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
     "configstore": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.0.tgz",
       "integrity": "sha1-Rd+QcHPibfoc9LLVL1tgVF6qEdE=",
       "dev": true,
       "requires": {
-        "dot-prop": "4.1.1",
-        "graceful-fs": "4.1.11",
-        "make-dir": "1.0.0",
-        "unique-string": "1.0.0",
-        "write-file-atomic": "2.1.0",
-        "xdg-basedir": "3.0.0"
+        "dot-prop": "^4.1.0",
+        "graceful-fs": "^4.1.2",
+        "make-dir": "^1.0.0",
+        "unique-string": "^1.0.0",
+        "write-file-atomic": "^2.0.0",
+        "xdg-basedir": "^3.0.0"
       }
     },
     "connect": {
@@ -1522,7 +1546,7 @@
       "requires": {
         "debug": "2.6.7",
         "finalhandler": "1.0.3",
-        "parseurl": "1.3.1",
+        "parseurl": "~1.3.1",
         "utils-merge": "1.0.0"
       },
       "dependencies": {
@@ -1549,7 +1573,7 @@
       "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
       "dev": true,
       "requires": {
-        "date-now": "0.1.4"
+        "date-now": "^0.1.4"
       }
     },
     "console-control-strings": {
@@ -1614,7 +1638,7 @@
       "integrity": "sha1-+mJ7h1Aq3JgEXkRnjpqOw7nA0qk=",
       "dev": true,
       "requires": {
-        "chalk": "2.3.0"
+        "chalk": "^2.0.0"
       },
       "dependencies": {
         "ansi-styles": {
@@ -1623,7 +1647,7 @@
           "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
           "dev": true,
           "requires": {
-            "color-convert": "1.9.1"
+            "color-convert": "^1.9.0"
           }
         },
         "chalk": {
@@ -1632,9 +1656,9 @@
           "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
           "dev": true,
           "requires": {
-            "ansi-styles": "3.2.0",
-            "escape-string-regexp": "1.0.5",
-            "supports-color": "4.5.0"
+            "ansi-styles": "^3.1.0",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^4.0.0"
           }
         },
         "has-flag": {
@@ -1649,7 +1673,7 @@
           "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
           "dev": true,
           "requires": {
-            "has-flag": "2.0.0"
+            "has-flag": "^2.0.0"
           }
         }
       }
@@ -1666,13 +1690,13 @@
       "integrity": "sha1-YXPOvVb6wELB9DkO33r2wHx8uJI=",
       "dev": true,
       "requires": {
-        "is-directory": "0.3.1",
-        "js-yaml": "3.7.0",
-        "minimist": "1.2.0",
-        "object-assign": "4.1.1",
-        "os-homedir": "1.0.2",
-        "parse-json": "2.2.0",
-        "require-from-string": "1.2.1"
+        "is-directory": "^0.3.1",
+        "js-yaml": "^3.4.3",
+        "minimist": "^1.2.0",
+        "object-assign": "^4.1.0",
+        "os-homedir": "^1.0.1",
+        "parse-json": "^2.2.0",
+        "require-from-string": "^1.1.0"
       },
       "dependencies": {
         "minimist": {
@@ -1689,8 +1713,8 @@
       "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=",
       "dev": true,
       "requires": {
-        "bn.js": "4.11.8",
-        "elliptic": "6.4.0"
+        "bn.js": "^4.1.0",
+        "elliptic": "^6.0.0"
       }
     },
     "create-error-class": {
@@ -1699,7 +1723,7 @@
       "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
       "dev": true,
       "requires": {
-        "capture-stack-trace": "1.0.0"
+        "capture-stack-trace": "^1.0.0"
       }
     },
     "create-hash": {
@@ -1708,10 +1732,10 @@
       "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
       "dev": true,
       "requires": {
-        "cipher-base": "1.0.4",
-        "inherits": "2.0.3",
-        "ripemd160": "2.0.1",
-        "sha.js": "2.4.9"
+        "cipher-base": "^1.0.1",
+        "inherits": "^2.0.1",
+        "ripemd160": "^2.0.0",
+        "sha.js": "^2.4.0"
       }
     },
     "create-hmac": {
@@ -1720,12 +1744,12 @@
       "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
       "dev": true,
       "requires": {
-        "cipher-base": "1.0.4",
-        "create-hash": "1.1.3",
-        "inherits": "2.0.3",
-        "ripemd160": "2.0.1",
-        "safe-buffer": "5.0.1",
-        "sha.js": "2.4.9"
+        "cipher-base": "^1.0.3",
+        "create-hash": "^1.1.0",
+        "inherits": "^2.0.1",
+        "ripemd160": "^2.0.0",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
       }
     },
     "cross-spawn": {
@@ -1734,8 +1758,8 @@
       "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
       "dev": true,
       "requires": {
-        "lru-cache": "4.1.0",
-        "which": "1.2.14"
+        "lru-cache": "^4.0.1",
+        "which": "^1.2.9"
       }
     },
     "cross-spawn-async": {
@@ -1744,8 +1768,8 @@
       "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=",
       "dev": true,
       "requires": {
-        "lru-cache": "4.1.0",
-        "which": "1.2.14"
+        "lru-cache": "^4.0.0",
+        "which": "^1.2.8"
       }
     },
     "crossvent": {
@@ -1753,7 +1777,7 @@
       "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.4.tgz",
       "integrity": "sha1-2ixPj0DJR4JRe/K+7BBEFIGUq5I=",
       "requires": {
-        "custom-event": "1.0.1"
+        "custom-event": "1.0.0"
       }
     },
     "cryptiles": {
@@ -1762,7 +1786,7 @@
       "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
       "dev": true,
       "requires": {
-        "boom": "2.10.1"
+        "boom": "2.x.x"
       }
     },
     "crypto-browserify": {
@@ -1771,17 +1795,17 @@
       "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=",
       "dev": true,
       "requires": {
-        "browserify-cipher": "1.0.0",
-        "browserify-sign": "4.0.4",
-        "create-ecdh": "4.0.0",
-        "create-hash": "1.1.3",
-        "create-hmac": "1.1.6",
-        "diffie-hellman": "5.0.2",
-        "inherits": "2.0.3",
-        "pbkdf2": "3.0.14",
-        "public-encrypt": "4.0.0",
-        "randombytes": "2.0.5",
-        "randomfill": "1.0.3"
+        "browserify-cipher": "^1.0.0",
+        "browserify-sign": "^4.0.0",
+        "create-ecdh": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "create-hmac": "^1.1.0",
+        "diffie-hellman": "^5.0.0",
+        "inherits": "^2.0.1",
+        "pbkdf2": "^3.0.3",
+        "public-encrypt": "^4.0.0",
+        "randombytes": "^2.0.0",
+        "randomfill": "^1.0.3"
       }
     },
     "crypto-random-string": {
@@ -1802,20 +1826,20 @@
       "integrity": "sha1-Xy7pid0y7dkHcX+VMxdlYWCZnBs=",
       "dev": true,
       "requires": {
-        "babel-code-frame": "6.22.0",
-        "css-selector-tokenizer": "0.7.0",
-        "cssnano": "3.10.0",
-        "icss-utils": "2.1.0",
-        "loader-utils": "1.1.0",
-        "lodash.camelcase": "4.3.0",
-        "object-assign": "4.1.1",
-        "postcss": "5.2.18",
-        "postcss-modules-extract-imports": "1.1.0",
-        "postcss-modules-local-by-default": "1.2.0",
-        "postcss-modules-scope": "1.1.0",
-        "postcss-modules-values": "1.3.0",
-        "postcss-value-parser": "3.3.0",
-        "source-list-map": "2.0.0"
+        "babel-code-frame": "^6.11.0",
+        "css-selector-tokenizer": "^0.7.0",
+        "cssnano": ">=2.6.1 <4",
+        "icss-utils": "^2.1.0",
+        "loader-utils": "^1.0.2",
+        "lodash.camelcase": "^4.3.0",
+        "object-assign": "^4.0.1",
+        "postcss": "^5.0.6",
+        "postcss-modules-extract-imports": "^1.0.0",
+        "postcss-modules-local-by-default": "^1.0.1",
+        "postcss-modules-scope": "^1.0.0",
+        "postcss-modules-values": "^1.1.0",
+        "postcss-value-parser": "^3.3.0",
+        "source-list-map": "^2.0.0"
       }
     },
     "css-parse": {
@@ -1830,10 +1854,10 @@
       "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
       "dev": true,
       "requires": {
-        "boolbase": "1.0.0",
-        "css-what": "2.1.0",
+        "boolbase": "~1.0.0",
+        "css-what": "2.1",
         "domutils": "1.5.1",
-        "nth-check": "1.0.1"
+        "nth-check": "~1.0.1"
       }
     },
     "css-selector-tokenizer": {
@@ -1842,9 +1866,9 @@
       "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=",
       "dev": true,
       "requires": {
-        "cssesc": "0.1.0",
-        "fastparse": "1.1.1",
-        "regexpu-core": "1.0.0"
+        "cssesc": "^0.1.0",
+        "fastparse": "^1.1.1",
+        "regexpu-core": "^1.0.0"
       }
     },
     "css-what": {
@@ -1859,7 +1883,7 @@
       "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=",
       "dev": true,
       "requires": {
-        "through": "2.3.8"
+        "through": "X.X.X"
       }
     },
     "cssesc": {
@@ -1874,38 +1898,38 @@
       "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=",
       "dev": true,
       "requires": {
-        "autoprefixer": "6.7.7",
-        "decamelize": "1.2.0",
-        "defined": "1.0.0",
-        "has": "1.0.1",
-        "object-assign": "4.1.1",
-        "postcss": "5.2.18",
-        "postcss-calc": "5.3.1",
-        "postcss-colormin": "2.2.2",
-        "postcss-convert-values": "2.6.1",
-        "postcss-discard-comments": "2.0.4",
-        "postcss-discard-duplicates": "2.1.0",
-        "postcss-discard-empty": "2.1.0",
-        "postcss-discard-overridden": "0.1.1",
-        "postcss-discard-unused": "2.2.3",
-        "postcss-filter-plugins": "2.0.2",
-        "postcss-merge-idents": "2.1.7",
-        "postcss-merge-longhand": "2.0.2",
-        "postcss-merge-rules": "2.1.2",
-        "postcss-minify-font-values": "1.0.5",
-        "postcss-minify-gradients": "1.0.5",
-        "postcss-minify-params": "1.2.2",
-        "postcss-minify-selectors": "2.1.1",
-        "postcss-normalize-charset": "1.1.1",
-        "postcss-normalize-url": "3.0.8",
-        "postcss-ordered-values": "2.2.3",
-        "postcss-reduce-idents": "2.4.0",
-        "postcss-reduce-initial": "1.0.1",
-        "postcss-reduce-transforms": "1.0.4",
-        "postcss-svgo": "2.1.6",
-        "postcss-unique-selectors": "2.0.2",
-        "postcss-value-parser": "3.3.0",
-        "postcss-zindex": "2.2.0"
+        "autoprefixer": "^6.3.1",
+        "decamelize": "^1.1.2",
+        "defined": "^1.0.0",
+        "has": "^1.0.1",
+        "object-assign": "^4.0.1",
+        "postcss": "^5.0.14",
+        "postcss-calc": "^5.2.0",
+        "postcss-colormin": "^2.1.8",
+        "postcss-convert-values": "^2.3.4",
+        "postcss-discard-comments": "^2.0.4",
+        "postcss-discard-duplicates": "^2.0.1",
+        "postcss-discard-empty": "^2.0.1",
+        "postcss-discard-overridden": "^0.1.1",
+        "postcss-discard-unused": "^2.2.1",
+        "postcss-filter-plugins": "^2.0.0",
+        "postcss-merge-idents": "^2.1.5",
+        "postcss-merge-longhand": "^2.0.1",
+        "postcss-merge-rules": "^2.0.3",
+        "postcss-minify-font-values": "^1.0.2",
+        "postcss-minify-gradients": "^1.0.1",
+        "postcss-minify-params": "^1.0.4",
+        "postcss-minify-selectors": "^2.0.4",
+        "postcss-normalize-charset": "^1.1.0",
+        "postcss-normalize-url": "^3.0.7",
+        "postcss-ordered-values": "^2.1.0",
+        "postcss-reduce-idents": "^2.2.2",
+        "postcss-reduce-initial": "^1.0.0",
+        "postcss-reduce-transforms": "^1.0.3",
+        "postcss-svgo": "^2.1.1",
+        "postcss-unique-selectors": "^2.0.2",
+        "postcss-value-parser": "^3.2.3",
+        "postcss-zindex": "^2.0.1"
       }
     },
     "csso": {
@@ -1914,8 +1938,8 @@
       "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=",
       "dev": true,
       "requires": {
-        "clap": "1.2.3",
-        "source-map": "0.5.6"
+        "clap": "^1.0.9",
+        "source-map": "^0.5.3"
       }
     },
     "currently-unhandled": {
@@ -1924,7 +1948,7 @@
       "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
       "dev": true,
       "requires": {
-        "array-find-index": "1.0.2"
+        "array-find-index": "^1.0.1"
       }
     },
     "custom-event": {
@@ -1938,7 +1962,7 @@
       "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
       "dev": true,
       "requires": {
-        "assert-plus": "1.0.0"
+        "assert-plus": "^1.0.0"
       },
       "dependencies": {
         "assert-plus": {
@@ -1982,7 +2006,7 @@
       "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=",
       "dev": true,
       "requires": {
-        "strip-bom": "2.0.0"
+        "strip-bom": "^2.0.0"
       }
     },
     "defined": {
@@ -1997,13 +2021,13 @@
       "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
       "dev": true,
       "requires": {
-        "globby": "5.0.0",
-        "is-path-cwd": "1.0.0",
-        "is-path-in-cwd": "1.0.0",
-        "object-assign": "4.1.1",
-        "pify": "2.3.0",
-        "pinkie-promise": "2.0.1",
-        "rimraf": "2.6.1"
+        "globby": "^5.0.0",
+        "is-path-cwd": "^1.0.0",
+        "is-path-in-cwd": "^1.0.0",
+        "object-assign": "^4.0.1",
+        "pify": "^2.0.0",
+        "pinkie-promise": "^2.0.0",
+        "rimraf": "^2.2.8"
       }
     },
     "delayed-stream": {
@@ -2036,8 +2060,8 @@
       "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
       "dev": true,
       "requires": {
-        "inherits": "2.0.3",
-        "minimalistic-assert": "1.0.0"
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0"
       }
     },
     "destroy": {
@@ -2052,7 +2076,7 @@
       "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
       "dev": true,
       "requires": {
-        "repeating": "2.0.1"
+        "repeating": "^2.0.0"
       }
     },
     "detect-node": {
@@ -2079,9 +2103,9 @@
       "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=",
       "dev": true,
       "requires": {
-        "bn.js": "4.11.8",
-        "miller-rabin": "4.0.1",
-        "randombytes": "2.0.5"
+        "bn.js": "^4.1.0",
+        "miller-rabin": "^4.0.0",
+        "randombytes": "^2.0.0"
       }
     },
     "directory-encoder": {
@@ -2090,9 +2114,9 @@
       "integrity": "sha1-WbTiqk8lQi9sY7UntGL14tDdLFg=",
       "dev": true,
       "requires": {
-        "fs-extra": "0.23.1",
-        "handlebars": "1.3.0",
-        "img-stats": "0.5.2"
+        "fs-extra": "^0.23.1",
+        "handlebars": "^1.3.0",
+        "img-stats": "^0.5.2"
       },
       "dependencies": {
         "fs-extra": {
@@ -2101,10 +2125,10 @@
           "integrity": "sha1-ZhHbpq3yq43Jxp+rN83fiBgVfj0=",
           "dev": true,
           "requires": {
-            "graceful-fs": "4.1.11",
-            "jsonfile": "2.4.0",
-            "path-is-absolute": "1.0.1",
-            "rimraf": "2.6.1"
+            "graceful-fs": "^4.1.2",
+            "jsonfile": "^2.1.0",
+            "path-is-absolute": "^1.0.0",
+            "rimraf": "^2.2.8"
           }
         },
         "jsonfile": {
@@ -2113,7 +2137,7 @@
           "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
           "dev": true,
           "requires": {
-            "graceful-fs": "4.1.11"
+            "graceful-fs": "^4.1.6"
           }
         }
       }
@@ -2124,7 +2148,7 @@
       "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=",
       "dev": true,
       "requires": {
-        "utila": "0.3.3"
+        "utila": "~0.3"
       },
       "dependencies": {
         "utila": {
@@ -2141,10 +2165,10 @@
       "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=",
       "dev": true,
       "requires": {
-        "custom-event": "1.0.1",
-        "ent": "2.2.0",
-        "extend": "3.0.1",
-        "void-elements": "2.0.1"
+        "custom-event": "~1.0.0",
+        "ent": "~2.2.0",
+        "extend": "^3.0.0",
+        "void-elements": "^2.0.0"
       }
     },
     "dom-serializer": {
@@ -2153,8 +2177,8 @@
       "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
       "dev": true,
       "requires": {
-        "domelementtype": "1.1.3",
-        "entities": "1.1.1"
+        "domelementtype": "~1.1.1",
+        "entities": "~1.1.1"
       },
       "dependencies": {
         "domelementtype": {
@@ -2183,7 +2207,7 @@
       "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=",
       "dev": true,
       "requires": {
-        "domelementtype": "1.3.0"
+        "domelementtype": "1"
       }
     },
     "domutils": {
@@ -2192,8 +2216,8 @@
       "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
       "dev": true,
       "requires": {
-        "dom-serializer": "0.1.0",
-        "domelementtype": "1.3.0"
+        "dom-serializer": "0",
+        "domelementtype": "1"
       }
     },
     "dot-prop": {
@@ -2202,7 +2226,7 @@
       "integrity": "sha1-qEk/C3te7sglJbXHWH+n3nyoWcE=",
       "dev": true,
       "requires": {
-        "is-obj": "1.0.1"
+        "is-obj": "^1.0.0"
       }
     },
     "dragula": {
@@ -2227,7 +2251,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "jsbn": "0.1.1"
+        "jsbn": "~0.1.0"
       }
     },
     "ee-first": {
@@ -2248,13 +2272,13 @@
       "integrity": "sha1-ELbxOHwDL+9gzFPlIRq/rim0L90=",
       "dev": true,
       "requires": {
-        "body-parser": "1.17.2",
-        "chalk": "1.1.3",
+        "body-parser": "1.15.2",
+        "chalk": "^1.1.3",
         "express": "5.0.0-alpha.2",
-        "lodash": "4.17.4",
-        "minimist": "0.0.8",
-        "protractor": "4.0.14",
-        "q": "1.5.0",
+        "lodash": "4.16.4",
+        "minimist": "^1.2.0",
+        "protractor": "^4.0.9",
+        "q": "2.0.3",
         "temp": "0.8.3"
       },
       "dependencies": {
@@ -2264,7 +2288,7 @@
           "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=",
           "dev": true,
           "requires": {
-            "mime-types": "2.1.15",
+            "mime-types": "~2.1.6",
             "negotiator": "0.5.3"
           }
         },
@@ -2319,33 +2343,33 @@
           "integrity": "sha1-/VQXf2V7akxFQHJ3Au3Ry6o6asU=",
           "dev": true,
           "requires": {
-            "accepts": "1.2.13",
+            "accepts": "~1.2.10",
             "array-flatten": "1.1.0",
             "content-disposition": "0.5.0",
-            "content-type": "1.0.2",
+            "content-type": "~1.0.1",
             "cookie": "0.1.3",
             "cookie-signature": "1.0.6",
-            "debug": "2.2.0",
-            "depd": "1.0.1",
+            "debug": "~2.2.0",
+            "depd": "~1.0.1",
             "escape-html": "1.0.2",
-            "etag": "1.7.0",
+            "etag": "~1.7.0",
             "finalhandler": "0.4.0",
             "fresh": "0.3.0",
             "merge-descriptors": "1.0.0",
-            "methods": "1.1.2",
-            "on-finished": "2.3.0",
-            "parseurl": "1.3.1",
+            "methods": "~1.1.1",
+            "on-finished": "~2.3.0",
+            "parseurl": "~1.3.0",
             "path-is-absolute": "1.0.0",
             "path-to-regexp": "0.1.6",
-            "proxy-addr": "1.0.10",
+            "proxy-addr": "~1.0.8",
             "qs": "4.0.0",
-            "range-parser": "1.0.3",
-            "router": "1.1.5",
+            "range-parser": "~1.0.2",
+            "router": "~1.1.2",
             "send": "0.13.0",
-            "serve-static": "1.10.3",
-            "type-is": "1.6.15",
+            "serve-static": "~1.10.0",
+            "type-is": "~1.6.4",
             "utils-merge": "1.0.0",
-            "vary": "1.0.1"
+            "vary": "~1.0.0"
           }
         },
         "finalhandler": {
@@ -2354,10 +2378,10 @@
           "integrity": "sha1-llpS2ejQXSuFdUhUH7ibU6JJfZs=",
           "dev": true,
           "requires": {
-            "debug": "2.2.0",
+            "debug": "~2.2.0",
             "escape-html": "1.0.2",
-            "on-finished": "2.3.0",
-            "unpipe": "1.0.0"
+            "on-finished": "~2.3.0",
+            "unpipe": "~1.0.0"
           }
         },
         "fresh": {
@@ -2372,8 +2396,8 @@
           "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=",
           "dev": true,
           "requires": {
-            "inherits": "2.0.3",
-            "statuses": "1.2.1"
+            "inherits": "~2.0.1",
+            "statuses": "1"
           }
         },
         "ipaddr.js": {
@@ -2424,21 +2448,21 @@
           "integrity": "sha1-78Sod/rDoYKp3e0mzVhp9HYv0XI=",
           "dev": true,
           "requires": {
-            "@types/jasmine": "2.5.38",
-            "@types/node": "6.0.78",
-            "@types/q": "0.0.32",
+            "@types/jasmine": "^2.5.36",
+            "@types/node": "^6.0.46",
+            "@types/q": "^0.0.32",
             "@types/selenium-webdriver": "2.53.37",
             "adm-zip": "0.4.7",
-            "chalk": "1.1.3",
-            "glob": "7.1.2",
+            "chalk": "^1.1.3",
+            "glob": "^7.0.3",
             "jasmine": "2.4.1",
             "jasminewd2": "0.0.10",
-            "optimist": "0.6.1",
+            "optimist": "~0.6.0",
             "q": "1.4.1",
-            "saucelabs": "1.3.0",
+            "saucelabs": "~1.3.0",
             "selenium-webdriver": "2.53.3",
-            "source-map-support": "0.4.15",
-            "webdriver-manager": "10.3.0"
+            "source-map-support": "~0.4.0",
+            "webdriver-manager": "^10.3.0"
           },
           "dependencies": {
             "minimist": {
@@ -2459,16 +2483,16 @@
               "integrity": "sha1-mTFFiKCx2+aIxEHXQojGyxh1+os=",
               "dev": true,
               "requires": {
-                "adm-zip": "0.4.7",
-                "chalk": "1.1.3",
-                "del": "2.2.2",
-                "glob": "7.1.2",
-                "ini": "1.3.4",
-                "minimist": "1.2.0",
-                "q": "1.4.1",
-                "request": "2.81.0",
-                "rimraf": "2.6.1",
-                "semver": "5.3.0"
+                "adm-zip": "^0.4.7",
+                "chalk": "^1.1.1",
+                "del": "^2.2.0",
+                "glob": "^7.0.3",
+                "ini": "^1.3.4",
+                "minimist": "^1.2.0",
+                "q": "^1.4.1",
+                "request": "^2.78.0",
+                "rimraf": "^2.5.2",
+                "semver": "^5.3.0"
               }
             }
           }
@@ -2479,7 +2503,7 @@
           "integrity": "sha1-DUCoL4Afw1VWfS7LZe/j8HfxIcU=",
           "dev": true,
           "requires": {
-            "forwarded": "0.1.0",
+            "forwarded": "~0.1.0",
             "ipaddr.js": "1.0.5"
           }
         },
@@ -2501,8 +2525,8 @@
           "integrity": "sha1-zlpuzTEB/tXsCYJ9rCKpwpv7BTU=",
           "dev": true,
           "requires": {
-            "escape-html": "1.0.3",
-            "parseurl": "1.3.1",
+            "escape-html": "~1.0.3",
+            "parseurl": "~1.3.1",
             "send": "0.13.2"
           },
           "dependencies": {
@@ -2524,18 +2548,18 @@
               "integrity": "sha1-dl52B8gFVFK7pvCwUllTUJhgNt4=",
               "dev": true,
               "requires": {
-                "debug": "2.2.0",
-                "depd": "1.1.1",
-                "destroy": "1.0.4",
-                "escape-html": "1.0.3",
-                "etag": "1.7.0",
+                "debug": "~2.2.0",
+                "depd": "~1.1.0",
+                "destroy": "~1.0.4",
+                "escape-html": "~1.0.3",
+                "etag": "~1.7.0",
                 "fresh": "0.3.0",
-                "http-errors": "1.3.1",
+                "http-errors": "~1.3.1",
                 "mime": "1.3.4",
                 "ms": "0.7.1",
-                "on-finished": "2.3.0",
-                "range-parser": "1.0.3",
-                "statuses": "1.2.1"
+                "on-finished": "~2.3.0",
+                "range-parser": "~1.0.3",
+                "statuses": "~1.2.1"
               }
             }
           }
@@ -2560,13 +2584,13 @@
       "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
       "dev": true,
       "requires": {
-        "bn.js": "4.11.8",
-        "brorand": "1.1.0",
-        "hash.js": "1.1.3",
-        "hmac-drbg": "1.0.1",
-        "inherits": "2.0.3",
-        "minimalistic-assert": "1.0.0",
-        "minimalistic-crypto-utils": "1.0.1"
+        "bn.js": "^4.4.0",
+        "brorand": "^1.0.1",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.0"
       }
     },
     "ember-cli-normalize-entity-name": {
@@ -2575,7 +2599,7 @@
       "integrity": "sha1-CxT3vLxZmqEXtf3cgeT9A8S61bc=",
       "dev": true,
       "requires": {
-        "silent-error": "1.1.0"
+        "silent-error": "^1.0.0"
       }
     },
     "ember-cli-string-utils": {
@@ -2690,10 +2714,10 @@
       "integrity": "sha1-lQlk7MfwMypCMhtnOzjcj/FVNbM=",
       "dev": true,
       "requires": {
-        "graceful-fs": "4.1.11",
-        "memory-fs": "0.4.1",
-        "object-assign": "4.1.1",
-        "tapable": "0.2.8"
+        "graceful-fs": "^4.1.2",
+        "memory-fs": "^0.4.0",
+        "object-assign": "^4.0.1",
+        "tapable": "^0.2.5"
       }
     },
     "ensure-posix-path": {
@@ -2720,7 +2744,7 @@
       "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=",
       "dev": true,
       "requires": {
-        "prr": "0.0.0"
+        "prr": "~0.0.0"
       }
     },
     "error-ex": {
@@ -2729,7 +2753,7 @@
       "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
       "dev": true,
       "requires": {
-        "is-arrayish": "0.2.1"
+        "is-arrayish": "^0.2.1"
       }
     },
     "es6-promise": {
@@ -2738,6 +2762,23 @@
       "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=",
       "dev": true
     },
+    "es6-promisify": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+      "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+      "dev": true,
+      "requires": {
+        "es6-promise": "^4.0.3"
+      },
+      "dependencies": {
+        "es6-promise": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz",
+          "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==",
+          "dev": true
+        }
+      }
+    },
     "escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -2786,7 +2827,7 @@
       "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=",
       "dev": true,
       "requires": {
-        "original": "1.0.0"
+        "original": ">=0.0.5"
       }
     },
     "evp_bytestokey": {
@@ -2795,8 +2836,8 @@
       "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=",
       "dev": true,
       "requires": {
-        "md5.js": "1.3.4",
-        "safe-buffer": "5.1.1"
+        "md5.js": "^1.3.4",
+        "safe-buffer": "^5.1.1"
       },
       "dependencies": {
         "safe-buffer": {
@@ -2813,12 +2854,12 @@
       "integrity": "sha1-TrZGejaglfq7KXD/nV4/t7zm68M=",
       "dev": true,
       "requires": {
-        "cross-spawn-async": "2.2.5",
-        "is-stream": "1.1.0",
-        "npm-run-path": "1.0.0",
-        "object-assign": "4.1.1",
-        "path-key": "1.0.0",
-        "strip-eof": "1.0.0"
+        "cross-spawn-async": "^2.1.1",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^1.0.0",
+        "object-assign": "^4.0.1",
+        "path-key": "^1.0.0",
+        "strip-eof": "^1.0.0"
       }
     },
     "exit": {
@@ -2833,9 +2874,9 @@
       "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=",
       "dev": true,
       "requires": {
-        "array-slice": "0.2.3",
-        "array-unique": "0.2.1",
-        "braces": "0.1.5"
+        "array-slice": "^0.2.3",
+        "array-unique": "^0.2.1",
+        "braces": "^0.1.2"
       },
       "dependencies": {
         "braces": {
@@ -2844,7 +2885,7 @@
           "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=",
           "dev": true,
           "requires": {
-            "expand-range": "0.1.1"
+            "expand-range": "^0.1.0"
           }
         },
         "expand-range": {
@@ -2853,8 +2894,8 @@
           "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=",
           "dev": true,
           "requires": {
-            "is-number": "0.1.1",
-            "repeat-string": "0.2.2"
+            "is-number": "^0.1.1",
+            "repeat-string": "^0.2.2"
           }
         },
         "is-number": {
@@ -2877,7 +2918,7 @@
       "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
       "dev": true,
       "requires": {
-        "is-posix-bracket": "0.1.1"
+        "is-posix-bracket": "^0.1.0"
       }
     },
     "expand-range": {
@@ -2886,7 +2927,7 @@
       "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
       "dev": true,
       "requires": {
-        "fill-range": "2.2.3"
+        "fill-range": "^2.1.0"
       }
     },
     "exports-loader": {
@@ -2895,8 +2936,8 @@
       "integrity": "sha1-1w/GEhl1s1/BKDDPUnVL4nQPyIY=",
       "dev": true,
       "requires": {
-        "loader-utils": "1.1.0",
-        "source-map": "0.5.6"
+        "loader-utils": "^1.0.2",
+        "source-map": "0.5.x"
       }
     },
     "express": {
@@ -2905,34 +2946,34 @@
       "integrity": "sha1-rxB/wUhQRFfy3Kmm8lcdcSm5ezU=",
       "dev": true,
       "requires": {
-        "accepts": "1.3.3",
+        "accepts": "~1.3.3",
         "array-flatten": "1.1.1",
         "content-disposition": "0.5.2",
-        "content-type": "1.0.2",
+        "content-type": "~1.0.2",
         "cookie": "0.3.1",
         "cookie-signature": "1.0.6",
         "debug": "2.6.1",
-        "depd": "1.1.0",
-        "encodeurl": "1.0.1",
-        "escape-html": "1.0.3",
-        "etag": "1.8.0",
-        "finalhandler": "1.0.3",
+        "depd": "~1.1.0",
+        "encodeurl": "~1.0.1",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.0",
+        "finalhandler": "~1.0.0",
         "fresh": "0.5.0",
         "merge-descriptors": "1.0.1",
-        "methods": "1.1.2",
-        "on-finished": "2.3.0",
-        "parseurl": "1.3.1",
+        "methods": "~1.1.2",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.1",
         "path-to-regexp": "0.1.7",
-        "proxy-addr": "1.1.4",
+        "proxy-addr": "~1.1.3",
         "qs": "6.4.0",
-        "range-parser": "1.2.0",
+        "range-parser": "~1.2.0",
         "send": "0.15.1",
         "serve-static": "1.12.1",
         "setprototypeof": "1.0.3",
-        "statuses": "1.3.1",
-        "type-is": "1.6.15",
+        "statuses": "~1.3.1",
+        "type-is": "~1.6.14",
         "utils-merge": "1.0.0",
-        "vary": "1.1.1"
+        "vary": "~1.1.0"
       },
       "dependencies": {
         "debug": {
@@ -2963,18 +3004,18 @@
           "dev": true,
           "requires": {
             "debug": "2.6.1",
-            "depd": "1.1.0",
-            "destroy": "1.0.4",
-            "encodeurl": "1.0.1",
-            "escape-html": "1.0.3",
-            "etag": "1.8.0",
+            "depd": "~1.1.0",
+            "destroy": "~1.0.4",
+            "encodeurl": "~1.0.1",
+            "escape-html": "~1.0.3",
+            "etag": "~1.8.0",
             "fresh": "0.5.0",
-            "http-errors": "1.6.1",
+            "http-errors": "~1.6.1",
             "mime": "1.3.4",
             "ms": "0.7.2",
-            "on-finished": "2.3.0",
-            "range-parser": "1.2.0",
-            "statuses": "1.3.1"
+            "on-finished": "~2.3.0",
+            "range-parser": "~1.2.0",
+            "statuses": "~1.3.1"
           }
         }
       }
@@ -2991,9 +3032,9 @@
       "integrity": "sha1-PQJqIbf5W1cmOH1CAKwWDTcsO0g=",
       "dev": true,
       "requires": {
-        "chardet": "0.4.2",
-        "iconv-lite": "0.4.19",
-        "tmp": "0.0.33"
+        "chardet": "^0.4.0",
+        "iconv-lite": "^0.4.17",
+        "tmp": "^0.0.33"
       },
       "dependencies": {
         "tmp": {
@@ -3002,7 +3043,7 @@
           "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
           "dev": true,
           "requires": {
-            "os-tmpdir": "1.0.2"
+            "os-tmpdir": "~1.0.2"
           }
         }
       }
@@ -3013,7 +3054,7 @@
       "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
       "dev": true,
       "requires": {
-        "is-extglob": "1.0.0"
+        "is-extglob": "^1.0.0"
       }
     },
     "extract-text-webpack-plugin": {
@@ -3022,10 +3063,33 @@
       "integrity": "sha1-dW7076gVXDaBgz+8NNpTuUF0bWw=",
       "dev": true,
       "requires": {
-        "async": "2.4.1",
-        "loader-utils": "1.1.0",
-        "schema-utils": "0.3.0",
-        "webpack-sources": "1.1.0"
+        "async": "^2.1.2",
+        "loader-utils": "^1.0.2",
+        "schema-utils": "^0.3.0",
+        "webpack-sources": "^1.0.1"
+      }
+    },
+    "extract-zip": {
+      "version": "1.6.7",
+      "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
+      "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
+      "dev": true,
+      "requires": {
+        "concat-stream": "1.6.2",
+        "debug": "2.6.9",
+        "mkdirp": "0.5.1",
+        "yauzl": "2.4.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        }
       }
     },
     "extsprintf": {
@@ -3058,7 +3122,16 @@
       "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
       "dev": true,
       "requires": {
-        "websocket-driver": "0.7.0"
+        "websocket-driver": ">=0.5.1"
+      }
+    },
+    "fd-slicer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
+      "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
+      "dev": true,
+      "requires": {
+        "pend": "~1.2.0"
       }
     },
     "figures": {
@@ -3067,7 +3140,7 @@
       "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
       "dev": true,
       "requires": {
-        "escape-string-regexp": "1.0.5"
+        "escape-string-regexp": "^1.0.5"
       }
     },
     "file-loader": {
@@ -3076,7 +3149,7 @@
       "integrity": "sha1-gVA0EZiR/GRB+1pkwRvJPCLd2EI=",
       "dev": true,
       "requires": {
-        "loader-utils": "1.1.0"
+        "loader-utils": "^1.0.2"
       }
     },
     "filename-regex": {
@@ -3091,8 +3164,8 @@
       "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=",
       "dev": true,
       "requires": {
-        "glob": "7.1.2",
-        "minimatch": "3.0.4"
+        "glob": "^7.0.3",
+        "minimatch": "^3.0.3"
       }
     },
     "fill-range": {
@@ -3101,11 +3174,11 @@
       "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=",
       "dev": true,
       "requires": {
-        "is-number": "2.1.0",
-        "isobject": "2.1.0",
-        "randomatic": "1.1.6",
-        "repeat-element": "1.1.2",
-        "repeat-string": "1.6.1"
+        "is-number": "^2.1.0",
+        "isobject": "^2.0.0",
+        "randomatic": "^1.1.3",
+        "repeat-element": "^1.1.2",
+        "repeat-string": "^1.5.2"
       }
     },
     "finalhandler": {
@@ -3115,12 +3188,12 @@
       "dev": true,
       "requires": {
         "debug": "2.6.7",
-        "encodeurl": "1.0.1",
-        "escape-html": "1.0.3",
-        "on-finished": "2.3.0",
-        "parseurl": "1.3.1",
-        "statuses": "1.3.1",
-        "unpipe": "1.0.0"
+        "encodeurl": "~1.0.1",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.1",
+        "statuses": "~1.3.1",
+        "unpipe": "~1.0.0"
       },
       "dependencies": {
         "debug": {
@@ -3140,8 +3213,8 @@
       "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
       "dev": true,
       "requires": {
-        "path-exists": "2.1.0",
-        "pinkie-promise": "2.0.1"
+        "path-exists": "^2.0.0",
+        "pinkie-promise": "^2.0.0"
       }
     },
     "findup-sync": {
@@ -3150,7 +3223,7 @@
       "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
       "dev": true,
       "requires": {
-        "glob": "5.0.15"
+        "glob": "~5.0.0"
       },
       "dependencies": {
         "glob": {
@@ -3159,11 +3232,11 @@
           "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
           "dev": true,
           "requires": {
-            "inflight": "1.0.6",
-            "inherits": "2.0.3",
-            "minimatch": "3.0.4",
-            "once": "1.4.0",
-            "path-is-absolute": "1.0.1"
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "2 || 3",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
           }
         }
       }
@@ -3191,7 +3264,7 @@
       "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
       "dev": true,
       "requires": {
-        "for-in": "1.0.2"
+        "for-in": "^1.0.1"
       }
     },
     "forever-agent": {
@@ -3206,9 +3279,9 @@
       "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
       "dev": true,
       "requires": {
-        "asynckit": "0.4.0",
-        "combined-stream": "1.0.5",
-        "mime-types": "2.1.15"
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.5",
+        "mime-types": "^2.1.12"
       }
     },
     "forwarded": {
@@ -3229,7 +3302,7 @@
       "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=",
       "dev": true,
       "requires": {
-        "null-check": "1.0.0"
+        "null-check": "^1.0.0"
       }
     },
     "fs-extra": {
@@ -3238,9 +3311,9 @@
       "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=",
       "dev": true,
       "requires": {
-        "graceful-fs": "4.1.11",
-        "jsonfile": "4.0.0",
-        "universalify": "0.1.1"
+        "graceful-fs": "^4.1.2",
+        "jsonfile": "^4.0.0",
+        "universalify": "^0.1.0"
       }
     },
     "fs.realpath": {
@@ -3256,8 +3329,8 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "nan": "2.6.2",
-        "node-pre-gyp": "0.6.33"
+        "nan": "^2.3.0",
+        "node-pre-gyp": "^0.6.29"
       },
       "dependencies": {
         "abbrev": {
@@ -3289,8 +3362,8 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "delegates": "1.0.0",
-            "readable-stream": "2.2.2"
+            "delegates": "^1.0.0",
+            "readable-stream": "^2.0.0 || ^1.1.13"
           }
         },
         "asn1": {
@@ -3334,7 +3407,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "tweetnacl": "0.14.5"
+            "tweetnacl": "^0.14.3"
           }
         },
         "block-stream": {
@@ -3342,7 +3415,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "inherits": "2.0.3"
+            "inherits": "~2.0.0"
           }
         },
         "boom": {
@@ -3350,7 +3423,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "hoek": "2.16.3"
+            "hoek": "2.x.x"
           }
         },
         "brace-expansion": {
@@ -3358,7 +3431,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "balanced-match": "0.4.2",
+            "balanced-match": "^0.4.1",
             "concat-map": "0.0.1"
           }
         },
@@ -3379,11 +3452,11 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "ansi-styles": "2.2.1",
-            "escape-string-regexp": "1.0.5",
-            "has-ansi": "2.0.0",
-            "strip-ansi": "3.0.1",
-            "supports-color": "2.0.0"
+            "ansi-styles": "^2.2.1",
+            "escape-string-regexp": "^1.0.2",
+            "has-ansi": "^2.0.0",
+            "strip-ansi": "^3.0.0",
+            "supports-color": "^2.0.0"
           }
         },
         "code-point-at": {
@@ -3396,7 +3469,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "delayed-stream": "1.0.0"
+            "delayed-stream": "~1.0.0"
           }
         },
         "commander": {
@@ -3405,7 +3478,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "graceful-readlink": "1.0.1"
+            "graceful-readlink": ">= 1.0.0"
           }
         },
         "concat-map": {
@@ -3429,7 +3502,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "boom": "2.10.1"
+            "boom": "2.x.x"
           }
         },
         "dashdash": {
@@ -3438,7 +3511,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "assert-plus": "1.0.0"
+            "assert-plus": "^1.0.0"
           },
           "dependencies": {
             "assert-plus": {
@@ -3481,7 +3554,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "jsbn": "0.1.1"
+            "jsbn": "~0.1.0"
           }
         },
         "escape-string-regexp": {
@@ -3513,9 +3586,9 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "asynckit": "0.4.0",
-            "combined-stream": "1.0.5",
-            "mime-types": "2.1.14"
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.5",
+            "mime-types": "^2.1.12"
           }
         },
         "fs.realpath": {
@@ -3528,10 +3601,10 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "graceful-fs": "4.1.11",
-            "inherits": "2.0.3",
-            "mkdirp": "0.5.1",
-            "rimraf": "2.5.4"
+            "graceful-fs": "^4.1.2",
+            "inherits": "~2.0.0",
+            "mkdirp": ">=0.5 0",
+            "rimraf": "2"
           }
         },
         "fstream-ignore": {
@@ -3540,9 +3613,9 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "fstream": "1.0.10",
-            "inherits": "2.0.3",
-            "minimatch": "3.0.3"
+            "fstream": "^1.0.0",
+            "inherits": "2",
+            "minimatch": "^3.0.0"
           }
         },
         "gauge": {
@@ -3551,14 +3624,14 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "aproba": "1.1.1",
-            "console-control-strings": "1.1.0",
-            "has-unicode": "2.0.1",
-            "object-assign": "4.1.1",
-            "signal-exit": "3.0.2",
-            "string-width": "1.0.2",
-            "strip-ansi": "3.0.1",
-            "wide-align": "1.1.0"
+            "aproba": "^1.0.3",
+            "console-control-strings": "^1.0.0",
+            "has-unicode": "^2.0.0",
+            "object-assign": "^4.1.0",
+            "signal-exit": "^3.0.0",
+            "string-width": "^1.0.1",
+            "strip-ansi": "^3.0.1",
+            "wide-align": "^1.1.0"
           }
         },
         "generate-function": {
@@ -3573,7 +3646,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "is-property": "1.0.2"
+            "is-property": "^1.0.0"
           }
         },
         "getpass": {
@@ -3582,7 +3655,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "assert-plus": "1.0.0"
+            "assert-plus": "^1.0.0"
           },
           "dependencies": {
             "assert-plus": {
@@ -3598,12 +3671,12 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "fs.realpath": "1.0.0",
-            "inflight": "1.0.6",
-            "inherits": "2.0.3",
-            "minimatch": "3.0.3",
-            "once": "1.4.0",
-            "path-is-absolute": "1.0.1"
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.2",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
           }
         },
         "graceful-fs": {
@@ -3623,10 +3696,10 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "chalk": "1.1.3",
-            "commander": "2.9.0",
-            "is-my-json-valid": "2.15.0",
-            "pinkie-promise": "2.0.1"
+            "chalk": "^1.1.1",
+            "commander": "^2.9.0",
+            "is-my-json-valid": "^2.12.4",
+            "pinkie-promise": "^2.0.0"
           }
         },
         "has-ansi": {
@@ -3635,7 +3708,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "ansi-regex": "2.1.1"
+            "ansi-regex": "^2.0.0"
           }
         },
         "has-unicode": {
@@ -3650,10 +3723,10 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "boom": "2.10.1",
-            "cryptiles": "2.0.5",
-            "hoek": "2.16.3",
-            "sntp": "1.0.9"
+            "boom": "2.x.x",
+            "cryptiles": "2.x.x",
+            "hoek": "2.x.x",
+            "sntp": "1.x.x"
           }
         },
         "hoek": {
@@ -3667,9 +3740,9 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "assert-plus": "0.2.0",
-            "jsprim": "1.3.1",
-            "sshpk": "1.10.2"
+            "assert-plus": "^0.2.0",
+            "jsprim": "^1.2.2",
+            "sshpk": "^1.7.0"
           }
         },
         "inflight": {
@@ -3677,8 +3750,8 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "once": "1.4.0",
-            "wrappy": "1.0.2"
+            "once": "^1.3.0",
+            "wrappy": "1"
           }
         },
         "inherits": {
@@ -3697,7 +3770,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "number-is-nan": "1.0.1"
+            "number-is-nan": "^1.0.0"
           }
         },
         "is-my-json-valid": {
@@ -3706,10 +3779,10 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "generate-function": "2.0.0",
-            "generate-object-property": "1.2.0",
-            "jsonpointer": "4.0.1",
-            "xtend": "4.0.1"
+            "generate-function": "^2.0.0",
+            "generate-object-property": "^1.1.0",
+            "jsonpointer": "^4.0.0",
+            "xtend": "^4.0.0"
           }
         },
         "is-property": {
@@ -3741,7 +3814,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "jsbn": "0.1.1"
+            "jsbn": "~0.1.0"
           }
         },
         "jsbn": {
@@ -3789,7 +3862,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "mime-db": "1.26.0"
+            "mime-db": "~1.26.0"
           }
         },
         "minimatch": {
@@ -3797,7 +3870,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "brace-expansion": "1.1.6"
+            "brace-expansion": "^1.0.0"
           }
         },
         "minimist": {
@@ -3825,15 +3898,15 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "mkdirp": "0.5.1",
-            "nopt": "3.0.6",
-            "npmlog": "4.0.2",
-            "rc": "1.1.7",
-            "request": "2.79.0",
-            "rimraf": "2.5.4",
-            "semver": "5.3.0",
-            "tar": "2.2.1",
-            "tar-pack": "3.3.0"
+            "mkdirp": "~0.5.1",
+            "nopt": "~3.0.6",
+            "npmlog": "^4.0.1",
+            "rc": "~1.1.6",
+            "request": "^2.79.0",
+            "rimraf": "~2.5.4",
+            "semver": "~5.3.0",
+            "tar": "~2.2.1",
+            "tar-pack": "~3.3.0"
           }
         },
         "nopt": {
@@ -3842,7 +3915,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "abbrev": "1.1.0"
+            "abbrev": "1"
           }
         },
         "npmlog": {
@@ -3851,10 +3924,10 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "are-we-there-yet": "1.1.2",
-            "console-control-strings": "1.1.0",
-            "gauge": "2.7.3",
-            "set-blocking": "2.0.0"
+            "are-we-there-yet": "~1.1.2",
+            "console-control-strings": "~1.1.0",
+            "gauge": "~2.7.1",
+            "set-blocking": "~2.0.0"
           }
         },
         "number-is-nan": {
@@ -3879,7 +3952,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "wrappy": "1.0.2"
+            "wrappy": "1"
           }
         },
         "path-is-absolute": {
@@ -3899,7 +3972,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "pinkie": "2.0.4"
+            "pinkie": "^2.0.0"
           }
         },
         "process-nextick-args": {
@@ -3925,10 +3998,10 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "deep-extend": "0.4.1",
-            "ini": "1.3.4",
-            "minimist": "1.2.0",
-            "strip-json-comments": "2.0.1"
+            "deep-extend": "~0.4.0",
+            "ini": "~1.3.0",
+            "minimist": "^1.2.0",
+            "strip-json-comments": "~2.0.1"
           },
           "dependencies": {
             "minimist": {
@@ -3945,13 +4018,13 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "buffer-shims": "1.0.0",
-            "core-util-is": "1.0.2",
-            "inherits": "2.0.3",
-            "isarray": "1.0.0",
-            "process-nextick-args": "1.0.7",
-            "string_decoder": "0.10.31",
-            "util-deprecate": "1.0.2"
+            "buffer-shims": "^1.0.0",
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~1.0.6",
+            "string_decoder": "~0.10.x",
+            "util-deprecate": "~1.0.1"
           }
         },
         "request": {
@@ -3960,26 +4033,26 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "aws-sign2": "0.6.0",
-            "aws4": "1.6.0",
-            "caseless": "0.11.0",
-            "combined-stream": "1.0.5",
-            "extend": "3.0.0",
-            "forever-agent": "0.6.1",
-            "form-data": "2.1.2",
-            "har-validator": "2.0.6",
-            "hawk": "3.1.3",
-            "http-signature": "1.1.1",
-            "is-typedarray": "1.0.0",
-            "isstream": "0.1.2",
-            "json-stringify-safe": "5.0.1",
-            "mime-types": "2.1.14",
-            "oauth-sign": "0.8.2",
-            "qs": "6.3.1",
-            "stringstream": "0.0.5",
-            "tough-cookie": "2.3.2",
-            "tunnel-agent": "0.4.3",
-            "uuid": "3.0.1"
+            "aws-sign2": "~0.6.0",
+            "aws4": "^1.2.1",
+            "caseless": "~0.11.0",
+            "combined-stream": "~1.0.5",
+            "extend": "~3.0.0",
+            "forever-agent": "~0.6.1",
+            "form-data": "~2.1.1",
+            "har-validator": "~2.0.6",
+            "hawk": "~3.1.3",
+            "http-signature": "~1.1.0",
+            "is-typedarray": "~1.0.0",
+            "isstream": "~0.1.2",
+            "json-stringify-safe": "~5.0.1",
+            "mime-types": "~2.1.7",
+            "oauth-sign": "~0.8.1",
+            "qs": "~6.3.0",
+            "stringstream": "~0.0.4",
+            "tough-cookie": "~2.3.0",
+            "tunnel-agent": "~0.4.1",
+            "uuid": "^3.0.0"
           }
         },
         "rimraf": {
@@ -3987,7 +4060,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "glob": "7.1.1"
+            "glob": "^7.0.5"
           }
         },
         "semver": {
@@ -4014,7 +4087,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "hoek": "2.16.3"
+            "hoek": "2.x.x"
           }
         },
         "sshpk": {
@@ -4023,15 +4096,15 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "asn1": "0.2.3",
-            "assert-plus": "1.0.0",
-            "bcrypt-pbkdf": "1.0.1",
-            "dashdash": "1.14.1",
-            "ecc-jsbn": "0.1.1",
-            "getpass": "0.1.6",
-            "jodid25519": "1.0.2",
-            "jsbn": "0.1.1",
-            "tweetnacl": "0.14.5"
+            "asn1": "~0.2.3",
+            "assert-plus": "^1.0.0",
+            "bcrypt-pbkdf": "^1.0.0",
+            "dashdash": "^1.12.0",
+            "ecc-jsbn": "~0.1.1",
+            "getpass": "^0.1.1",
+            "jodid25519": "^1.0.0",
+            "jsbn": "~0.1.0",
+            "tweetnacl": "~0.14.0"
           },
           "dependencies": {
             "assert-plus": {
@@ -4047,9 +4120,9 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "code-point-at": "1.1.0",
-            "is-fullwidth-code-point": "1.0.0",
-            "strip-ansi": "3.0.1"
+            "code-point-at": "^1.0.0",
+            "is-fullwidth-code-point": "^1.0.0",
+            "strip-ansi": "^3.0.0"
           }
         },
         "string_decoder": {
@@ -4068,7 +4141,7 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "ansi-regex": "2.1.1"
+            "ansi-regex": "^2.0.0"
           }
         },
         "strip-json-comments": {
@@ -4088,9 +4161,9 @@
           "bundled": true,
           "dev": true,
           "requires": {
-            "block-stream": "0.0.9",
-            "fstream": "1.0.10",
-            "inherits": "2.0.3"
+            "block-stream": "*",
+            "fstream": "^1.0.2",
+            "inherits": "2"
           }
         },
         "tar-pack": {
@@ -4099,14 +4172,14 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "debug": "2.2.0",
-            "fstream": "1.0.10",
-            "fstream-ignore": "1.0.5",
-            "once": "1.3.3",
-            "readable-stream": "2.1.5",
-            "rimraf": "2.5.4",
-            "tar": "2.2.1",
-            "uid-number": "0.0.6"
+            "debug": "~2.2.0",
+            "fstream": "~1.0.10",
+            "fstream-ignore": "~1.0.5",
+            "once": "~1.3.3",
+            "readable-stream": "~2.1.4",
+            "rimraf": "~2.5.1",
+            "tar": "~2.2.1",
+            "uid-number": "~0.0.6"
           },
           "dependencies": {
             "once": {
@@ -4115,7 +4188,7 @@
               "dev": true,
               "optional": true,
               "requires": {
-                "wrappy": "1.0.2"
+                "wrappy": "1"
               }
             },
             "readable-stream": {
@@ -4124,13 +4197,13 @@
               "dev": true,
               "optional": true,
               "requires": {
-                "buffer-shims": "1.0.0",
-                "core-util-is": "1.0.2",
-                "inherits": "2.0.3",
-                "isarray": "1.0.0",
-                "process-nextick-args": "1.0.7",
-                "string_decoder": "0.10.31",
-                "util-deprecate": "1.0.2"
+                "buffer-shims": "^1.0.0",
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.1",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~1.0.6",
+                "string_decoder": "~0.10.x",
+                "util-deprecate": "~1.0.1"
               }
             }
           }
@@ -4141,7 +4214,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "punycode": "1.4.1"
+            "punycode": "^1.4.1"
           }
         },
         "tunnel-agent": {
@@ -4188,7 +4261,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "string-width": "1.0.2"
+            "string-width": "^1.0.1"
           }
         },
         "wrappy": {
@@ -4210,10 +4283,10 @@
       "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
       "dev": true,
       "requires": {
-        "graceful-fs": "4.1.11",
-        "inherits": "2.0.3",
-        "mkdirp": "0.5.1",
-        "rimraf": "2.6.1"
+        "graceful-fs": "^4.1.2",
+        "inherits": "~2.0.0",
+        "mkdirp": ">=0.5 0",
+        "rimraf": "2"
       }
     },
     "function-bind": {
@@ -4228,14 +4301,14 @@
       "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
       "dev": true,
       "requires": {
-        "aproba": "1.1.2",
-        "console-control-strings": "1.1.0",
-        "has-unicode": "2.0.1",
-        "object-assign": "4.1.1",
-        "signal-exit": "3.0.2",
-        "string-width": "1.0.2",
-        "strip-ansi": "3.0.1",
-        "wide-align": "1.1.2"
+        "aproba": "^1.0.3",
+        "console-control-strings": "^1.0.0",
+        "has-unicode": "^2.0.0",
+        "object-assign": "^4.1.0",
+        "signal-exit": "^3.0.0",
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wide-align": "^1.1.0"
       },
       "dependencies": {
         "is-fullwidth-code-point": {
@@ -4244,7 +4317,7 @@
           "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
           "dev": true,
           "requires": {
-            "number-is-nan": "1.0.1"
+            "number-is-nan": "^1.0.0"
           }
         },
         "string-width": {
@@ -4253,9 +4326,9 @@
           "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
           "dev": true,
           "requires": {
-            "code-point-at": "1.1.0",
-            "is-fullwidth-code-point": "1.0.0",
-            "strip-ansi": "3.0.1"
+            "code-point-at": "^1.0.0",
+            "is-fullwidth-code-point": "^1.0.0",
+            "strip-ansi": "^3.0.0"
           }
         }
       }
@@ -4266,7 +4339,7 @@
       "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=",
       "dev": true,
       "requires": {
-        "globule": "1.1.0"
+        "globule": "^1.0.0"
       }
     },
     "get-caller-file": {
@@ -4293,7 +4366,7 @@
       "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
       "dev": true,
       "requires": {
-        "assert-plus": "1.0.0"
+        "assert-plus"

<TRUNCATED>

[38/50] [abbrv] metron git commit: METRON-1737: Document Job cleanup (merrimanr via mmiklavc) closes apache/metron#1164

Posted by ni...@apache.org.
METRON-1737: Document Job cleanup (merrimanr via mmiklavc) closes apache/metron#1164


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/6b70571d
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/6b70571d
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/6b70571d

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 6b70571d6de3951c98269bbf5b38e8b69deddfab
Parents: d9e1f38
Author: merrimanr <me...@gmail.com>
Authored: Wed Aug 15 16:00:13 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Wed Aug 15 16:00:13 2018 -0600

----------------------------------------------------------------------
 metron-interface/metron-rest/README.md | 11 +++++++++++
 1 file changed, 11 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/6b70571d/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md
index 080422d..2c216d1 100644
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@ -222,6 +222,17 @@ Out of the box it is a simple wrapper around the tshark command to transform raw
 REST will supply the script with raw pcap data through standard in and expects PDML data serialized as XML.
 
 Pcap query jobs can be configured for submission to a YARN queue.  This setting is exposed as the Spring property `pcap.yarn.queue`.  If configured, the REST application will set the `mapreduce.job.queuename` Hadoop property to that value.
+It is highly recommended that a dedicated YARN queue be created and configured for Pcap queries to prevent a job from consuming too many cluster resources.  More information about setting up YARN queues can be found [here](https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/CapacityScheduler.html#Setting_up_queues).
+
+Pcap query results are stored in HDFS.  The location of query results when run through the REST app is determined by a couple factors.  The root of Pcap query results defaults to `/apps/metron/pcap/output` but can be changed with the 
+Spring property `pcap.final.output.path`.  Assuming the default Pcap query output directory, the path to a result page will follow this pattern:
+```
+/apps/metron/pcap/output/{username}/MAP_REDUCE/{job id}/page-{page number}.pcap
+```
+Over time Pcap query results will accumulate in HDFS.  Currently these results are not cleaned up automatically so cluster administrators should be aware of this and monitor them.  It is highly recommended that a process be put in place to 
+periodically delete files and directories under the Pcap query results root.
+
+Users should also be mindful of date ranges used in queries so they don't produce result sets that are too large.  Currently there are no limits enforced on date ranges.
 
 Queries can also be configured on a global level for setting the number of results per page via a Spring property `pcap.page.size`. By default, this value is set to 10 pcaps per page, but you may choose to set this value higher
 based on observing frequenetly-run query result sizes. This setting works in conjunction with the property for setting finalizer threadpool size when optimizing query performance.


[16/50] [abbrv] metron git commit: Merge branch 'master' into feature/METRON-1554-pcap-query-panel

Posted by ni...@apache.org.
Merge branch 'master' into feature/METRON-1554-pcap-query-panel


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/89726659
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/89726659
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/89726659

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 897266598b7984884d6cf8a6a699fe902e792483
Parents: 09a7f18 1b2bdff
Author: merrimanr <me...@gmail.com>
Authored: Mon Aug 6 11:08:48 2018 -0500
Committer: merrimanr <me...@gmail.com>
Committed: Mon Aug 6 11:08:48 2018 -0500

----------------------------------------------------------------------
 .../METRON/CURRENT/repos/repoinfo.xml           |   7 ++
 .../packaging/docker/ansible-docker/Dockerfile  | 118 ++++++++++++-------
 .../packaging/docker/deb-docker/Dockerfile      |  23 ++--
 .../packaging/docker/rpm-docker/Dockerfile      |  46 ++++++--
 metron-sensors/pycapa/README.md                 |  47 +++++---
 5 files changed, 156 insertions(+), 85 deletions(-)
----------------------------------------------------------------------



[50/50] [abbrv] metron git commit: Merge remote-tracking branch 'apache/master' into metron-1699-create-batch-profiler

Posted by ni...@apache.org.
Merge remote-tracking branch 'apache/master' into metron-1699-create-batch-profiler


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/f5eaef3c
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/f5eaef3c
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/f5eaef3c

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: f5eaef3c24e939ef14757eb2db4b758e71d33fca
Parents: 113ac0d d32bd50
Author: Nick Allen <ni...@nickallen.org>
Authored: Fri Aug 31 15:11:49 2018 -0400
Committer: Nick Allen <ni...@nickallen.org>
Committed: Fri Aug 31 15:11:49 2018 -0400

----------------------------------------------------------------------
 dependencies_with_url.csv                       |     8 +
 metron-analytics/metron-profiler-common/pom.xml |    26 +-
 .../metron/profiler/ProfileMeasurement.java     |    35 +
 .../apache/metron/profiler/ProfilePeriod.java   |    11 +
 .../metron/profiler/ProfileMeasurementTest.java |    28 +-
 .../metron/profiler/ProfilePeriodTest.java      |    22 +-
 .../profiler/hbase/SaltyRowKeyBuilderTest.java  |    11 -
 metron-analytics/metron-profiler/README.md      |    18 +-
 .../profiler/bolt/ProfileSplitterBolt.java      |    22 +-
 .../profiler/bolt/ProfileSplitterBoltTest.java  |    16 +
 .../integration/ProfilerIntegrationTest.java    |    30 +-
 metron-deployment/Kerberos-manual-setup.md      |     4 +-
 metron-deployment/amazon-ec2/conf/defaults.yml  |     1 -
 metron-deployment/amazon-ec2/playbook.yml       |     2 +-
 .../ansible/playbooks/metron_install.yml        |     7 -
 .../roles/ambari_config/vars/single_node_vm.yml |     1 +
 .../roles/metron_pcapservice/defaults/main.yml  |    28 -
 .../roles/metron_pcapservice/meta/main.yml      |    19 -
 .../metron_pcapservice/tasks/config-hbase.yml   |    26 -
 .../roles/metron_pcapservice/tasks/main.yml     |    25 -
 .../metron_pcapservice/tasks/pcapservice.yml    |    30 -
 .../templates/metron-pcapservice-logrotate.yml  |    27 -
 .../metron_pcapservice/templates/pcapservice    |    84 -
 .../ubuntu14/ansible/inventory/hosts            |     4 -
 .../CURRENT/configuration/metron-rest-env.xml   |    48 +
 .../package/scripts/params/params_linux.py      |    16 +
 .../package/scripts/params/status_params.py     |     7 +
 .../CURRENT/package/scripts/rest_commands.py    |    62 +
 .../CURRENT/package/scripts/rest_master.py      |     9 +
 .../METRON/CURRENT/package/templates/metron.j2  |     6 +
 .../METRON/CURRENT/themes/metron_theme.json     |    30 +
 .../docker/rpm-docker/SPECS/metron.spec         |     1 +
 .../metron-alerts/package-lock.json             |  3719 ++--
 metron-interface/metron-alerts/pom.xml          |     6 +-
 .../metron-alerts/src/app/app-routing.module.ts |     3 +-
 .../metron-alerts/src/app/app.component.html    |    10 +-
 .../metron-alerts/src/app/app.component.scss    |    18 +
 .../metron-alerts/src/app/app.component.ts      |     3 +
 .../metron-alerts/src/app/app.module.ts         |     6 +-
 .../src/app/pcap/model/pcap-pagination.ts       |    21 +
 .../src/app/pcap/model/pcap-status-response.ts  |    24 +
 .../src/app/pcap/model/pcap.mock.ts             |    39 +
 .../src/app/pcap/model/pcap.request.ts          |    29 +
 .../src/app/pcap/model/pdml.mock.ts             |    93 +
 .../metron-alerts/src/app/pcap/model/pdml.ts    |    50 +
 .../pcap-filters/pcap-filters.component.html    |    73 +
 .../pcap-filters/pcap-filters.component.scss    |    70 +
 .../pcap-filters/pcap-filters.component.spec.ts |   464 +
 .../pcap/pcap-filters/pcap-filters.component.ts |   167 +
 .../app/pcap/pcap-list/pcap-list.component.html |    38 +
 .../app/pcap/pcap-list/pcap-list.component.scss |    21 +
 .../pcap/pcap-list/pcap-list.component.spec.ts  |    98 +
 .../app/pcap/pcap-list/pcap-list.component.ts   |    43 +
 .../pcap-packet-line.component.html             |    19 +
 .../pcap-packet-line.component.scss             |    17 +
 .../pcap-packet-line.component.spec.ts          |   179 +
 .../pcap-packet-line.component.ts               |    56 +
 .../pcap/pcap-packet/pcap-packet.component.html |    22 +
 .../pcap/pcap-packet/pcap-packet.component.scss |    36 +
 .../pcap-packet/pcap-packet.component.spec.ts   |    79 +
 .../pcap/pcap-packet/pcap-packet.component.ts   |    34 +
 .../pcap-pagination.component.html              |    19 +
 .../pcap-pagination.component.scss              |    40 +
 .../pcap-pagination.component.spec.ts           |    78 +
 .../pcap-pagination.component.ts                |    41 +
 .../pcap/pcap-panel/pcap-panel.component.html   |    31 +
 .../pcap/pcap-panel/pcap-panel.component.scss   |    67 +
 .../pcap-panel/pcap-panel.component.spec.ts     |   557 +
 .../app/pcap/pcap-panel/pcap-panel.component.ts |   164 +
 .../metron-alerts/src/app/pcap/pcap.module.ts   |    55 +
 .../metron-alerts/src/app/pcap/pcap.routing.ts  |    27 +
 .../src/app/pcap/service/pcap.service.spec.ts   |   197 +
 .../src/app/pcap/service/pcap.service.ts        |    88 +
 .../date-picker/date-picker.component.scss      |    14 +-
 .../shared/date-picker/date-picker.component.ts |    34 +-
 .../shared/date-picker/date-picker.module.ts    |     3 +-
 .../shared/directives/alert-search.directive.ts |     2 +-
 .../metron-alerts/src/app/utils/constants.ts    |     3 +
 .../metron-alerts/src/app/utils/utils.ts        |    12 +-
 .../src/environments/environment.e2e.ts         |     3 +-
 .../src/environments/environment.prod.ts        |     3 +-
 .../src/environments/environment.ts             |     3 +-
 metron-interface/metron-alerts/src/index.html   |     2 +-
 .../metron-config/package-lock.json             | 17734 +++++++++++++++++
 metron-interface/metron-config/pom.xml          |    30 +-
 metron-interface/metron-rest-client/pom.xml     |    10 +
 .../apache/metron/rest/model/PcapResponse.java  |    38 +
 .../apache/metron/rest/model/pcap/Field.java    |   154 +
 .../rest/model/pcap/FixedPcapOptions.java       |    42 +
 .../rest/model/pcap/FixedPcapRequest.java       |   116 +
 .../apache/metron/rest/model/pcap/Packet.java   |    53 +
 .../metron/rest/model/pcap/PcapRequest.java     |    83 +
 .../metron/rest/model/pcap/PcapStatus.java      |    87 +
 .../org/apache/metron/rest/model/pcap/Pdml.java |    98 +
 .../apache/metron/rest/model/pcap/Proto.java    |   108 +
 .../rest/model/pcap/QueryPcapOptions.java       |    35 +
 .../rest/model/pcap/QueryPcapRequest.java       |    41 +
 metron-interface/metron-rest/README.md          |   105 +
 metron-interface/metron-rest/pom.xml            |    44 +-
 .../src/main/config/rest_application.yml        |    11 +-
 .../apache/metron/rest/MetronRestConstants.java |     9 +
 .../apache/metron/rest/config/PcapConfig.java   |    49 +
 .../metron/rest/config/PcapJobSupplier.java     |    54 +
 .../metron/rest/controller/PcapController.java  |   180 +
 .../apache/metron/rest/service/PcapService.java |   116 +
 .../rest/service/impl/PcapServiceImpl.java      |   299 +
 .../service/impl/PcapToPdmlScriptWrapper.java   |    59 +
 .../src/main/resources/application.yml          |     9 +-
 .../metron-rest/src/main/scripts/metron-rest.sh |     2 +-
 .../src/main/scripts/pcap_to_pdml.sh            |    19 +
 .../apache/metron/rest/config/TestConfig.java   |    47 +-
 .../PcapControllerIntegrationTest.java          |   474 +
 .../apache/metron/rest/mock/MockPcapJob.java    |   161 +
 .../metron/rest/mock/MockPcapJobSupplier.java   |    36 +
 .../rest/mock/MockPcapToPdmlScriptWrapper.java  |    55 +
 .../rest/service/impl/PcapServiceImplTest.java  |   746 +
 metron-interface/pom.xml                        |     3 +
 metron-platform/metron-api/README.md            |    65 -
 metron-platform/metron-api/pom.xml              |   269 -
 .../apache/metron/api/ConfigurationManager.java |   135 -
 .../api/helper/service/PcapServiceCli.java      |   169 -
 .../metron/pcapservice/ConfigurationUtil.java   |    64 -
 .../pcapservice/PcapReceiverImplRestEasy.java   |   299 -
 .../metron/pcapservice/PcapsResponse.java       |   118 -
 .../metron/pcapservice/RestTestingUtil.java     |   329 -
 .../pcapservice/rest/JettyServiceRunner.java    |    44 -
 .../metron/pcapservice/rest/PcapService.java    |    56 -
 .../src/main/resources/META-INF/LICENSE         |   605 -
 .../src/main/resources/META-INF/NOTICE          |    39 -
 .../pcapservice/ConfigurationUtilTest.java      |    43 -
 .../PcapReceiverImplRestEasyTest.java           |   255 -
 .../src/test/resources/log4j.properties         |    24 -
 .../src/test/resources/test-tcp-packet.pcap     |   Bin 144 -> 0 bytes
 .../common/configuration/ConfigOption.java      |    71 +
 .../configuration/profiler/ProfileResult.java   |     8 +-
 .../profiler/ProfileResultExpressions.java      |     8 +-
 .../apache/metron/common/utils/HDFSUtils.java   |    53 +-
 .../common/configuration/ConfigOptionTest.java  |   112 +
 .../profiler/ProfilerConfigTest.java            |    35 +-
 .../metron/common/utils/HDFSUtilsTest.java      |    59 +
 metron-platform/metron-job/README.md            |    26 +
 .../metron-job_state_statechart_diagram.svg     |    14 +
 .../metron-job_state_statechart_diagram.xml     |    14 +
 metron-platform/metron-job/pom.xml              |    39 +
 .../java/org/apache/metron/job/Finalizer.java   |    38 +
 .../org/apache/metron/job/JobException.java     |    31 +
 .../apache/metron/job/JobNotFoundException.java |    30 +
 .../java/org/apache/metron/job/JobStatus.java   |   124 +
 .../java/org/apache/metron/job/Pageable.java    |    38 +
 .../apache/metron/job/RuntimeJobException.java  |    30 +
 .../java/org/apache/metron/job/Statusable.java  |    82 +
 .../metron/job/manager/InMemoryJobManager.java  |    94 +
 .../apache/metron/job/manager/JobManager.java   |    41 +
 .../org/apache/metron/job/JobStatusTest.java    |    55 +
 .../job/manager/InMemoryJobManagerTest.java     |   208 +
 .../metron/parsers/cef/CEFParserTest.java       |     4 +-
 metron-platform/metron-pcap-backend/README.md   |     4 +
 metron-platform/metron-pcap-backend/pom.xml     |     6 +
 .../src/main/config/pcap.properties             |     2 +-
 .../org/apache/metron/pcap/query/CliConfig.java |   128 -
 .../org/apache/metron/pcap/query/CliParser.java |    55 +-
 .../metron/pcap/query/FixedCliConfig.java       |    50 -
 .../metron/pcap/query/FixedCliParser.java       |    10 +-
 .../org/apache/metron/pcap/query/PcapCli.java   |   112 +-
 .../metron/pcap/query/QueryCliConfig.java       |    34 -
 .../metron/pcap/query/QueryCliParser.java       |    10 +-
 .../apache/metron/pcap/query/ResultsWriter.java |    48 -
 .../apache/metron/pcap/FixedPcapFilterTest.java |   286 -
 .../org/apache/metron/pcap/PcapJobTest.java     |    49 -
 .../apache/metron/pcap/QueryPcapFilterTest.java |   228 -
 .../PcapTopologyIntegrationTest.java            |   847 +-
 .../apache/metron/pcap/query/PcapCliTest.java   |   229 +-
 metron-platform/metron-pcap/pom.xml             |     7 +-
 .../java/org/apache/metron/pcap/PcapHelper.java |    18 +-
 .../java/org/apache/metron/pcap/PcapPages.java  |    86 +
 .../metron/pcap/config/FixedPcapConfig.java     |    46 +
 .../apache/metron/pcap/config/PcapConfig.java   |   156 +
 .../metron/pcap/config/PcapGlobalDefaults.java  |    29 +
 .../apache/metron/pcap/config/PcapOptions.java  |    70 +
 .../metron/pcap/config/QueryPcapConfig.java     |    33 +
 .../pcap/filter/fixed/FixedPcapFilter.java      |    14 +-
 .../pcap/filter/query/QueryPcapFilter.java      |    17 +-
 .../metron/pcap/finalizer/PcapCliFinalizer.java |    55 +
 .../metron/pcap/finalizer/PcapFinalizer.java    |   190 +
 .../pcap/finalizer/PcapFinalizerStrategies.java |    47 +
 .../pcap/finalizer/PcapRestFinalizer.java       |    57 +
 .../metron/pcap/mr/OutputDirFormatter.java      |    37 +
 .../java/org/apache/metron/pcap/mr/PcapJob.java |   426 +-
 .../metron/pcap/writer/PcapResultsWriter.java   |    75 +
 .../org/apache/metron/pcap/PcapHelperTest.java  |    76 +
 .../org/apache/metron/pcap/PcapPagesTest.java   |    76 +
 .../pcap/filter/fixed/FixedPcapFilterTest.java  |   271 +-
 .../pcap/filter/query/QueryPcapFilterTest.java  |   207 +-
 .../metron/pcap/mr/OutputDirFormatterTest.java  |    62 +
 .../org/apache/metron/pcap/mr/PcapJobTest.java  |   323 +
 metron-platform/pom.xml                         |     2 +-
 metron-sensors/pycapa/README.md                 |   139 +-
 metron-sensors/pycapa/requirements.txt          |     6 +-
 site-book/bin/generate-md.sh                    |     2 +
 199 files changed, 30846 insertions(+), 6158 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/f5eaef3c/dependencies_with_url.csv
----------------------------------------------------------------------
diff --cc dependencies_with_url.csv
index 6b4385b,bf3e382..ffd0fbc
--- a/dependencies_with_url.csv
+++ b/dependencies_with_url.csv
@@@ -174,8 -148,8 +175,10 @@@ com.fasterxml.jackson.datatype:jackson-
  com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.9.5:compile,ASLv2,https://github.com/FasterXML/jackson-modules-java8
  com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.9.5:compile,ASLv2,https://github.com/FasterXML/jackson-modules-java8
  com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.9.5:compile,ASLv2,https://github.com/FasterXML/jackson-modules-java8
 +com.fasterxml.jackson.module:jackson-module-paranamer:jar:2.7.9:compile,ASLv2,https://github.com/FasterXML/jackson-modules-base
 +com.fasterxml.jackson.module:jackson-module-scala_2.11:jar:2.6.7.1:compile,ASLv2,https://github.com/FasterXML/jackson-module-scala
+ com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar:2.9.5:compile,ASLv2,https://github.com/FasterXML/jackson-modules-java8
+ com.fasterxml.woodstox:woodstox-core:jar:5.0.3:compile,ASLv2,https://github.com/FasterXML/woodstox
  com.fasterxml:classmate:jar:1.3.1:compile,ASLv2,http://github.com/cowtowncoder/java-classmate
  com.fasterxml:classmate:jar:1.3.4:compile,ASLv2,http://github.com/cowtowncoder/java-classmate
  com.google.code.gson:gson:jar:2.2.4:compile,The Apache Software License, Version 2.0,http://code.google.com/p/google-gson/
@@@ -243,21 -212,14 +246,26 @@@ commons-text:commons-text:jar:1.1:compi
  commons-validator:commons-validator:jar:1.4.0:compile,ASLv2,http://commons.apache.org/validator/
  commons-validator:commons-validator:jar:1.5.1:compile,ASLv2,http://commons.apache.org/proper/commons-validator/
  commons-validator:commons-validator:jar:1.6:compile,ASLv2,http://commons.apache.org/proper/commons-validator/
 +et.razorvine:pyrolite:jar:4.13:compile,MIT,https://github.com/irmen/Pyrolite
 +io.airlift:aircompressor:jar:0.8:compile,ASLv2,https://github.com/airlift/aircompressor
  io.confluent:kafka-avro-serializer:jar:1.0:compile,ASLv2,https://github.com/confluentinc/schema-registry/
  io.confluent:kafka-schema-registry-client:jar:1.0:compile,ASLv2,https://github.com/confluentinc/schema-registry/
 +io.dropwizard.metrics:metrics-core:jar:3.1.5:compile,ASLv2,https://github.com/dropwizard/metrics
 +io.dropwizard.metrics:metrics-graphite:jar:3.1.5:compile,ASLv2,https://github.com/dropwizard/metrics
 +io.dropwizard.metrics:metrics-json:jar:3.1.5:compile,ASLv2,https://github.com/dropwizard/metrics
 +io.dropwizard.metrics:metrics-jvm:jar:3.1.5:compile,ASLv2,https://github.com/dropwizard/metrics
  io.netty:netty-all:jar:4.0.23.Final:compile,ASLv2,
  io.netty:netty-all:jar:4.0.23.Final:provided,ASLv2,
++<<<<<<< HEAD
 +io.netty:netty-all:jar:4.1.17.Final:compile,ASLv2,
++=======
+ io.netty:netty-all:jar:4.1.23.Final:compile,ASLv2,
+ io.netty:netty:jar:3.10.5.Final:compile,Apache License, Version 2.0,http://netty.io/
++>>>>>>> apache/master
  io.netty:netty:jar:3.6.2.Final:compile,Apache License, Version 2.0,http://netty.io/
  io.netty:netty:jar:3.7.0.Final:compile,Apache License, Version 2.0,http://netty.io/
 +io.netty:netty:jar:3.9.9.Final:compile,Apache License, Version 2.0,http://netty.io/
 +io.netty:netty:jar:3.10.5.Final:compile,Apache License, Version 2.0,http://netty.io/
  io.thekraken:grok:jar:0.1.0:compile,Apache License, Version 2.0,http://maven.apache.org
  javax.inject:javax.inject:jar:1:compile,The Apache Software License, Version 2.0,http://code.google.com/p/atinject/
  joda-time:joda-time:jar:2.3:compile,Apache 2,http://www.joda.org/joda-time/

http://git-wip-us.apache.org/repos/asf/metron/blob/f5eaef3c/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfileMeasurement.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/metron/blob/f5eaef3c/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfilePeriod.java
----------------------------------------------------------------------
diff --cc metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfilePeriod.java
index 78aa796,6c971a7..02a4932
--- a/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfilePeriod.java
+++ b/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfilePeriod.java
@@@ -47,20 -47,11 +47,31 @@@ public class ProfilePeriod implements S
     */
    private long durationMillis;
  
 +  /**
++   * A no-arg constructor is required for using Kryo serialization in Storm. Using this
++   * constructor elsewhere is discouraged.
++   *
++   * <p>Use either {@link #fromPeriodId(long, long, TimeUnit)} or {@link #fromTimestamp(long, long, TimeUnit)}
++   * to create a {@link ProfilePeriod}.
++   */
+   public ProfilePeriod() {
+     // no-arg constructor required for kryo serialization in storm
+   }
+ 
+   /**
 +   * @param periodId A monotonically increasing number identifying the period.
 +   * @param duration The duration of each profile period.
 +   * @param units The units of the duration; hours, minutes, etc.
 +   */
 +  private ProfilePeriod(long periodId, long duration, TimeUnit units) {
 +    this.durationMillis = units.toMillis(duration);
 +    this.period = periodId;
 +  }
 +
 +  /**
 +   * Creates a {@link ProfilePeriod} given a timestamp defined in milliseconds
 +   * from the epoch.
 +   *
     * @param epochMillis A timestamp contained somewhere within the profile period.
     * @param duration The duration of each profile period.
     * @param units The units of the duration; hours, minutes, etc.

http://git-wip-us.apache.org/repos/asf/metron/blob/f5eaef3c/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfilePeriodTest.java
----------------------------------------------------------------------
diff --cc metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfilePeriodTest.java
index 295117a,17bb8fa..afb6299
--- a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfilePeriodTest.java
+++ b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfilePeriodTest.java
@@@ -137,12 -141,24 +141,24 @@@ public class ProfilePeriodTest 
     */
    @Test
    public void testKryoSerialization() throws Exception {
 -    ProfilePeriod expected = new ProfilePeriod(AUG2016, 1, TimeUnit.HOURS);
 +    ProfilePeriod expected = ProfilePeriod.fromTimestamp(AUG2016, 1, TimeUnit.HOURS);
+     Kryo kryo = new Kryo();
  
-     // round-trip java serialization
-     byte[] raw = SerDeUtils.toBytes(expected);
-     Object actual = SerDeUtils.fromBytes(raw, Object.class);
+     // serialize
+     ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+     Output output = new Output(byteStream);
+     kryo.writeObject(output, expected);
  
+     // validate serialization
+     byte[] bits = output.toBytes();
+     assertNotNull(bits);
+ 
+     // deserialize
+     Input input = new Input(new ByteArrayInputStream(bits));
+     ProfilePeriod actual = kryo.readObject(input, ProfilePeriod.class);
+ 
+     // validate deserialization
+     assertNotNull(actual);
      assertEquals(expected, actual);
    }
  

http://git-wip-us.apache.org/repos/asf/metron/blob/f5eaef3c/metron-analytics/metron-profiler/src/main/java/org/apache/metron/profiler/bolt/ProfileSplitterBolt.java
----------------------------------------------------------------------
diff --cc metron-analytics/metron-profiler/src/main/java/org/apache/metron/profiler/bolt/ProfileSplitterBolt.java
index f57deb7,87f1ba9..f73a9f1
--- a/metron-analytics/metron-profiler/src/main/java/org/apache/metron/profiler/bolt/ProfileSplitterBolt.java
+++ b/metron-analytics/metron-profiler/src/main/java/org/apache/metron/profiler/bolt/ProfileSplitterBolt.java
@@@ -151,16 -157,25 +151,20 @@@ public class ProfileSplitterBolt extend
  
      // retrieve the input message
      byte[] data = input.getBinary(0);
-     JSONObject message = (JSONObject) parser.parse(new String(data, "UTF8"));
+     if(data == null) {
+       LOG.debug("Received null message. Nothing to do.");
+       return;
+     }
 -    JSONObject message = (JSONObject) parser.parse(new String(data, "UTF8"));
  
      // ensure there is a valid profiler configuration
      ProfilerConfig config = getProfilerConfig();
-     if(config != null && config.getProfiles().size() > 0) {
-       routeMessage(input, message, config);
- 
-     } else if(LOG.isDebugEnabled()) {
-       LOG.debug("No Profiler configuration found.  Nothing to do.");
+     if(config == null || getProfilerConfig().getProfiles().size() == 0) {
+       LOG.debug("No Profiler configuration found. Nothing to do.");
+       return;
      }
+ 
 -    // what time is it?
 -    Clock clock = clockFactory.createClock(config);
 -    Optional<Long> timestamp = clock.currentTimeMillis(message);
 -
 -    // route the message.  if a message does not contain the timestamp field, it cannot be routed.
 -    timestamp.ifPresent(ts -> routeMessage(input, message, config, ts));
++    JSONObject message = (JSONObject) parser.parse(new String(data, "UTF8"));
++    routeMessage(input, message, config);
    }
  
    /**

http://git-wip-us.apache.org/repos/asf/metron/blob/f5eaef3c/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/bolt/ProfileSplitterBoltTest.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/metron/blob/f5eaef3c/metron-deployment/packaging/docker/rpm-docker/SPECS/metron.spec
----------------------------------------------------------------------


[33/50] [abbrv] metron git commit: METRON-1725 Add ability to specify YARN queue for pcap jobs (merrimanr) closes apache/metron#1153

Posted by ni...@apache.org.
METRON-1725 Add ability to specify YARN queue for pcap jobs (merrimanr) closes apache/metron#1153


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/7a8c2467
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/7a8c2467
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/7a8c2467

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 7a8c246748a2c9e8c5b9230800b075dd99a7f3a4
Parents: 73dc63e
Author: merrimanr <me...@gmail.com>
Authored: Fri Aug 10 16:46:31 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Fri Aug 10 16:46:31 2018 -0500

----------------------------------------------------------------------
 .../CURRENT/configuration/metron-rest-env.xml   |  9 ++++++
 .../package/scripts/params/params_linux.py      |  1 +
 .../METRON/CURRENT/package/templates/metron.j2  |  1 +
 .../METRON/CURRENT/themes/metron_theme.json     | 10 ++++++
 metron-interface/metron-rest/README.md          |  2 ++
 .../src/main/config/rest_application.yml        |  1 +
 .../apache/metron/rest/MetronRestConstants.java |  1 +
 .../metron/rest/config/PcapJobSupplier.java     |  2 +-
 .../rest/service/impl/PcapServiceImpl.java      | 12 ++++++-
 .../apache/metron/rest/mock/MockPcapJob.java    |  8 +++++
 .../rest/service/impl/PcapServiceImplTest.java  |  7 ++++-
 metron-platform/metron-pcap-backend/README.md   |  2 ++
 .../org/apache/metron/pcap/query/CliParser.java |  4 +++
 .../org/apache/metron/pcap/query/PcapCli.java   |  3 ++
 .../apache/metron/pcap/query/PcapCliTest.java   | 33 ++++++++++++++++++--
 .../apache/metron/pcap/config/PcapConfig.java   | 10 ++++++
 16 files changed, 101 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
index 20f9767..895c091 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
@@ -192,5 +192,14 @@
         <description>The number of pcaps written to a page/file as a result of a pcap query.</description>
         <value>10</value>
     </property>
+    <property>
+        <name>pcap_yarn_queue</name>
+        <display-name>Pcap YARN Queue</display-name>
+        <description>The YARN queue pcap jobs will be submitted to.</description>
+        <value/>
+        <value-attributes>
+            <empty-value-valid>true</empty-value-valid>
+        </value-attributes>
+    </property>
 
 </configuration>

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
index 73d3469..4f8a9a7 100755
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
@@ -390,6 +390,7 @@ pcap_base_path = config['configurations']['metron-rest-env']['pcap_base_path']
 pcap_base_interim_result_path = config['configurations']['metron-rest-env']['pcap_base_interim_result_path']
 pcap_final_output_path = config['configurations']['metron-rest-env']['pcap_final_output_path']
 pcap_page_size = config['configurations']['metron-rest-env']['pcap_page_size']
+pcap_yarn_queue = config['configurations']['metron-rest-env']['pcap_yarn_queue']
 pcap_configured_flag_file = status_params.pcap_configured_flag_file
 
 # MapReduce

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2 b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
index 278d6f8..55422d0 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
@@ -44,3 +44,4 @@ PCAP_BASE_PATH="{{pcap_base_path}}"
 PCAP_BASE_INTERIM_RESULT_PATH="{{pcap_base_interim_result_path}}"
 PCAP_FINAL_OUTPUT_PATH="{{pcap_final_output_path}}"
 PCAP_PAGE_SIZE="{{pcap_page_size}}"
+PCAP_YARN_QUEUE="{{pcap_yarn_queue}}"

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
index 9f5b04e..db06b61 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
@@ -824,6 +824,10 @@
           "subsection-name": "subsection-rest"
         },
         {
+          "config": "metron-rest-env/pcap_yarn_queue",
+          "subsection-name": "subsection-rest"
+        },
+        {
           "config": "metron-management-ui-env/metron_management_ui_port",
           "subsection-name": "subsection-management-ui"
         },
@@ -1431,6 +1435,12 @@
         }
       },
       {
+        "config": "metron-rest-env/pcap_yarn_queue",
+        "widget": {
+          "type": "text-field"
+        }
+      },
+      {
         "config": "metron-management-ui-env/metron_management_ui_port",
         "widget": {
           "type": "text-field"

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md
index 489cd9f..d19d8c3 100644
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@ -221,6 +221,8 @@ The REST application uses a Java Process object to call out to the `pcap_to_pdml
 Out of the box it is a simple wrapper around the tshark command to transform raw pcap data to PDML.  However it can be extended to do additional processing as long as the expected input/output is maintained.
 REST will supply the script with raw pcap data through standard in and expects PDML data serialized as XML.
 
+Pcap query jobs can be configured for submission to a YARN queue.  This setting is exposed as the Spring property `pcap.yarn.queue`.  If configured, the REST application will set the `mapreduce.job.queuename` Hadoop property to that value.
+
 ## API
 
 Request and Response objects are JSON formatted.  The JSON schemas are available in the Swagger UI.

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-interface/metron-rest/src/main/config/rest_application.yml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/config/rest_application.yml b/metron-interface/metron-rest/src/main/config/rest_application.yml
index 7486112..e25ad82 100644
--- a/metron-interface/metron-rest/src/main/config/rest_application.yml
+++ b/metron-interface/metron-rest/src/main/config/rest_application.yml
@@ -60,3 +60,4 @@ pcap:
   base.interim.result.path: ${PCAP_BASE_INTERIM_RESULT_PATH}
   final.output.path: ${PCAP_FINAL_OUTPUT_PATH}
   page.size: ${PCAP_PAGE_SIZE}
+  yarn.queue: ${PCAP_YARN_QUEUE}

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
index d38aedb..02655298 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
@@ -81,4 +81,5 @@ public class MetronRestConstants {
   public static final String PCAP_FINAL_OUTPUT_PATH_SPRING_PROPERTY = "pcap.final.output.path";
   public static final String PCAP_PAGE_SIZE_SPRING_PROPERTY = "pcap.page.size";
   public static final String PCAP_PDML_SCRIPT_PATH_SPRING_PROPERTY = "pcap.pdml.script.path";
+  public static final String PCAP_YARN_QUEUE_SPRING_PROPERTY = "pcap.yarn.queue";
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/PcapJobSupplier.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/PcapJobSupplier.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/PcapJobSupplier.java
index 1e79f6a..538e41a 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/PcapJobSupplier.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/PcapJobSupplier.java
@@ -39,7 +39,7 @@ public class PcapJobSupplier implements Supplier<Statusable<Path>> {
       PcapJob<Path> pcapJob = createPcapJob();
       return pcapJob.submit(PcapFinalizerStrategies.REST, pcapRequest);
     } catch (JobException e) {
-      throw new RuntimeJobException(e.getMessage());
+      throw new RuntimeJobException(e.getMessage(), e);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
index ae3f807..db2e17b 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
@@ -21,6 +21,7 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.mapreduce.MRJobConfig;
 import org.apache.metron.job.JobException;
 import org.apache.metron.job.JobNotFoundException;
 import org.apache.metron.job.JobStatus;
@@ -57,6 +58,8 @@ import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Map;
 
+import static org.apache.metron.rest.MetronRestConstants.PCAP_YARN_QUEUE_SPRING_PROPERTY;
+
 @Service
 public class PcapServiceImpl implements PcapService {
 
@@ -250,7 +253,14 @@ public class PcapServiceImpl implements PcapService {
   protected void setPcapOptions(String username, PcapRequest pcapRequest) throws IOException {
     PcapOptions.JOB_NAME.put(pcapRequest, "jobName");
     PcapOptions.USERNAME.put(pcapRequest, username);
-    PcapOptions.HADOOP_CONF.put(pcapRequest, configuration);
+    Configuration hadoopConf = new Configuration(configuration);
+    if (environment.containsProperty(PCAP_YARN_QUEUE_SPRING_PROPERTY)) {
+      String queue = environment.getProperty(PCAP_YARN_QUEUE_SPRING_PROPERTY);
+      if (queue != null && !queue.isEmpty()) {
+        hadoopConf.set(MRJobConfig.QUEUE_NAME, environment.getProperty(PCAP_YARN_QUEUE_SPRING_PROPERTY));
+      }
+    }
+    PcapOptions.HADOOP_CONF.put(pcapRequest, hadoopConf);
     PcapOptions.FILESYSTEM.put(pcapRequest, getFileSystem());
 
     if (pcapRequest.getBasePath() == null) {

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
index 779589d..c977faa 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
@@ -17,7 +17,9 @@
  */
 package org.apache.metron.rest.mock;
 
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.mapreduce.MRJobConfig;
 import org.apache.metron.job.Finalizer;
 import org.apache.metron.job.JobException;
 import org.apache.metron.job.JobStatus;
@@ -45,6 +47,7 @@ public class MockPcapJob extends PcapJob<Path> {
   private PcapFilterConfigurator filterImpl;
   private int recPerFile;
   private String query;
+  private String yarnQueue;
   private Statusable<Path> statusable;
 
   public MockPcapJob() {
@@ -68,6 +71,7 @@ public class MockPcapJob extends PcapJob<Path> {
     }
     this.filterImpl = PcapOptions.FILTER_IMPL.get(configuration, PcapFilterConfigurator.class);
     this.recPerFile = PcapOptions.NUM_RECORDS_PER_FILE.get(configuration, Integer.class);
+    this.yarnQueue = PcapOptions.HADOOP_CONF.get(configuration, Configuration.class).get(MRJobConfig.QUEUE_NAME);
     return statusable;
   }
 
@@ -144,4 +148,8 @@ public class MockPcapJob extends PcapJob<Path> {
   public int getRecPerFile() {
     return recPerFile;
   }
+
+  public String getYarnQueue() {
+    return yarnQueue;
+  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
index d539c71..6635598 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
@@ -22,6 +22,7 @@ import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FSDataInputStream;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.mapreduce.MRJobConfig;
 import org.apache.metron.common.Constants;
 import org.apache.metron.common.utils.JSONUtils;
 import org.apache.metron.job.JobException;
@@ -186,7 +187,7 @@ public class PcapServiceImplTest {
   @Before
   public void setUp() throws Exception {
     environment = mock(Environment.class);
-    configuration = mock(Configuration.class);
+    configuration = new Configuration();
     mockPcapJobSupplier = new MockPcapJobSupplier();
     pcapToPdmlScriptWrapper = new PcapToPdmlScriptWrapper();
 
@@ -200,6 +201,9 @@ public class PcapServiceImplTest {
 
   @Test
   public void submitShouldProperlySubmitFixedPcapRequest() throws Exception {
+    when(environment.containsProperty(MetronRestConstants.PCAP_YARN_QUEUE_SPRING_PROPERTY)).thenReturn(true);
+    when(environment.getProperty(MetronRestConstants.PCAP_YARN_QUEUE_SPRING_PROPERTY)).thenReturn("pcap");
+
     FixedPcapRequest fixedPcapRequest = new FixedPcapRequest();
     fixedPcapRequest.setBasePath("basePath");
     fixedPcapRequest.setBaseInterimResultPath("baseOutputPath");
@@ -250,6 +254,7 @@ public class PcapServiceImplTest {
     Assert.assertEquals(2000000, mockPcapJob.getEndTimeNs());
     Assert.assertEquals(2, mockPcapJob.getNumReducers());
     Assert.assertEquals(100, mockPcapJob.getRecPerFile());
+    Assert.assertEquals("pcap", mockPcapJob.getYarnQueue());
     Assert.assertTrue(mockPcapJob.getFilterImpl() instanceof FixedPcapFilter.Configurator);
     Map<String, String> actualFixedFields = mockPcapJob.getFixedFields();
     Assert.assertEquals("ip_src_addr", actualFixedFields.get(Constants.Fields.SRC_ADDR.getName()));

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-platform/metron-pcap-backend/README.md
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/README.md b/metron-platform/metron-pcap-backend/README.md
index 2ff20d8..031328d 100644
--- a/metron-platform/metron-pcap-backend/README.md
+++ b/metron-platform/metron-pcap-backend/README.md
@@ -139,6 +139,7 @@ usage: Fixed filter options
  -sa,--ip_src_addr <arg>         Source IP address
  -sp,--ip_src_port <arg>         Source port
  -st,--start_time <arg>          (required) Packet start time range.
+ -yq,--yarn_queue <arg>          Yarn queue this job will be submitted to
 ```
 
 ```
@@ -158,6 +159,7 @@ usage: Query filter options
  -ps,--print_status              Print the status of the job as it runs
  -q,--query <arg>                Query string to use as a filter
  -st,--start_time <arg>          (required) Packet start time range.
+ -yq,--yarn_queue <arg>          Yarn queue this job will be submitted to
 ```
 
 The Query filter's `--query` argument specifies the Stellar expression to

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
index 69c725c..5040f90 100644
--- a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
+++ b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
@@ -56,6 +56,7 @@ public class CliParser {
     options.addOption(newOption("et", "end_time", true, "Packet end time range. Default is current system time."));
     options.addOption(newOption("df", "date_format", true, "Date format to use for parsing start_time and end_time. Default is to use time in millis since the epoch."));
     options.addOption(newOption("ps", "print_status", false, "Print the status of the job as it runs"));
+    options.addOption(newOption("yq", "yarn_queue", true, "Yarn queue this job will be submitted to"));
     return options;
   }
 
@@ -129,6 +130,9 @@ public class CliParser {
     if (commandLine.hasOption("print_status")) {
       config.setPrintJobStatus(true);
     }
+    if (commandLine.hasOption("yarn_queue")) {
+      config.setYarnQueue(commandLine.getOptionValue("yarn_queue"));
+    }
   }
 
   public void printHelp(String msg, Options opts) {

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
index c23f037..eebf366 100644
--- a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
+++ b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
@@ -26,6 +26,7 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.mapreduce.MRJobConfig;
 import org.apache.hadoop.util.GenericOptionsParser;
 import org.apache.metron.common.utils.timestamp.TimestampConverters;
 import org.apache.metron.job.JobException;
@@ -99,6 +100,7 @@ public class PcapCli {
         return 0;
       }
       PcapOptions.FILTER_IMPL.put(commonConfig, new FixedPcapFilter.Configurator());
+      config.getYarnQueue().ifPresent(s -> hadoopConf.set(MRJobConfig.QUEUE_NAME, s));
       PcapOptions.HADOOP_CONF.put(commonConfig, hadoopConf);
       try {
         PcapOptions.FILESYSTEM.put(commonConfig, FileSystem.get(hadoopConf));
@@ -124,6 +126,7 @@ public class PcapCli {
         return 0;
       }
       PcapOptions.FILTER_IMPL.put(commonConfig, new FixedPcapFilter.Configurator());
+      config.getYarnQueue().ifPresent(s -> hadoopConf.set(MRJobConfig.QUEUE_NAME, s));
       PcapOptions.HADOOP_CONF.put(commonConfig, hadoopConf);
       try {
         PcapOptions.FILESYSTEM.put(commonConfig, FileSystem.get(hadoopConf));

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
index 7c75224..a71e997 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
@@ -33,10 +33,18 @@ import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.mapreduce.MRJobConfig;
 import org.apache.metron.common.Constants;
 import org.apache.metron.common.system.Clock;
 import org.apache.metron.common.utils.timestamp.TimestampConverters;
@@ -114,7 +122,24 @@ public class PcapCliTest {
     return new TypeSafeMatcher<Map<K, V>>() {
       @Override
       protected boolean matchesSafely(Map<K, V> item) {
-        return item.entrySet().containsAll(map.entrySet());
+        for(K key: map.keySet()) {
+          if (key.equals(PcapOptions.HADOOP_CONF.getKey())) {
+            Configuration itemConfiguration = (Configuration) item.get(PcapOptions.HADOOP_CONF.getKey());
+            Map<String, Object> mapConfiguration = (Map<String, Object>) map.get(PcapOptions.HADOOP_CONF.getKey());
+            for(String setting: mapConfiguration.keySet()) {
+              if (!mapConfiguration.get(setting).equals(itemConfiguration.get(setting, ""))) {
+                return false;
+              }
+            }
+          } else {
+            V itemValue = item.get(key);
+            V mapValue = map.get(key);
+            if (itemValue != null ? !itemValue.equals(mapValue) : mapValue != null) {
+              return false;
+            }
+          }
+        }
+        return true;
       }
 
       @Override
@@ -192,7 +217,8 @@ public class PcapCliTest {
             "-include_reverse",
             "-num_reducers", "10",
             "-records_per_file", "1000",
-            "-ps"
+            "-ps",
+            "-yq", "pcap"
     };
     Map<String, String> query = new HashMap<String, String>() {{
       put(Constants.Fields.SRC_ADDR.getName(), "192.168.1.1");
@@ -215,6 +241,9 @@ public class PcapCliTest {
     PcapOptions.END_TIME_MS.put(config, endAsNanos / 1000000L);  // needed bc defaults in config
     PcapOptions.NUM_RECORDS_PER_FILE.put(config, 1000);
     PcapOptions.PRINT_JOB_STATUS.put(config, true);
+    PcapOptions.HADOOP_CONF.put(config, new HashMap<String, Object>() {{
+      put(MRJobConfig.QUEUE_NAME, "pcap");
+    }});
 
     when(jobRunner.submit(isA(Finalizer.class), argThat(mapContaining(config)))).thenReturn(jobRunner);
 

http://git-wip-us.apache.org/repos/asf/metron/blob/7a8c2467/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
index cbb8170..4a08e14 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
@@ -25,6 +25,7 @@ import org.apache.metron.common.configuration.ConfigOption;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.HashMap;
+import java.util.Optional;
 import java.util.function.Function;
 
 public class PcapConfig extends AbstractMapDecorator<String, Object>{
@@ -32,6 +33,7 @@ public class PcapConfig extends AbstractMapDecorator<String, Object>{
 
   private boolean showHelp;
   private DateFormat dateFormat;
+  private String yarnQueue;
 
   public PcapConfig() {
     super(new HashMap<>());
@@ -137,4 +139,12 @@ public class PcapConfig extends AbstractMapDecorator<String, Object>{
   public void setNumRecordsPerFile(int numRecordsPerFile) {
     PcapOptions.NUM_RECORDS_PER_FILE.put(this, numRecordsPerFile);
   }
+
+  public void setYarnQueue(String yarnQueue) {
+    this.yarnQueue = yarnQueue;
+  }
+
+  public Optional<String> getYarnQueue() {
+    return Optional.ofNullable(yarnQueue);
+  }
 }


[23/50] [abbrv] metron git commit: METRON-1720 Better error messages when there are no results or wireshark is not installed (merrimanr) closes apache/metron#1154

Posted by ni...@apache.org.
METRON-1720 Better error messages when there are no results or wireshark is not installed (merrimanr) closes apache/metron#1154


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/afb9607d
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/afb9607d
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/afb9607d

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: afb9607dfbbaf89f06a30823bf668523bb7b6f9a
Parents: 7967f35
Author: merrimanr <me...@gmail.com>
Authored: Wed Aug 8 14:11:26 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Wed Aug 8 14:11:26 2018 -0500

----------------------------------------------------------------------
 .../pcap-panel/pcap-panel.component.spec.ts     | 64 ++++++++++++++++++++
 .../app/pcap/pcap-panel/pcap-panel.component.ts |  9 ++-
 .../src/app/pcap/service/pcap.service.ts        |  3 +-
 3 files changed, 73 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/afb9607d/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
index bd732ad..0804b79 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
@@ -26,6 +26,7 @@ import { PcapPagination } from '../model/pcap-pagination';
 import { By } from '../../../../node_modules/@angular/platform-browser';
 import { PcapRequest } from '../model/pcap.request';
 import { defer } from 'rxjs/observable/defer';
+import {RestError} from "../../model/rest-error";
 
 @Component({
   selector: 'app-pcap-filters',
@@ -305,6 +306,7 @@ describe('PcapPanelComponent', () => {
     );
 
     const pollResponse = new PcapStatusResponse();
+    pollResponse.jobStatus = 'SUCCEEDED';
     pcapService.pollStatus = jasmine.createSpy('pollStatus').and.returnValue(
       defer(() => Promise.resolve(pollResponse))
     );
@@ -321,6 +323,68 @@ describe('PcapPanelComponent', () => {
     tick();
     fixture.detectChanges();
 
+    expect(component.pdml).toEqual(myPdml);
     expect(fixture.debugElement.query(By.css('app-pcap-list'))).toBeDefined();
   }));
+
+  it('should handle get packet 404', fakeAsync(() => {
+    const searchResponse = new PcapStatusResponse();
+    searchResponse.jobId = '42';
+
+    pcapService.submitRequest = jasmine.createSpy('submitRequest').and.returnValue(
+            defer(() => Promise.resolve(searchResponse))
+    );
+
+    const pollResponse = new PcapStatusResponse();
+    pollResponse.jobStatus = 'SUCCEEDED';
+    pcapService.pollStatus = jasmine.createSpy('pollStatus').and.returnValue(
+            defer(() => Promise.resolve(pollResponse))
+    );
+
+    const restError = new RestError();
+    restError.responseCode = 404;
+    pcapService.getPackets = jasmine.createSpy('getPackets').and.returnValue(
+            defer(() => Promise.reject(restError))
+    );
+
+    component.onSearch(new PcapRequest());
+
+    expect(component.errorMsg).toBeFalsy();
+
+    tick();
+    fixture.detectChanges();
+
+    expect(component.errorMsg).toEqual('No results returned');
+  }));
+
+  it('should handle get packet error', fakeAsync(() => {
+    const searchResponse = new PcapStatusResponse();
+    searchResponse.jobId = '42';
+
+    pcapService.submitRequest = jasmine.createSpy('submitRequest').and.returnValue(
+            defer(() => Promise.resolve(searchResponse))
+    );
+
+    const pollResponse = new PcapStatusResponse();
+    pollResponse.jobStatus = 'SUCCEEDED';
+    pcapService.pollStatus = jasmine.createSpy('pollStatus').and.returnValue(
+            defer(() => Promise.resolve(pollResponse))
+    );
+
+    const restError = new RestError();
+    restError.responseCode = 500;
+    restError.message = 'error message';
+    pcapService.getPackets = jasmine.createSpy('getPackets').and.returnValue(
+            defer(() => Promise.reject(restError))
+    );
+
+    component.onSearch(new PcapRequest());
+
+    expect(component.errorMsg).toBeFalsy();
+
+    tick();
+    fixture.detectChanges();
+
+    expect(component.errorMsg).toEqual('Response message: error message. Something went wrong retrieving pdml results!');
+  }));
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/afb9607d/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
index 82121d4..8e4ced0 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
@@ -23,6 +23,7 @@ import { PcapRequest } from '../model/pcap.request';
 import { Pdml } from '../model/pdml';
 import { Subscription } from 'rxjs/Rx';
 import { PcapPagination } from '../model/pcap-pagination';
+import {RestError} from "../../model/rest-error";
 
 @Component({
   selector: 'app-pcap-panel',
@@ -72,8 +73,14 @@ export class PcapPanelComponent {
             this.pagination.total = statusResponse.pageTotal;
             this.statusSubscription.unsubscribe();
             this.queryRunning = false;
-            this.pcapService.getPackets(submitResponse.jobId, this.pagination.selectedPage).toPromise().then(pdml => {
+            this.pcapService.getPackets(id, this.pagination.selectedPage).toPromise().then(pdml => {
               this.pdml = pdml;
+            }, (error: RestError) => {
+              if (error.responseCode === 404) {
+                this.errorMsg = 'No results returned';
+              } else {
+                this.errorMsg = `Response message: ${error.message}. Something went wrong retrieving pdml results!`;
+              }
             });
           } else if ('FAILED' === statusResponse.jobStatus) {
             this.statusSubscription.unsubscribe();

http://git-wip-us.apache.org/repos/asf/metron/blob/afb9607d/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
index 6418ad0..518cc92 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
@@ -56,8 +56,7 @@ export class PcapService {
     public getPackets(id: string, pageId: number): Observable<Pdml> {
         return this.http.get(`/api/v1/pcap/${id}/pdml?page=${pageId}`, new RequestOptions({headers: new Headers(this.defaultHeaders)}))
             .map(HttpUtil.extractData)
-            .catch(HttpUtil.handleError)
-            .onErrorResumeNext();
+            .catch(HttpUtil.handleError);
     }
 
     public getDownloadUrl(id: string, pageId: number) {


[19/50] [abbrv] metron git commit: METRON-1683 PCAP UI - Fix the download progress bar (sardell via merrimanr) closes apache/metron#1122

Posted by ni...@apache.org.
http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
index fdaa569..c8cb937 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
@@ -15,1738 +15,139 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { TestBed, async, inject } from '@angular/core/testing';
+import {
+  TestBed,
+  inject,
+  fakeAsync,
+  tick,
+  discardPeriodicTasks
+} from '@angular/core/testing';
 import {
   BaseRequestOptions,
   HttpModule,
-  Http,
   Response,
-  ResponseOptions
+  ResponseOptions,
+  XHRBackend
 } from '@angular/http';
 import { MockBackend } from '@angular/http/testing';
+import { Observable } from 'rxjs/Rx';
 
 import { PcapService } from './pcap.service';
+import { PcapStatusResponse } from '../model/pcap-status-response';
 import { PcapRequest } from '../model/pcap.request';
+import { fakePdml, fakePacket } from '../model/pdml.mock';
+import { fakePcapStatusResponse, fakePcapRequest } from '../model/pcap.mock';
+
+const jobId = 'job_1234567890123_4567';
+let pdmlJsonMock = fakePdml;
+pdmlJsonMock['pdml']['packet'].push(fakePacket);
 
 describe('PcapService', () => {
   beforeEach(() => {
-
     TestBed.configureTestingModule({
       imports: [HttpModule],
       providers: [
         PcapService,
-        {
-          provide: Http,
-          useFactory: (mockBackend, options) => {
-            return new Http(mockBackend, options);
-          },
-          deps: [MockBackend, BaseRequestOptions]
-        },
+        { provide: XHRBackend, useClass: MockBackend },
         MockBackend,
         BaseRequestOptions
       ]
     });
   });
   describe('getPackets()', () => {
-    it('should return an Observable<Response>',
-      inject([PcapService, MockBackend], (pcapService, mockBackend) => {
-
-        let request: PcapRequest = {
-          startTimeMs: 0,
-          endTimeMs: 0,
-          ipSrcAddr: '0.0.0.0',
-          ipSrcPort: '80',
-          ipDstAddr: '0.0.0.0',
-          ipDstPort: '80',
-          protocol: '*',
-          packetFilter: '*',
-          includeReverse: false,
-        };
-
-        mockBackend.connections.subscribe((connection) => {
-          connection.mockRespond(new Response(new ResponseOptions({body: pdml_json()})));
-        });
+    it('should return an Observable<Response>', inject(
+      [PcapService, XHRBackend],
+      (pcapService, mockBackend) => {
         let packets;
-        pcapService.getPackets(request).subscribe(r => packets = r)
-        expect(packets).toBeTruthy()
-        expect(packets.pdml).toBeTruthy()
-        expect(packets.pdml.packet.length).toBe(1)
-        expect(packets.pdml.packet[0].proto.length).toBeGreaterThan(3)
-
-        console.log(packets)
-      }))
-
-
-    it('should ...', inject([PcapService], (service: PcapService) => {
-      expect(service).toBeTruthy();
-    }));
-
-  })
-
-});
 
+        mockBackend.connections.subscribe(connection => {
+          expect(connection.request.url).toMatch(
+            /\/api\/v1\/pcap\/job_1234567890123_4567\/pdml\?page=1/
+          );
+          connection.mockRespond(
+            new Response(new ResponseOptions({ body: pdmlJsonMock }))
+          );
+        });
+        pcapService.getPackets(jobId, 1).subscribe(r => (packets = r));
+        expect(packets).toBeTruthy();
+        expect(packets.pdml).toBeTruthy();
+        expect(packets.pdml.packet.length).toBe(1);
+        expect(packets.pdml.packet[0].protos.length).toBe(3);
+      }
+    ));
+  });
 
+  describe('pollStatus()', () => {
+    it('should call getStatus() in intervals', fakeAsync(inject(
+      [PcapService, XHRBackend], (pcapService, mockBackend) => {
+          const responseMock: PcapStatusResponse = fakePcapStatusResponse;
+          const spy = spyOn(pcapService, 'getStatus').and.returnValue(
+            Observable.of(responseMock)
+          );
+          let response;
+
+          pcapService.pollStatus(jobId).subscribe(r => (response = r));
+          tick(4000);
+          expect(spy.calls.count()).toBe(1);
+          tick(4000);
+          expect(spy.calls.count()).toBe(2);
+          discardPeriodicTasks();
+        })
+      )
+    );
+  });
 
+  describe('submitRequest()', () => {
+    it('should return an Observable<PcapStatusResponse>', inject(
+      [PcapService, XHRBackend],
+      (pcapService, mockBackend) => {
+        const request: PcapRequest = fakePcapRequest;
+        const responseMock: PcapStatusResponse = fakePcapStatusResponse;
+        let response;
+
+        mockBackend.connections.subscribe(connection => {
+          expect(connection.request.url).toMatch(/\/api\/v1\/pcap\/fixed/);
+          connection.mockRespond(
+            new Response(new ResponseOptions({ body: responseMock }))
+          );
+        });
 
-function pdml_json() {
-  return `{
-  "pdml": {
-    "$": {
-      "version": "0",
-      "creator": "wireshark/2.4.2",
-      "time": "Tue Mar 27 21:55:25 2018",
-      "capture_file": "./metron-platform/metron-api/src/test/resources/test-tcp-packet.pcap"
-    },
-    "packet": [
-      {
-        "proto": [
-          {
-            "$": {
-              "name": "geninfo",
-              "pos": "0",
-              "showname": "General information",
-              "size": "104"
-            },
-            "field": [
-              {
-                "$": {
-                  "name": "num",
-                  "pos": "0",
-                  "show": "1",
-                  "showname": "Number",
-                  "value": "1",
-                  "size": "104"
-                }
-              },
-              {
-                "$": {
-                  "name": "len",
-                  "pos": "0",
-                  "show": "104",
-                  "showname": "Frame Length",
-                  "value": "68",
-                  "size": "104"
-                }
-              },
-              {
-                "$": {
-                  "name": "caplen",
-                  "pos": "0",
-                  "show": "104",
-                  "showname": "Captured Length",
-                  "value": "68",
-                  "size": "104"
-                }
-              },
-              {
-                "$": {
-                  "name": "timestamp",
-                  "pos": "0",
-                  "show": "Mar 26, 2014 19:59:40.024362000 GMT",
-                  "showname": "Captured Time",
-                  "value": "1395863980.024362000",
-                  "size": "104"
-                }
-              }
-            ]
-          },
-          {
-            "$": {
-              "name": "frame",
-              "showname": "Frame 1: 104 bytes on wire (832 bits), 104 bytes captured (832 bits)",
-              "size": "104",
-              "pos": "0"
-            },
-            "field": [
-              {
-                "$": {
-                  "name": "frame.encap_type",
-                  "showname": "Encapsulation type: Ethernet (1)",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "1"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.time",
-                  "showname": "Arrival Time: Mar 26, 2014 19:59:40.024362000 GMT",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "Mar 26, 2014 19:59:40.024362000 GMT"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.offset_shift",
-                  "showname": "Time shift for this packet: 0.000000000 seconds",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "0.000000000"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.time_epoch",
-                  "showname": "Epoch Time: 1395863980.024362000 seconds",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "1395863980.024362000"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.time_delta",
-                  "showname": "Time delta from previous captured frame: 0.000000000 seconds",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "0.000000000"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.time_delta_displayed",
-                  "showname": "Time delta from previous displayed frame: 0.000000000 seconds",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "0.000000000"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.time_relative",
-                  "showname": "Time since reference or first frame: 0.000000000 seconds",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "0.000000000"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.number",
-                  "showname": "Frame Number: 1",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "1"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.len",
-                  "showname": "Frame Length: 104 bytes (832 bits)",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "104"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.cap_len",
-                  "showname": "Capture Length: 104 bytes (832 bits)",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "104"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.marked",
-                  "showname": "Frame is marked: False",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "0"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.ignored",
-                  "showname": "Frame is ignored: False",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "0"
-                }
-              },
-              {
-                "$": {
-                  "name": "frame.protocols",
-                  "showname": "Protocols in frame: eth:ethertype:ip:tcp:smtp",
-                  "size": "0",
-                  "pos": "0",
-                  "show": "eth:ethertype:ip:tcp:smtp"
-                }
-              }
-            ]
-          },
-          {
-            "$": {
-              "name": "eth",
-              "showname": "Ethernet II, Src: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02), Dst: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)",
-              "size": "14",
-              "pos": "0"
-            },
-            "field": [
-              {
-                "$": {
-                  "name": "eth.dst",
-                  "showname": "Destination: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)",
-                  "size": "6",
-                  "pos": "0",
-                  "show": "02:1a:c5:05:00:02",
-                  "value": "021ac5050002"
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "eth.dst_resolved",
-                      "showname": "Destination (resolved): MS-NLB-PhysServer-26_c5:05:00:02",
-                      "hide": "yes",
-                      "size": "6",
-                      "pos": "0",
-                      "show": "MS-NLB-PhysServer-26_c5:05:00:02",
-                      "value": "021ac5050002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "eth.addr",
-                      "showname": "Address: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)",
-                      "size": "6",
-                      "pos": "0",
-                      "show": "02:1a:c5:05:00:02",
-                      "value": "021ac5050002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "eth.addr_resolved",
-                      "showname": "Address (resolved): MS-NLB-PhysServer-26_c5:05:00:02",
-                      "hide": "yes",
-                      "size": "6",
-                      "pos": "0",
-                      "show": "MS-NLB-PhysServer-26_c5:05:00:02",
-                      "value": "021ac5050002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "eth.lg",
-                      "showname": ".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)",
-                      "size": "3",
-                      "pos": "0",
-                      "show": "1",
-                      "value": "1",
-                      "unmaskedvalue": "021ac5"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "eth.ig",
-                      "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
-                      "size": "3",
-                      "pos": "0",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "021ac5"
-                    }
-                  }
-                ]
-              },
-              {
-                "$": {
-                  "name": "eth.src",
-                  "showname": "Source: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02)",
-                  "size": "6",
-                  "pos": "6",
-                  "show": "02:1a:c5:01:00:02",
-                  "value": "021ac5010002"
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "eth.src_resolved",
-                      "showname": "Source (resolved): MS-NLB-PhysServer-26_c5:01:00:02",
-                      "hide": "yes",
-                      "size": "6",
-                      "pos": "6",
-                      "show": "MS-NLB-PhysServer-26_c5:01:00:02",
-                      "value": "021ac5010002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "eth.addr",
-                      "showname": "Address: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02)",
-                      "size": "6",
-                      "pos": "6",
-                      "show": "02:1a:c5:01:00:02",
-                      "value": "021ac5010002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "eth.addr_resolved",
-                      "showname": "Address (resolved): MS-NLB-PhysServer-26_c5:01:00:02",
-                      "hide": "yes",
-                      "size": "6",
-                      "pos": "6",
-                      "show": "MS-NLB-PhysServer-26_c5:01:00:02",
-                      "value": "021ac5010002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "eth.lg",
-                      "showname": ".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)",
-                      "size": "3",
-                      "pos": "6",
-                      "show": "1",
-                      "value": "1",
-                      "unmaskedvalue": "021ac5"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "eth.ig",
-                      "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
-                      "size": "3",
-                      "pos": "6",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "021ac5"
-                    }
-                  }
-                ]
-              },
-              {
-                "$": {
-                  "name": "eth.type",
-                  "showname": "Type: IPv4 (0x0800)",
-                  "size": "2",
-                  "pos": "12",
-                  "show": "0x00000800",
-                  "value": "0800"
-                }
-              },
-              {
-                "$": {
-                  "name": "eth.fcs",
-                  "showname": "Frame check sequence: 0x26469e92 [correct]",
-                  "size": "4",
-                  "pos": "100",
-                  "show": "0x26469e92",
-                  "value": "26469e92"
-                }
-              },
-              {
-                "$": {
-                  "name": "eth.fcs.status",
-                  "showname": "FCS Status: Good",
-                  "size": "0",
-                  "pos": "100",
-                  "show": "1"
-                }
-              }
-            ]
-          },
-          {
-            "$": {
-              "name": "ip",
-              "showname": "Internet Protocol Version 4, Src: 24.0.0.2, Dst: 24.128.0.2",
-              "size": "20",
-              "pos": "14"
-            },
-            "field": [
-              {
-                "$": {
-                  "name": "ip.version",
-                  "showname": "0100 .... = Version: 4",
-                  "size": "1",
-                  "pos": "14",
-                  "show": "4",
-                  "value": "4",
-                  "unmaskedvalue": "45"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.hdr_len",
-                  "showname": ".... 0101 = Header Length: 20 bytes (5)",
-                  "size": "1",
-                  "pos": "14",
-                  "show": "20",
-                  "value": "45"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.dsfield",
-                  "showname": "Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)",
-                  "size": "1",
-                  "pos": "15",
-                  "show": "0x00000000",
-                  "value": "00"
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "ip.dsfield.dscp",
-                      "showname": "0000 00.. = Differentiated Services Codepoint: Default (0)",
-                      "size": "1",
-                      "pos": "15",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "00"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.dsfield.ecn",
-                      "showname": ".... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)",
-                      "size": "1",
-                      "pos": "15",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "00"
-                    }
-                  }
-                ]
-              },
-              {
-                "$": {
-                  "name": "ip.len",
-                  "showname": "Total Length: 86",
-                  "size": "2",
-                  "pos": "16",
-                  "show": "86",
-                  "value": "0056"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.id",
-                  "showname": "Identification: 0xcff6 (53238)",
-                  "size": "2",
-                  "pos": "18",
-                  "show": "0x0000cff6",
-                  "value": "cff6"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.flags",
-                  "showname": "Flags: 0x02 (Don't Fragment)",
-                  "size": "1",
-                  "pos": "20",
-                  "show": "0x00000002",
-                  "value": "40"
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "ip.flags.rb",
-                      "showname": "0... .... = Reserved bit: Not set",
-                      "size": "1",
-                      "pos": "20",
-                      "show": "0",
-                      "value": "40"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.flags.df",
-                      "showname": ".1.. .... = Don't fragment: Set",
-                      "size": "1",
-                      "pos": "20",
-                      "show": "1",
-                      "value": "40"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.flags.mf",
-                      "showname": "..0. .... = More fragments: Not set",
-                      "size": "1",
-                      "pos": "20",
-                      "show": "0",
-                      "value": "40"
-                    }
-                  }
-                ]
-              },
-              {
-                "$": {
-                  "name": "ip.frag_offset",
-                  "showname": "Fragment offset: 0",
-                  "size": "2",
-                  "pos": "20",
-                  "show": "0",
-                  "value": "4000"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.ttl",
-                  "showname": "Time to live: 32",
-                  "size": "1",
-                  "pos": "22",
-                  "show": "32",
-                  "value": "20"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.proto",
-                  "showname": "Protocol: TCP (6)",
-                  "size": "1",
-                  "pos": "23",
-                  "show": "6",
-                  "value": "06"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.checksum",
-                  "showname": "Header checksum: 0x5a28 [validation disabled]",
-                  "size": "2",
-                  "pos": "24",
-                  "show": "0x00005a28",
-                  "value": "5a28"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.checksum.status",
-                  "showname": "Header checksum status: Unverified",
-                  "size": "0",
-                  "pos": "24",
-                  "show": "2"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.src",
-                  "showname": "Source: 24.0.0.2",
-                  "size": "4",
-                  "pos": "26",
-                  "show": "24.0.0.2",
-                  "value": "18000002"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.addr",
-                  "showname": "Source or Destination Address: 24.0.0.2",
-                  "hide": "yes",
-                  "size": "4",
-                  "pos": "26",
-                  "show": "24.0.0.2",
-                  "value": "18000002"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.src_host",
-                  "showname": "Source Host: 24.0.0.2",
-                  "hide": "yes",
-                  "size": "4",
-                  "pos": "26",
-                  "show": "24.0.0.2",
-                  "value": "18000002"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.host",
-                  "showname": "Source or Destination Host: 24.0.0.2",
-                  "hide": "yes",
-                  "size": "4",
-                  "pos": "26",
-                  "show": "24.0.0.2",
-                  "value": "18000002"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.dst",
-                  "showname": "Destination: 24.128.0.2",
-                  "size": "4",
-                  "pos": "30",
-                  "show": "24.128.0.2",
-                  "value": "18800002"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.addr",
-                  "showname": "Source or Destination Address: 24.128.0.2",
-                  "hide": "yes",
-                  "size": "4",
-                  "pos": "30",
-                  "show": "24.128.0.2",
-                  "value": "18800002"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.dst_host",
-                  "showname": "Destination Host: 24.128.0.2",
-                  "hide": "yes",
-                  "size": "4",
-                  "pos": "30",
-                  "show": "24.128.0.2",
-                  "value": "18800002"
-                }
-              },
-              {
-                "$": {
-                  "name": "ip.host",
-                  "showname": "Source or Destination Host: 24.128.0.2",
-                  "hide": "yes",
-                  "size": "4",
-                  "pos": "30",
-                  "show": "24.128.0.2",
-                  "value": "18800002"
-                }
-              },
-              {
-                "$": {
-                  "name": "",
-                  "show": "Source GeoIP: United States, Woodbridge, NJ, AS7922 Comcast Cable Communications, LLC, United States, Woodbridge, NJ, AS7922 Comcast Cable Communications, LLC, 40.557598, -74.284599",
-                  "size": "4",
-                  "pos": "26",
-                  "value": "18000002"
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "ip.geoip.src_country",
-                      "showname": "Source GeoIP Country: United States",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "United States",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.country",
-                      "showname": "Source or Destination GeoIP Country: United States",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "United States",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.src_city",
-                      "showname": "Source GeoIP City: Woodbridge, NJ",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "Woodbridge, NJ",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.city",
-                      "showname": "Source or Destination GeoIP City: Woodbridge, NJ",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "Woodbridge, NJ",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.src_asnum",
-                      "showname": "Source GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "AS7922 Comcast Cable Communications, LLC",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.asnum",
-                      "showname": "Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "AS7922 Comcast Cable Communications, LLC",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.src_country",
-                      "showname": "Source GeoIP Country: United States",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "United States",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.country",
-                      "showname": "Source or Destination GeoIP Country: United States",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "United States",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.src_city",
-                      "showname": "Source GeoIP City: Woodbridge, NJ",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "Woodbridge, NJ",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.city",
-                      "showname": "Source or Destination GeoIP City: Woodbridge, NJ",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "Woodbridge, NJ",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.src_asnum",
-                      "showname": "Source GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "AS7922 Comcast Cable Communications, LLC",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.asnum",
-                      "showname": "Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "AS7922 Comcast Cable Communications, LLC",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.src_lat",
-                      "showname": "Source GeoIP Latitude: 40.557598",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "40.557598",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.lat",
-                      "showname": "Source or Destination GeoIP Latitude: 40.557598",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "40.557598",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.src_lon",
-                      "showname": "Source GeoIP Longitude: -74.284599",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "-74.284599",
-                      "value": "18000002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.lon",
-                      "showname": "Source or Destination GeoIP Longitude: -74.284599",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "26",
-                      "show": "-74.284599",
-                      "value": "18000002"
-                    }
-                  }
-                ]
-              },
-              {
-                "$": {
-                  "name": "",
-                  "show": "Destination GeoIP: United States, Groton, CT, AS7922 Comcast Cable Communications, LLC, United States, Groton, CT, AS7922 Comcast Cable Communications, LLC, 41.353199, -72.038597",
-                  "size": "4",
-                  "pos": "30",
-                  "value": "18800002"
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "ip.geoip.dst_country",
-                      "showname": "Destination GeoIP Country: United States",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "United States",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.country",
-                      "showname": "Source or Destination GeoIP Country: United States",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "United States",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.dst_city",
-                      "showname": "Destination GeoIP City: Groton, CT",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "Groton, CT",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.city",
-                      "showname": "Source or Destination GeoIP City: Groton, CT",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "Groton, CT",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.dst_asnum",
-                      "showname": "Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "AS7922 Comcast Cable Communications, LLC",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.asnum",
-                      "showname": "Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "AS7922 Comcast Cable Communications, LLC",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.dst_country",
-                      "showname": "Destination GeoIP Country: United States",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "United States",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.country",
-                      "showname": "Source or Destination GeoIP Country: United States",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "United States",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.dst_city",
-                      "showname": "Destination GeoIP City: Groton, CT",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "Groton, CT",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.city",
-                      "showname": "Source or Destination GeoIP City: Groton, CT",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "Groton, CT",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.dst_asnum",
-                      "showname": "Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "AS7922 Comcast Cable Communications, LLC",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.asnum",
-                      "showname": "Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "AS7922 Comcast Cable Communications, LLC",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.dst_lat",
-                      "showname": "Destination GeoIP Latitude: 41.353199",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "41.353199",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.lat",
-                      "showname": "Source or Destination GeoIP Latitude: 41.353199",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "41.353199",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.dst_lon",
-                      "showname": "Destination GeoIP Longitude: -72.038597",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "-72.038597",
-                      "value": "18800002"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "ip.geoip.lon",
-                      "showname": "Source or Destination GeoIP Longitude: -72.038597",
-                      "hide": "yes",
-                      "size": "4",
-                      "pos": "30",
-                      "show": "-72.038597",
-                      "value": "18800002"
-                    }
-                  }
-                ]
-              }
-            ]
-          },
-          {
-            "$": {
-              "name": "tcp",
-              "showname": "Transmission Control Protocol, Src Port: 2137, Dst Port: 25, Seq: 1, Ack: 1, Len: 34",
-              "size": "32",
-              "pos": "34"
-            },
-            "field": [
-              {
-                "$": {
-                  "name": "tcp.srcport",
-                  "showname": "Source Port: 2137",
-                  "size": "2",
-                  "pos": "34",
-                  "show": "2137",
-                  "value": "0859"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.dstport",
-                  "showname": "Destination Port: 25",
-                  "size": "2",
-                  "pos": "36",
-                  "show": "25",
-                  "value": "0019"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.port",
-                  "showname": "Source or Destination Port: 2137",
-                  "hide": "yes",
-                  "size": "2",
-                  "pos": "34",
-                  "show": "2137",
-                  "value": "0859"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.port",
-                  "showname": "Source or Destination Port: 25",
-                  "hide": "yes",
-                  "size": "2",
-                  "pos": "36",
-                  "show": "25",
-                  "value": "0019"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.stream",
-                  "showname": "Stream index: 0",
-                  "size": "0",
-                  "pos": "34",
-                  "show": "0"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.len",
-                  "showname": "TCP Segment Len: 34",
-                  "size": "1",
-                  "pos": "46",
-                  "show": "34",
-                  "value": "80"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.seq",
-                  "showname": "Sequence number: 1    (relative sequence number)",
-                  "size": "4",
-                  "pos": "38",
-                  "show": "1",
-                  "value": "f88900ce"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.nxtseq",
-                  "showname": "Next sequence number: 35    (relative sequence number)",
-                  "size": "0",
-                  "pos": "34",
-                  "show": "35"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.ack",
-                  "showname": "Acknowledgment number: 1    (relative ack number)",
-                  "size": "4",
-                  "pos": "42",
-                  "show": "1",
-                  "value": "365aa74f"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.hdr_len",
-                  "showname": "1000 .... = Header Length: 32 bytes (8)",
-                  "size": "1",
-                  "pos": "46",
-                  "show": "32",
-                  "value": "80"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.flags",
-                  "showname": "Flags: 0x018 (PSH, ACK)",
-                  "size": "2",
-                  "pos": "46",
-                  "show": "0x00000018",
-                  "value": "18",
-                  "unmaskedvalue": "8018"
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "tcp.flags.res",
-                      "showname": "000. .... .... = Reserved: Not set",
-                      "size": "1",
-                      "pos": "46",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "80"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.ns",
-                      "showname": "...0 .... .... = Nonce: Not set",
-                      "size": "1",
-                      "pos": "46",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "80"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.cwr",
-                      "showname": ".... 0... .... = Congestion Window Reduced (CWR): Not set",
-                      "size": "1",
-                      "pos": "47",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "18"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.ecn",
-                      "showname": ".... .0.. .... = ECN-Echo: Not set",
-                      "size": "1",
-                      "pos": "47",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "18"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.urg",
-                      "showname": ".... ..0. .... = Urgent: Not set",
-                      "size": "1",
-                      "pos": "47",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "18"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.ack",
-                      "showname": ".... ...1 .... = Acknowledgment: Set",
-                      "size": "1",
-                      "pos": "47",
-                      "show": "1",
-                      "value": "1",
-                      "unmaskedvalue": "18"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.push",
-                      "showname": ".... .... 1... = Push: Set",
-                      "size": "1",
-                      "pos": "47",
-                      "show": "1",
-                      "value": "1",
-                      "unmaskedvalue": "18"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.reset",
-                      "showname": ".... .... .0.. = Reset: Not set",
-                      "size": "1",
-                      "pos": "47",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "18"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.syn",
-                      "showname": ".... .... ..0. = Syn: Not set",
-                      "size": "1",
-                      "pos": "47",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "18"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.fin",
-                      "showname": ".... .... ...0 = Fin: Not set",
-                      "size": "1",
-                      "pos": "47",
-                      "show": "0",
-                      "value": "0",
-                      "unmaskedvalue": "18"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.flags.str",
-                      "showname": "TCP Flags: \\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7AP\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7",
-                      "size": "2",
-                      "pos": "46",
-                      "show": "\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7AP\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7",
-                      "value": "8018"
-                    }
-                  }
-                ]
-              },
-              {
-                "$": {
-                  "name": "tcp.window_size_value",
-                  "showname": "Window size value: 7240",
-                  "size": "2",
-                  "pos": "48",
-                  "show": "7240",
-                  "value": "1c48"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.window_size",
-                  "showname": "Calculated window size: 7240",
-                  "size": "2",
-                  "pos": "48",
-                  "show": "7240",
-                  "value": "1c48"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.window_size_scalefactor",
-                  "showname": "Window size scaling factor: -1 (unknown)",
-                  "size": "2",
-                  "pos": "48",
-                  "show": "-1",
-                  "value": "1c48"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.checksum",
-                  "showname": "Checksum: 0x681f [unverified]",
-                  "size": "2",
-                  "pos": "50",
-                  "show": "0x0000681f",
-                  "value": "681f"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.checksum.status",
-                  "showname": "Checksum Status: Unverified",
-                  "size": "0",
-                  "pos": "50",
-                  "show": "2"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.urgent_pointer",
-                  "showname": "Urgent pointer: 0",
-                  "size": "2",
-                  "pos": "52",
-                  "show": "0",
-                  "value": "0000"
-                }
-              },
-              {
-                "$": {
-                  "name": "tcp.options",
-                  "showname": "Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps",
-                  "size": "12",
-                  "pos": "54",
-                  "show": "01:01:08:0a:eb:83:4b:08:e8:8c:de:cb",
-                  "value": "0101080aeb834b08e88cdecb"
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "tcp.options.nop",
-                      "showname": "TCP Option - No-Operation (NOP)",
-                      "size": "1",
-                      "pos": "54",
-                      "show": "01",
-                      "value": "01"
-                    },
-                    "field": [
-                      {
-                        "$": {
-                          "name": "tcp.option_kind",
-                          "showname": "Kind: No-Operation (1)",
-                          "size": "1",
-                          "pos": "54",
-                          "show": "1",
-                          "value": "01"
-                        }
-                      }
-                    ]
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.options.nop",
-                      "showname": "TCP Option - No-Operation (NOP)",
-                      "size": "1",
-                      "pos": "55",
-                      "show": "01",
-                      "value": "01"
-                    },
-                    "field": [
-                      {
-                        "$": {
-                          "name": "tcp.option_kind",
-                          "showname": "Kind: No-Operation (1)",
-                          "size": "1",
-                          "pos": "55",
-                          "show": "1",
-                          "value": "01"
-                        }
-                      }
-                    ]
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.options.timestamp",
-                      "showname": "TCP Option - Timestamps: TSval 3951250184, TSecr 3901546187",
-                      "size": "10",
-                      "pos": "56",
-                      "show": "08:0a:eb:83:4b:08:e8:8c:de:cb",
-                      "value": "080aeb834b08e88cdecb"
-                    },
-                    "field": [
-                      {
-                        "$": {
-                          "name": "tcp.option_kind",
-                          "showname": "Kind: Time Stamp Option (8)",
-                          "size": "1",
-                          "pos": "56",
-                          "show": "8",
-                          "value": "08"
-                        }
-                      },
-                      {
-                        "$": {
-                          "name": "tcp.option_len",
-                          "showname": "Length: 10",
-                          "size": "1",
-                          "pos": "57",
-                          "show": "10",
-                          "value": "0a"
-                        }
-                      },
-                      {
-                        "$": {
-                          "name": "tcp.options.timestamp.tsval",
-                          "showname": "Timestamp value: 3951250184",
-                          "size": "4",
-                          "pos": "58",
-                          "show": "3951250184",
-                          "value": "eb834b08"
-                        }
-                      },
-                      {
-                        "$": {
-                          "name": "tcp.options.timestamp.tsecr",
-                          "showname": "Timestamp echo reply: 3901546187",
-                          "size": "4",
-                          "pos": "62",
-                          "show": "3901546187",
-                          "value": "e88cdecb"
-                        }
-                      }
-                    ]
-                  }
-                ]
-              },
-              {
-                "$": {
-                  "name": "tcp.analysis",
-                  "showname": "SEQ/ACK analysis",
-                  "size": "0",
-                  "pos": "34",
-                  "show": "",
-                  "value": ""
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "tcp.analysis.bytes_in_flight",
-                      "showname": "Bytes in flight: 34",
-                      "size": "0",
-                      "pos": "34",
-                      "show": "34"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "tcp.analysis.push_bytes_sent",
-                      "showname": "Bytes sent since last PSH flag: 34",
-                      "size": "0",
-                      "pos": "34",
-                      "show": "34"
-                    }
-                  }
-                ]
-              },
-              {
-                "$": {
-                  "name": "tcp.payload",
-                  "showname": "TCP payload (34 bytes)",
-                  "size": "34",
-                  "pos": "66",
-                  "show": "45:48:4c:4f:20:63:6c:69:65:6e:74:2d:31:38:30:30:30:30:30:33:2e:65:78:61:6d:70:6c:65:2e:69:6e:74:0d:0a",
-                  "value": "45484c4f20636c69656e742d31383030303030332e6578616d706c652e696e740d0a"
-                }
-              }
-            ]
-          },
-          {
-            "$": {
-              "name": "smtp",
-              "showname": "Simple Mail Transfer Protocol",
-              "size": "34",
-              "pos": "66"
-            },
-            "field": [
-              {
-                "$": {
-                  "name": "smtp.req",
-                  "showname": "Request: True",
-                  "hide": "yes",
-                  "size": "0",
-                  "pos": "66",
-                  "show": "1"
-                }
-              },
-              {
-                "$": {
-                  "name": "smtp.command_line",
-                  "showname": "Command Line: EHLO client-18000003.example.int\\\\r\\\\n",
-                  "size": "34",
-                  "pos": "66",
-                  "show": "EHLO client-18000003.example.int\\\\xd\\\\xa",
-                  "value": "45484c4f20636c69656e742d31383030303030332e6578616d706c652e696e740d0a"
-                },
-                "field": [
-                  {
-                    "$": {
-                      "name": "smtp.req.command",
-                      "showname": "Command: EHLO",
-                      "size": "4",
-                      "pos": "66",
-                      "show": "EHLO",
-                      "value": "45484c4f"
-                    }
-                  },
-                  {
-                    "$": {
-                      "name": "smtp.req.parameter",
-                      "showname": "Request parameter: client-18000003.example.int",
-                      "size": "27",
-                      "pos": "71",
-                      "show": "client-18000003.example.int",
-                      "value": "636c69656e742d31383030303030332e6578616d706c652e696e74"
-                    }
-                  }
-                ]
-              }
-            ]
-          }
-        ]
+        pcapService.submitRequest(request).subscribe(r => (response = r));
+        expect(response).toBeTruthy();
       }
-    ]
-  }
-}
-`
-}
+    ));
+  });
 
-function pdml() {
-  return `<?xml version="1.0" encoding="utf-8"?>
-  <?xml-stylesheet type="text/xsl" href="pdml2html.xsl"?>
-  <!-- You can find pdml2html.xsl in /usr/share/wireshark or at https://code.wireshark.org/review/gitweb?p=wireshark.git;a=blob_plain;f=pdml2html.xsl. -->
-  <pdml version="0" creator="wireshark/2.4.2" time="Tue Mar 27 21:55:25 2018" capture_file="./metron-platform/metron-api/src/test/resources/test-tcp-packet.pcap">
-  <packet>
-    <proto name="geninfo" pos="0" showname="General information" size="104">
-      <field name="num" pos="0" show="1" showname="Number" value="1" size="104"/>
-      <field name="len" pos="0" show="104" showname="Frame Length" value="68" size="104"/>
-      <field name="caplen" pos="0" show="104" showname="Captured Length" value="68" size="104"/>
-      <field name="timestamp" pos="0" show="Mar 26, 2014 19:59:40.024362000 GMT" showname="Captured Time" value="1395863980.024362000" size="104"/>
-    </proto>
-    <proto name="frame" showname="Frame 1: 104 bytes on wire (832 bits), 104 bytes captured (832 bits)" size="104" pos="0">
-      <field name="frame.encap_type" showname="Encapsulation type: Ethernet (1)" size="0" pos="0" show="1"/>
-      <field name="frame.time" showname="Arrival Time: Mar 26, 2014 19:59:40.024362000 GMT" size="0" pos="0" show="Mar 26, 2014 19:59:40.024362000 GMT"/>
-      <field name="frame.offset_shift" showname="Time shift for this packet: 0.000000000 seconds" size="0" pos="0" show="0.000000000"/>
-      <field name="frame.time_epoch" showname="Epoch Time: 1395863980.024362000 seconds" size="0" pos="0" show="1395863980.024362000"/>
-      <field name="frame.time_delta" showname="Time delta from previous captured frame: 0.000000000 seconds" size="0" pos="0" show="0.000000000"/>
-      <field name="frame.time_delta_displayed" showname="Time delta from previous displayed frame: 0.000000000 seconds" size="0" pos="0" show="0.000000000"/>
-      <field name="frame.time_relative" showname="Time since reference or first frame: 0.000000000 seconds" size="0" pos="0" show="0.000000000"/>
-      <field name="frame.number" showname="Frame Number: 1" size="0" pos="0" show="1"/>
-      <field name="frame.len" showname="Frame Length: 104 bytes (832 bits)" size="0" pos="0" show="104"/>
-      <field name="frame.cap_len" showname="Capture Length: 104 bytes (832 bits)" size="0" pos="0" show="104"/>
-      <field name="frame.marked" showname="Frame is marked: False" size="0" pos="0" show="0"/>
-      <field name="frame.ignored" showname="Frame is ignored: False" size="0" pos="0" show="0"/>
-      <field name="frame.protocols" showname="Protocols in frame: eth:ethertype:ip:tcp:smtp" size="0" pos="0" show="eth:ethertype:ip:tcp:smtp"/>
-    </proto>
-    <proto name="eth" showname="Ethernet II, Src: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02), Dst: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)" size="14" pos="0">
-      <field name="eth.dst" showname="Destination: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)" size="6" pos="0" show="02:1a:c5:05:00:02" value="021ac5050002">
-        <field name="eth.dst_resolved" showname="Destination (resolved): MS-NLB-PhysServer-26_c5:05:00:02" hide="yes" size="6" pos="0" show="MS-NLB-PhysServer-26_c5:05:00:02" value="021ac5050002"/>
-        <field name="eth.addr" showname="Address: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)" size="6" pos="0" show="02:1a:c5:05:00:02" value="021ac5050002"/>
-        <field name="eth.addr_resolved" showname="Address (resolved): MS-NLB-PhysServer-26_c5:05:00:02" hide="yes" size="6" pos="0" show="MS-NLB-PhysServer-26_c5:05:00:02" value="021ac5050002"/>
-        <field name="eth.lg" showname=".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)" size="3" pos="0" show="1" value="1" unmaskedvalue="021ac5"/>
-        <field name="eth.ig" showname=".... ...0 .... .... .... .... = IG bit: Individual address (unicast)" size="3" pos="0" show="0" value="0" unmaskedvalue="021ac5"/>
-      </field>
-      <field name="eth.src" showname="Source: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02)" size="6" pos="6" show="02:1a:c5:01:00:02" value="021ac5010002">
-        <field name="eth.src_resolved" showname="Source (resolved): MS-NLB-PhysServer-26_c5:01:00:02" hide="yes" size="6" pos="6" show="MS-NLB-PhysServer-26_c5:01:00:02" value="021ac5010002"/>
-        <field name="eth.addr" showname="Address: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02)" size="6" pos="6" show="02:1a:c5:01:00:02" value="021ac5010002"/>
-        <field name="eth.addr_resolved" showname="Address (resolved): MS-NLB-PhysServer-26_c5:01:00:02" hide="yes" size="6" pos="6" show="MS-NLB-PhysServer-26_c5:01:00:02" value="021ac5010002"/>
-        <field name="eth.lg" showname=".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)" size="3" pos="6" show="1" value="1" unmaskedvalue="021ac5"/>
-        <field name="eth.ig" showname=".... ...0 .... .... .... .... = IG bit: Individual address (unicast)" size="3" pos="6" show="0" value="0" unmaskedvalue="021ac5"/>
-      </field>
-      <field name="eth.type" showname="Type: IPv4 (0x0800)" size="2" pos="12" show="0x00000800" value="0800"/>
-      <field name="eth.fcs" showname="Frame check sequence: 0x26469e92 [correct]" size="4" pos="100" show="0x26469e92" value="26469e92"/>
-      <field name="eth.fcs.status" showname="FCS Status: Good" size="0" pos="100" show="1"/>
-    </proto>
-    <proto name="ip" showname="Internet Protocol Version 4, Src: 24.0.0.2, Dst: 24.128.0.2" size="20" pos="14">
-      <field name="ip.version" showname="0100 .... = Version: 4" size="1" pos="14" show="4" value="4" unmaskedvalue="45"/>
-      <field name="ip.hdr_len" showname=".... 0101 = Header Length: 20 bytes (5)" size="1" pos="14" show="20" value="45"/>
-      <field name="ip.dsfield" showname="Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)" size="1" pos="15" show="0x00000000" value="00">
-        <field name="ip.dsfield.dscp" showname="0000 00.. = Differentiated Services Codepoint: Default (0)" size="1" pos="15" show="0" value="0" unmaskedvalue="00"/>
-        <field name="ip.dsfield.ecn" showname=".... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)" size="1" pos="15" show="0" value="0" unmaskedvalue="00"/>
-      </field>
-      <field name="ip.len" showname="Total Length: 86" size="2" pos="16" show="86" value="0056"/>
-      <field name="ip.id" showname="Identification: 0xcff6 (53238)" size="2" pos="18" show="0x0000cff6" value="cff6"/>
-      <field name="ip.flags" showname="Flags: 0x02 (Don&#x27;t Fragment)" size="1" pos="20" show="0x00000002" value="40">
-        <field name="ip.flags.rb" showname="0... .... = Reserved bit: Not set" size="1" pos="20" show="0" value="40"/>
-        <field name="ip.flags.df" showname=".1.. .... = Don&#x27;t fragment: Set" size="1" pos="20" show="1" value="40"/>
-        <field name="ip.flags.mf" showname="..0. .... = More fragments: Not set" size="1" pos="20" show="0" value="40"/>
-      </field>
-      <field name="ip.frag_offset" showname="Fragment offset: 0" size="2" pos="20" show="0" value="4000"/>
-      <field name="ip.ttl" showname="Time to live: 32" size="1" pos="22" show="32" value="20"/>
-      <field name="ip.proto" showname="Protocol: TCP (6)" size="1" pos="23" show="6" value="06"/>
-      <field name="ip.checksum" showname="Header checksum: 0x5a28 [validation disabled]" size="2" pos="24" show="0x00005a28" value="5a28"/>
-      <field name="ip.checksum.status" showname="Header checksum status: Unverified" size="0" pos="24" show="2"/>
-      <field name="ip.src" showname="Source: 24.0.0.2" size="4" pos="26" show="24.0.0.2" value="18000002"/>
-      <field name="ip.addr" showname="Source or Destination Address: 24.0.0.2" hide="yes" size="4" pos="26" show="24.0.0.2" value="18000002"/>
-      <field name="ip.src_host" showname="Source Host: 24.0.0.2" hide="yes" size="4" pos="26" show="24.0.0.2" value="18000002"/>
-      <field name="ip.host" showname="Source or Destination Host: 24.0.0.2" hide="yes" size="4" pos="26" show="24.0.0.2" value="18000002"/>
-      <field name="ip.dst" showname="Destination: 24.128.0.2" size="4" pos="30" show="24.128.0.2" value="18800002"/>
-      <field name="ip.addr" showname="Source or Destination Address: 24.128.0.2" hide="yes" size="4" pos="30" show="24.128.0.2" value="18800002"/>
-      <field name="ip.dst_host" showname="Destination Host: 24.128.0.2" hide="yes" size="4" pos="30" show="24.128.0.2" value="18800002"/>
-      <field name="ip.host" showname="Source or Destination Host: 24.128.0.2" hide="yes" size="4" pos="30" show="24.128.0.2" value="18800002"/>
-      <field name="" show="Source GeoIP: United States, Woodbridge, NJ, AS7922 Comcast Cable Communications, LLC, United States, Woodbridge, NJ, AS7922 Comcast Cable Communications, LLC, 40.557598, -74.284599" size="4" pos="26" value="18000002">
-        <field name="ip.geoip.src_country" showname="Source GeoIP Country: United States" size="4" pos="26" show="United States" value="18000002"/>
-        <field name="ip.geoip.country" showname="Source or Destination GeoIP Country: United States" hide="yes" size="4" pos="26" show="United States" value="18000002"/>
-        <field name="ip.geoip.src_city" showname="Source GeoIP City: Woodbridge, NJ" size="4" pos="26" show="Woodbridge, NJ" value="18000002"/>
-        <field name="ip.geoip.city" showname="Source or Destination GeoIP City: Woodbridge, NJ" hide="yes" size="4" pos="26" show="Woodbridge, NJ" value="18000002"/>
-        <field name="ip.geoip.src_asnum" showname="Source GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" size="4" pos="26" show="AS7922 Comcast Cable Communications, LLC" value="18000002"/>
-        <field name="ip.geoip.asnum" showname="Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" hide="yes" size="4" pos="26" show="AS7922 Comcast Cable Communications, LLC" value="18000002"/>
-        <field name="ip.geoip.src_country" showname="Source GeoIP Country: United States" size="4" pos="26" show="United States" value="18000002"/>
-        <field name="ip.geoip.country" showname="Source or Destination GeoIP Country: United States" hide="yes" size="4" pos="26" show="United States" value="18000002"/>
-        <field name="ip.geoip.src_city" showname="Source GeoIP City: Woodbridge, NJ" size="4" pos="26" show="Woodbridge, NJ" value="18000002"/>
-        <field name="ip.geoip.city" showname="Source or Destination GeoIP City: Woodbridge, NJ" hide="yes" size="4" pos="26" show="Woodbridge, NJ" value="18000002"/>
-        <field name="ip.geoip.src_asnum" showname="Source GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" size="4" pos="26" show="AS7922 Comcast Cable Communications, LLC" value="18000002"/>
-        <field name="ip.geoip.asnum" showname="Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" hide="yes" size="4" pos="26" show="AS7922 Comcast Cable Communications, LLC" value="18000002"/>
-        <field name="ip.geoip.src_lat" showname="Source GeoIP Latitude: 40.557598" size="4" pos="26" show="40.557598" value="18000002"/>
-        <field name="ip.geoip.lat" showname="Source or Destination GeoIP Latitude: 40.557598" hide="yes" size="4" pos="26" show="40.557598" value="18000002"/>
-        <field name="ip.geoip.src_lon" showname="Source GeoIP Longitude: -74.284599" size="4" pos="26" show="-74.284599" value="18000002"/>
-        <field name="ip.geoip.lon" showname="Source or Destination GeoIP Longitude: -74.284599" hide="yes" size="4" pos="26" show="-74.284599" value="18000002"/>
-      </field>
-      <field name="" show="Destination GeoIP: United States, Groton, CT, AS7922 Comcast Cable Communications, LLC, United States, Groton, CT, AS7922 Comcast Cable Communications, LLC, 41.353199, -72.038597" size="4" pos="30" value="18800002">
-        <field name="ip.geoip.dst_country" showname="Destination GeoIP Country: United States" size="4" pos="30" show="United States" value="18800002"/>
-        <field name="ip.geoip.country" showname="Source or Destination GeoIP Country: United States" hide="yes" size="4" pos="30" show="United States" value="18800002"/>
-        <field name="ip.geoip.dst_city" showname="Destination GeoIP City: Groton, CT" size="4" pos="30" show="Groton, CT" value="18800002"/>
-        <field name="ip.geoip.city" showname="Source or Destination GeoIP City: Groton, CT" hide="yes" size="4" pos="30" show="Groton, CT" value="18800002"/>
-        <field name="ip.geoip.dst_asnum" showname="Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" size="4" pos="30" show="AS7922 Comcast Cable Communications, LLC" value="18800002"/>
-        <field name="ip.geoip.asnum" showname="Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" hide="yes" size="4" pos="30" show="AS7922 Comcast Cable Communications, LLC" value="18800002"/>
-        <field name="ip.geoip.dst_country" showname="Destination GeoIP Country: United States" size="4" pos="30" show="United States" value="18800002"/>
-        <field name="ip.geoip.country" showname="Source or Destination GeoIP Country: United States" hide="yes" size="4" pos="30" show="United States" value="18800002"/>
-        <field name="ip.geoip.dst_city" showname="Destination GeoIP City: Groton, CT" size="4" pos="30" show="Groton, CT" value="18800002"/>
-        <field name="ip.geoip.city" showname="Source or Destination GeoIP City: Groton, CT" hide="yes" size="4" pos="30" show="Groton, CT" value="18800002"/>
-        <field name="ip.geoip.dst_asnum" showname="Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" size="4" pos="30" show="AS7922 Comcast Cable Communications, LLC" value="18800002"/>
-        <field name="ip.geoip.asnum" showname="Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" hide="yes" size="4" pos="30" show="AS7922 Comcast Cable Communications, LLC" value="18800002"/>
-        <field name="ip.geoip.dst_lat" showname="Destination GeoIP Latitude: 41.353199" size="4" pos="30" show="41.353199" value="18800002"/>
-        <field name="ip.geoip.lat" showname="Source or Destination GeoIP Latitude: 41.353199" hide="yes" size="4" pos="30" show="41.353199" value="18800002"/>
-        <field name="ip.geoip.dst_lon" showname="Destination GeoIP Longitude: -72.038597" size="4" pos="30" show="-72.038597" value="18800002"/>
-        <field name="ip.geoip.lon" showname="Source or Destination GeoIP Longitude: -72.038597" hide="yes" size="4" pos="30" show="-72.038597" value="18800002"/>
-      </field>
-    </proto>
-    <proto name="tcp" showname="Transmission Control Protocol, Src Port: 2137, Dst Port: 25, Seq: 1, Ack: 1, Len: 34" size="32" pos="34">
-      <field name="tcp.srcport" showname="Source Port: 2137" size="2" pos="34" show="2137" value="0859"/>
-      <field name="tcp.dstport" showname="Destination Port: 25" size="2" pos="36" show="25" value="0019"/>
-      <field name="tcp.port" showname="Source or Destination Port: 2137" hide="yes" size="2" pos="34" show="2137" value="0859"/>
-      <field name="tcp.port" showname="Source or Destination Port: 25" hide="yes" size="2" pos="36" show="25" value="0019"/>
-      <field name="tcp.stream" showname="Stream index: 0" size="0" pos="34" show="0"/>
-      <field name="tcp.len" showname="TCP Segment Len: 34" size="1" pos="46" show="34" value="80"/>
-      <field name="tcp.seq" showname="Sequence number: 1    (relative sequence number)" size="4" pos="38" show="1" value="f88900ce"/>
-      <field name="tcp.nxtseq" showname="Next sequence number: 35    (relative sequence number)" size="0" pos="34" show="35"/>
-      <field name="tcp.ack" showname="Acknowledgment number: 1    (relative ack number)" size="4" pos="42" show="1" value="365aa74f"/>
-      <field name="tcp.hdr_len" showname="1000 .... = Header Length: 32 bytes (8)" size="1" pos="46" show="32" value="80"/>
-      <field name="tcp.flags" showname="Flags: 0x018 (PSH, ACK)" size="2" pos="46" show="0x00000018" value="18" unmaskedvalue="8018">
-        <field name="tcp.flags.res" showname="000. .... .... = Reserved: Not set" size="1" pos="46" show="0" value="0" unmaskedvalue="80"/>
-        <field name="tcp.flags.ns" showname="...0 .... .... = Nonce: Not set" size="1" pos="46" show="0" value="0" unmaskedvalue="80"/>
-        <field name="tcp.flags.cwr" showname=".... 0... .... = Congestion Window Reduced (CWR): Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
-        <field name="tcp.flags.ecn" showname=".... .0.. .... = ECN-Echo: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
-        <field name="tcp.flags.urg" showname=".... ..0. .... = Urgent: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
-        <field name="tcp.flags.ack" showname=".... ...1 .... = Acknowledgment: Set" size="1" pos="47" show="1" value="1" unmaskedvalue="18"/>
-        <field name="tcp.flags.push" showname=".... .... 1... = Push: Set" size="1" pos="47" show="1" value="1" unmaskedvalue="18"/>
-        <field name="tcp.flags.reset" showname=".... .... .0.. = Reset: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
-        <field name="tcp.flags.syn" showname=".... .... ..0. = Syn: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
-        <field name="tcp.flags.fin" showname=".... .... ...0 = Fin: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
-        <field name="tcp.flags.str" showname="TCP Flags: \\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7AP\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7" size="2" pos="46" show="\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7AP\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7" value="8018"/>
-      </field>
-      <field name="tcp.window_size_value" showname="Window size value: 7240" size="2" pos="48" show="7240" value="1c48"/>
-      <field name="tcp.window_size" showname="Calculated window size: 7240" size="2" pos="48" show="7240" value="1c48"/>
-      <field name="tcp.window_size_scalefactor" showname="Window size scaling factor: -1 (unknown)" size="2" pos="48" show="-1" value="1c48"/>
-      <field name="tcp.checksum" showname="Checksum: 0x681f [unverified]" size="2" pos="50" show="0x0000681f" value="681f"/>
-      <field name="tcp.checksum.status" showname="Checksum Status: Unverified" size="0" pos="50" show="2"/>
-      <field name="tcp.urgent_pointer" showname="Urgent pointer: 0" size="2" pos="52" show="0" value="0000"/>
-      <field name="tcp.options" showname="Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps" size="12" pos="54" show="01:01:08:0a:eb:83:4b:08:e8:8c:de:cb" value="0101080aeb834b08e88cdecb">
-        <field name="tcp.options.nop" showname="TCP Option - No-Operation (NOP)" size="1" pos="54" show="01" value="01">
-          <field name="tcp.option_kind" showname="Kind: No-Operation (1)" size="1" pos="54" show="1" value="01"/>
-        </field>
-        <field name="tcp.options.nop" showname="TCP Option - No-Operation (NOP)" size="1" pos="55" show="01" value="01">
-          <field name="tcp.option_kind" showname="Kind: No-Operation (1)" size="1" pos="55" show="1" value="01"/>
-        </field>
-        <field name="tcp.options.timestamp" showname="TCP Option - Timestamps: TSval 3951250184, TSecr 3901546187" size="10" pos="56" show="08:0a:eb:83:4b:08:e8:8c:de:cb" value="080aeb834b08e88cdecb">
-          <field name="tcp.option_kind" showname="Kind: Time Stamp Option (8)" size="1" pos="56" show="8" value="08"/>
-          <field name="tcp.option_len" showname="Length: 10" size="1" pos="57" show="10" value="0a"/>
-          <field name="tcp.options.timestamp.tsval" showname="Timestamp value: 3951250184" size="4" pos="58" show="3951250184" value="eb834b08"/>
-          <field name="tcp.options.timestamp.tsecr" showname="Timestamp echo reply: 3901546187" size="4" pos="62" show="3901546187" value="e88cdecb"/>
-        </field>
-      </field>
-      <field name="tcp.analysis" showname="SEQ/ACK analysis" size="0" pos="34" show="" value="">
-        <field name="tcp.analysis.bytes_in_flight" showname="Bytes in flight: 34" size="0" pos="34" show="34"/>
-        <field name="tcp.analysis.push_bytes_sent" showname="Bytes sent since last PSH flag: 34" size="0" pos="34" show="34"/>
-      </field>
-      <field name="tcp.payload" showname="TCP payload (34 bytes)" size="34" pos="66" show="45:48:4c:4f:20:63:6c:69:65:6e:74:2d:31:38:30:30:30:30:30:33:2e:65:78:61:6d:70:6c:65:2e:69:6e:74:0d:0a" value="45484c4f20636c69656e742d31383030303030332e6578616d706c652e696e740d0a"/>
-    </proto>
-    <proto name="smtp" showname="Simple Mail Transfer Protocol" size="34" pos="66">
-      <field name="smtp.req" showname="Request: True" hide="yes" size="0" pos="66" show="1"/>
-      <field name="smtp.command_line" showname="Command Line: EHLO client-18000003.example.int\\r\\n" size="34" pos="66" show="EHLO client-18000003.example.int\\xd\\xa" value="45484c4f20636c69656e742d31383030303030332e6578616d706c652e696e740d0a">
-        <field name="smtp.req.command" showname="Command: EHLO" size="4" pos="66" show="EHLO" value="45484c4f"/>
-        <field name="smtp.req.parameter" showname="Request parameter: client-18000003.example.int" size="27" pos="71" show="client-18000003.example.int" value="636c69656e742d31383030303030332e6578616d706c652e696e74"/>
-      </field>
-    </proto>
-  </packet>
+  describe('getStatus()', () => {
+    it('should return an Observable<PcapStatusResponse>', inject(
+      [PcapService, XHRBackend],
+      (pcapService, mockBackend) => {
+        const responseMock: PcapStatusResponse = fakePcapStatusResponse;
+        let response;
+
+        mockBackend.connections.subscribe(connection => {
+          expect(connection.request.url).toMatch(
+            /\/api\/v1\/pcap\/job_1234567890123_4567/
+          );
+          connection.mockRespond(
+            new Response(new ResponseOptions({ body: responseMock }))
+          );
+        });
 
+        pcapService.getStatus(jobId).subscribe(r => (response = r));
+        expect(response).toBeTruthy();
+      }
+    ));
+  });
 
-  </pdml>`
-}
\ No newline at end of file
+  describe('getDownloadUrl()', () => {
+    it('should return a url with the correct page to download the pdml', inject(
+      [PcapService],
+      pcapService => {
+        expect(pcapService.getDownloadUrl(jobId, 2)).toBe(
+          `/api/v1/pcap/job_1234567890123_4567/raw?page=2`
+        );
+      }
+    ));
+  });
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
index 0bbcef2..6418ad0 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
@@ -42,10 +42,9 @@ export class PcapService {
     }
 
     public submitRequest(pcapRequest: PcapRequest): Observable<PcapStatusResponse> {
-      return this.http.post('/api/v1/pcap/fixed', pcapRequest, new RequestOptions({headers: new Headers(this.defaultHeaders)}))
-          .map(result => result.json() as PcapStatusResponse)
-          .catch(HttpUtil.handleError)
-          .onErrorResumeNext();
+        return this.http.post('/api/v1/pcap/fixed', pcapRequest, new RequestOptions({headers: new Headers(this.defaultHeaders)}))
+                .map(HttpUtil.extractData)
+                .catch(HttpUtil.handleError);
     }
 
     public getStatus(id: string): Observable<PcapStatusResponse> {

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/environments/environment.e2e.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/environments/environment.e2e.ts b/metron-interface/metron-alerts/src/environments/environment.e2e.ts
index ebede73..9a11443 100644
--- a/metron-interface/metron-alerts/src/environments/environment.e2e.ts
+++ b/metron-interface/metron-alerts/src/environments/environment.e2e.ts
@@ -18,5 +18,6 @@
 export const environment = {
   production: false,
   indices: 'alerts_ui_e2e,metaalert',
-  defaultPollingState: true
+  defaultPollingState: true,
+  noTransition: true
 };

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/environments/environment.prod.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/environments/environment.prod.ts b/metron-interface/metron-alerts/src/environments/environment.prod.ts
index 5f082a8..0cd6fb3 100644
--- a/metron-interface/metron-alerts/src/environments/environment.prod.ts
+++ b/metron-interface/metron-alerts/src/environments/environment.prod.ts
@@ -18,5 +18,6 @@
 export const environment = {
   production: true,
   indices: null,
-  defaultPollingState: false
+  defaultPollingState: false,
+  noTransition: false
 };

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/environments/environment.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/environments/environment.ts b/metron-interface/metron-alerts/src/environments/environment.ts
index b079ab8..bd79959 100644
--- a/metron-interface/metron-alerts/src/environments/environment.ts
+++ b/metron-interface/metron-alerts/src/environments/environment.ts
@@ -23,5 +23,6 @@
 export const environment = {
   production: false,
   indices: null,
-  defaultPollingState: false
+  defaultPollingState: false,
+  noTransition: false
 };

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/index.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/index.html b/metron-interface/metron-alerts/src/index.html
index e63e9bd..e051e27 100644
--- a/metron-interface/metron-alerts/src/index.html
+++ b/metron-interface/metron-alerts/src/index.html
@@ -22,7 +22,7 @@
   <link rel="icon" type="image/x-icon" href="favicon.ico">
   <link rel="stylesheet" type="text/css" href="assets/fonts/font.css">
 </head>
-<body class="notransition">
+<body class="">
   <metron-alerts-root>Loading...</metron-alerts-root>
 </body>
 </html>


[47/50] [abbrv] metron git commit: METRON-1743 CEF testPaloAltoCEF test using a confusing variable name (JonZeolla via justinleet) closes apache/metron#1173

Posted by ni...@apache.org.
METRON-1743 CEF testPaloAltoCEF test using a confusing variable name (JonZeolla via justinleet) closes apache/metron#1173


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/29cbdcc4
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/29cbdcc4
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/29cbdcc4

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 29cbdcc43c66080e1464e4c9c365c63a3f9035b1
Parents: 09826da
Author: JonZeolla <ze...@gmail.com>
Authored: Wed Aug 29 09:43:43 2018 -0400
Committer: leet <le...@apache.org>
Committed: Wed Aug 29 09:43:43 2018 -0400

----------------------------------------------------------------------
 .../test/java/org/apache/metron/parsers/cef/CEFParserTest.java   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/29cbdcc4/metron-platform/metron-parsers/src/test/java/org/apache/metron/parsers/cef/CEFParserTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-parsers/src/test/java/org/apache/metron/parsers/cef/CEFParserTest.java b/metron-platform/metron-parsers/src/test/java/org/apache/metron/parsers/cef/CEFParserTest.java
index a1fe2e8..f679c91 100644
--- a/metron-platform/metron-parsers/src/test/java/org/apache/metron/parsers/cef/CEFParserTest.java
+++ b/metron-platform/metron-parsers/src/test/java/org/apache/metron/parsers/cef/CEFParserTest.java
@@ -196,8 +196,8 @@ public class CEFParserTest {
 
 	@Test
 	public void testPaloAltoCEF() throws Exception {
-		URL waf_url = Resources.getResource(getClass(), "palo.cef");
-		runTest("palo", Resources.readLines(waf_url, UTF_8),
+		URL palo_url = Resources.getResource(getClass(), "palo.cef");
+		runTest("palo", Resources.readLines(palo_url, UTF_8),
 				Resources.toString(Resources.getResource(getClass(), "palo.schema"), UTF_8));
 	}
 


[45/50] [abbrv] metron git commit: METRON-1752 Prevent package.lock from changing during build (sardell via merrimanr) closes apache/metron#1177

Posted by ni...@apache.org.
http://git-wip-us.apache.org/repos/asf/metron/blob/09826da8/metron-interface/metron-config/package-lock.json
----------------------------------------------------------------------
diff --git a/metron-interface/metron-config/package-lock.json b/metron-interface/metron-config/package-lock.json
new file mode 100644
index 0000000..e49bc4a
--- /dev/null
+++ b/metron-interface/metron-config/package-lock.json
@@ -0,0 +1,17734 @@
+{
+  "name": "metron-management-ui",
+  "version": "0.5.1",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@angular-cli/ast-tools": {
+      "version": "1.0.16",
+      "resolved": "https://registry.npmjs.org/@angular-cli/ast-tools/-/ast-tools-1.0.16.tgz",
+      "integrity": "sha1-YxmULBol+4TjKUID6fejJmMvzlA=",
+      "dev": true,
+      "requires": {
+        "@angular/tsc-wrapped": "0.5.2",
+        "denodeify": "1.2.1",
+        "rxjs": "5.1.0",
+        "typescript": "2.0.10"
+      },
+      "dependencies": {
+        "@angular/tsc-wrapped": {
+          "version": "0.5.2",
+          "resolved": "https://registry.npmjs.org/@angular/tsc-wrapped/-/tsc-wrapped-0.5.2.tgz",
+          "integrity": "sha1-Lt30csRn/LM06pTe3aqnGZDFpII=",
+          "dev": true,
+          "requires": {
+            "tsickle": "0.2.5"
+          }
+        },
+        "denodeify": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz",
+          "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=",
+          "dev": true
+        },
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        },
+        "mkdirp": {
+          "version": "0.5.1",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+          "dev": true,
+          "requires": {
+            "minimist": "0.0.8"
+          },
+          "dependencies": {
+            "minimist": {
+              "version": "0.0.8",
+              "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+              "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+              "dev": true
+            }
+          }
+        },
+        "rxjs": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.1.0.tgz",
+          "integrity": "sha1-CqkBi39EC1BfpCvXQrZzi+VQ5yA=",
+          "dev": true,
+          "requires": {
+            "symbol-observable": "1.0.4"
+          }
+        },
+        "source-map": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+          "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
+          "dev": true
+        },
+        "source-map-support": {
+          "version": "0.4.11",
+          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.11.tgz",
+          "integrity": "sha1-ZH+TmXizhTWQlTCIUwPa8jJ58yI=",
+          "dev": true,
+          "requires": {
+            "source-map": "0.5.6"
+          }
+        },
+        "symbol-observable": {
+          "version": "1.0.4",
+          "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz",
+          "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=",
+          "dev": true
+        },
+        "tsickle": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.2.5.tgz",
+          "integrity": "sha1-YNjhJGLm+PvayS1fX+rSv0kIXYI=",
+          "dev": true,
+          "requires": {
+            "minimist": "1.2.0",
+            "mkdirp": "0.5.1",
+            "source-map": "0.5.6",
+            "source-map-support": "0.4.11"
+          }
+        },
+        "typescript": {
+          "version": "2.0.10",
+          "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.0.10.tgz",
+          "integrity": "sha1-zN1O2G/VVQpAcQGggUAS4bP6w90=",
+          "dev": true
+        }
+      }
+    },
+    "@angular-cli/base-href-webpack": {
+      "version": "1.0.16",
+      "resolved": "https://registry.npmjs.org/@angular-cli/base-href-webpack/-/base-href-webpack-1.0.16.tgz",
+      "integrity": "sha1-Qpai/324TdwuZ8KhB+J29yRj/40=",
+      "dev": true
+    },
+    "@angular/common": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@angular/common/-/common-2.0.0.tgz",
+      "integrity": "sha1-bVs4zvi+xpcmWQ85XaEMLYkUYkc="
+    },
+    "@angular/compiler": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-2.0.0.tgz",
+      "integrity": "sha1-ck58aKwXeqkzqnWmtMsR5ap5XSw="
+    },
+    "@angular/compiler-cli": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-0.6.4.tgz",
+      "integrity": "sha1-o093G6Ss5g7+elT953ldZGXE0B8=",
+      "dev": true,
+      "requires": {
+        "@angular/tsc-wrapped": "0.3.0",
+        "minimist": "1.2.0",
+        "parse5": "2.2.3",
+        "reflect-metadata": "0.1.12"
+      }
+    },
+    "@angular/core": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@angular/core/-/core-2.0.0.tgz",
+      "integrity": "sha1-h7XqSnrgY9J7zyMypKqTLYh6vis="
+    },
+    "@angular/forms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-2.0.0.tgz",
+      "integrity": "sha1-T0ciI/Ya7EnydXXvSbDWysO+HjE="
+    },
+    "@angular/http": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@angular/http/-/http-2.0.0.tgz",
+      "integrity": "sha1-7jIGzRaDWB3Vfau6k8DiOvGbRzI="
+    },
+    "@angular/platform-browser": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-2.0.0.tgz",
+      "integrity": "sha1-f4YDvkJRHTEz6ViIobXFEy/InEY="
+    },
+    "@angular/platform-browser-dynamic": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-2.0.0.tgz",
+      "integrity": "sha1-LuPyuwi0hkkCxbfCg/d4Y8idhxU="
+    },
+    "@angular/platform-server": {
+      "version": "2.4.10",
+      "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-2.4.10.tgz",
+      "integrity": "sha1-7eAwdQgfgH7YPdjskvKSSFj7NFU=",
+      "dev": true,
+      "requires": {
+        "parse5": "2.2.3"
+      }
+    },
+    "@angular/router": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@angular/router/-/router-3.0.0.tgz",
+      "integrity": "sha1-zfkbOl2FrTmv9P2IJFXJuMMSnNk="
+    },
+    "@angular/tsc-wrapped": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@angular/tsc-wrapped/-/tsc-wrapped-0.3.0.tgz",
+      "integrity": "sha1-mM3rXDjRRbGHwK0Dl6jZiyF/M/I=",
+      "dev": true,
+      "requires": {
+        "tsickle": "0.1.7"
+      }
+    },
+    "@types/ace": {
+      "version": "0.0.32",
+      "resolved": "https://registry.npmjs.org/@types/ace/-/ace-0.0.32.tgz",
+      "integrity": "sha1-1YSBOWBgdUDdsgLeiz4ilQTUXP8="
+    },
+    "@types/bootstrap": {
+      "version": "3.3.39",
+      "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-3.3.39.tgz",
+      "integrity": "sha512-UGvzSQFkv0Oh2vjj30AfZructi7XvY0aRa1Y/vrgFq+tfrTMxtqQ9+s5liCYLJnrISc9LinEtOY5N8Ibrhj2Tg==",
+      "requires": {
+        "@types/jquery": "2.0.49"
+      }
+    },
+    "@types/form-data": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
+      "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
+      "dev": true,
+      "requires": {
+        "@types/node": "10.9.2"
+      }
+    },
+    "@types/jasmine": {
+      "version": "2.2.30",
+      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.2.30.tgz",
+      "integrity": "sha1-dUvjklVHlzGthBR4vSl3LXHPWZY="
+    },
+    "@types/jquery": {
+      "version": "2.0.49",
+      "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-2.0.49.tgz",
+      "integrity": "sha512-/9xLnYmohN/vD2gDnLS4cym8TUmrJu7DvZa/LELKzZjdPsvWVJiedsdu2SXNtb/DA7FGimqL2g0IoyhbNKLl8g=="
+    },
+    "@types/node": {
+      "version": "10.9.2",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.9.2.tgz",
+      "integrity": "sha512-pwZnkVyCGJ3LsQ0/3flQK5lCFao4esIzwUVzzk5NvL9vnkEyDhNf4fhHzUMHvyr56gNZywWTS2MR0euabMSz4A==",
+      "dev": true
+    },
+    "@types/request": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@types/request/-/request-2.0.3.tgz",
+      "integrity": "sha512-cIvnyFRARxwE4OHpCyYue7H+SxaKFPpeleRCHJicft8QhyTNbVYsMwjvEzEPqG06D2LGHZ+sN5lXc8+bTu6D8A==",
+      "dev": true,
+      "requires": {
+        "@types/form-data": "2.2.1",
+        "@types/node": "10.9.2"
+      }
+    },
+    "@types/tether": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/@types/tether/-/tether-1.4.4.tgz",
+      "integrity": "sha512-6qhsFJVMuMqaQRVyQVi3zUBLfKYyryktL0ZP0Z3zegzeQ7WKm0PZNCdl3JsaitJbzqaoQ9qsFKMfaj5MiMfcSQ=="
+    },
+    "abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+      "dev": true
+    },
+    "accepts": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
+      "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
+      "dev": true,
+      "requires": {
+        "mime-types": "2.1.20",
+        "negotiator": "0.6.1"
+      }
+    },
+    "ace-builds": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.1.tgz",
+      "integrity": "sha512-ulsfTOi/oAXBIt9hYlK4asyFZYsFOOUAbwBZidbjWWJycRX4LtwIithMyndcnoQ4PavkZ0iGJ+c9nqHNiYbubA=="
+    },
+    "acorn": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz",
+      "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==",
+      "dev": true
+    },
+    "adm-zip": {
+      "version": "0.4.7",
+      "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz",
+      "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=",
+      "dev": true
+    },
+    "after": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/after/-/after-0.8.1.tgz",
+      "integrity": "sha1-q11PuIP1loFtNRX495HAr0ht1ic=",
+      "dev": true
+    },
+    "agent-base": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz",
+      "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=",
+      "dev": true,
+      "requires": {
+        "extend": "3.0.2",
+        "semver": "5.0.3"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.0.3",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz",
+          "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=",
+          "dev": true
+        }
+      }
+    },
+    "ajv": {
+      "version": "5.5.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+      "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+      "dev": true,
+      "requires": {
+        "co": "4.6.0",
+        "fast-deep-equal": "1.1.0",
+        "fast-json-stable-stringify": "2.0.0",
+        "json-schema-traverse": "0.3.1"
+      }
+    },
+    "align-text": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+      "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+      "dev": true,
+      "requires": {
+        "kind-of": "3.2.2",
+        "longest": "1.0.1",
+        "repeat-string": "1.6.1"
+      }
+    },
+    "alphanum-sort": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
+      "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
+      "dev": true
+    },
+    "alter": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz",
+      "integrity": "sha1-x1iICGF1cgNKrmJICvJrHU0cs80=",
+      "dev": true,
+      "requires": {
+        "stable": "0.1.8"
+      }
+    },
+    "amd-name-resolver": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/amd-name-resolver/-/amd-name-resolver-0.0.5.tgz",
+      "integrity": "sha1-dpYtrIdu0zEbBdKcaljBTh7zMEs=",
+      "dev": true,
+      "requires": {
+        "ensure-posix-path": "1.0.2"
+      }
+    },
+    "amdefine": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+      "dev": true
+    },
+    "angular-cli": {
+      "version": "1.0.0-beta.15",
+      "resolved": "https://registry.npmjs.org/angular-cli/-/angular-cli-1.0.0-beta.15.tgz",
+      "integrity": "sha1-tK+R6aOnDp6VKGRiJroC7/ugFG8=",
+      "dev": true,
+      "requires": {
+        "@angular-cli/ast-tools": "1.0.16",
+        "@angular-cli/base-href-webpack": "1.0.16",
+        "@angular/common": "2.0.0",
+        "@angular/compiler": "2.0.0",
+        "@angular/compiler-cli": "0.6.4",
+        "@angular/core": "2.0.0",
+        "@angular/platform-browser": "2.0.0",
+        "@angular/platform-server": "2.4.10",
+        "@angular/tsc-wrapped": "0.3.0",
+        "angular2-template-loader": "0.5.0",
+        "awesome-typescript-loader": "2.2.4",
+        "chalk": "1.1.3",
+        "common-tags": "1.8.0",
+        "compression-webpack-plugin": "0.3.2",
+        "copy-webpack-plugin": "3.0.1",
+        "core-js": "2.5.7",
+        "css-loader": "0.23.1",
+        "denodeify": "1.2.1",
+        "ember-cli": "2.5.0",
+        "ember-cli-string-utils": "1.1.0",
+        "enhanced-resolve": "2.3.0",
+        "exit": "0.1.2",
+        "exports-loader": "0.6.4",
+        "expose-loader": "0.7.5",
+        "file-loader": "0.8.5",
+        "fs-extra": "0.30.0",
+        "fs.realpath": "1.0.0",
+        "glob": "7.1.3",
+        "handlebars": "4.0.11",
+        "html-webpack-plugin": "2.30.1",
+        "istanbul-instrumenter-loader": "0.2.0",
+        "json-loader": "0.5.7",
+        "karma-sourcemap-loader": "0.3.7",
+        "karma-webpack": "1.8.1",
+        "leek": "0.0.21",
+        "less": "2.7.3",
+        "less-loader": "2.2.3",
+        "lodash": "4.17.10",
+        "node-sass": "3.13.1",
+        "npm-run-all": "3.1.2",
+        "offline-plugin": "3.4.2",
+        "opn": "4.0.1",
+        "parse5": "2.2.3",
+        "postcss-loader": "0.9.1",
+        "protractor": "3.3.0",
+        "raw-loader": "0.5.1",
+        "remap-istanbul": "0.6.4",
+        "resolve": "1.8.1",
+        "rimraf": "2.6.2",
+        "rxjs": "5.0.0-beta.12",
+        "sass-loader": "3.2.3",
+        "script-loader": "0.7.2",
+        "shelljs": "0.7.8",
+        "silent-error": "1.1.0",
+        "source-map-loader": "0.1.6",
+        "sourcemap-istanbul-instrumenter-loader": "0.2.0",
+        "string-replace-loader": "1.3.0",
+        "style-loader": "0.13.2",
+        "stylus": "0.54.5",
+        "stylus-loader": "2.5.1",
+        "symlink-or-copy": "1.2.0",
+        "ts-loader": "0.8.2",
+        "tslint": "3.15.1",
+        "tslint-loader": "2.1.5",
+        "typedoc": "0.4.5",
+        "typescript": "2.0.2",
+        "url-loader": "0.5.9",
+        "webpack": "2.1.0-beta.22",
+        "webpack-dev-server": "2.1.0-beta.3",
+        "webpack-md5-hash": "0.0.5",
+        "webpack-merge": "0.14.1",
+        "zone.js": "0.6.26"
+      },
+      "dependencies": {
+        "assert-plus": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
+          "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
+          "dev": true
+        },
+        "async": {
+          "version": "2.6.1",
+          "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+          "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+          "dev": true,
+          "requires": {
+            "lodash": "4.17.10"
+          }
+        },
+        "aws-sign2": {
+          "version": "0.6.0",
+          "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
+          "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
+          "dev": true
+        },
+        "caseless": {
+          "version": "0.11.0",
+          "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
+          "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=",
+          "dev": true
+        },
+        "findup-sync": {
+          "version": "0.3.0",
+          "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
+          "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
+          "dev": true,
+          "requires": {
+            "glob": "5.0.15"
+          },
+          "dependencies": {
+            "glob": {
+              "version": "5.0.15",
+              "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+              "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+              "dev": true,
+              "requires": {
+                "inflight": "1.0.6",
+                "inherits": "2.0.3",
+                "minimatch": "3.0.4",
+                "once": "1.4.0",
+                "path-is-absolute": "1.0.1"
+              }
+            }
+          }
+        },
+        "form-data": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz",
+          "integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=",
+          "dev": true,
+          "requires": {
+            "async": "2.6.1",
+            "combined-stream": "1.0.6",
+            "mime-types": "2.1.20"
+          }
+        },
+        "har-validator": {
+          "version": "2.0.6",
+          "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
+          "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
+          "dev": true,
+          "requires": {
+            "chalk": "1.1.3",
+            "commander": "2.17.1",
+            "is-my-json-valid": "2.19.0",
+            "pinkie-promise": "2.0.1"
+          }
+        },
+        "http-signature": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
+          "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
+          "dev": true,
+          "requires": {
+            "assert-plus": "0.2.0",
+            "jsprim": "1.4.1",
+            "sshpk": "1.14.2"
+          }
+        },
+        "node-uuid": {
+          "version": "1.4.8",
+          "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
+          "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
+          "dev": true
+        },
+        "oauth-sign": {
+          "version": "0.8.2",
+          "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+          "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
+          "dev": true
+        },
+        "protractor": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/protractor/-/protractor-3.3.0.tgz",
+          "integrity": "sha1-f0RoMGrCmjFQhtr2SitKFcC8Exc=",
+          "dev": true,
+          "requires": {
+            "adm-zip": "0.4.7",
+            "chalk": "1.1.3",
+            "glob": "6.0.4",
+            "jasmine": "2.4.1",
+            "jasminewd2": "0.0.9",
+            "optimist": "0.6.1",
+            "q": "1.4.1",
+            "request": "2.67.0",
+            "saucelabs": "1.0.1",
+            "selenium-webdriver": "2.52.0",
+            "source-map-support": "0.4.18"
+          },
+          "dependencies": {
+            "glob": {
+              "version": "6.0.4",
+              "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
+              "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
+              "dev": true,
+              "requires": {
+                "inflight": "1.0.6",
+                "inherits": "2.0.3",
+                "minimatch": "3.0.4",
+                "once": "1.4.0",
+                "path-is-absolute": "1.0.1"
+              }
+            }
+          }
+        },
+        "q": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
+          "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
+          "dev": true
+        },
+        "qs": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.1.tgz",
+          "integrity": "sha1-gB/uAw4LlFDWOFrcSKTMVbRK7fw=",
+          "dev": true
+        },
+        "request": {
+          "version": "2.67.0",
+          "resolved": "https://registry.npmjs.org/request/-/request-2.67.0.tgz",
+          "integrity": "sha1-ivdHgOK/EeoK6aqWXBHxGv0nJ0I=",
+          "dev": true,
+          "requires": {
+            "aws-sign2": "0.6.0",
+            "bl": "1.0.3",
+            "caseless": "0.11.0",
+            "combined-stream": "1.0.6",
+            "extend": "3.0.2",
+            "forever-agent": "0.6.1",
+            "form-data": "1.0.1",
+            "har-validator": "2.0.6",
+            "hawk": "3.1.3",
+            "http-signature": "1.1.1",
+            "is-typedarray": "1.0.0",
+            "isstream": "0.1.2",
+            "json-stringify-safe": "5.0.1",
+            "mime-types": "2.1.20",
+            "node-uuid": "1.4.8",
+            "oauth-sign": "0.8.2",
+            "qs": "5.2.1",
+            "stringstream": "0.0.6",
+            "tough-cookie": "2.2.2",
+            "tunnel-agent": "0.4.3"
+          }
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        },
+        "source-map-support": {
+          "version": "0.4.18",
+          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+          "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+          "dev": true,
+          "requires": {
+            "source-map": "0.5.7"
+          }
+        },
+        "tough-cookie": {
+          "version": "2.2.2",
+          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz",
+          "integrity": "sha1-yDoYMPTl7wuT7yo0iOck+N4Basc=",
+          "dev": true
+        },
+        "tslint": {
+          "version": "3.15.1",
+          "resolved": "https://registry.npmjs.org/tslint/-/tslint-3.15.1.tgz",
+          "integrity": "sha1-2hZcqT2P3CwIa1EWXuG6y0jJjqU=",
+          "dev": true,
+          "requires": {
+            "colors": "1.3.2",
+            "diff": "2.2.3",
+            "findup-sync": "0.3.0",
+            "glob": "7.1.3",
+            "optimist": "0.6.1",
+            "resolve": "1.8.1",
+            "underscore.string": "3.3.4"
+          }
+        },
+        "tunnel-agent": {
+          "version": "0.4.3",
+          "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
+          "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=",
+          "dev": true
+        },
+        "typescript": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.0.2.tgz",
+          "integrity": "sha1-pj+EgHRJik36fyk6/aCDXVAdlUA=",
+          "dev": true
+        },
+        "underscore.string": {
+          "version": "3.3.4",
+          "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz",
+          "integrity": "sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s=",
+          "dev": true,
+          "requires": {
+            "sprintf-js": "1.0.3",
+            "util-deprecate": "1.0.2"
+          }
+        }
+      }
+    },
+    "angular2-template-loader": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/angular2-template-loader/-/angular2-template-loader-0.5.0.tgz",
+      "integrity": "sha1-oW2xkPqvn46OlBDzxGg3DedRJ6Y=",
+      "dev": true,
+      "requires": {
+        "codecov": "1.0.1",
+        "loader-utils": "0.2.17"
+      }
+    },
+    "ansi-escapes": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
+      "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=",
+      "dev": true
+    },
+    "ansi-gray": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+      "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
+      "dev": true,
+      "requires": {
+        "ansi-wrap": "0.1.0"
+      }
+    },
+    "ansi-green": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-green/-/ansi-green-0.1.1.tgz",
+      "integrity": "sha1-il2al55FjVfEDjNYCzc5C44Q0Pc=",
+      "dev": true,
+      "requires": {
+        "ansi-wrap": "0.1.0"
+      }
+    },
+    "ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+      "dev": true
+    },
+    "ansi-wrap": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+      "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
+      "dev": true
+    },
+    "ansicolors": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz",
+      "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=",
+      "dev": true
+    },
+    "ansistyles": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz",
+      "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=",
+      "dev": true
+    },
+    "any-promise": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+      "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=",
+      "dev": true
+    },
+    "anymatch": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
+      "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
+      "dev": true,
+      "requires": {
+        "micromatch": "2.3.11",
+        "normalize-path": "2.1.1"
+      }
+    },
+    "aproba": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+      "dev": true
+    },
+    "are-we-there-yet": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+      "dev": true,
+      "requires": {
+        "delegates": "1.0.0",
+        "readable-stream": "2.3.6"
+      }
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "1.0.3"
+      }
+    },
+    "argv": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz",
+      "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=",
+      "dev": true
+    },
+    "arr-diff": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+      "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+      "dev": true,
+      "requires": {
+        "arr-flatten": "1.1.0"
+      }
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+      "dev": true
+    },
+    "arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+      "dev": true
+    },
+    "array-differ": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz",
+      "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=",
+      "dev": true
+    },
+    "array-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+      "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+      "dev": true
+    },
+    "array-filter": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
+      "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
+      "dev": true
+    },
+    "array-find-index": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+      "dev": true
+    },
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
+      "dev": true
+    },
+    "array-map": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
+      "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
+      "dev": true
+    },
+    "array-reduce": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
+      "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
+      "dev": true
+    },
+    "array-slice": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
+      "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
+      "dev": true
+    },
+    "array-to-error": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-to-error/-/array-to-error-1.1.1.tgz",
+      "integrity": "sha1-1ogSkm0UCXogVXmmZ+6vGFakTAc=",
+      "dev": true,
+      "requires": {
+        "array-to-sentence": "1.1.0"
+      }
+    },
+    "array-to-sentence": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/array-to-sentence/-/array-to-sentence-1.1.0.tgz",
+      "integrity": "sha1-yASVba+lMjJJWyBalFJ1OiWNOfw=",
+      "dev": true
+    },
+    "array-union": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+      "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+      "dev": true,
+      "requires": {
+        "array-uniq": "1.0.3"
+      }
+    },
+    "array-uniq": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+      "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+      "dev": true
+    },
+    "array-unique": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+      "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+      "dev": true
+    },
+    "arraybuffer.slice": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz",
+      "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=",
+      "dev": true
+    },
+    "arrify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+      "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+      "dev": true
+    },
+    "asap": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+      "dev": true
+    },
+    "asn1": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "dev": true,
+      "requires": {
+        "safer-buffer": "2.1.2"
+      }
+    },
+    "asn1.js": {
+      "version": "4.10.1",
+      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+      "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+      "dev": true,
+      "requires": {
+        "bn.js": "4.11.8",
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.1"
+      }
+    },
+    "assert": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+      "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+      "dev": true,
+      "requires": {
+        "util": "0.10.3"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+          "dev": true
+        },
+        "util": {
+          "version": "0.10.3",
+          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+          "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+          "dev": true,
+          "requires": {
+            "inherits": "2.0.1"
+          }
+        }
+      }
+    },
+    "assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "dev": true
+    },
+    "assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+      "dev": true
+    },
+    "ast-traverse": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz",
+      "integrity": "sha1-ac8rg4bxnc2hux4F1o/jWdiJfeY=",
+      "dev": true
+    },
+    "ast-types": {
+      "version": "0.9.6",
+      "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz",
+      "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=",
+      "dev": true
+    },
+    "async": {
+      "version": "0.2.10",
+      "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+      "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
+      "dev": true
+    },
+    "async-array-reduce": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/async-array-reduce/-/async-array-reduce-0.2.1.tgz",
+      "integrity": "sha1-yL4BCitc0A3qlsgRFgNGk9/dgtE=",
+      "dev": true
+    },
+    "async-disk-cache": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/async-disk-cache/-/async-disk-cache-1.3.3.tgz",
+      "integrity": "sha512-GyaWSbDAZCltxSobtj1m1ptXa0+zSdjWs3sM4IqnvhoRwMDHW5786sXQ1RiXbR3ZGuQe6NXMB4N0vUmW163cew==",
+      "dev": true,
+      "requires": {
+        "debug": "2.6.9",
+        "heimdalljs": "0.2.6",
+        "istextorbinary": "2.1.0",
+        "mkdirp": "0.5.1",
+        "rimraf": "2.6.2",
+        "rsvp": "3.6.2",
+        "username-sync": "1.0.1"
+      }
+    },
+    "async-each": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
+      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
+      "dev": true
+    },
+    "async-foreach": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
+      "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
+      "dev": true
+    },
+    "async-promise-queue": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/async-promise-queue/-/async-promise-queue-1.0.4.tgz",
+      "integrity": "sha512-GQ5X3DT+TefYuFPHdvIPXFTlKnh39U7dwtl+aUBGeKjMea9nBpv3c91DXgeyBQmY07vQ97f3Sr9XHqkamEameQ==",
+      "dev": true,
+      "requires": {
+        "async": "2.6.1",
+        "debug": "2.6.9"
+      },
+      "dependencies": {
+        "async": {
+          "version": "2.6.1",
+          "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+          "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+          "dev": true,
+          "requires": {
+            "lodash": "4.17.10"
+          }
+        }
+      }
+    },
+    "async-some": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/async-some/-/async-some-1.0.2.tgz",
+      "integrity": "sha1-TYqBYg1ZWHkbW5j4AtMgd3bpVQk=",
+      "dev": true,
+      "requires": {
+        "dezalgo": "1.0.3"
+      }
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "dev": true
+    },
+    "atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+      "dev": true
+    },
+    "autoprefixer": {
+      "version": "6.7.7",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz",
+      "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=",
+      "dev": true,
+      "requires": {
+        "browserslist": "1.7.7",
+        "caniuse-db": "1.0.30000878",
+        "normalize-range": "0.1.2",
+        "num2fraction": "1.2.2",
+        "postcss": "5.2.18",
+        "postcss-value-parser": "3.3.0"
+      }
+    },
+    "awesome-typescript-loader": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-2.2.4.tgz",
+      "integrity": "sha1-QYXWDANcJVFfnCp0f6X2myoAHp4=",
+      "dev": true,
+      "requires": {
+        "colors": "1.3.2",
+        "enhanced-resolve": "2.3.0",
+        "loader-utils": "0.2.17",
+        "lodash": "4.17.10",
+        "object-assign": "4.1.1",
+        "source-map-support": "0.4.18"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        },
+        "source-map-support": {
+          "version": "0.4.18",
+          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+          "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+          "dev": true,
+          "requires": {
+            "source-map": "0.5.7"
+          }
+        }
+      }
+    },
+    "aws-sign2": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+      "dev": true
+    },
+    "aws4": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+      "dev": true
+    },
+    "babel-core": {
+      "version": "5.8.38",
+      "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-5.8.38.tgz",
+      "integrity": "sha1-H8ruedfmG3ULALjlT238nQr4ZVg=",
+      "dev": true,
+      "requires": {
+        "babel-plugin-constant-folding": "1.0.1",
+        "babel-plugin-dead-code-elimination": "1.0.2",
+        "babel-plugin-eval": "1.0.1",
+        "babel-plugin-inline-environment-variables": "1.0.1",
+        "babel-plugin-jscript": "1.0.4",
+        "babel-plugin-member-expression-literals": "1.0.1",
+        "babel-plugin-property-literals": "1.0.1",
+        "babel-plugin-proto-to-assign": "1.0.4",
+        "babel-plugin-react-constant-elements": "1.0.3",
+        "babel-plugin-react-display-name": "1.0.3",
+        "babel-plugin-remove-console": "1.0.1",
+        "babel-plugin-remove-debugger": "1.0.1",
+        "babel-plugin-runtime": "1.0.7",
+        "babel-plugin-undeclared-variables-check": "1.0.2",
+        "babel-plugin-undefined-to-void": "1.1.6",
+        "babylon": "5.8.38",
+        "bluebird": "2.11.0",
+        "chalk": "1.1.3",
+        "convert-source-map": "1.5.1",
+        "core-js": "1.2.7",
+        "debug": "2.6.9",
+        "detect-indent": "3.0.1",
+        "esutils": "2.0.2",
+        "fs-readdir-recursive": "0.1.2",
+        "globals": "6.4.1",
+        "home-or-tmp": "1.0.0",
+        "is-integer": "1.0.7",
+        "js-tokens": "1.0.1",
+        "json5": "0.4.0",
+        "lodash": "3.10.1",
+        "minimatch": "2.0.10",
+        "output-file-sync": "1.1.2",
+        "path-exists": "1.0.0",
+        "path-is-absolute": "1.0.1",
+        "private": "0.1.8",
+        "regenerator": "0.8.40",
+        "regexpu": "1.3.0",
+        "repeating": "1.1.3",
+        "resolve": "1.8.1",
+        "shebang-regex": "1.0.0",
+        "slash": "1.0.0",
+        "source-map": "0.5.7",
+        "source-map-support": "0.2.10",
+        "to-fast-properties": "1.0.3",
+        "trim-right": "1.0.1",
+        "try-resolve": "1.0.1"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "1.2.7",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+          "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
+          "dev": true
+        },
+        "json5": {
+          "version": "0.4.0",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz",
+          "integrity": "sha1-BUNS5MTIDIbAkjh31EneF2pzLI0=",
+          "dev": true
+        },
+        "lodash": {
+          "version": "3.10.1",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+          "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
+          "dev": true
+        },
+        "minimatch": {
+          "version": "2.0.10",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz",
+          "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "1.1.11"
+          }
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        },
+        "source-map-support": {
+          "version": "0.2.10",
+          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.2.10.tgz",
+          "integrity": "sha1-6lo5AKHByyUJagrozFwrSxDe09w=",
+          "dev": true,
+          "requires": {
+            "source-map": "0.1.32"
+          },
+          "dependencies": {
+            "source-map": {
+              "version": "0.1.32",
+              "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz",
+              "integrity": "sha1-yLbBZ3l7pHQKjqMyUhYv8IWRsmY=",
+              "dev": true,
+              "requires": {
+                "amdefine": "1.0.1"
+              }
+            }
+          }
+        }
+      }
+    },
+    "babel-plugin-constant-folding": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz",
+      "integrity": "sha1-g2HTZMmORJw2kr26Ue/whEKQqo4=",
+      "dev": true
+    },
+    "babel-plugin-dead-code-elimination": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz",
+      "integrity": "sha1-X3xFEnTc18zNv7s+C4XdKBIfD2U=",
+      "dev": true
+    },
+    "babel-plugin-eval": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz",
+      "integrity": "sha1-ovrtJc5r5preS/7CY/cBaRlZUNo=",
+      "dev": true
+    },
+    "babel-plugin-inline-environment-variables": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz",
+      "integrity": "sha1-H1jOkSB61qgmqL9kX6/mj/X+P/4=",
+      "dev": true
+    },
+    "babel-plugin-jscript": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz",
+      "integrity": "sha1-jzQsOCduh6R9X6CovT1etsytj8w=",
+      "dev": true
+    },
+    "babel-plugin-member-expression-literals": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz",
+      "integrity": "sha1-zF7bD6qNyScXDnTW0cAkQAIWJNM=",
+      "dev": true
+    },
+    "babel-plugin-property-literals": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz",
+      "integrity": "sha1-AlIwGQAZKYCxwRjv6kjOk6q4MzY=",
+      "dev": true
+    },
+    "babel-plugin-proto-to-assign": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz",
+      "integrity": "sha1-xJ56/QL1d7xNoF6i3wAiUM980SM=",
+      "dev": true,
+      "requires": {
+        "lodash": "3.10.1"
+      },
+      "dependencies": {
+        "lodash": {
+          "version": "3.10.1",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+          "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
+          "dev": true
+        }
+      }
+    },
+    "babel-plugin-react-constant-elements": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz",
+      "integrity": "sha1-lGc26DeEKcvDSdz/YvUcFDs041o=",
+      "dev": true
+    },
+    "babel-plugin-react-display-name": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz",
+      "integrity": "sha1-dU/jiSboQkpOexWrbqYTne4FFPw=",
+      "dev": true
+    },
+    "babel-plugin-remove-console": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz",
+      "integrity": "sha1-2PJFVsOgUAXUKqqv0neH9T/wE6c=",
+      "dev": true
+    },
+    "babel-plugin-remove-debugger": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz",
+      "integrity": "sha1-/S6jzWGkKK0fO5yJiC/0KT6MFMc=",
+      "dev": true
+    },
+    "babel-plugin-runtime": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz",
+      "integrity": "sha1-v3x9lm3Vbs1cF/ocslPJrLflSq8=",
+      "dev": true
+    },
+    "babel-plugin-undeclared-variables-check": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz",
+      "integrity": "sha1-XPGqU52BP/ZOmWQSkK9iCWX2Xe4=",
+      "dev": true,
+      "requires": {
+        "leven": "1.0.2"
+      }
+    },
+    "babel-plugin-undefined-to-void": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz",
+      "integrity": "sha1-f1eO+LeN+uYAM4XYQXph7aBuL4E=",
+      "dev": true
+    },
+    "babylon": {
+      "version": "5.8.38",
+      "resolved": "https://registry.npmjs.org/babylon/-/babylon-5.8.38.tgz",
+      "integrity": "sha1-7JsSCxG/bM1Bc6GL8hfmC3mFn/0=",
+      "dev": true
+    },
+    "backbone": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.3.3.tgz",
+      "integrity": "sha1-TMgOp8sWMaxHSInOQPL4vGg7KZk=",
+      "dev": true,
+      "requires": {
+        "underscore": "1.9.1"
+      }
+    },
+    "backo2": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "dev": true,
+      "requires": {
+        "cache-base": "1.0.1",
+        "class-utils": "0.3.6",
+        "component-emitter": "1.2.1",
+        "define-property": "1.0.0",
+        "isobject": "3.0.1",
+        "mixin-deep": "1.3.1",
+        "pascalcase": "0.1.1"
+      },
+      "dependencies": {
+        "component-emitter": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+          "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+          "dev": true
+        },
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "1.0.2"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "6.0.2"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "6.0.2"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "1.0.0",
+            "is-data-descriptor": "1.0.0",
+            "kind-of": "6.0.2"
+          }
+        },
+        "isobject": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+          "dev": true
+        },
+        "kind-of": {
+          "version": "6.0.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+          "dev": true
+        }
+      }
+    },
+    "base64-arraybuffer": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+      "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
+      "dev": true
+    },
+    "base64-js": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
+      "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
+      "dev": true
+    },
+    "base64id": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/base64id/-/base64id-0.1.0.tgz",
+      "integrity": "sha1-As4P3u4M709ACA4ec+g08LG/zj8=",
+      "dev": true
+    },
+    "basic-auth": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
+      "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "5.1.1"
+      },
+      "dependencies": {
+        "safe-buffer": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+          "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
+          "dev": true
+        }
+      }
+    },
+    "batch": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+      "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+      "dev": true
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "tweetnacl": "0.14.5"
+      }
+    },
+    "beeper": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz",
+      "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=",
+      "dev": true
+    },
+    "benchmark": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-1.0.0.tgz",
+      "integrity": "sha1-Lx4vpMNZ8REiqhgwgiGOlX45DHM=",
+      "dev": true
+    },
+    "better-assert": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+      "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+      "dev": true,
+      "requires": {
+        "callsite": "1.0.0"
+      }
+    },
+    "big.js": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
+      "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
+      "dev": true
+    },
+    "binary-extensions": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
+      "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
+      "dev": true
+    },
+    "binaryextensions": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.1.tgz",
+      "integrity": "sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==",
+      "dev": true
+    },
+    "bl": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz",
+      "integrity": "sha1-/FQhoo/UImA2w7OJGmaiW8ZNIm4=",
+      "dev": true,
+      "requires": {
+        "readable-stream": "2.0.6"
+      },
+      "dependencies": {
+        "process-nextick-args": {
+          "version": "1.0.7",
+          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+          "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "2.0.6",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+          "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
+          "dev": true,
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "string_decoder": "0.10.31",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+          "dev": true
+        }
+      }
+    },
+    "blank-object": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/blank-object/-/blank-object-1.0.2.tgz",
+      "integrity": "sha1-+ZB5P76ajI3QE/syGUIL7IHV9Lk=",
+      "dev": true
+    },
+    "blob": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
+      "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=",
+      "dev": true
+    },
+    "block-stream": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
+      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+      "dev": true,
+      "requires": {
+        "inherits": "2.0.3"
+      }
+    },
+    "bluebird": {
+      "version": "2.11.0",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz",
+      "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=",
+      "dev": true
+    },
+    "bn.js": {
+      "version": "4.11.8",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+      "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+      "dev": true
+    },
+    "body-parser": {
+      "version": "1.18.2",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
+      "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
+      "dev": true,
+      "requires": {
+        "bytes": "3.0.0",
+        "content-type": "1.0.4",
+        "debug": "2.6.9",
+        "depd": "1.1.2",
+        "http-errors": "1.6.3",
+        "iconv-lite": "0.4.19",
+        "on-finished": "2.3.0",
+        "qs": "6.5.1",
+        "raw-body": "2.3.2",
+        "type-is": "1.6.16"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.4.19",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+          "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
+          "dev": true
+        },
+        "qs": {
+          "version": "6.5.1",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
+          "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==",
+          "dev": true
+        }
+      }
+    },
+    "boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+      "dev": true
+    },
+    "boom": {
+      "version": "2.10.1",
+      "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
+      "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+      "dev": true,
+      "requires": {
+        "hoek": "2.16.3"
+      }
+    },
+    "bootstrap": {
+      "version": "4.0.0-alpha.5",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.0.0-alpha.5.tgz",
+      "integrity": "sha1-oSa2SMO9L1K4+tS7xeLQrSq/cGQ=",
+      "requires": {
+        "jquery": "3.3.1",
+        "tether": "1.4.4"
+      }
+    },
+    "bower": {
+      "version": "1.8.4",
+      "resolved": "https://registry.npmjs.org/bower/-/bower-1.8.4.tgz",
+      "integrity": "sha1-54dqB23rgTf30GUl3F6MZtuC8oo=",
+      "dev": true
+    },
+    "bower-config": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/bower-config/-/bower-config-1.4.1.tgz",
+      "integrity": "sha1-hf2d82fCuNu9DKpMXyutQM2Ewsw=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "4.1.11",
+        "mout": "1.1.0",
+        "optimist": "0.6.1",
+        "osenv": "0.1.5",
+        "untildify": "2.1.0"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+          "dev": true
+        }
+      }
+    },
+    "bower-endpoint-parser": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/bower-endpoint-parser/-/bower-endpoint-parser-0.2.2.tgz",
+      "integrity": "sha1-ALVlrb+rby01rd3pd+l5Yqy8s/Y=",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
+      "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+      "dev": true,
+      "requires": {
+        "expand-range": "1.8.2",
+        "preserve": "0.2.0",
+        "repeat-element": "1.1.3"
+      }
+    },
+    "breakable": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/breakable/-/breakable-1.0.0.tgz",
+      "integrity": "sha1-eEp5eRWjjq0nutRWtVcstLuqeME=",
+      "dev": true
+    },
+    "broccoli-babel-transpiler": {
+      "version": "5.7.4",
+      "resolved": "https://registry.npmjs.org/broccoli-babel-transpiler/-/broccoli-babel-transpiler-5.7.4.tgz",
+      "integrity": "sha512-gI14Pqc4qbmn5RW4SuAmybLiOoYW59D+HzQyhY6WdaGMAjikKBwJN0p17phyvafQ+kvG0mUiMd83lgHLeATnEA==",
+      "dev": true,
+      "requires": {
+        "babel-core": "5.8.38",
+        "broccoli-funnel": "1.2.0",
+        "broccoli-merge-trees": "1.2.4",
+        "broccoli-persistent-filter": "1.4.3",
+        "clone": "0.2.0",
+        "hash-for-dep": "1.2.3",
+        "heimdalljs-logger": "0.1.10",
+        "json-stable-stringify": "1.0.1",
+        "rsvp": "3.6.2",
+        "workerpool": "2.3.1"
+      },
+      "dependencies": {
+        "clone": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz",
+          "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=",
+          "dev": true
+        }
+      }
+    },
+    "broccoli-caching-writer": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/broccoli-caching-writer/-/broccoli-caching-writer-2.3.1.tgz",
+      "integrity": "sha1-uTz1j5Jk8AMHWGjbBXdPTn8lvQc=",
+      "dev": true,
+      "requires": {
+        "broccoli-kitchen-sink-helpers": "0.2.9",
+        "broccoli-plugin": "1.1.0",
+        "debug": "2.6.9",
+        "rimraf": "2.6.2",
+        "rsvp": "3.6.2",
+        "walk-sync": "0.2.7"
+      },
+      "dependencies": {
+        "broccoli-kitchen-sink-helpers": {
+          "version": "0.2.9",
+          "resolved": "https://registry.npmjs.org/broccoli-kitchen-sink-helpers/-/broccoli-kitchen-sink-helpers-0.2.9.tgz",
+          "integrity": "sha1-peCYbtjXb7WYS2jD8EUNOpbjbsw=",
+          "dev": true,
+          "requires": {
+            "glob": "5.0.15",
+            "mkdirp": "0.5.1"
+          }
+        },
+        "broccoli-plugin": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/broccoli-plugin/-/broccoli-plugin-1.1.0.tgz",
+          "integrity": "sha1-c+LPoF+OoeP8FCDEDD2efcckvwI=",
+          "dev": true,
+          "requires": {
+            "promise-map-series": "0.2.3",
+            "quick-temp": "0.1.5",
+            "rimraf": "2.6.2",
+            "symlink-or-copy": "1.2.0"
+          }
+        },
+        "glob": {
+          "version": "5.0.15",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+          "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+          "dev": true,
+          "requires": {
+            "inflight": "1.0.6",
+            "inherits": "2.0.3",
+            "minimatch": "3.0.4",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
+          }
+        }
+      }
+    },
+    "broccoli-clean-css": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/broccoli-clean-css/-/broccoli-clean-css-1.1.0.tgz",
+      "integrity": "sha1-nbFD2a9+CuecJuOsWpuy1yDqGfo=",
+      "dev": true,
+      "requires": {
+        "broccoli-persistent-filter": "1.4.3",
+        "clean-css-promise": "0.1.1",
+        "inline-source-map-comment": "1.0.5",
+        "json-stable-stringify": "1.0.1"
+      }
+    },
+    "broccoli-concat": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/broccoli-concat/-/broccoli-concat-2.3.8.tgz",
+      "integrity": "sha1-WQzcwCG7kFtsEh2HwtHVffRKKkg=",
+      "dev": true,
+      "requires": {
+        "broccoli-caching-writer": "2.3.1",
+        "broccoli-kitchen-sink-helpers": "0.3.1",
+        "broccoli-stew": "1.6.0",
+        "fast-sourcemap-concat": "1.4.0",
+        "fs-extra": "0.30.0",
+        "lodash.merge": "4.6.1",
+        "lodash.omit": "4.5.0",
+        "lodash.uniq": "4.5.0"
+      }
+    },
+    "broccoli-config-loader": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/broccoli-config-loader/-/broccoli-config-loader-1.0.1.tgz",
+      "integrity": "sha512-MDKYQ50rxhn+g17DYdfzfEM9DjTuSGu42Db37A8TQHQe8geYEcUZ4SQqZRgzdAI3aRQNlA1yBHJfOeGmOjhLIg==",
+      "dev": true,
+      "requires": {
+        "broccoli-caching-writer": "3.0.3"
+      },
+      "dependencies": {
+        "broccoli-caching-writer": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/broccoli-caching-writer/-/broccoli-caching-writer-3.0.3.tgz",
+          "integrity": "sha1-C9LJapc41qarWQ8HujXFFX19tHY=",
+          "dev": true,
+          "requires": {
+            "broccoli-kitchen-sink-helpers": "0.3.1",
+            "broccoli-plugin": "1.3.1",
+            "debug": "2.6.9",
+            "rimraf": "2.6.2",
+            "rsvp": "3.6.2",
+            "walk-sync": "0.3.3"
+          }
+        },
+        "walk-sync": {
+          "version": "0.3.3",
+          "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.3.3.tgz",
+          "integrity": "sha512-jQgTHmCazUngGqvHZFlr30u2VLKEKErBMLFe+fBl5mn4rh9aI/QVRog8PT1hv2vaOu4EBwigfmpRTyZrbnpRVA==",
+          "dev": true,
+          "requires": {
+            "ensure-posix-path": "1.0.2",
+            "matcher-collection": "1.0.5"
+          }
+        }
+      }
+    },
+    "broccoli-config-replace": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/broccoli-config-replace/-/broccoli-config-replace-1.1.2.tgz",
+      "integrity": "sha1-bqh52SpbrWNNETKbUfxfSq/anAA=",
+      "dev": true,
+      "requires": {
+        "broccoli-kitchen-sink-helpers": "0.3.1",
+        "broccoli-plugin": "1.3.1",
+        "debug": "2.6.9",
+        "fs-extra": "0.24.0"
+      },
+      "dependencies": {
+        "fs-extra": {
+          "version": "0.24.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.24.0.tgz",
+          "integrity": "sha1-1OQ0KpZnXLeEZjOmCZJJMytTmVI=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11",
+            "jsonfile": "2.4.0",
+            "path-is-absolute": "1.0.1",
+            "rimraf": "2.6.2"
+          }
+        },
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+          "dev": true
+        }
+      }
+    },
+    "broccoli-debug": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/broccoli-debug/-/broccoli-debug-0.6.4.tgz",
+      "integrity": "sha512-CixMUndBqTljCc26i6ubhBrGbAWXpWBsGJFce6ZOr76Tul2Ev1xxM0tmf7OjSzdYhkr5BrPd/CNbR9VMPi+NBg==",
+      "dev": true,
+      "requires": {
+        "broccoli-plugin": "1.3.1",
+        "fs-tree-diff": "0.5.9",
+        "heimdalljs": "0.2.6",
+        "heimdalljs-logger": "0.1.10",
+        "symlink-or-copy": "1.2.0",
+        "tree-sync": "1.2.2"
+      },
+      "dependencies": {
+        "fs-tree-diff": {
+          "version": "0.5.9",
+          "resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-0.5.9.tgz",
+          "integrity": "sha512-872G8ax0kHh01m9n/2KDzgYwouKza0Ad9iFltBpNykvROvf2AGtoOzPJgGx125aolGPER3JuC7uZFrQ7bG1AZw==",
+          "dev": true,
+          "requires": {
+            "heimdalljs-logger": "0.1.10",
+            "object-assign": "4.1.1",
+            "path-posix": "1.0.0",
+            "symlink-or-copy": "1.2.0"
+          }
+        }
+      }
+    },
+    "broccoli-funnel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/broccoli-funnel/-/broccoli-funnel-1.2.0.tgz",
+      "integrity": "sha1-zdw6/F/xaFqAI0iP/3TOb7WlEpY=",
+      "dev": true,
+      "requires": {
+        "array-equal": "1.0.0",
+        "blank-object": "1.0.2",
+        "broccoli-plugin": "1.3.1",
+        "debug": "2.6.9",
+        "exists-sync": "0.0.4",
+        "fast-ordered-set": "1.0.3",
+        "fs-tree-diff": "0.5.9",
+        "heimdalljs": "0.2.6",
+        "minimatch": "3.0.4",
+        "mkdirp": "0.5.1",
+        "path-posix": "1.0.0",
+        "rimraf": "2.6.2",
+        "symlink-or-copy": "1.2.0",
+        "walk-sync": "0.3.3"
+      },
+      "dependencies": {
+        "exists-sync": {
+          "version": "0.0.4",
+          "resolved": "https://registry.npmjs.org/exists-sync/-/exists-sync-0.0.4.tgz",
+          "integrity": "sha1-l0TCxCjMA7AQYNtFTUsS8O88iHk=",
+          "dev": true
+        },
+        "fs-tree-diff": {
+          "version": "0.5.9",
+          "resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-0.5.9.tgz",
+          "integrity": "sha512-872G8ax0kHh01m9n/2KDzgYwouKza0Ad9iFltBpNykvROvf2AGtoOzPJgGx125aolGPER3JuC7uZFrQ7bG1AZw==",
+          "dev": true,
+          "requires": {
+            "heimdalljs-logger": "0.1.10",
+            "object-assign": "4.1.1",
+            "path-posix": "1.0.0",
+            "symlink-or-copy": "1.2.0"
+          }
+        },
+        "walk-sync": {
+          "version": "0.3.3",
+          "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.3.3.tgz",
+          "integrity": "sha512-jQgTHmCazUngGqvHZFlr30u2VLKEKErBMLFe+fBl5mn4rh9aI/QVRog8PT1hv2vaOu4EBwigfmpRTyZrbnpRVA==",
+          "dev": true,
+          "requires": {
+            "ensure-posix-path": "1.0.2",
+            "matcher-collection": "1.0.5"
+          }
+        }
+      }
+    },
+    "broccoli-funnel-reducer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/broccoli-funnel-reducer/-/broccoli-funnel-reducer-1.0.0.tgz",
+      "integrity": "sha1-ETZbKnha7JsXlyo234fu8kxcwOo=",
+      "dev": true
+    },
+    "broccoli-kitchen-sink-helpers": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/broccoli-kitchen-sink-helpers/-/broccoli-kitchen-sink-helpers-0.3.1.tgz",
+      "integrity": "sha1-d8fBgZS5ZkFj7E/O4nk0RJJuDAY=",
+      "dev": true,
+      "requires": {
+        "glob": "5.0.15",
+        "mkdirp": "0.5.1"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "5.0.15",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+          "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+          "dev": true,
+          "requires": {
+            "inflight": "1.0.6",
+            "inherits": "2.0.3",
+            "minimatch": "3.0.4",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
+          }
+        }
+      }
+    },
+    "broccoli-merge-trees": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/broccoli-merge-trees/-/broccoli-merge-trees-1.2.4.tgz",
+      "integrity": "sha1-oAFRm7UGfwZYnZGvopQkRaLQ/bU=",
+      "dev": true,
+      "requires": {
+        "broccoli-plugin": "1.3.1",
+        "can-symlink": "1.0.0",
+        "fast-ordered-set": "1.0.3",
+        "fs-tree-diff": "0.5.9",
+        "heimdalljs": "0.2.6",
+        "heimdalljs-logger": "0.1.10",
+        "rimraf": "2.6.2",
+        "symlink-or-copy": "1.2.0"
+      },
+      "dependencies": {
+        "fs-tree-diff": {
+          "version": "0.5.9",
+          "resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-0.5.9.tgz",
+          "integrity": "sha512-872G8ax0kHh01m9n/2KDzgYwouKza0Ad9iFltBpNykvROvf2AGtoOzPJgGx125aolGPER3JuC7uZFrQ7bG1AZw==",
+          "dev": true,
+          "requires": {
+            "heimdalljs-logger": "0.1.10",
+            "object-assign": "4.1.1",
+            "path-posix": "1.0.0",
+            "symlink-or-copy": "1.2.0"
+          }
+        }
+      }
+    },
+    "broccoli-persistent-filter": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/broccoli-persistent-filter/-/broccoli-persistent-filter-1.4.3.tgz",
+      "integrity": "sha512-JwNLDvvXJlhUmr+CHcbVhCyp33NbCIAITjQZmJY9e8QzANXh3jpFWlhSFvkWghwKA8rTAKcXkW12agtiZjxr4g==",
+      "dev": true,
+      "requires": {
+        "async-disk-cache": "1.3.3",
+        "async-promise-queue": "1.0.4",
+        "broccoli-plugin": "1.3.1",
+        "fs-tree-diff": "0.5.9",
+        "hash-for-dep": "1.2.3",
+        "heimdalljs": "0.2.6",
+        "heimdalljs-logger": "0.1.10",
+        "mkdirp": "0.5.1",
+        "promise-map-series": "0.2.3",
+        "rimraf": "2.6.2",
+        "rsvp": "3.6.2",
+        "symlink-or-copy": "1.2.0",
+        "walk-sync": "0.3.3"
+      },
+      "dependencies": {
+        "fs-tree-diff": {
+          "version": "0.5.9",
+          "resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-0.5.9.tgz",
+          "integrity": "sha512-872G8ax0kHh01m9n/2KDzgYwouKza0Ad9iFltBpNykvROvf2AGtoOzPJgGx125aolGPER3JuC7uZFrQ7bG1AZw==",
+          "dev": true,
+          "requires": {
+            "heimdalljs-logger": "0.1.10",
+            "object-assign": "4.1.1",
+            "path-posix": "1.0.0",
+            "symlink-or-copy": "1.2.0"
+          }
+        },
+        "walk-sync": {
+          "version": "0.3.3",
+          "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.3.3.tgz",
+          "integrity": "sha512-jQgTHmCazUngGqvHZFlr30u2VLKEKErBMLFe+fBl5mn4rh9aI/QVRog8PT1hv2vaOu4EBwigfmpRTyZrbnpRVA==",
+          "dev": true,
+          "requires": {
+            "ensure-posix-path": "1.0.2",
+            "matcher-collection": "1.0.5"
+          }
+        }
+      }
+    },
+    "broccoli-plugin": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/broccoli-plugin/-/broccoli-plugin-1.3.1.tgz",
+      "integrity": "sha512-DW8XASZkmorp+q7J4EeDEZz+LoyKLAd2XZULXyD9l4m9/hAKV3vjHmB1kiUshcWAYMgTP1m2i4NnqCE/23h6AQ==",
+      "dev": true,
+      "requires": {
+        "promise-map-series": "0.2.3",
+        "quick-temp": "0.1.5",
+        "rimraf": "2.6.2",
+        "symlink-or-copy": "1.2.0"
+      }
+    },
+    "broccoli-sane-watcher": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/broccoli-sane-watcher/-/broccoli-sane-watcher-1.1.5.tgz",
+      "integrity": "sha1-8rCvnPCvt0x6Sc2I6xHGhp7owMA=",
+      "dev": true,
+      "requires": {
+        "broccoli-slow-trees": "1.1.0",
+        "debug": "2.6.9",
+        "rsvp": "3.6.2",
+        "sane": "1.7.0"
+      }
+    },
+    "broccoli-slow-trees": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/broccoli-slow-trees/-/broccoli-slow-trees-1.1.0.tgz",
+      "integrity": "sha1-QmxXJOAIEH5Fc/c+ipynApFrePc=",
+      "dev": true
+    },
+    "broccoli-source": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/broccoli-source/-/broccoli-source-1.1.0.tgz",
+      "integrity": "sha1-VPDoLItz9GWAy7xPV48LMvyo+Ak=",
+      "dev": true
+    },
+    "broccoli-stew": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/broccoli-stew/-/broccoli-stew-1.6.0.tgz",
+      "integrity": "sha512-sUwCJNnYH4Na690By5xcEMAZqKgquUQnMAEuIiL3Z2k63mSw9Xg+7Ew4wCrFrMmXMcLpWjZDOm6Yqnq268N+ZQ==",
+      "dev": true,
+      "requires": {
+        "broccoli-debug": "0.6.4",
+        "broccoli-funnel": "2.0.1",
+        "broccoli-merge-trees": "2.0.1",
+        "broccoli-persistent-filter": "1.4.3",
+        "broccoli-plugin": "1.3.1",
+        "chalk": "2.4.1",
+        "debug": "3.1.0",
+        "ensure-posix-path": "1.0.2",
+        "fs-extra": "5.0.0",
+        "minimatch": "3.0.4",
+        "resolve": "1.8.1",
+        "rsvp": "4.8.3",
+        "symlink-or-copy": "1.2.0",
+        "walk-sync": "0.3.3"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "dev": true,
+          "requires": {
+            "color-convert": "1.9.2"
+          }
+        },
+        "broccoli-funnel": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/broccoli-funnel/-/broccoli-funnel-2.0.1.tgz",
+          "integrity": "sha512-C8Lnp9TVsSSiZMGEF16C0dCiNg2oJqUKwuZ1K4kVC6qRPG/2Cj/rtB5kRCC9qEbwqhX71bDbfHROx0L3J7zXQg==",
+          "dev": true,
+          "requires": {
+            "array-equal": "1.0.0",
+            "blank-object": "1.0.2",
+            "broccoli-plugin": "1.3.1",
+            "debug": "2.6.9",
+            "fast-ordered-set": "1.0.3",
+            "fs-tree-diff": "0.5.9",
+            "heimdalljs": "0.2.6",
+            "minimatch": "3.0.4",
+            "mkdirp": "0.5.1",
+            "path-posix": "1.0.0",
+            "rimraf": "2.6.2",
+            "symlink-or-copy": "1.2.0",
+            "walk-sync": "0.3.3"
+          },
+          "dependencies": {
+            "debug": {
+              "version": "2.6.9",
+              "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+              "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+              "dev": true,
+              "requires": {
+                "ms": "2.0.0"
+              }
+            }
+          }
+        },
+        "broccoli-merge-trees": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/broccoli-merge-trees/-/broccoli-merge-trees-2.0.1.tgz",
+          "integrity": "sha512-WjaexJ+I8BxP5V5RNn6um/qDRSmKoiBC/QkRi79FT9ClHfldxRyCDs9mcV7mmoaPlsshmmPaUz5jdtcKA6DClQ==",
+          "dev": true,
+          "requires": {
+            "broccoli-plugin": "1.3.1",
+            "merge-trees": "1.0.1"
+          }
+        },
+        "chalk": {
+          "version": "2.4.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "3.2.1",
+            "escape-string-regexp": "1.0.5",
+            "supports-color": "5.5.0"
+          }
+        },
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "fs-extra": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
+          "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11",
+            "jsonfile": "4.0.0",
+            "universalify": "0.1.2"
+          }
+        },
+        "fs-tree-diff": {
+          "version": "0.5.9",
+          "resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-0.5.9.tgz",
+          "integrity": "sha512-872G8ax0kHh01m9n/2KDzgYwouKza0Ad9iFltBpNykvROvf2AGtoOzPJgGx125aolGPER3JuC7uZFrQ7bG1AZw==",
+          "dev": true,
+          "requires": {
+            "heimdalljs-logger": "0.1.10",
+            "object-assign": "4.1.1",
+            "path-posix": "1.0.0",
+            "symlink-or-copy": "1.2.0"
+          }
+        },
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "dev": true
+        },
+        "jsonfile": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+          "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11"
+          }
+        },
+        "rsvp": {
+          "version": "4.8.3",
+          "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.3.tgz",
+          "integrity": "sha512-/OlbK31XtkPnLD2ktmZXj4g/v6q1boTDr6/3lFuDTgxVsrA3h7PH5eYyAxIvDMjRHr/DoOlzNicqDwBEo9xU7g==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "3.0.0"
+          }
+        },
+        "walk-sync": {
+          "version": "0.3.3",
+          "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.3.3.tgz",
+          "integrity": "sha512-jQgTHmCazUngGqvHZFlr30u2VLKEKErBMLFe+fBl5mn4rh9aI/QVRog8PT1hv2vaOu4EBwigfmpRTyZrbnpRVA==",
+          "dev": true,
+          "requires": {
+            "ensure-posix-path": "1.0.2",
+            "matcher-collection": "1.0.5"
+          }
+        }
+      }
+    },
+    "broccoli-viz": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/broccoli-viz/-/broccoli-viz-2.0.1.tgz",
+      "integrity": "sha1-Pz7S+4PjaKpTBvrkYIAd6lUuQNs=",
+      "dev": true
+    },
+    "brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+      "dev": true
+    },
+    "browserify-aes": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+      "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+      "dev": true,
+      "requires": {
+        "buffer-xor": "1.0.3",
+        "cipher-base": "1.0.4",
+        "create-hash": "1.2.0",
+        "evp_bytestokey": "1.0.3",
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.2"
+      }
+    },
+    "browserify-cipher": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+      "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+      "dev": true,
+      "requires": {
+        "browserify-aes": "1.2.0",
+        "browserify-des": "1.0.2",
+        "evp_bytestokey": "1.0.3"
+      }
+    },
+    "browserify-des": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+      "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "1.0.4",
+        "des.js": "1.0.0",
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.2"
+      }
+    },
+    "browserify-rsa": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+      "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+      "dev": true,
+      "requires": {
+        "bn.js": "4.11.8",
+        "randombytes": "2.0.6"
+      }
+    },
+    "browserify-sign": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+      "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+      "dev": true,
+      "requires": {
+        "bn.js": "4.11.8",
+        "browserify-rsa": "4.0.1",
+        "create-hash": "1.2.0",
+        "create-hmac": "1.1.7",
+        "elliptic": "6.4.1",
+        "inherits": "2.0.3",
+        "parse-asn1": "5.1.1"
+      }
+    },
+    "browserify-zlib": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
+      "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
+      "dev": true,
+      "requires": {
+        "pako": "0.2.9"
+      }
+    },
+    "browserslist": {
+      "version": "1.7.7",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
+      "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
+      "dev": true,
+      "requires": {
+        "caniuse-db": "1.0.30000878",
+        "electron-to-chromium": "1.3.61"
+      }
+    },
+    "bser": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz",
+      "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=",
+      "dev": true,
+      "requires": {
+        "node-int64": "0.4.0"
+      }
+    },
+    "buffer": {
+      "version": "4.9.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+      "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
+      "dev": true,
+      "requires": {
+        "base64-js": "1.3.0",
+        "ieee754": "1.1.12",
+        "isarray": "1.0.0"
+      }
+    },
+    "buffer-alloc": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
+      "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+      "dev": true,
+      "requires": {
+        "buffer-alloc-unsafe": "1.1.0",
+        "buffer-fill": "1.0.0"
+      }
+    },
+    "buffer-alloc-unsafe": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
+      "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
+      "dev": true
+    },
+    "buffer-fill": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
+      "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=",
+      "dev": true
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "dev": true
+    },
+    "buffer-shims": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
+      "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=",
+      "dev": true
+    },
+    "buffer-xor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+      "dev": true
+    },
+    "builtin-modules": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+      "dev": true
+    },
+    "builtin-status-codes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status

<TRUNCATED>

[13/50] [abbrv] metron git commit: METRON-1700 Create REST endpoint to get job configuration (merrimanr) closes apache/metron#1135

Posted by ni...@apache.org.
METRON-1700 Create REST endpoint to get job configuration (merrimanr) closes apache/metron#1135


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/2b6959b4
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/2b6959b4
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/2b6959b4

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 2b6959b46db79256d12b8522385212559cabada2
Parents: d5eb56a
Author: merrimanr <me...@gmail.com>
Authored: Thu Aug 2 12:17:37 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Thu Aug 2 12:17:37 2018 -0500

----------------------------------------------------------------------
 metron-interface/metron-rest/README.md          |  10 ++
 .../metron/rest/controller/PcapController.java  |  16 +++
 .../apache/metron/rest/service/PcapService.java |  71 ++++++++++++-
 .../rest/service/impl/PcapServiceImpl.java      |  54 +++++++++-
 .../PcapControllerIntegrationTest.java          |  50 ++++++++++
 .../apache/metron/rest/mock/MockPcapJob.java    |   4 +-
 .../rest/service/impl/PcapServiceImplTest.java  | 100 +++++++++++++++++++
 .../java/org/apache/metron/job/Statusable.java  |   2 +
 .../java/org/apache/metron/pcap/mr/PcapJob.java |   5 +
 9 files changed, 306 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/2b6959b4/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md
index 68ec559..489cd9f 100644
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@ -259,6 +259,8 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
 | [ `GET /api/v1/pcap/{jobId}`](#get-apiv1pcapjobid)|
 | [ `GET /api/v1/pcap/{jobId}/pdml`](#get-apiv1pcapjobidpdml)|
 | [ `GET /api/v1/pcap/{jobId}/raw`](#get-apiv1pcapjobidraw)|
+| [ `DELETE /api/v1/pcap/kill/{jobId}`](#delete-apiv1pcapkilljobid)|
+| [ `GET /api/v1/pcap/{jobId}/config`](#get-apiv1pcapjobidconfig)|
 | [ `GET /api/v1/search/search`](#get-apiv1searchsearch)|
 | [ `POST /api/v1/search/search`](#post-apiv1searchsearch)|
 | [ `POST /api/v1/search/group`](#post-apiv1searchgroup)|
@@ -556,6 +558,14 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
     * jobId - Job ID of submitted job
   * Returns:
     * 200 - Kills passed job.
+    
+### `GET /api/v1/pcap/{jobId}/config`
+  * Description: Gets job configuration for Pcap query job.
+  * Input:
+    * jobId - Job ID of submitted job
+  * Returns:
+    * 200 - Returns a map of job properties for the Job ID.
+    * 404 - Job is missing.
 
 ### `POST /api/v1/search/search`
   * Description: Searches the indexing store. GUIDs must be quoted to ensure correct results.

http://git-wip-us.apache.org/repos/asf/metron/blob/2b6959b4/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
index 13a623a..97713d8 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
@@ -46,6 +46,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.List;
+import java.util.Map;
 
 @RestController
 @RequestMapping("/api/v1/pcap")
@@ -161,4 +162,19 @@ public class PcapController {
     }
   }
 
+  @ApiOperation(value = "Gets job configuration for Pcap query job.")
+  @ApiResponses(value = {
+          @ApiResponse(message = "Returns a map of job properties for the Job ID.", code = 200),
+          @ApiResponse(message = "Job is missing.", code = 404)
+  })
+  @RequestMapping(value = "/{jobId}/config", method = RequestMethod.GET)
+  ResponseEntity<Map<String, Object>> getConfiguration(@ApiParam(name="jobId", value="Job ID of submitted job", required=true)@PathVariable String jobId) throws RestException {
+    Map<String, Object> configuration = pcapQueryService.getConfiguration(SecurityUtils.getCurrentUser(), jobId);
+    if (configuration != null) {
+      return new ResponseEntity<>(configuration, HttpStatus.OK);
+    } else {
+      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/2b6959b4/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
index 00efab9..5ec937f 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
@@ -26,22 +26,91 @@ import org.apache.metron.rest.model.pcap.Pdml;
 
 import java.io.InputStream;
 import java.util.List;
+import java.util.Map;
 
 public interface PcapService {
 
+  /**
+   * Submits a pcap query.  Jobs that do not match any pcap data will return a status without a job id and a
+   * description of why data did not match.
+   *
+   * @param username User that is submitting the job
+   * @param pcapRequest Either a fixed or query filter pcap request
+   * @return Status of the submitted job
+   * @throws RestException
+   */
   PcapStatus submit(String username, PcapRequest pcapRequest) throws RestException;
 
+  /**
+   * Returns the status of a job given a job id and user who submitted it.
+   *
+   * @param username User that submitted the job
+   * @param jobId Job id of a submitted job
+   * @return Status of the submitted job or null if job does not exist
+   * @throws RestException
+   */
   PcapStatus getJobStatus(String username, String jobId) throws RestException;
 
+  /**
+   * Returns a list of statuses for jobs a user has submitted that match the given job state.
+   *
+   * @param username User that submitted the job
+   * @param state Job state
+   * @return List of job statuses of an empty list of no jobs match
+   * @throws RestException
+   */
   List<PcapStatus> getJobStatus(String username, JobStatus.State state) throws RestException;
 
+  /**
+   * Kills a running job.
+   *
+   * @param username User that submitted the job
+   * @param jobId Job id of a submitted job
+   * @return Status of the killed job
+   * @throws RestException
+   */
   PcapStatus killJob(String username, String jobId) throws RestException;
 
+  /**
+   * Gets an HDFS path to results of a pcap query given a user, job id and page number.
+   *
+   * @param username User that submitted the job
+   * @param jobId Job id
+   * @param page Page number
+   * @return HDFS path to results
+   * @throws RestException
+   */
   Path getPath(String username, String jobId, Integer page) throws RestException;
 
+  /**
+   * Gets pcap query results in <a href="https://wiki.wireshark.org/PDML">PDML</a> format for a given user, job id and page number.
+   *
+   * @param username User that submitted the job
+   * @param jobId Job id
+   * @param page Page number
+   * @return Results in PDML format
+   * @throws RestException
+   */
   Pdml getPdml(String username, String jobId, Integer page) throws RestException;
 
+  /**
+   * Returns an input stream of raw binary pcap results for a given user, job id and page number.
+   *
+   * @param username User that submitted the job
+   * @param jobId Job id
+   * @param page Page number
+   * @return Input stream of raw pcap results
+   * @throws RestException
+   */
   InputStream getRawPcap(String username, String jobId, Integer page) throws RestException;
 
-
+  /**
+   * Gets the configuration of a submitted pcap query.  Internal properties such as file system, hadoop config, etc are not included.
+   *
+   * @param username User that submitted the job
+   * @param jobId Job id
+   * @return Configuration of a submitted pcap query
+   * @throws RestException
+   */
+  Map<String, Object> getConfiguration(String username, String jobId) throws RestException;
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/2b6959b4/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
index ff80c8f..f3af0dd 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
@@ -28,26 +28,40 @@ import org.apache.metron.job.Pageable;
 import org.apache.metron.job.Statusable;
 import org.apache.metron.job.manager.JobManager;
 import org.apache.metron.pcap.config.PcapOptions;
+import org.apache.metron.pcap.filter.PcapFilterConfigurator;
+import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
+import org.apache.metron.pcap.filter.query.QueryPcapFilter;
 import org.apache.metron.rest.MetronRestConstants;
 import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.config.PcapJobSupplier;
+import org.apache.metron.rest.model.pcap.FixedPcapOptions;
 import org.apache.metron.rest.model.pcap.PcapRequest;
 import org.apache.metron.rest.model.pcap.PcapStatus;
 import org.apache.metron.rest.model.pcap.Pdml;
+import org.apache.metron.rest.model.pcap.QueryPcapOptions;
 import org.apache.metron.rest.service.PcapService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.env.Environment;
 import org.springframework.stereotype.Service;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.stream.Collectors;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
 
 @Service
 public class PcapServiceImpl implements PcapService {
 
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
   private Environment environment;
   private Configuration configuration;
   private PcapJobSupplier pcapJobSupplier;
@@ -93,7 +107,7 @@ public class PcapServiceImpl implements PcapService {
         pcapStatus = statusableToPcapStatus(statusable);
       }
     } catch (JobNotFoundException | InterruptedException e) {
-      // do nothing and return null pcapStatus
+      LOG.warn(String.format("Could not get job status.  Job not found for user %s with job id %s", username, jobId));
     } catch (JobException e) {
       throw new RestException(e);
     }
@@ -139,7 +153,7 @@ public class PcapServiceImpl implements PcapService {
     try {
       jobManager.killJob(username, jobId);
     } catch (JobNotFoundException e) {
-      // do nothing and return null pcapStatus
+      LOG.warn(String.format("Could not kill job.  Job not found for user %s with job id %s", username, jobId));
       return null;
     } catch (JobException e) {
       throw new RestException(e);
@@ -159,7 +173,7 @@ public class PcapServiceImpl implements PcapService {
         }
       }
     } catch (JobNotFoundException e) {
-      // do nothing and return null pcapStatus
+      LOG.warn(String.format("Could not get path for page %s.  Job not found for user %s with job id %s", page, username, jobId));
     } catch (JobException | InterruptedException e) {
       throw new RestException(e);
     }
@@ -199,6 +213,40 @@ public class PcapServiceImpl implements PcapService {
     return inputStream;
   }
 
+  @Override
+  public Map<String, Object> getConfiguration(String username, String jobId) throws RestException {
+    Map<String, Object> configuration = new HashMap<>();
+    try {
+      Statusable<Path> statusable = jobManager.getJob(username, jobId);
+      if (statusable != null) {
+        Map<String, Object> jobConfiguration = statusable.getConfiguration();
+        configuration.put(PcapOptions.BASE_PATH.getKey(), PcapOptions.BASE_PATH.get(jobConfiguration, String.class));
+        configuration.put(PcapOptions.FINAL_OUTPUT_PATH.getKey(), PcapOptions.FINAL_OUTPUT_PATH.get(jobConfiguration, String.class));
+        configuration.put(PcapOptions.START_TIME_MS.getKey(), PcapOptions.START_TIME_MS.get(jobConfiguration, String.class));
+        configuration.put(PcapOptions.END_TIME_MS.getKey(), PcapOptions.END_TIME_MS.get(jobConfiguration, String.class));
+        configuration.put(PcapOptions.NUM_REDUCERS.getKey(), PcapOptions.NUM_REDUCERS.get(jobConfiguration, Integer.class));
+
+        boolean isFixedFilter = PcapOptions.FILTER_IMPL.get(jobConfiguration, PcapFilterConfigurator.class) instanceof FixedPcapFilter.Configurator;
+        if (isFixedFilter) {
+          configuration.put(FixedPcapOptions.IP_SRC_ADDR.getKey(), FixedPcapOptions.IP_SRC_ADDR.get(jobConfiguration, String.class));
+          configuration.put(FixedPcapOptions.IP_DST_ADDR.getKey(), FixedPcapOptions.IP_DST_ADDR.get(jobConfiguration, String.class));
+          configuration.put(FixedPcapOptions.IP_SRC_PORT.getKey(), FixedPcapOptions.IP_SRC_PORT.get(jobConfiguration, String.class));
+          configuration.put(FixedPcapOptions.IP_DST_PORT.getKey(), FixedPcapOptions.IP_DST_PORT.get(jobConfiguration, String.class));
+          configuration.put(FixedPcapOptions.PROTOCOL.getKey(), FixedPcapOptions.PROTOCOL.get(jobConfiguration, String.class));
+          configuration.put(FixedPcapOptions.PACKET_FILTER.getKey(), FixedPcapOptions.PACKET_FILTER.get(jobConfiguration, String.class));
+          configuration.put(FixedPcapOptions.INCLUDE_REVERSE.getKey(), FixedPcapOptions.INCLUDE_REVERSE.get(jobConfiguration, String.class));
+        } else {
+          configuration.put(QueryPcapOptions.QUERY.getKey(), QueryPcapOptions.QUERY.get(jobConfiguration, String.class));
+        }
+      }
+    } catch (JobNotFoundException e) {
+      LOG.warn(String.format("Could not get job configuration.  Job not found for user %s with job id %s", username, jobId));
+    } catch (JobException e) {
+      throw new RestException(e);
+    }
+    return configuration;
+  }
+
   protected void setPcapOptions(String username, PcapRequest pcapRequest) throws IOException {
     PcapOptions.JOB_NAME.put(pcapRequest, "jobName");
     PcapOptions.USERNAME.put(pcapRequest, username);

http://git-wip-us.apache.org/repos/asf/metron/blob/2b6959b4/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
index 5d30e72..2d7c505 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
@@ -420,5 +420,55 @@ public class PcapControllerIntegrationTest {
             .andExpect(status().isNotFound());
   }
 
+  @Test
+  public void testGetFixedFilterConfiguration() throws Exception {
+    MockPcapJob mockPcapJob = (MockPcapJob) wac.getBean("mockPcapJob");
+
+    mockPcapJob.setStatus(new JobStatus().withJobId("jobId").withState(JobStatus.State.RUNNING));
+
+    this.mockMvc.perform(post(pcapUrl + "/fixed").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content(fixedJson))
+            .andExpect(status().isOk())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(jsonPath("$.jobId").value("jobId"))
+            .andExpect(jsonPath("$.jobStatus").value("RUNNING"));
+
+    this.mockMvc.perform(get(pcapUrl + "/jobId/config").with(httpBasic(user, password)))
+            .andExpect(status().isOk())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(jsonPath("$.basePath").value("/base/path"))
+            .andExpect(jsonPath("$.finalOutputPath").value("/final/output/path"))
+            .andExpect(jsonPath("$.startTimeMs").value(10))
+            .andExpect(jsonPath("$.endTimeMs").value(20))
+            .andExpect(jsonPath("$.numReducers").value(2))
+            .andExpect(jsonPath("$.ipSrcAddr").value("192.168.1.2"))
+            .andExpect(jsonPath("$.ipDstAddr").value("192.168.1.1"))
+            .andExpect(jsonPath("$.ipSrcPort").value("2000"))
+            .andExpect(jsonPath("$.ipDstPort").value("1000"))
+            .andExpect(jsonPath("$.protocol").value("TCP"))
+            .andExpect(jsonPath("$.packetFilter").value("filter"))
+            .andExpect(jsonPath("$.includeReverse").value("true"));
+  }
 
+  @Test
+  public void testGetQueryFilterConfiguration() throws Exception {
+    MockPcapJob mockPcapJob = (MockPcapJob) wac.getBean("mockPcapJob");
+
+    mockPcapJob.setStatus(new JobStatus().withJobId("jobId").withState(JobStatus.State.RUNNING));
+
+    this.mockMvc.perform(post(pcapUrl + "/query").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content(queryJson))
+            .andExpect(status().isOk())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(jsonPath("$.jobId").value("jobId"))
+            .andExpect(jsonPath("$.jobStatus").value("RUNNING"));
+
+    this.mockMvc.perform(get(pcapUrl + "/jobId/config").with(httpBasic(user, password)))
+            .andExpect(status().isOk())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(jsonPath("$.basePath").value("/base/path"))
+            .andExpect(jsonPath("$.finalOutputPath").value("/final/output/path"))
+            .andExpect(jsonPath("$.startTimeMs").value(10))
+            .andExpect(jsonPath("$.endTimeMs").value(20))
+            .andExpect(jsonPath("$.numReducers").value(2))
+            .andExpect(jsonPath("$.query").value("query"));
+  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/2b6959b4/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
index 6a954e8..779589d 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
@@ -18,7 +18,6 @@
 package org.apache.metron.rest.mock;
 
 import org.apache.hadoop.fs.Path;
-import org.apache.metron.common.hadoop.SequenceFileIterable;
 import org.apache.metron.job.Finalizer;
 import org.apache.metron.job.JobException;
 import org.apache.metron.job.JobStatus;
@@ -28,7 +27,7 @@ import org.apache.metron.pcap.config.PcapOptions;
 import org.apache.metron.pcap.filter.PcapFilterConfigurator;
 import org.apache.metron.pcap.mr.PcapJob;
 
-import java.util.List;
+import java.util.HashMap;
 import java.util.Map;
 
 import static org.mockito.Mockito.mock;
@@ -54,6 +53,7 @@ public class MockPcapJob extends PcapJob<Path> {
 
   @Override
   public Statusable<Path> submit(Finalizer<Path> finalizer, Map<String, Object> configuration) throws JobException {
+    when(statusable.getConfiguration()).thenReturn(new HashMap<>(configuration));
     this.basePath = PcapOptions.BASE_PATH.get(configuration, String.class);
     this.baseInterrimResultPath = PcapOptions.BASE_INTERIM_RESULT_PATH.get(configuration, String.class);
     this.finalOutputPath = PcapOptions.FINAL_OUTPUT_PATH.get(configuration, String.class);

http://git-wip-us.apache.org/repos/asf/metron/blob/2b6959b4/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
index f99ab93..2552df7 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
@@ -32,6 +32,7 @@ import org.apache.metron.job.Statusable;
 import org.apache.metron.job.manager.InMemoryJobManager;
 import org.apache.metron.job.manager.JobManager;
 import org.apache.metron.pcap.PcapHelper;
+import org.apache.metron.pcap.config.PcapOptions;
 import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
 import org.apache.metron.pcap.filter.query.QueryPcapFilter;
 import org.apache.metron.rest.MetronRestConstants;
@@ -39,7 +40,9 @@ import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.config.PcapJobSupplier;
 import org.apache.metron.rest.mock.MockPcapJob;
 import org.apache.metron.rest.mock.MockPcapJobSupplier;
+import org.apache.metron.rest.model.pcap.FixedPcapOptions;
 import org.apache.metron.rest.model.pcap.FixedPcapRequest;
+import org.apache.metron.rest.model.pcap.QueryPcapOptions;
 import org.apache.metron.rest.model.pcap.QueryPcapRequest;
 import org.apache.metron.rest.model.pcap.PcapStatus;
 import org.apache.metron.rest.model.pcap.Pdml;
@@ -639,4 +642,101 @@ public class PcapServiceImplTest {
     pcapService.getRawPcap("user", "jobId", 1);
   }
 
+  @Test
+  public void getConfigurationShouldProperlyReturnFixedFilterConfiguration() throws Exception {
+    FixedPcapRequest fixedPcapRequest = new FixedPcapRequest();
+    fixedPcapRequest.setBasePath("basePath");
+    fixedPcapRequest.setBaseInterimResultPath("baseOutputPath");
+    fixedPcapRequest.setFinalOutputPath("finalOutputPath");
+    fixedPcapRequest.setStartTimeMs(1L);
+    fixedPcapRequest.setEndTimeMs(2L);
+    fixedPcapRequest.setNumReducers(2);
+    fixedPcapRequest.setIpSrcAddr("ip_src_addr");
+    fixedPcapRequest.setIpDstAddr("ip_dst_addr");
+    fixedPcapRequest.setIpSrcPort(1000);
+    fixedPcapRequest.setIpDstPort(2000);
+    fixedPcapRequest.setProtocol("tcp");
+    fixedPcapRequest.setPacketFilter("filter");
+    fixedPcapRequest.setIncludeReverse(true);
+    MockPcapJob mockPcapJob = new MockPcapJob();
+    mockPcapJobSupplier.setMockPcapJob(mockPcapJob);
+    JobManager jobManager = new InMemoryJobManager<>();
+
+    PcapServiceImpl pcapService = spy(new PcapServiceImpl(environment, configuration, mockPcapJobSupplier, jobManager, pcapToPdmlScriptWrapper));
+    FileSystem fileSystem = mock(FileSystem.class);
+    doReturn(fileSystem).when(pcapService).getFileSystem();
+    mockPcapJob.setStatus(new JobStatus()
+                    .withJobId("jobId"));
+
+    pcapService.submit("user", fixedPcapRequest);
+
+    Map<String, Object> configuration = pcapService.getConfiguration("user", "jobId");
+    Assert.assertEquals("basePath", PcapOptions.BASE_PATH.get(configuration, String.class));
+    Assert.assertEquals("finalOutputPath", PcapOptions.FINAL_OUTPUT_PATH.get(configuration, String.class));
+    Assert.assertEquals(1L, PcapOptions.START_TIME_MS.get(configuration, Long.class).longValue());
+    Assert.assertEquals(2L, PcapOptions.END_TIME_MS.get(configuration, Long.class).longValue());
+    Assert.assertEquals(2, PcapOptions.NUM_REDUCERS.get(configuration, Integer.class).intValue());
+    Assert.assertEquals("ip_src_addr", FixedPcapOptions.IP_SRC_ADDR.get(configuration, String.class));
+    Assert.assertEquals("ip_dst_addr", FixedPcapOptions.IP_DST_ADDR.get(configuration, String.class));
+    Assert.assertEquals(1000, FixedPcapOptions.IP_SRC_PORT.get(configuration, Integer.class).intValue());
+    Assert.assertEquals(2000, FixedPcapOptions.IP_DST_PORT.get(configuration, Integer.class).intValue());
+    Assert.assertEquals("tcp", FixedPcapOptions.PROTOCOL.get(configuration, String.class));
+    Assert.assertEquals("filter", FixedPcapOptions.PACKET_FILTER.get(configuration, String.class));
+    Assert.assertEquals(true, FixedPcapOptions.INCLUDE_REVERSE.get(configuration, Boolean.class));
+  }
+
+  @Test
+  public void getConfigurationShouldProperlyReturnQueryFilterConfiguration() throws Exception {
+    QueryPcapRequest queryPcapRequest = new QueryPcapRequest();
+    queryPcapRequest.setBasePath("basePath");
+    queryPcapRequest.setBaseInterimResultPath("baseOutputPath");
+    queryPcapRequest.setFinalOutputPath("finalOutputPath");
+    queryPcapRequest.setStartTimeMs(1L);
+    queryPcapRequest.setEndTimeMs(2L);
+    queryPcapRequest.setNumReducers(2);
+    queryPcapRequest.setQuery("query");
+    MockPcapJob mockPcapJob = new MockPcapJob();
+    mockPcapJobSupplier.setMockPcapJob(mockPcapJob);
+    JobManager jobManager = new InMemoryJobManager<>();
+
+    PcapServiceImpl pcapService = spy(new PcapServiceImpl(environment, configuration, mockPcapJobSupplier, jobManager, pcapToPdmlScriptWrapper));
+    FileSystem fileSystem = mock(FileSystem.class);
+    doReturn(fileSystem).when(pcapService).getFileSystem();
+    mockPcapJob.setStatus(new JobStatus()
+            .withJobId("jobId"));
+
+    pcapService.submit("user", queryPcapRequest);
+
+    Map<String, Object> configuration = pcapService.getConfiguration("user", "jobId");
+    Assert.assertEquals("basePath", PcapOptions.BASE_PATH.get(configuration, String.class));
+    Assert.assertEquals("finalOutputPath", PcapOptions.FINAL_OUTPUT_PATH.get(configuration, String.class));
+    Assert.assertEquals(1L, PcapOptions.START_TIME_MS.get(configuration, Long.class).longValue());
+    Assert.assertEquals(2L, PcapOptions.END_TIME_MS.get(configuration, Long.class).longValue());
+    Assert.assertEquals(2, PcapOptions.NUM_REDUCERS.get(configuration, Integer.class).intValue());
+    Assert.assertEquals("query", QueryPcapOptions.QUERY.get(configuration, String.class));
+  }
+
+  @Test
+  public void getConfigurationShouldReturnEmptyMapOnMissingJob() throws Exception {
+    MockPcapJob mockPcapJob = mock(MockPcapJob.class);
+    JobManager jobManager = mock(JobManager.class);
+    doThrow(new JobNotFoundException("Not found test exception.")).when(jobManager).getJob("user", "jobId");
+
+    PcapServiceImpl pcapService = new PcapServiceImpl(environment, configuration, mockPcapJobSupplier, jobManager, pcapToPdmlScriptWrapper);
+    Map<String, Object> configuration = pcapService.getConfiguration("user", "jobId");
+    Assert.assertEquals(new HashMap<>(), configuration);
+  }
+
+  @Test
+  public void getConfigurationShouldThrowRestException() throws Exception {
+    exception.expect(RestException.class);
+    exception.expectMessage("some job exception");
+
+    JobManager jobManager = mock(JobManager.class);
+    when(jobManager.getJob("user", "jobId")).thenThrow(new JobException("some job exception"));
+
+    PcapServiceImpl pcapService = new PcapServiceImpl(environment, configuration, new PcapJobSupplier(), jobManager, pcapToPdmlScriptWrapper);
+    pcapService.getConfiguration("user", "jobId");
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/2b6959b4/metron-platform/metron-job/src/main/java/org/apache/metron/job/Statusable.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-job/src/main/java/org/apache/metron/job/Statusable.java b/metron-platform/metron-job/src/main/java/org/apache/metron/job/Statusable.java
index 9bdea35..8634b33 100644
--- a/metron-platform/metron-job/src/main/java/org/apache/metron/job/Statusable.java
+++ b/metron-platform/metron-job/src/main/java/org/apache/metron/job/Statusable.java
@@ -77,4 +77,6 @@ public interface Statusable<PAGE_T> {
    */
   boolean validate(Map<String, Object> configuration);
 
+  Map<String, Object> getConfiguration();
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/2b6959b4/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
index a26e5ff..ea2aa29 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
@@ -28,6 +28,7 @@ import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -499,4 +500,8 @@ public class PcapJob<T> implements Statusable<Path> {
     return true;
   }
 
+  @Override
+  public Map<String, Object> getConfiguration() {
+    return new HashMap<>(this.configuration);
+  }
 }


[43/50] [abbrv] metron git commit: METRON-1724 Date/time validation missing in PCAP query (tiborm via nickwallen) closes apache/metron#1172

Posted by ni...@apache.org.
METRON-1724 Date/time validation missing in PCAP query (tiborm via nickwallen) closes apache/metron#1172


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/e66cfc80
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/e66cfc80
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/e66cfc80

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: e66cfc80e6a6fa53110c3f2fa8ee0d31ea997bf6
Parents: 9fdccba
Author: tiborm <ti...@gmail.com>
Authored: Mon Aug 27 09:04:21 2018 -0400
Committer: nickallen <ni...@apache.org>
Committed: Mon Aug 27 09:04:21 2018 -0400

----------------------------------------------------------------------
 .../src/app/pcap/model/pcap.mock.ts             |   4 +-
 .../src/app/pcap/model/pcap.request.ts          |  18 +-
 .../pcap-filters/pcap-filters.component.html    |  55 +++--
 .../pcap-filters/pcap-filters.component.scss    |  13 +-
 .../pcap-filters/pcap-filters.component.spec.ts | 207 ++++++++++---------
 .../pcap/pcap-filters/pcap-filters.component.ts | 156 +++++++++++---
 .../app/pcap/pcap-list/pcap-list.component.html |   4 +-
 .../pcap-packet-line.component.spec.ts          |   3 +-
 .../pcap-packet-line.component.ts               |  22 +-
 .../pcap-panel/pcap-panel.component.spec.ts     |  16 +-
 .../app/pcap/pcap-panel/pcap-panel.component.ts |   6 +-
 .../metron-alerts/src/app/pcap/pcap.module.ts   |   5 +-
 .../date-picker/date-picker.component.scss      |  14 +-
 .../shared/date-picker/date-picker.component.ts |  34 ++-
 .../shared/date-picker/date-picker.module.ts    |   3 +-
 .../metron-alerts/src/app/utils/constants.ts    |   3 +
 .../metron-alerts/src/app/utils/utils.ts        |  12 +-
 17 files changed, 360 insertions(+), 215 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
index bf02da8..c867fe9 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
@@ -22,9 +22,9 @@ export const fakePcapRequest = {
   startTimeMs: 0,
   endTimeMs: 0,
   ipSrcAddr: '0.0.0.0',
-  ipSrcPort: 80,
+  ipSrcPort: '80',
   ipDstAddr: '0.0.0.0',
-  ipDstPort: 80,
+  ipDstPort: '80',
   protocol: '*',
   packetFilter: '*',
   includeReverse: false

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
index 8afc963..3e00a4b 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
@@ -17,13 +17,13 @@
  */
 
 export class PcapRequest {
-  startTimeMs: number = 0;
-  endTimeMs: number = 150000000000000000;
-  ipSrcAddr: string = '';
-  ipSrcPort: number;
-  ipDstAddr: string = '';
-  ipDstPort: number;
-  protocol: string = '';
-  packetFilter: string = '';
-  includeReverse: boolean = false;
+  startTimeMs: number;
+  endTimeMs: number;
+  ipSrcAddr = '';
+  ipSrcPort = '';
+  ipDstAddr = '';
+  ipDstPort = '';
+  protocol = '';
+  packetFilter = '';
+  includeReverse = false;
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
index 039307a..c7a4db5 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
@@ -11,52 +11,63 @@
 	OR CONDITIONS OF ANY KIND, either express or implied. See the License for
   the specific language governing permissions and limitations under the License.
   -->
-<form (ngSubmit)="onSubmit()" #f="ngForm" class="form-inline pcap-search">
+<form (ngSubmit)="onSubmit()" #f="ngForm" [formGroup]="filterForm" class="form-inline pcap-search">
 
   <div class="form-group">
     <label>From</label>
-    <app-date-picker id="startTime" [(date)]="startTimeStr"> </app-date-picker>
+    <app-date-picker formControlName="startTime" data-qe-id="start-time"> </app-date-picker>
   </div>
   <div class="form-group">
     <label>To</label>
-    <app-date-picker id="endTime" [(date)]="endTimeStr"> </app-date-picker>
+    <app-date-picker formControlName="endTime" data-qe-id="end-time"> </app-date-picker>
   </div>
 
   <div class="form-group">
-    <label for="ipSrcAddr">IP Source Address</label>
-    <input name="ipSrcAddr" #ipSrcAddr="ngModel" class="form-control" pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}$" [(ngModel)]="model.ipSrcAddr" data-qe-id="ip-src-addr">
+    <label>IP Source Address</label>
+    <input class="form-control" formControlName="ipSrcAddr" data-qe-id="ip-src-addr">
   </div>
 
   <div class="form-group">
-    <label for="ipSrcPort">IP Source Port</label>
-    <input name="ipSrcPort" class="form-control" pattern="^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$" [(ngModel)]="ipSrcPort" data-qe-id="ip-src-port">
+    <label>IP Source Port</label>
+    <input class="form-control" formControlName="ipSrcPort" data-qe-id="ip-src-port">
   </div>
 
-  <div class="form-group"><label for="ipDstAddr">IP Dest Address</label>
-    <input name="ipDstAddr" #ipDstAddr="ngModel" class="form-control" pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}$" [(ngModel)]="model.ipDstAddr" data-qe-id="ip-dst-addr">
+  <div class="form-group">
+    <label>IP Dest Address</label>
+    <input class="form-control" formControlName="ipDstAddr" data-qe-id="ip-dst-addr">
   </div>
 
   <div class="form-group">
-    <label for="ipDstPort">IP Dest Port</label>
-    <input id="ipDstPort" name="ipDstPort" class="form-control" pattern="^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$" [(ngModel)]="ipDstPort" data-qe-id="ip-dest-port">
+    <label>IP Dest Port</label>
+    <input class="form-control" formControlName="ipDstPort" data-qe-id="ip-dst-port">
   </div>
 
-    <div class="form-group">
-        <label for="protocol">Protocol</label>
-        <input id="protocol" name="protocol" #protocol="ngModel" class="form-control" [(ngModel)]="model.protocol">
-    </div>
+  <div class="form-group">
+    <label>Protocol</label>
+    <input class="form-control" formControlName="protocol" data-qe-id="protocol">
+  </div>
 
-    <div class="form-group">
-        <label for="includeReverse">Include Reverse Traffic</label>
-        <input id="includeReverse" name="includeReverse" #includeReverse="ngModel" class="form-control" type="checkbox" [(ngModel)]="model.includeReverse">
-    </div>
+  <div class="form-group">
+    <label>Include Reverse Traffic</label>
+    <input class="form-control" type="checkbox" formControlName="includeReverse" data-qe-id="include-reverse">
+  </div>
 
   <div class="form-group">
-    <label for="filter">Filter</label>
-    <input id="filter" name="filter" #filter="ngModel" class="form-control" [(ngModel)]="model.packetFilter">
+    <label>Filter</label>
+    <input class="form-control" formControlName="packetFilter" data-qe-id="packet-filter">
   </div>
 
   <div class="form-group">
-    <button type="submit" [ngClass]="{'disabled':!f.form.valid || queryRunning}" class="btn btn-primary btn-search" [disabled]="!f.form.valid || queryRunning" data-qe-id="submit-button"></button>
+    <button type="submit" [ngClass]="{'disabled': filterForm.status === 'INVALID' || queryRunning}" class="btn btn-primary btn-search" [disabled]="filterForm.status === 'INVALID' || queryRunning" data-qe-id="submit-button"></button>
   </div>
 </form>
+<div class="pcap-search-validation-errors alert alert-danger" *ngIf="filterForm.invalid">
+  <h5 class="alert-heading">Validation errors</h5>
+  <ul>
+    <li *ngIf="filterForm.controls.startTime.invalid || filterForm.controls.endTime.invalid">Selected date range is invalid. The "To" date must be later than the "From" date and the "To" date cannot be in the future.</li>
+    <li *ngIf="filterForm.controls.ipSrcAddr.invalid">Source IP address format is invalid. Use valid v4IP format, for example, 192.168.0.1</li>
+    <li *ngIf="filterForm.controls.ipSrcPort.invalid">Source port is invalid. Port number must be within the range of 0-65535.</li>
+    <li *ngIf="filterForm.controls.ipDstAddr.invalid">Destination IP address format is invalid. Use valid v4IP format, for example, 192.168.0.1</li>
+    <li *ngIf="filterForm.controls.ipDstPort.invalid">Destination port is invalid. Port number must be within the range of 0-65535.</li>
+  </ul>
+</div>

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
index b33e804..909ce02 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
@@ -19,11 +19,7 @@
 @import "../../../styles.scss";
 @import "../../../variables.scss";
 
-.ng-valid[required], .ng-valid.required  {
-  
-}
-
-.ng-invalid:not(form)  {
+.ng-invalid:not(form) {
   border-left: 5px solid #a94442; /* red */
 }
 
@@ -35,7 +31,6 @@
 }
 
 .btn-search {
-
   min-width: 42px;
   padding-left: 0;
   padding-right: 0;
@@ -67,3 +62,9 @@
     background: $icon-button-background;
   }
 }
+
+.pcap-search-validation-errors {
+  & ul {
+    margin: 0
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
index 3f1ab07..c707451 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
@@ -19,19 +19,14 @@
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 import { By } from '@angular/platform-browser';
 
+import { DebugElement, SimpleChange } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
 import { PcapFiltersComponent } from './pcap-filters.component';
-import { FormsModule } from '../../../../node_modules/@angular/forms';
-import { Component, Input, Output, EventEmitter, DebugElement, SimpleChange } from '@angular/core';
+import { DatePickerModule } from '../../shared/date-picker/date-picker.module';
 import { PcapRequest } from '../model/pcap.request';
-
-@Component({
-  selector: 'app-date-picker',
-  template: '<input type="text" [(value)]="date">',
-})
-class FakeDatePickerComponent {
-  @Input() date: string;
-  @Output() dateChange = new EventEmitter<string>();
-}
+import { DEFAULT_TIMESTAMP_FORMAT } from '../../utils/constants';
+import * as moment from 'moment/moment';
 
 describe('PcapFiltersComponent', () => {
   let component: PcapFiltersComponent;
@@ -40,10 +35,11 @@ describe('PcapFiltersComponent', () => {
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [
-        FormsModule
+        FormsModule,
+        ReactiveFormsModule,
+        DatePickerModule,
       ],
       declarations: [
-        FakeDatePickerComponent,
         PcapFiltersComponent,
       ]
     })
@@ -59,114 +55,113 @@ describe('PcapFiltersComponent', () => {
   });
 
   it('From date should be bound to the component', () => {
-    let input = fixture.debugElement.query(By.css('#startTime'));
+    let input = fixture.debugElement.query(By.css('[data-qe-id="start-time"]'));
     const dateString = '2020-11-11 11:11:11';
-    input.componentInstance.dateChange.emit(dateString);
+    input.componentInstance.onChange(dateString);
     fixture.detectChanges();
-
-    expect(component.startTimeStr).toBe(dateString);
+    expect(component.filterForm.controls.startTime.value).toBe(dateString);
   });
 
   it('To date should be bound to the component', () => {
-    let input = fixture.debugElement.query(By.css('#endTime'));
+    let input = fixture.debugElement.query(By.css('[data-qe-id="end-time"]'));
     const dateString = '2030-11-11 11:11:11';
-    input.componentInstance.dateChange.emit(dateString);
+    input.componentInstance.onChange(dateString);
     fixture.detectChanges();
 
-    expect(component.endTimeStr).toBe(dateString);
+    expect(component.filterForm.controls.endTime.value).toBe(dateString);
   });
 
   it('IP Source Address should be bound to the model', () => {
-    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipSrcAddr"]');
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[data-qe-id="ip-src-addr"]');
     input.value = '192.168.0.1';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.model.ipSrcAddr).toBe('192.168.0.1');
+    expect(component.filterForm.controls.ipSrcAddr.value).toBe('192.168.0.1');
   });
 
   it('IP Source Port should be bound to the property', () => {
-    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipSrcPort"]');
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[data-qe-id="ip-src-port"]');
     input.value = '9345';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.ipSrcPort).toBe('9345');
+    expect(component.filterForm.controls.ipSrcPort.value).toBe('9345');
   });
 
   it('IP Source Port should be converted to number on submit', () => {
-    component.ipSrcPort = '42';
+    component.filterForm.patchValue({ ipSrcPort: '42' });
     component.search.emit = (model: PcapRequest) => {
-      expect(model.ipSrcPort).toBe(42);
+      expect(model.ipSrcPort).toBe('42');
     };
     component.onSubmit();
   });
 
   it('IP Dest Address should be bound to the model', () => {
-    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipDstAddr"]');
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[data-qe-id="ip-dst-addr"]');
     input.value = '256.0.0.7';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.model.ipDstAddr).toBe('256.0.0.7');
+    expect(component.filterForm.controls.ipDstAddr.value).toBe('256.0.0.7');
   });
 
   it('IP Dest Port should be bound to the property', () => {
-    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipDstPort"]');
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[data-qe-id="ip-dst-port"]');
     input.value = '8989';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.ipDstPort).toBe('8989');
+    expect(component.filterForm.controls.ipDstPort.value).toBe('8989');
   });
 
   it('IP Dest Port should be converted to number on submit', () => {
-    component.ipDstPort = '42';
+    component.filterForm.patchValue({ ipDstPort: '42' });
     component.search.emit = (model: PcapRequest) => {
-      expect(model.ipDstPort).toBe(42);
+      expect(model.ipDstPort).toBe('42');
     };
     component.onSubmit();
   });
 
   it('Protocol should be bound to the model', () => {
-    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="protocol"]');
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[data-qe-id="protocol"]');
     input.value = 'TCP';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.model.protocol).toBe('TCP');
+    expect(component.filterForm.controls.protocol.value).toBe('TCP');
   });
 
   it('Include Reverse Traffic should be bound to the model', () => {
-    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="includeReverse"]');
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[data-qe-id="include-reverse"]');
     input.click();
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.model.includeReverse).toBe(true);
+    expect(component.filterForm.controls.includeReverse.value).toBe(true);
   });
 
   it('Text filter should be bound to the model', () => {
-    let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="protocol"]');
+    let input: HTMLInputElement = fixture.nativeElement.querySelector('[data-qe-id="packet-filter"]');
     input.value = 'TestStringFilter';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.model.protocol).toBe('TestStringFilter');
+    expect(component.filterForm.controls.packetFilter.value).toBe('TestStringFilter');
   });
 
   it('From date should be converted to timestamp on submit', () => {
-    component.startTimeStr = '2220-12-12 12:12:12';
+    component.filterForm.patchValue({ startTime: '2220-12-12 12:12:12' });
     component.search.emit = (model: PcapRequest) => {
-      expect(model.startTimeMs).toBe(new Date(component.startTimeStr).getTime());
+      expect(model.startTimeMs).toBe(new Date(component.filterForm.controls.startTime.value).getTime());
     };
     component.onSubmit();
   });
 
   it('To date should be converted to timestamp on submit', () => {
-    component.endTimeStr = '2320-03-13 13:13:13';
+    component.filterForm.patchValue({ endTimeStr: '2320-03-13 13:13:13' });
     component.search.emit = (model: PcapRequest) => {
-      expect(model.endTimeMs).toBe(new Date(component.endTimeStr).getTime());
+      expect(model.endTimeMs).toBe(new Date(component.filterForm.controls.endTime.value).getTime());
     };
     component.onSubmit();
   });
@@ -179,19 +174,6 @@ describe('PcapFiltersComponent', () => {
     component.onSubmit();
   });
 
-  it('Port fields should be removed from request when set to empty', () => {
-    component.model.ipSrcPort = 44;
-    component.model.ipDstPort = 44;
-    component.ipSrcPort = '';
-    component.ipDstPort = '';
-
-    component.search.emit = (model: PcapRequest) => {
-      expect(model.ipSrcPort).toBeFalsy();
-      expect(model.ipDstPort).toBeFalsy();
-    };
-    component.onSubmit();
-  });
-
   it('Filter should have an output called search', () => {
     component.search.subscribe((filterModel) => {
       expect(filterModel).toBeDefined();
@@ -205,68 +187,43 @@ describe('PcapFiltersComponent', () => {
     expect(component.search.emit).toHaveBeenCalled();
   });
 
-  it('Search event should contains the filter model', () => {
-    spyOn(component.search, 'emit');
-    component.onSubmit();
-    expect(component.search.emit).toHaveBeenCalledWith(component.model);
-  });
-
-  it('Filter model structure aka PcapRequest', () => {
-    expect(fixture.componentInstance.model.hasOwnProperty('startTimeMs')).toBeTruthy();
-    expect(fixture.componentInstance.model.hasOwnProperty('endTimeMs')).toBeTruthy();
-    expect(fixture.componentInstance.model.hasOwnProperty('ipSrcAddr')).toBeTruthy();
-    expect(fixture.componentInstance.model.hasOwnProperty('ipSrcPort')).toBeFalsy();
-    expect(fixture.componentInstance.model.hasOwnProperty('ipDstAddr')).toBeTruthy();
-    expect(fixture.componentInstance.model.hasOwnProperty('ipDstPort')).toBeFalsy();
-    expect(fixture.componentInstance.model.hasOwnProperty('protocol')).toBeTruthy();
-    expect(fixture.componentInstance.model.hasOwnProperty('packetFilter')).toBeTruthy();
-    expect(fixture.componentInstance.model.hasOwnProperty('includeReverse')).toBeTruthy();
-  });
-
   it('should update request on changes', () => {
+    const startTimeStr = '2220-12-12 12:12:12';
+    const endTimeStr = '2320-03-13 13:13:13';
 
-    let startTimeStr = '2220-12-12 12:12:12';
-    let endTimeStr = '2320-03-13 13:13:13';
-
-    let newModel = {
-      startTimeMs: new Date(startTimeStr).getTime(),
-      endTimeMs: new Date(endTimeStr).getTime(),
-      ipSrcPort: 9345,
-      ipDstPort: 8989
-    };
-    component.model.startTimeMs = new Date(startTimeStr).getTime();
-    component.model.endTimeMs = new Date(endTimeStr).getTime();
+    const newModel = new PcapRequest();
+    newModel.startTimeMs = new Date(startTimeStr).getTime();
+    newModel.endTimeMs = new Date(endTimeStr).getTime();
+    newModel.ipSrcPort = '9345';
+    newModel.ipDstPort = '8989';
 
     component.ngOnChanges({
       model: new SimpleChange(null, newModel, false)
     });
 
-    expect(component.startTimeStr).toBe(startTimeStr);
-    expect(component.endTimeStr).toBe(endTimeStr);
-    expect(component.ipSrcPort).toBe('9345');
-    expect(component.ipDstPort).toBe('8989');
+    expect(component.filterForm.controls.startTime.value).toBe(startTimeStr);
+    expect(component.filterForm.controls.endTime.value).toBe(endTimeStr);
+    expect(component.filterForm.controls.ipSrcPort.value).toBe('9345');
+    expect(component.filterForm.controls.ipDstPort.value).toBe('8989');
   });
 
   it('should update request on changes with missing port filters', () => {
 
-    let startTimeStr = '2220-12-12 12:12:12';
-    let endTimeStr = '2320-03-13 13:13:13';
+    const startTimeStr = '2220-12-12 12:12:12';
+    const endTimeStr = '2320-03-13 13:13:13';
 
-    let newModel = {
-      startTimeMs: new Date(startTimeStr).getTime(),
-      endTimeMs: new Date(endTimeStr).getTime()
-    };
-    component.model.startTimeMs = new Date(startTimeStr).getTime();
-    component.model.endTimeMs = new Date(endTimeStr).getTime();
+    let newModel = new PcapRequest();
+    newModel.startTimeMs = new Date(startTimeStr).getTime();
+    newModel.endTimeMs = new Date(endTimeStr).getTime();
 
     component.ngOnChanges({
       model: new SimpleChange(null, newModel, false)
     });
 
-    expect(component.startTimeStr).toBe(startTimeStr);
-    expect(component.endTimeStr).toBe(endTimeStr);
-    expect(component.ipSrcPort).toBe('');
-    expect(component.ipDstPort).toBe('');
+    expect(component.filterForm.controls.startTime.value).toBe(startTimeStr);
+    expect(component.filterForm.controls.endTime.value).toBe(endTimeStr);
+    expect(component.filterForm.controls.ipSrcPort.value).toBe('');
+    expect(component.filterForm.controls.ipDstPort.value).toBe('');
   });
 
   describe('Filter validation', () => {
@@ -379,7 +336,7 @@ describe('PcapFiltersComponent', () => {
       ];
 
       invalidValues.forEach((value) => {
-        const els = getFieldWithSubmit('ip-dest-port');
+        const els = getFieldWithSubmit('ip-dst-port');
         expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
         expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
 
@@ -403,7 +360,7 @@ describe('PcapFiltersComponent', () => {
       ];
 
       validValues.forEach((value) => {
-        const els = getFieldWithSubmit('ip-dest-port');
+        const els = getFieldWithSubmit('ip-dst-port');
         expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
         expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
 
@@ -453,11 +410,55 @@ describe('PcapFiltersComponent', () => {
 
         setFieldValue(els.field, value);
 
-        expect(isFieldInvalid(els.field)).toBe(false, 'tthe field should be valid with ' + value);
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid with ' + value);
         expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled with ' + value);
         tearDown(els.field);
       });
     });
 
+    it('start date should be valid by default', () => {
+      expect(component.filterForm.get('startTime').valid).toBe(true);
+    });
+
+    it('start date is invalid if it is bigger than end date', () => {
+      // start time is bigger than end time
+      component.filterForm.patchValue({
+        startTime: '2018-08-24 16:30:00',
+        endTime: '2018-08-23 16:30:00'
+      });
+
+      expect(component.filterForm.get('startTime').valid).toBe(false);
+    });
+
+    it('start date should be valid again based on the end date', () => {
+      // start time is bigger than end time
+      component.filterForm.patchValue({
+        startTime: '2018-08-24 16:30:00',
+        endTime: '2018-08-23 16:30:00'
+      });
+
+      expect(component.filterForm.get('startTime').valid).toBe(false);
+
+      component.filterForm.patchValue({
+        endTime: '2018-08-25 16:30:00'
+      });
+
+      expect(component.filterForm.get('startTime').valid).toBe(true);
+    });
+
+    it('end date should be valid by default', () => {
+      expect(component.filterForm.get('endTime').valid).toBe(true);
+    });
+
+    it('end date is invalid if it is in the future', () => {
+
+      expect(component.filterForm.get('endTime').valid).toBe(true);
+
+      component.filterForm.patchValue({
+        endTime: moment(new Date()).add(2, 'days').format(DEFAULT_TIMESTAMP_FORMAT)
+      });
+
+      expect(component.filterForm.get('endTime').valid).toBe(false);
+    });
   });
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
index b23a2e2..7cc8980 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
@@ -15,63 +15,153 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges} from '@angular/core';
+import {Component, Input, Output, EventEmitter, OnChanges, OnInit, OnDestroy, SimpleChanges} from '@angular/core';
+import { FormGroup, FormControl, Validators, ValidationErrors } from '@angular/forms';
+
 import * as moment from 'moment/moment';
-import { DEFAULT_TIMESTAMP_FORMAT } from '../../utils/constants';
+import { DEFAULT_START_TIME, DEFAULT_END_TIME, DEFAULT_TIMESTAMP_FORMAT } from '../../utils/constants';
 
 import { PcapRequest } from '../model/pcap.request';
+import { Observable, Subscription } from 'rxjs';
+
+function validateStartDate(formControl: FormControl): ValidationErrors | null {
+  if (!formControl.parent) {
+    return null;
+  }
+  const filterForm = formControl.parent;
+  const startTimeMs = new Date(filterForm.controls['startTime'].value).getTime();
+  const endTimeMs = new Date(filterForm.controls['endTime'].value).getTime();
+  if (startTimeMs > endTimeMs) {
+    return { error: 'Start time cannot be bigger than end date.' };
+  }
+  return null;
+}
+
+function validateEndDate(formControl: FormControl): ValidationErrors | null {
+  if (!formControl.parent) {
+    return null;
+  }
+  const filterForm = formControl.parent;
+  const endTimeMs = new Date(filterForm.controls['endTime'].value).getTime();
+  if (endTimeMs > new Date().getTime()) {
+    return { error: 'End time cannot be in the future.' };
+  }
+  return null;
+}
+
+function transformPcapRequestToFormGroupValue(model: PcapRequest): PcapFilterFormValue {
+  const startTimeStr = moment(model.startTimeMs > 0 ? model.startTimeMs : DEFAULT_START_TIME).format(DEFAULT_TIMESTAMP_FORMAT);
+  let endTimeStr = moment(model.endTimeMs).format(DEFAULT_TIMESTAMP_FORMAT);
+  if (isNaN((new Date(model.endTimeMs).getTime()))) {
+    endTimeStr = moment(DEFAULT_END_TIME).format(DEFAULT_TIMESTAMP_FORMAT);
+  } else {
+    endTimeStr = moment(model.endTimeMs).format(DEFAULT_TIMESTAMP_FORMAT);
+  }
+
+  return {
+    startTime: startTimeStr,
+    endTime: endTimeStr,
+    ipSrcAddr: model.ipSrcAddr,
+    ipDstAddr: model.ipDstAddr,
+    ipSrcPort: model.ipSrcPort ? String(model.ipSrcPort) : '',
+    ipDstPort: model.ipDstPort ? String(model.ipDstPort) : '',
+    protocol: model.protocol,
+    includeReverse: model.includeReverse,
+    packetFilter: model.packetFilter
+  };
+}
+
+function transformFormGroupValueToPcapRequest(control: FormGroup): PcapRequest {
+  const pcapRequest = new PcapRequest();
+  pcapRequest.startTimeMs = new Date(control.value.startTime).getTime();
+  pcapRequest.endTimeMs = new Date(control.value.endTime).getTime();
+  pcapRequest.ipSrcAddr = control.value.ipSrcAddr;
+  pcapRequest.ipDstAddr = control.value.ipDstAddr;
+  pcapRequest.ipSrcPort = control.value.ipSrcPort;
+  pcapRequest.ipDstPort = control.value.ipDstPort;
+  pcapRequest.protocol =  control.value.protocol;
+  pcapRequest.includeReverse = control.value.includeReverse;
+  pcapRequest.packetFilter = control.value.packetFilter;
+  return pcapRequest;
+}
+
+export type PcapFilterFormValue = {
+  startTime: string,
+  endTime: string,
+  ipSrcAddr: string,
+  ipDstAddr: string,
+  ipSrcPort: string,
+  ipDstPort: string,
+  protocol: string,
+  includeReverse: boolean,
+  packetFilter: string
+};
 
 @Component({
   selector: 'app-pcap-filters',
   templateUrl: './pcap-filters.component.html',
   styleUrls: ['./pcap-filters.component.scss']
 })
-export class PcapFiltersComponent implements OnInit, OnChanges {
+export class PcapFiltersComponent implements OnInit, OnChanges, OnDestroy {
 
-  @Input() queryRunning: boolean = true;
+  @Input() queryRunning = true;
   @Input() model: PcapRequest = new PcapRequest();
   @Output() search: EventEmitter<PcapRequest> = new EventEmitter<PcapRequest>();
 
-  startTimeStr: string;
-  endTimeStr: string;
-  ipSrcPort: string = '';
-  ipDstPort: string = '';
+  private validIp: RegExp = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}$/;
+  private validPort: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
 
-  constructor() { }
+  private dateRangeChangeSubscription: Subscription;
 
-  ngOnInit() {
-    const endTime = new Date();
-    const startTime = new Date().setDate(endTime.getDate() - 5);
+  filterForm = new FormGroup({
+    startTime: new FormControl(moment(DEFAULT_START_TIME).format(DEFAULT_TIMESTAMP_FORMAT), validateStartDate),
+    endTime: new FormControl(moment(DEFAULT_END_TIME).format(DEFAULT_TIMESTAMP_FORMAT), validateEndDate),
+    ipSrcAddr: new FormControl('', Validators.pattern(this.validIp)),
+    ipSrcPort: new FormControl('', Validators.pattern(this.validPort)),
+    ipDstAddr: new FormControl('', Validators.pattern(this.validIp)),
+    ipDstPort: new FormControl('', Validators.pattern(this.validPort)),
+    protocol: new FormControl(''),
+    includeReverse: new FormControl(),
+    packetFilter: new FormControl(''),
+  });
+
+  subscribeToDateRangeChanges(callback: () => void): Subscription {
+    const startTimeChanges: Observable<string> = this.filterForm.get('startTime').valueChanges;
+    const endTimeChanges: Observable<string> = this.filterForm.get('endTime').valueChanges;
+    return startTimeChanges.merge(endTimeChanges).subscribe(callback);
+  }
 
-    this.startTimeStr = moment(startTime).format(DEFAULT_TIMESTAMP_FORMAT);
-    this.endTimeStr = moment(endTime).format(DEFAULT_TIMESTAMP_FORMAT);
+  forceValidateDateRangeFields() {
+    [
+      this.filterForm.get('startTime'),
+      this.filterForm.get('endTime'),
+    ].forEach((control: FormControl) => {
+      control.updateValueAndValidity({
+        emitEvent: false
+      });
+    });
+  }
+
+  ngOnInit() {
+    this.dateRangeChangeSubscription = this.subscribeToDateRangeChanges(() => {
+      this.forceValidateDateRangeFields();
+    });
   }
 
   ngOnChanges(changes: SimpleChanges): void {
     if (changes['model']) {
-      this.startTimeStr = moment(changes['model'].currentValue.startTimeMs).format(DEFAULT_TIMESTAMP_FORMAT);
-      this.endTimeStr = moment(changes['model'].currentValue.endTimeMs).format(DEFAULT_TIMESTAMP_FORMAT);
-      let newIpSrcPort = changes['model'].currentValue.ipSrcPort;
-      this.ipSrcPort = newIpSrcPort ? newIpSrcPort.toString() : '';
-      let newIpDstPort = changes['model'].currentValue.ipDstPort;
-      this.ipDstPort = newIpDstPort ? newIpDstPort.toString() : '';
+      const newModel: PcapRequest = changes['model'].currentValue;
+      const controlValue = transformPcapRequestToFormGroupValue(newModel);
+      this.filterForm.setValue(controlValue);
     }
   }
 
   onSubmit() {
-    this.model.startTimeMs = moment(this.startTimeStr, DEFAULT_TIMESTAMP_FORMAT).valueOf();
-    this.model.endTimeMs = moment(this.endTimeStr, DEFAULT_TIMESTAMP_FORMAT).valueOf();
-    if (this.ipSrcPort !== '') {
-      this.model.ipSrcPort = +this.ipSrcPort;
-    } else {
-      delete this.model.ipSrcPort;
-    }
-    if (this.ipDstPort !== '') {
-      this.model.ipDstPort = +this.ipDstPort;
-    } else {
-      delete this.model.ipDstPort;
-    }
+    const pcapRequest = transformFormGroupValueToPcapRequest(this.filterForm);
+    this.search.emit(pcapRequest);
+  }
 
-    this.search.emit(this.model);
+  ngOnDestroy() {
+    this.dateRangeChangeSubscription.unsubscribe();
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
index 5337935..10b7be2 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
@@ -16,9 +16,9 @@
     <thead>
       <tr>
         <th scope="col">Timestamp</th>
-        <th scope="col">Source Addr</th>
+        <th scope="col">Source Address</th>
         <th scope="col">Source Port</th>
-        <th scope="col">Dest Addr</th>
+        <th scope="col">Dest Address</th>
         <th scope="col">Dest Port</th>
         <th scope="col">Protocol</th>
       </tr>

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
index 00f081f..dccfaa2 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
@@ -16,8 +16,7 @@
  * limitations under the License.
  */
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { fakePacket } from '../model/pdml.mock';
-import { fakeUdpPacket } from '../model/pdml.mock';
+import { fakePacket, fakeUdpPacket } from '../model/pdml.mock';
 
 import { PcapPacketLineComponent } from './pcap-packet-line.component';
 

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
index b1546af..913aff9 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 import { Component, OnInit, Input } from '@angular/core';
-import { PdmlPacket, PdmlProto, PdmlField } from '../model/pdml'
+import { PdmlPacket, PdmlProto, PdmlField } from '../model/pdml';
 
 @Component({
   selector: '[app-pcap-packet-line]',
@@ -37,18 +37,18 @@ export class PcapPacketLineComponent implements OnInit {
   constructor() { }
 
   ngOnInit() {
-    const genProto: PdmlProto = this.packet.protos.filter(p => p.name == "geninfo")[0];
-    const ipProto: PdmlProto = this.packet.protos.filter(p => p.name == "ip")[0];
-    const tcpProto: PdmlProto = this.packet.protos.filter(p => p.name == "tcp")[0];
-    const udpProto: PdmlProto = this.packet.protos.filter(p => p.name == "udp")[0];
+    const genProto: PdmlProto = this.packet.protos.filter(p => p.name === 'geninfo')[0];
+    const ipProto: PdmlProto = this.packet.protos.filter(p => p.name === 'ip')[0];
+    const tcpProto: PdmlProto = this.packet.protos.filter(p => p.name === 'tcp')[0];
+    const udpProto: PdmlProto = this.packet.protos.filter(p => p.name === 'udp')[0];
 
     this.ip = {
-      timestamp: PdmlProto.findField(genProto,'timestamp'),
-      ipSrcAddr: PdmlProto.findField(ipProto,'ip.src'),
-      ipSrcPort: tcpProto ? PdmlProto.findField(tcpProto,'tcp.srcport') : PdmlProto.findField(udpProto,'udp.srcport'),
-      ipDestAddr: PdmlProto.findField(ipProto,'ip.dst'),
-      ipDestPort: tcpProto ? PdmlProto.findField(tcpProto,'tcp.dstport') : PdmlProto.findField(udpProto,'udp.dstport'),
-      protocol: PdmlProto.findField(ipProto,'ip.proto')
+      timestamp: PdmlProto.findField(genProto, 'timestamp'),
+      ipSrcAddr: PdmlProto.findField(ipProto, 'ip.src'),
+      ipSrcPort: tcpProto ? PdmlProto.findField(tcpProto, 'tcp.srcport') : PdmlProto.findField(udpProto, 'udp.srcport'),
+      ipDestAddr: PdmlProto.findField(ipProto, 'ip.dst'),
+      ipDestPort: tcpProto ? PdmlProto.findField(tcpProto, 'tcp.dstport') : PdmlProto.findField(udpProto, 'udp.dstport'),
+      protocol: PdmlProto.findField(ipProto, 'ip.proto')
     };
   }
 

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
index fe4df1e..2ebf884 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
@@ -26,8 +26,8 @@ import { PcapPagination } from '../model/pcap-pagination';
 import { By } from '../../../../node_modules/@angular/platform-browser';
 import { PcapRequest } from '../model/pcap.request';
 import { defer } from 'rxjs/observable/defer';
-import {Observable} from "rxjs/Observable";
-import {RestError} from "../../model/rest-error";
+import { Observable } from 'rxjs/Observable';
+import { RestError } from '../../model/rest-error';
 
 @Component({
   selector: 'app-pcap-filters',
@@ -349,7 +349,7 @@ describe('PcapPanelComponent', () => {
 
   it('should hide the progress bar if the user clicks on the cancel button', fakeAsync(() => {
     component.queryRunning = true;
-    component.queryId = 'testid';
+    component.queryId = '42';
     fixture.detectChanges();
     expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeDefined();
 
@@ -360,7 +360,7 @@ describe('PcapPanelComponent', () => {
     tick();
     fixture.detectChanges();
 
-    expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeFalsy();
+    expect(fixture.debugElement.query(By.css('.pcap-progress')) == null).toBe(true);
   }));
 
   it('should hide the progress bar if the cancellation request fails', fakeAsync(() => {
@@ -370,7 +370,7 @@ describe('PcapPanelComponent', () => {
     );
 
     component.queryRunning = true;
-    component.queryId = 'testid';
+    component.queryId = '42';
     fixture.detectChanges();
     expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeDefined();
 
@@ -381,7 +381,7 @@ describe('PcapPanelComponent', () => {
     tick();
     fixture.detectChanges();
 
-    expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeFalsy();
+    expect(fixture.debugElement.query(By.css('.pcap-progress')) == null).toBe(true);
   }));
 
   it('should show an error message if the cancellation request fails', fakeAsync(() => {
@@ -392,9 +392,9 @@ describe('PcapPanelComponent', () => {
     );
 
     component.queryRunning = true;
-    component.queryId = 'testid';
+    component.queryId = '42';
     fixture.detectChanges();
-    expect(fixture.debugElement.query(By.css('[data-qe-id="error"]'))).toBeFalsy();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="error"]')) == null).toBe(true);
 
     const cancelBtn = fixture.debugElement.query(By.css('[data-qe-id="pcap-cancel-query-button"]'));
     const cancelBtnEl = cancelBtn.nativeElement;

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
index 7c88007..4a017db 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
@@ -23,7 +23,7 @@ import { PcapRequest } from '../model/pcap.request';
 import { Pdml } from '../model/pdml';
 import { Subscription } from 'rxjs/Rx';
 import { PcapPagination } from '../model/pcap-pagination';
-import { RestError } from "../../model/rest-error";
+import { RestError } from '../../model/rest-error';
 
 @Component({
   selector: 'app-pcap-panel',
@@ -40,9 +40,9 @@ export class PcapPanelComponent implements OnInit, OnDestroy {
   cancelSubscription: Subscription;
   submitSubscription: Subscription;
   getSubscription: Subscription;
-  queryRunning: boolean = false;
+  queryRunning = false;
   queryId: string;
-  progressWidth: number = 0;
+  progressWidth = 0;
   pagination: PcapPagination = new PcapPagination();
   savedPcapRequest: {};
   errorMsg: string;

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
index 8c0db02..784bc93 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
@@ -17,7 +17,7 @@
  */
 import {NgModule} from '@angular/core';
 import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { HttpModule } from '@angular/http';
 
 import { routing } from './pcap.routing';
@@ -29,7 +29,7 @@ import { PcapPacketComponent } from './pcap-packet/pcap-packet.component';
 import { PcapFiltersComponent } from './pcap-filters/pcap-filters.component';
 import { PcapPanelComponent } from './pcap-panel/pcap-panel.component';
 import { PcapPacketLineComponent } from './pcap-packet-line/pcap-packet-line.component';
-import { PcapPaginationComponent } from './pcap-pagination/pcap-pagination.component'
+import { PcapPaginationComponent } from './pcap-pagination/pcap-pagination.component';
 import { PcapService } from './service/pcap.service';
 
 @NgModule({
@@ -37,6 +37,7 @@ import { PcapService } from './service/pcap.service';
     routing,
     CommonModule,
     FormsModule,
+    ReactiveFormsModule,
     HttpModule,
     DatePickerModule,
   ],

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.scss b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.scss
index 813b6a5..f46599a 100644
--- a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.scss
+++ b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.scss
@@ -28,4 +28,16 @@
     font-family: "FontAwesome";
     content: '\f073';
   }
-}
\ No newline at end of file
+}
+
+:host(.ng-invalid):not(form) {
+  border-left: none !important;
+}
+
+:host(.ng-invalid) input {
+  border-left: 5px solid #a94442; /* red */
+
+  &:focus {
+    border-color: #4D4D4D;
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.ts b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.ts
index d58bb5f..9014058 100644
--- a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.ts
+++ b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.ts
@@ -15,21 +15,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 import { Component, OnInit, ViewChild, ElementRef, OnChanges, SimpleChanges, Input, Output, EventEmitter } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
 import * as moment from 'moment/moment';
-import * as Pikaday from "pikaday-time";
+import * as Pikaday from 'pikaday-time';
 
 @Component({
   selector: 'app-date-picker',
   templateUrl: './date-picker.component.html',
+  providers : [{
+    provide : NG_VALUE_ACCESSOR,
+    useExisting: DatePickerComponent,
+    multi: true,
+  }],
   styleUrls: ['./date-picker.component.scss']
 })
-export class DatePickerComponent implements OnInit, OnChanges {
+export class DatePickerComponent implements OnInit, OnChanges, ControlValueAccessor {
   defaultDateStr = 'now';
   picker: Pikaday;
   dateStr = this.defaultDateStr;
 
+  private onChange: Function;
+  private onTouched: Function;
+
   @Input() date = '';
   @Input() minDate = '';
   @Output() dateChange = new EventEmitter<string>();
@@ -45,7 +53,12 @@ export class DatePickerComponent implements OnInit, OnChanges {
       use24hour: true,
       onSelect: function() {
         _datePickerComponent.dateStr = this.getMoment().format('YYYY-MM-DD HH:mm:ss');
-        setTimeout(() => _datePickerComponent.dateChange.emit(_datePickerComponent.dateStr), 0);
+        setTimeout(() => {
+          _datePickerComponent.dateChange.emit(_datePickerComponent.dateStr);
+          if (_datePickerComponent.onChange) {
+            _datePickerComponent.onChange(_datePickerComponent.dateStr);
+          }
+        }, 0);
       }
     };
     this.picker = new Pikaday(pikadayConfig);
@@ -62,6 +75,19 @@ export class DatePickerComponent implements OnInit, OnChanges {
     }
   }
 
+  writeValue(value) {
+    this.date = value;
+    this.setDate();
+  }
+
+  registerOnChange(fn) {
+    this.onChange = fn;
+  }
+
+  registerOnTouched(fn) {
+    this.onTouched = fn;
+  }
+
   setDate() {
     if (this.date === '') {
       this.dateStr = this.defaultDateStr;

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.module.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.module.ts b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.module.ts
index d59d566..e5dfd6c 100644
--- a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.module.ts
+++ b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.module.ts
@@ -19,8 +19,7 @@
 import { NgModule } from '@angular/core';
 import { CommonModule }        from '@angular/common';
 import { FormsModule }         from '@angular/forms';
-import {DatePickerComponent} from './date-picker.component';
-import {SharedModule} from '../shared.module';
+import { DatePickerComponent } from './date-picker.component';
 
 @NgModule({
   imports: [

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/utils/constants.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/utils/constants.ts b/metron-interface/metron-alerts/src/app/utils/constants.ts
index 74e887a..703e0f7 100644
--- a/metron-interface/metron-alerts/src/app/utils/constants.ts
+++ b/metron-interface/metron-alerts/src/app/utils/constants.ts
@@ -37,3 +37,6 @@ export let INDEXES =  environment.indices ? environment.indices.split(',') : [];
 export let POLLING_DEFAULT_STATE = environment.defaultPollingState;
 
 export let MAX_ALERTS_IN_META_ALERTS = 350;
+
+export const DEFAULT_END_TIME = new Date();
+export const DEFAULT_START_TIME = new Date().setDate(DEFAULT_END_TIME.getDate() - 5);

http://git-wip-us.apache.org/repos/asf/metron/blob/e66cfc80/metron-interface/metron-alerts/src/app/utils/utils.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/utils/utils.ts b/metron-interface/metron-alerts/src/app/utils/utils.ts
index 61cc45b..4eb098a 100644
--- a/metron-interface/metron-alerts/src/app/utils/utils.ts
+++ b/metron-interface/metron-alerts/src/app/utils/utils.ts
@@ -17,12 +17,15 @@
  */
 import * as moment from 'moment/moment';
 
-import {DEFAULT_TIMESTAMP_FORMAT, META_ALERTS_SENSOR_TYPE} from './constants';
-import {Alert} from '../model/alert';
-import {DateFilterValue} from '../model/date-filter-value';
-import { environment } from 'environments/environment';
+import { DEFAULT_START_TIME, DEFAULT_END_TIME, DEFAULT_TIMESTAMP_FORMAT, META_ALERTS_SENSOR_TYPE } from './constants';
+import { Alert } from '../model/alert';
+import { DateFilterValue } from '../model/date-filter-value';
+import { PcapRequest } from '../pcap/model/pcap.request';
+import { PcapFilterFormValue } from '../pcap/pcap-filters/pcap-filters.component';
+import { FormGroup } from '@angular/forms';
 
 export class Utils {
+  
   public static escapeESField(field: string): string {
     return field.replace(/:/g, '\\:');
   }
@@ -201,5 +204,4 @@ export class Utils {
 
     return {toDate: toDate, fromDate: fromDate};
   }
-
 }


[46/50] [abbrv] metron git commit: METRON-1752 Prevent package.lock from changing during build (sardell via merrimanr) closes apache/metron#1177

Posted by ni...@apache.org.
METRON-1752 Prevent package.lock from changing during build (sardell via merrimanr) closes apache/metron#1177


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/09826da8
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/09826da8
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/09826da8

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 09826da85eddb014a4b718233d53480220b1f5dd
Parents: e66cfc8
Author: sardell <sa...@hortonworks.com>
Authored: Mon Aug 27 15:14:19 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Mon Aug 27 15:14:19 2018 -0500

----------------------------------------------------------------------
 metron-interface/metron-alerts/pom.xml          |     6 +-
 .../metron-config/package-lock.json             | 17734 +++++++++++++++++
 metron-interface/metron-config/pom.xml          |    30 +-
 3 files changed, 17752 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/09826da8/metron-interface/metron-alerts/pom.xml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/pom.xml b/metron-interface/metron-alerts/pom.xml
index 996f473..f67a013 100644
--- a/metron-interface/metron-alerts/pom.xml
+++ b/metron-interface/metron-alerts/pom.xml
@@ -26,7 +26,7 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <node.version>v9.11.1</node.version>
-        <npm.version>5.8.0</npm.version>
+        <npm.version>6.2.0</npm.version>
     </properties>
     <dependencies>
     </dependencies>
@@ -52,12 +52,12 @@
                     </execution>
                     <execution>
                         <phase>generate-resources</phase>
-                        <id>npm install</id>
+                        <id>npm ci</id>
                         <goals>
                             <goal>npm</goal>
                         </goals>
                         <configuration>
-                            <arguments>install</arguments>
+                            <arguments>ci</arguments>
                         </configuration>
                     </execution>
                     <execution>


[07/50] [abbrv] metron git commit: METRON-1701 Update General notes on the installation of Pycapa on Kerberized cluster (MohanDV via nickwallen) closes apache/metron#1136

Posted by ni...@apache.org.
METRON-1701 Update General notes on the installation of Pycapa on Kerberized cluster (MohanDV via nickwallen) closes apache/metron#1136


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/1b2bdff5
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/1b2bdff5
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/1b2bdff5

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 1b2bdff53106694a9223ecc97f7ce9d0a78b4b09
Parents: a1e25a4
Author: MohanDV <mo...@gmail.com>
Authored: Wed Aug 1 12:13:19 2018 -0400
Committer: nickallen <ni...@apache.org>
Committed: Wed Aug 1 12:13:19 2018 -0400

----------------------------------------------------------------------
 metron-sensors/pycapa/README.md | 47 ++++++++++++++++++++++--------------
 1 file changed, 29 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/1b2bdff5/metron-sensors/pycapa/README.md
----------------------------------------------------------------------
diff --git a/metron-sensors/pycapa/README.md b/metron-sensors/pycapa/README.md
index 7e688a8..9f01d33 100644
--- a/metron-sensors/pycapa/README.md
+++ b/metron-sensors/pycapa/README.md
@@ -34,9 +34,9 @@ Pycapa performs network packet capture, both off-the-wire and from a Kafka topic
 Installation
 ============
 
-General notes on the installation of Pycapa.
+General notes on the installation of Pycapa. 
 * Python 2.7 is required.
-* The following package dependencies are required and can be installed automatically with `pip`.
+* The following package dependencies are required and can be installed automatically with `pip`. The requirements are installed as part of step 4
   * [confluent-kafka-python](https://github.com/confluentinc/confluent-kafka-python)
   * [pcapy](https://github.com/CoreSecurity/pcapy)
 * These instructions can be used directly on CentOS 7+.  
@@ -54,8 +54,8 @@ General notes on the installation of Pycapa.
 
     ```
     export PREFIX=/usr
-    wget https://github.com/edenhill/librdkafka/archive/v0.9.4.tar.gz   -O - | tar -xz
-    cd librdkafka-0.9.4/
+    wget https://github.com/edenhill/librdkafka/archive/v0.11.5.tar.gz   -O - | tar -xz
+    cd librdkafka-0.11.5/
     ./configure --prefix=$PREFIX
     make
     make install
@@ -231,18 +231,24 @@ Capturing on 'Standard input'
 
 ### Kerberos
 
-The probe can be used in a Kerberized environment.  Follow these additional steps to use Pycapa with Kerberos.  The following assumptions have been made.  These may need altered to fit your environment.
+The probe can be used in a Kerberized environment. The Python client README (https://github.com/confluentinc/confluent-kafka-python) has an important note for Kerberos case that the pre-built Linux wheels do NOT contain SASL Kerberos support. You will need to use the non-binary wheel to install confluent-kafka-python and build/install librdkafka separately. Follow these additional steps to use Pycapa with Kerberos.  The following assumptions have been made.  These may need altered to fit your environment.
 
   * The Kafka broker is at `kafka1:6667`
   * Zookeeper is at `zookeeper1:2181`
   * The Kafka security protocol is `SASL_PLAINTEXT`
   * The keytab used is located at `/etc/security/keytabs/metron.headless.keytab`
   * The service principal is `metron@EXAMPLE.COM`
+   
+ 
+1. If it is not, ensure that you have `libsasl` or `libsasl2` installed.  On CentOS, this can be installed with the following command.
+     ```
+        yum install -y cyrus-sasl cyrus-sasl-devel cyrus-sasl-gssapi
+     ```
 
 1. Build Librdkafka with SASL support (` --enable-sasl`) and install at your chosen $PREFIX.
     ```
-    wget https://github.com/edenhill/librdkafka/archive/v0.9.4.tar.gz  -O - | tar -xz
-    cd librdkafka-0.9.4/
+    wget https://github.com/edenhill/librdkafka/archive/v0.11.5.tar.gz  -O - | tar -xz
+    cd librdkafka-0.11.5/
     ./configure --prefix=$PREFIX --enable-sasl
     make
     make install
@@ -250,14 +256,19 @@ The probe can be used in a Kerberized environment.  Follow these additional step
 
 1. Validate Librdkafka does indeed support SASL.  Run the following command and ensure that `sasl` is returned as a built-in feature.
     ```
-    $ examples/rdkafka_example -X builtin.features
-    builtin.features = gzip,snappy,ssl,sasl,regex
-    ```
-
-   If it is not, ensure that you have `libsasl` or `libsasl2` installed.  On CentOS, this can be installed with the following command.
-    ```
-    yum install -y cyrus-sasl cyrus-sasl-devel cyrus-sasl-gssapi
+    $ examples/rdkafka_example -X builtin.features    
+      builtin.features = gzip,snappy,ssl,sasl,regex,lz4,sasl_gssapi,sasl_plain,sasl_scram,plugins
     ```
+1. The source install of confluent-kafka.
+       
+    If you have already installed, remove the binary wheel python client first, repeat until it says no longer installed          
+      ```       
+       pip uninstall -y confluent-kafka 
+      ```
+       
+      ```
+       pip install --no-binary :all: confluent-kafka
+      ```
 
 1. Grant access to your Kafka topic.  In this example the topic is simply named `pcap`.
     ```
@@ -279,8 +290,8 @@ The probe can be used in a Kerberized environment.  Follow these additional step
   * `security.protocol`
   * `sasl.kerberos.keytab`
   * `sasl.kerberos.principal`
-
-        ```
+  
+    ```
         $ pycapa --producer \
             --interface eth0 \
             --kafka-broker kafka1:6667 \
@@ -292,8 +303,8 @@ The probe can be used in a Kerberized environment.  Follow these additional step
         INFO:root:Starting packet capture
         INFO:root:Waiting for '1' message(s) to flush
         INFO:root:'10' packet(s) in, '10' packet(s) out
-        ```
-
+    ```
+    
 FAQs
 ====
 


[20/50] [abbrv] metron git commit: METRON-1683 PCAP UI - Fix the download progress bar (sardell via merrimanr) closes apache/metron#1122

Posted by ni...@apache.org.
http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.spec.ts
index 155e1f8..de39a0a 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.spec.ts
@@ -16,9 +16,10 @@
  * limitations under the License.
  */
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { fakePacket } from '../model/pdml.mock';
 
 import { PcapPacketComponent } from './pcap-packet.component';
-import { PdmlPacket } from '../model/pdml';
+import { By } from '@angular/platform-browser';
 
 describe('PcapPacketComponent', () => {
   let component: PcapPacketComponent;
@@ -34,1250 +35,45 @@ describe('PcapPacketComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(PcapPacketComponent);
     component = fixture.componentInstance;
-    component.packet = fakePacket as PdmlPacket;
+    component.packet = fakePacket;
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
-});
 
-const fakePacket = {
-  name: "something",
-  expanded: false,
-  protos: [
-    {
-      "name": "geninfo",
-      "pos": "0",
-      "showname": "General information",
-      "size": "722",
-      "hide": null,
-      "fields": [
-        {
-          "name": "num",
-          "pos": "0",
-          "showname": "Number",
-          "size": "722",
-          "value": "1",
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "len",
-          "pos": "0",
-          "showname": "Frame Length",
-          "size": "722",
-          "value": "2d2",
-          "show": "722",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "caplen",
-          "pos": "0",
-          "showname": "Captured Length",
-          "size": "722",
-          "value": "2d2",
-          "show": "722",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "timestamp",
-          "pos": "0",
-          "showname": "Captured Time",
-          "size": "722",
-          "value": "1458240269.373968000",
-          "show": "Mar 17, 2016 18:44:29.373968000 UTC",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "frame",
-      "pos": "0",
-      "showname": "Frame 1: 722 bytes on wire (5776 bits), 722 bytes captured (5776 bits)",
-      "size": "722",
-      "hide": null,
-      "fields": [
-        {
-          "name": "frame.dlt",
-          "pos": "0",
-          "showname": "WTAP_ENCAP: 1",
-          "size": "0",
-          "value": null,
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time",
-          "pos": "0",
-          "showname": "Arrival Time: Mar 17, 2016 18:44:29.373968000 UTC",
-          "size": "0",
-          "value": null,
-          "show": "Mar 17, 2016 18:44:29.373968000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.offset_shift",
-          "pos": "0",
-          "showname": "Time shift for this packet: 0.000000000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "0.000000000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time_epoch",
-          "pos": "0",
-          "showname": "Epoch Time: 1458240269.373968000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "1458240269.373968000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time_delta",
-          "pos": "0",
-          "showname": "Time delta from previous captured frame: 0.000000000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "0.000000000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time_delta_displayed",
-          "pos": "0",
-          "showname": "Time delta from previous displayed frame: 0.000000000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "0.000000000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time_relative",
-          "pos": "0",
-          "showname": "Time since reference or first frame: 0.000000000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "0.000000000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.number",
-          "pos": "0",
-          "showname": "Frame Number: 1",
-          "size": "0",
-          "value": null,
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.len",
-          "pos": "0",
-          "showname": "Frame Length: 722 bytes (5776 bits)",
-          "size": "0",
-          "value": null,
-          "show": "722",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.cap_len",
-          "pos": "0",
-          "showname": "Capture Length: 722 bytes (5776 bits)",
-          "size": "0",
-          "value": null,
-          "show": "722",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.marked",
-          "pos": "0",
-          "showname": "Frame is marked: False",
-          "size": "0",
-          "value": null,
-          "show": "0",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.ignored",
-          "pos": "0",
-          "showname": "Frame is ignored: False",
-          "size": "0",
-          "value": null,
-          "show": "0",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.protocols",
-          "pos": "0",
-          "showname": "Protocols in frame: eth:ip:tcp:ssh",
-          "size": "0",
-          "value": null,
-          "show": "eth:ip:tcp:ssh",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "eth",
-      "pos": "0",
-      "showname": "Ethernet II, Src: CadmusCo_96:a4:7a (08:00:27:96:a4:7a), Dst: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
-      "size": "14",
-      "hide": null,
-      "fields": [
-        {
-          "name": "eth.dst",
-          "pos": "0",
-          "showname": "Destination: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
-          "size": "6",
-          "value": "0a0027000000",
-          "show": "0a:00:27:00:00:00",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "eth.addr",
-              "pos": "0",
-              "showname": "Address: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
-              "size": "6",
-              "value": "0a0027000000",
-              "show": "0a:00:27:00:00:00",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "eth.lg",
-              "pos": "0",
-              "showname": ".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)",
-              "size": "3",
-              "value": "1",
-              "show": "1",
-              "unmaskedvalue": "0a0027",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "eth.ig",
-              "pos": "0",
-              "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
-              "size": "3",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "0a0027",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "eth.src",
-          "pos": "6",
-          "showname": "Source: CadmusCo_96:a4:7a (08:00:27:96:a4:7a)",
-          "size": "6",
-          "value": "08002796a47a",
-          "show": "08:00:27:96:a4:7a",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "eth.addr",
-              "pos": "6",
-              "showname": "Address: CadmusCo_96:a4:7a (08:00:27:96:a4:7a)",
-              "size": "6",
-              "value": "08002796a47a",
-              "show": "08:00:27:96:a4:7a",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "eth.lg",
-              "pos": "6",
-              "showname": ".... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)",
-              "size": "3",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "080027",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "eth.ig",
-              "pos": "6",
-              "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
-              "size": "3",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "080027",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "eth.type",
-          "pos": "12",
-          "showname": "Type: IP (0x0800)",
-          "size": "2",
-          "value": "0800",
-          "show": "0x0800",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "ip",
-      "pos": "14",
-      "showname": "Internet Protocol Version 4, Src: 192.168.66.121 (192.168.66.121), Dst: 192.168.66.1 (192.168.66.1)",
-      "size": "20",
-      "hide": null,
-      "fields": [
-        {
-          "name": "ip.version",
-          "pos": "14",
-          "showname": "Version: 4",
-          "size": "1",
-          "value": "45",
-          "show": "4",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.hdr_len",
-          "pos": "14",
-          "showname": "Header length: 20 bytes",
-          "size": "1",
-          "value": "45",
-          "show": "20",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.dsfield",
-          "pos": "15",
-          "showname": "Differentiated Services Field: 0x10 (DSCP 0x04: Unknown DSCP; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))",
-          "size": "1",
-          "value": "10",
-          "show": "16",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "ip.dsfield.dscp",
-              "pos": "15",
-              "showname": "0001 00.. = Differentiated Services Codepoint: Unknown (0x04)",
-              "size": "1",
-              "value": "4",
-              "show": "0x04",
-              "unmaskedvalue": "10",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "ip.dsfield.ecn",
-              "pos": "15",
-              "showname": ".... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)",
-              "size": "1",
-              "value": "0",
-              "show": "0x00",
-              "unmaskedvalue": "10",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "ip.len",
-          "pos": "16",
-          "showname": "Total Length: 708",
-          "size": "2",
-          "value": "02c4",
-          "show": "708",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.id",
-          "pos": "18",
-          "showname": "Identification: 0x7cd9 (31961)",
-          "size": "2",
-          "value": "7cd9",
-          "show": "0x7cd9",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.flags",
-          "pos": "20",
-          "showname": "Flags: 0x02 (Don't Fragment)",
-          "size": "1",
-          "value": "40",
-          "show": "0x02",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "ip.flags.rb",
-              "pos": "20",
-              "showname": "0... .... = Reserved bit: Not set",
-              "size": "1",
-              "value": "40",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "ip.flags.df",
-              "pos": "20",
-              "showname": ".1.. .... = Don't fragment: Set",
-              "size": "1",
-              "value": "40",
-              "show": "1",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "ip.flags.mf",
-              "pos": "20",
-              "showname": "..0. .... = More fragments: Not set",
-              "size": "1",
-              "value": "40",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "ip.frag_offset",
-          "pos": "20",
-          "showname": "Fragment offset: 0",
-          "size": "2",
-          "value": "4000",
-          "show": "0",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.ttl",
-          "pos": "22",
-          "showname": "Time to live: 64",
-          "size": "1",
-          "value": "40",
-          "show": "64",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.proto",
-          "pos": "23",
-          "showname": "Protocol: TCP (6)",
-          "size": "1",
-          "value": "06",
-          "show": "6",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.checksum",
-          "pos": "24",
-          "showname": "Header checksum: 0xb57f [correct]",
-          "size": "2",
-          "value": "b57f",
-          "show": "0xb57f",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "ip.checksum_good",
-              "pos": "24",
-              "showname": "Good: True",
-              "size": "2",
-              "value": "b57f",
-              "show": "1",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "ip.checksum_bad",
-              "pos": "24",
-              "showname": "Bad: False",
-              "size": "2",
-              "value": "b57f",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "ip.src",
-          "pos": "26",
-          "showname": "Source: 192.168.66.121 (192.168.66.121)",
-          "size": "4",
-          "value": "c0a84279",
-          "show": "192.168.66.121",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.addr",
-          "pos": "26",
-          "showname": "Source or Destination Address: 192.168.66.121 (192.168.66.121)",
-          "size": "4",
-          "value": "c0a84279",
-          "show": "192.168.66.121",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.src_host",
-          "pos": "26",
-          "showname": "Source Host: 192.168.66.121",
-          "size": "4",
-          "value": "c0a84279",
-          "show": "192.168.66.121",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.host",
-          "pos": "26",
-          "showname": "Source or Destination Host: 192.168.66.121",
-          "size": "4",
-          "value": "c0a84279",
-          "show": "192.168.66.121",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.dst",
-          "pos": "30",
-          "showname": "Destination: 192.168.66.1 (192.168.66.1)",
-          "size": "4",
-          "value": "c0a84201",
-          "show": "192.168.66.1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.addr",
-          "pos": "30",
-          "showname": "Source or Destination Address: 192.168.66.1 (192.168.66.1)",
-          "size": "4",
-          "value": "c0a84201",
-          "show": "192.168.66.1",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.dst_host",
-          "pos": "30",
-          "showname": "Destination Host: 192.168.66.1",
-          "size": "4",
-          "value": "c0a84201",
-          "show": "192.168.66.1",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.host",
-          "pos": "30",
-          "showname": "Source or Destination Host: 192.168.66.1",
-          "size": "4",
-          "value": "c0a84201",
-          "show": "192.168.66.1",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "tcp",
-      "pos": "34",
-      "showname": "Transmission Control Protocol, Src Port: ssh (22), Dst Port: 55791 (55791), Seq: 1, Ack: 1, Len: 656",
-      "size": "32",
-      "hide": null,
-      "fields": [
-        {
-          "name": "tcp.srcport",
-          "pos": "34",
-          "showname": "Source port: ssh (22)",
-          "size": "2",
-          "value": "0016",
-          "show": "22",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.dstport",
-          "pos": "36",
-          "showname": "Destination port: 55791 (55791)",
-          "size": "2",
-          "value": "d9ef",
-          "show": "55791",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.port",
-          "pos": "34",
-          "showname": "Source or Destination Port: 22",
-          "size": "2",
-          "value": "0016",
-          "show": "22",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.port",
-          "pos": "36",
-          "showname": "Source or Destination Port: 55791",
-          "size": "2",
-          "value": "d9ef",
-          "show": "55791",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.stream",
-          "pos": "34",
-          "showname": "Stream index: 0",
-          "size": "0",
-          "value": null,
-          "show": "0",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.len",
-          "pos": "46",
-          "showname": "TCP Segment Len: 656",
-          "size": "1",
-          "value": "80",
-          "show": "656",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.seq",
-          "pos": "38",
-          "showname": "Sequence number: 1    (relative sequence number)",
-          "size": "4",
-          "value": "12903044",
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.nxtseq",
-          "pos": "34",
-          "showname": "Next sequence number: 657    (relative sequence number)",
-          "size": "0",
-          "value": null,
-          "show": "657",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.ack",
-          "pos": "42",
-          "showname": "Acknowledgment number: 1    (relative ack number)",
-          "size": "4",
-          "value": "8b92f3e7",
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.hdr_len",
-          "pos": "46",
-          "showname": "Header length: 32 bytes",
-          "size": "1",
-          "value": "80",
-          "show": "32",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.flags",
-          "pos": "46",
-          "showname": "Flags: 0x018 (PSH, ACK)",
-          "size": "2",
-          "value": "18",
-          "show": "0x0018",
-          "unmaskedvalue": "8018",
-          "hide": null,
-          "fields": [
-            {
-              "name": "tcp.flags.res",
-              "pos": "46",
-              "showname": "000. .... .... = Reserved: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "80",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.ns",
-              "pos": "46",
-              "showname": "...0 .... .... = Nonce: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "80",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.cwr",
-              "pos": "47",
-              "showname": ".... 0... .... = Congestion Window Reduced (CWR): Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.ecn",
-              "pos": "47",
-              "showname": ".... .0.. .... = ECN-Echo: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.urg",
-              "pos": "47",
-              "showname": ".... ..0. .... = Urgent: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.ack",
-              "pos": "47",
-              "showname": ".... ...1 .... = Acknowledgment: Set",
-              "size": "1",
-              "value": "1",
-              "show": "1",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.push",
-              "pos": "47",
-              "showname": ".... .... 1... = Push: Set",
-              "size": "1",
-              "value": "1",
-              "show": "1",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.reset",
-              "pos": "47",
-              "showname": ".... .... .0.. = Reset: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.syn",
-              "pos": "47",
-              "showname": ".... .... ..0. = Syn: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.fin",
-              "pos": "47",
-              "showname": ".... .... ...0 = Fin: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "tcp.window_size_value",
-          "pos": "48",
-          "showname": "Window size value: 501",
-          "size": "2",
-          "value": "01f5",
-          "show": "501",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.window_size",
-          "pos": "48",
-          "showname": "Calculated window size: 501",
-          "size": "2",
-          "value": "01f5",
-          "show": "501",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.window_size_scalefactor",
-          "pos": "48",
-          "showname": "Window size scaling factor: -1 (unknown)",
-          "size": "2",
-          "value": "01f5",
-          "show": "-1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.checksum",
-          "pos": "50",
-          "showname": "Checksum: 0x0882 [validation disabled]",
-          "size": "2",
-          "value": "0882",
-          "show": "0x0882",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "tcp.checksum_good",
-              "pos": "50",
-              "showname": "Good Checksum: False",
-              "size": "2",
-              "value": "0882",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.checksum_bad",
-              "pos": "50",
-              "showname": "Bad Checksum: False",
-              "size": "2",
-              "value": "0882",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "tcp.options",
-          "pos": "54",
-          "showname": "Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps",
-          "size": "12",
-          "value": "0101080a0014f4f811bdb98f",
-          "show": "01:01:08:0a:00:14:f4:f8:11:bd:b9:8f",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "",
-              "pos": "54",
-              "showname": null,
-              "size": "1",
-              "value": "01",
-              "show": "No-Operation (NOP)",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": [
-                {
-                  "name": "ip.opt.type",
-                  "pos": "54",
-                  "showname": "Type: 1",
-                  "size": "1",
-                  "value": "01",
-                  "show": "1",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": [
-                    {
-                      "name": "ip.opt.type.copy",
-                      "pos": "54",
-                      "showname": "0... .... = Copy on fragmentation: No",
-                      "size": "1",
-                      "value": "0",
-                      "show": "0",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    },
-                    {
-                      "name": "ip.opt.type.class",
-                      "pos": "54",
-                      "showname": ".00. .... = Class: Control (0)",
-                      "size": "1",
-                      "value": "0",
-                      "show": "0",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    },
-                    {
-                      "name": "ip.opt.type.number",
-                      "pos": "54",
-                      "showname": "...0 0001 = Number: No-Operation (NOP) (1)",
-                      "size": "1",
-                      "value": "1",
-                      "show": "1",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    }
-                  ],
-                  "protos": null
-                }
-              ],
-              "protos": null
-            },
-            {
-              "name": "",
-              "pos": "55",
-              "showname": null,
-              "size": "1",
-              "value": "01",
-              "show": "No-Operation (NOP)",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": [
-                {
-                  "name": "ip.opt.type",
-                  "pos": "55",
-                  "showname": "Type: 1",
-                  "size": "1",
-                  "value": "01",
-                  "show": "1",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": [
-                    {
-                      "name": "ip.opt.type.copy",
-                      "pos": "55",
-                      "showname": "0... .... = Copy on fragmentation: No",
-                      "size": "1",
-                      "value": "0",
-                      "show": "0",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    },
-                    {
-                      "name": "ip.opt.type.class",
-                      "pos": "55",
-                      "showname": ".00. .... = Class: Control (0)",
-                      "size": "1",
-                      "value": "0",
-                      "show": "0",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    },
-                    {
-                      "name": "ip.opt.type.number",
-                      "pos": "55",
-                      "showname": "...0 0001 = Number: No-Operation (NOP) (1)",
-                      "size": "1",
-                      "value": "1",
-                      "show": "1",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    }
-                  ],
-                  "protos": null
-                }
-              ],
-              "protos": null
-            },
-            {
-              "name": "",
-              "pos": "56",
-              "showname": null,
-              "size": "10",
-              "value": "080a0014f4f811bdb98f",
-              "show": "Timestamps: TSval 1373432, TSecr 297646479",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": [
-                {
-                  "name": "tcp.option_kind",
-                  "pos": "56",
-                  "showname": "Kind: Timestamp (8)",
-                  "size": "1",
-                  "value": "08",
-                  "show": "8",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": null,
-                  "protos": null
-                },
-                {
-                  "name": "tcp.option_len",
-                  "pos": "57",
-                  "showname": "Length: 10",
-                  "size": "1",
-                  "value": "0a",
-                  "show": "10",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": null,
-                  "protos": null
-                },
-                {
-                  "name": "tcp.options.timestamp.tsval",
-                  "pos": "58",
-                  "showname": "Timestamp value: 1373432",
-                  "size": "4",
-                  "value": "0014f4f8",
-                  "show": "1373432",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": null,
-                  "protos": null
-                },
-                {
-                  "name": "tcp.options.timestamp.tsecr",
-                  "pos": "62",
-                  "showname": "Timestamp echo reply: 297646479",
-                  "size": "4",
-                  "value": "11bdb98f",
-                  "show": "297646479",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": null,
-                  "protos": null
-                }
-              ],
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "tcp.analysis",
-          "pos": "34",
-          "showname": "SEQ/ACK analysis",
-          "size": "0",
-          "value": "",
-          "show": "",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "tcp.analysis.bytes_in_flight",
-              "pos": "34",
-              "showname": "Bytes in flight: 656",
-              "size": "0",
-              "value": null,
-              "show": "656",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "ssh",
-      "pos": "66",
-      "showname": "SSH Protocol",
-      "size": "656",
-      "hide": null,
-      "fields": [
-        {
-          "name": "ssh.encrypted_packet",
-          "pos": "66",
-          "showname": "Encrypted Packet: 5b2bfe1fa006867834412184af9f5b239737763adead7140...",
-          "size": "656",
-          "value": "5b2bfe1fa006867834412184af9f5b239737763adead71408fc01b88e548b2cc86f341a39771c6ed16f2b0bb3e6ab6109e73c7d68ca7545852f91930e4633c17fb9dc7aa794b0d820d0fa3ac65bf0f013e5449d5953d2943506657e2b76b548e67f5c9ce1a4c53db1b52465bde7208baf11f3fe01975418b4db186a38ad32947d1908b62e532da4b729353a932423d25f5f734484076aa4143c6a74937a4ea49448e261ae1ecb6b07bbdf5c98d0855940a19018c88263b8936f7b3e9a4b6cd98090fa10a10e37ad20fe5d833071ad6d5b2886ba85ec72affb83e316443dbe29dbf643e6aa05595c90765cf85f6da55cc1c09d8dccc7d05da022429ad602a559a044b7e2663b0c153a3011bf824ef8d1fa56cba957c5f5d2276a1c9e92de65782f406848c6e20f634c5d1fea843a8bf1a4058e85553f5838f7299958fbf54be84e46c5a3c3965f8bed7fe03a9a1168a892e0073adeb54deca171a318d11fc1a8179f91632310213da327965a40bc6fe18eae55e8da6b57d7ef9f3a05b42381bcb3db8f8efd6d0c638a2cdd46efb0b8f1274e98672f644b2275947e626b02e5166f86c2dd4a67b81e213f8c064927a396815db589f10e5e521ffedb13f8edbe2de01c6fc8bf0e12c82212e497794aa045e9b6fcca83b4cad0a3b5e6ca2d1feaf8887b4d64f22989396e
 cfa8f7f1835eed422580505109fed36797bdc10a9168d5148daef6a8710c3df1d6366c9763ab4ebd359d86a8ea14819252fb52ba423422d1f60b0179316b3729e479ba07e88cb886938c8daae65d470dde91e5336e0fc4221a72cc49057d878aa5924875d097483e94bc44a4ea93aee8780e56c50a405932841f50da156e1f90559a7c4f76999442fb433a26fc703dea656bbe03790ac3c9c5318ff5f81d87d483524bbfe7ff167",
-          "show": "5b:2b:fe:1f:a0:06:86:78:34:41:21:84:af:9f:5b:23:97:37:76:3a:de:ad:71:40:8f:c0:1b:88:e5:48:b2:cc:86:f3:41:a3:97:71:c6:ed:16:f2:b0:bb:3e:6a:b6:10:9e:73:c7:d6:8c:a7:54:58:52:f9:19:30:e4:63:3c:17:fb:9d:c7:aa:79:4b:0d:82:0d:0f:a3:ac:65:bf:0f:01:3e:54:49:d5:95:3d:29:43:50:66:57:e2:b7:6b:54:8e:67:f5:c9:ce:1a:4c:53:db:1b:52:46:5b:de:72:08:ba:f1:1f:3f:e0:19:75:41:8b:4d:b1:86:a3:8a:d3:29:47:d1:90:8b:62:e5:32:da:4b:72:93:53:a9:32:42:3d:25:f5:f7:34:48:40:76:aa:41:43:c6:a7:49:37:a4:ea:49:44:8e:26:1a:e1:ec:b6:b0:7b:bd:f5:c9:8d:08:55:94:0a:19:01:8c:88:26:3b:89:36:f7:b3:e9:a4:b6:cd:98:09:0f:a1:0a:10:e3:7a:d2:0f:e5:d8:33:07:1a:d6:d5:b2:88:6b:a8:5e:c7:2a:ff:b8:3e:31:64:43:db:e2:9d:bf:64:3e:6a:a0:55:95:c9:07:65:cf:85:f6:da:55:cc:1c:09:d8:dc:cc:7d:05:da:02:24:29:ad:60:2a:55:9a:04:4b:7e:26:63:b0:c1:53:a3:01:1b:f8:24:ef:8d:1f:a5:6c:ba:95:7c:5f:5d:22:76:a1:c9:e9:2d:e6:57:82:f4:06:84:8c:6e:20:f6:34:c5:d1:fe:a8:43:a8:bf:1a:40:58:e8:55:53:f5:83:8f:72:99:95:8f:bf:54:be:84:e4:6c:5a:3c:39:65:
 f8:be:d7:fe:03:a9:a1:16:8a:89:2e:00:73:ad:eb:54:de:ca:17:1a:31:8d:11:fc:1a:81:79:f9:16:32:31:02:13:da:32:79:65:a4:0b:c6:fe:18:ea:e5:5e:8d:a6:b5:7d:7e:f9:f3:a0:5b:42:38:1b:cb:3d:b8:f8:ef:d6:d0:c6:38:a2:cd:d4:6e:fb:0b:8f:12:74:e9:86:72:f6:44:b2:27:59:47:e6:26:b0:2e:51:66:f8:6c:2d:d4:a6:7b:81:e2:13:f8:c0:64:92:7a:39:68:15:db:58:9f:10:e5:e5:21:ff:ed:b1:3f:8e:db:e2:de:01:c6:fc:8b:f0:e1:2c:82:21:2e:49:77:94:aa:04:5e:9b:6f:cc:a8:3b:4c:ad:0a:3b:5e:6c:a2:d1:fe:af:88:87:b4:d6:4f:22:98:93:96:ec:fa:8f:7f:18:35:ee:d4:22:58:05:05:10:9f:ed:36:79:7b:dc:10:a9:16:8d:51:48:da:ef:6a:87:10:c3:df:1d:63:66:c9:76:3a:b4:eb:d3:59:d8:6a:8e:a1:48:19:25:2f:b5:2b:a4:23:42:2d:1f:60:b0:17:93:16:b3:72:9e:47:9b:a0:7e:88:cb:88:69:38:c8:da:ae:65:d4:70:dd:e9:1e:53:36:e0:fc:42:21:a7:2c:c4:90:57:d8:78:aa:59:24:87:5d:09:74:83:e9:4b:c4:4a:4e:a9:3a:ee:87:80:e5:6c:50:a4:05:93:28:41:f5:0d:a1:56:e1:f9:05:59:a7:c4:f7:69:99:44:2f:b4:33:a2:6f:c7:03:de:a6:56:bb:e0:37:90:ac:3c:9c:53:18:ff:5f:81:d8:7d:48:35:24:bb:fe:7f:f1:67",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        }
-      ]
-    }
-  ]
-};
+  it('should expand the packet`s proto fieldset', () => {
+
+    const protos = fixture.debugElement.queryAll(By.css('[data-qe-id="proto"]'));
+
+    protos.forEach((proto, i) => {
+
+      expect(proto.query(By.css('[data-qe-id="proto-fields"]'))).toBeFalsy();
+      proto.nativeElement.click();
+      fixture.detectChanges();
+      const fieldsContainer = proto.query(By.css('[data-qe-id="proto-fields"]'));
+      expect(fieldsContainer).toBeDefined();
+
+      const fields = fieldsContainer.queryAll(By.css('[data-qe-id="proto-field"]'));
+
+      fields.forEach((field, j) => {
+        const name = field.query(By.css('[data-qe-id="proto-field-name"]'));
+        expect(name.nativeElement.textContent.trim()).toBe(fakePacket.protos[i].fields[j].name);
+        const showname = field.query(By.css('[data-qe-id="proto-field-showname"]'));
+        expect(showname.nativeElement.textContent.trim()).toBe(fakePacket.protos[i].fields[j].showname);
+      });
+    });
+  });
+
+  it('should render proto`s showname property', () => {
+    const protos = fixture.debugElement.queryAll(By.css('[data-qe-id="proto"]'));
+    protos.forEach((proto, i) => {
+      expect(
+        proto.query(By.css('[data-qe-id="proto-showname"]'))
+          .nativeElement
+          .textContent.trim()
+      ).toBe(fakePacket.protos[i].showname);
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
index 12d8df0..950f49c 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
@@ -16,13 +16,13 @@
     <app-pcap-filters [queryRunning]="queryRunning" (search)="onSearch($event)"></app-pcap-filters>
   </div>
   <div *ngIf="queryRunning" class="progress pcap-progress-background">
-    <div class="progress-bar progress-bar-animated pcap-progress" role="progressbar" attr.aria-valuenow="{{progressWidth}}" aria-valuemin="0" aria-valuemax="100" [ngStyle]="{'max-width': progressWidth + '%', 'transition-duration':'1000ms'}">{{progressWidth}}%</div>
+    <div class="progress-bar progress-bar-animated pcap-progress" role="progressbar" attr.aria-valuenow="{{progressWidth}}" aria-valuemin="0" aria-valuemax="100" [ngStyle]="{'width': progressWidth + '%'}">{{progressWidth}}%</div>
   </div>
-  <div *ngIf="errorMsg" class="alert alert-danger" role="alert">
+  <div *ngIf="errorMsg" class="alert alert-danger" role="alert" data-qe-id="error">
     {{ errorMsg }}
   </div>
   <div class="panel-body" *ngIf="pdml">
     <app-pcap-list [packets]="pdml.packets" [pagination]="pagination" (pageUpdate)="changePage($event)"></app-pcap-list>
   </div>
-  <a class="btn btn-primary" [attr.href]="getDownloadUrl()" *ngIf="pdml">Download PCAP</a>
+  <a class="btn btn-primary" [attr.href]="getDownloadUrl()" *ngIf="pdml" data-qe-id="download-link">Download PCAP</a>
 </div>

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
index a8cc2ce..8989bf9 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
@@ -31,22 +31,4 @@
 
 .progress-bar {
   width: 0;
-  animation: progress 1.5s ease-in-out forwards;
 }
-
-@keyframes progress {
-  from {
-    width: 0;
-  }
-  to {
-    width: 100%;
-  }
-}
-@keyframes show  {
-  from {
-    opacity: 0;
-  }
-  to {
-    opacity: 1;
-  }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
index 6eb375e..bd732ad 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
@@ -15,13 +15,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
 
 import { PcapPanelComponent } from './pcap-panel.component';
 import { Component, Input } from '../../../../node_modules/@angular/core';
-import { PdmlPacket } from '../model/pdml';
+import { PdmlPacket, Pdml } from '../model/pdml';
 import { PcapService } from '../service/pcap.service';
+import { PcapStatusResponse } from '../model/pcap-status-response';
 import { PcapPagination } from '../model/pcap-pagination';
+import { By } from '../../../../node_modules/@angular/platform-browser';
+import { PcapRequest } from '../model/pcap.request';
+import { defer } from 'rxjs/observable/defer';
 
 @Component({
   selector: 'app-pcap-filters',
@@ -40,9 +44,17 @@ class FakePcapListComponent {
   @Input() pagination: PcapPagination;
 }
 
+class FakePcapService {
+  getDownloadUrl() {
+    return '';
+  }
+  submitRequest() {}
+}
+
 describe('PcapPanelComponent', () => {
   let component: PcapPanelComponent;
   let fixture: ComponentFixture<PcapPanelComponent>;
+  let pcapService: PcapService;
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
@@ -52,13 +64,14 @@ describe('PcapPanelComponent', () => {
         PcapPanelComponent,
       ],
       providers: [
-        { provide: PcapService, useValue: {} },
+        { provide: PcapService, useClass: FakePcapService },
       ]
     })
     .compileComponents();
   }));
 
   beforeEach(() => {
+    pcapService = TestBed.get(PcapService);
     fixture = TestBed.createComponent(PcapPanelComponent);
     component = fixture.componentInstance;
     fixture.detectChanges();
@@ -67,4 +80,247 @@ describe('PcapPanelComponent', () => {
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should hold filter bar', () => {
+    expect(fixture.debugElement.query(By.css('app-pcap-filters'))).toBeDefined();
+  });
+
+  it('should pass queryRunning to filter bar', () => {
+    const myBoolean = new Boolean(true);
+    component.queryRunning = myBoolean as boolean;
+    fixture.detectChanges();
+    const filterBar = fixture.debugElement.query(By.css('app-pcap-filters'));
+    expect(filterBar.componentInstance.queryRunning).toBe(myBoolean);
+  });
+
+  it('should show download link if page/pdml availabe', () => {
+    component.pdml = new Pdml();
+    fixture.detectChanges();
+    const submitButton = fixture.debugElement.query(By.css('[data-qe-id="download-link"]'));
+    expect(submitButton).toBeTruthy();
+  });
+
+  it('should hide download link if page/pdml not availabe', () => {
+    component.pdml = null;
+    fixture.detectChanges();
+    const submitButton = fixture.debugElement.query(By.css('[data-qe-id="download-link"]'));
+    expect(submitButton).toBeFalsy();
+  });
+
+  it('should show the progress bar if the query is running', () => {
+    expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeFalsy();
+    component.progressWidth = 42;
+    component.queryRunning = true;
+    fixture.detectChanges();
+    const progress = fixture.debugElement.query(By.css('.pcap-progress'));
+    expect(progress).toBeTruthy();
+    expect(progress.nativeElement.textContent).toBe(component.progressWidth + '%');
+    expect(progress.attributes['aria-valuenow']).toBe(String(component.progressWidth));
+    expect(progress.styles.width).toBe(component.progressWidth + '%');
+  });
+
+  it('should render the given error message', () => {
+    expect(fixture.debugElement.query(By.css('[data-qe-id="error"]'))).toBeFalsy();
+    component.errorMsg = 'something went wrong!';
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="error"]')).nativeElement.textContent.trim()).toBe(component.errorMsg);
+  });
+
+  it('should hide the progress bar if the query is not running', () => {
+    expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeFalsy();
+    component.queryRunning = false;
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('.pcap-progress'))).toBeFalsy();
+  });
+
+  it('should render the pcap list and the download link if a valid pdml is provided', fakeAsync(() => {
+
+    const page = 42;
+    const myPdml = new Pdml();
+    myPdml.packets = [];
+
+    pcapService.getPackets = jasmine.createSpy('getPackets').and.returnValue(
+      defer(() => Promise.resolve(myPdml))
+    );
+
+    component.pdml = null;
+    fixture.detectChanges();
+
+    component.changePage(page);
+
+    expect(fixture.debugElement.query(By.css('app-pcap-list'))).toBeFalsy();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="download-link"]'))).toBeFalsy();
+
+    tick();
+    fixture.detectChanges();
+
+    const pcapList = fixture.debugElement.query(By.css('app-pcap-list'));
+
+    expect(pcapList).toBeTruthy();
+    expect((pcapList.componentInstance.pagination as PcapPagination).selectedPage).toBe(page, 'it should pass the selected page number');
+    expect(pcapList.componentInstance.packets).toBe(myPdml.packets, 'it should pass the packets from the given pdml');
+    expect(fixture.debugElement.query(By.css('[data-qe-id="download-link"]'))).toBeTruthy();
+  }));
+
+  it('should not render the pcap list and the download link if there is no pdml', fakeAsync(() => {
+
+    pcapService.getPackets = jasmine.createSpy('getPackets').and.returnValue(
+      defer(() => Promise.resolve(null))
+    );
+
+    component.pdml = null;
+    fixture.detectChanges();
+
+    component.changePage(42);
+
+    expect(fixture.debugElement.query(By.css('app-pcap-list'))).toBeFalsy();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="download-link"]'))).toBeFalsy();
+
+    tick();
+    fixture.detectChanges();
+
+    expect(fixture.debugElement.query(By.css('app-pcap-list'))).toBeFalsy();
+    expect(fixture.debugElement.query(By.css('[data-qe-id="download-link"]'))).toBeFalsy();
+  }));
+
+  it('should render the error message if the search response has no valid job id', fakeAsync(() => {
+    const response = new PcapStatusResponse();
+    response.jobId = '';
+    response.description = 'error message';
+    pcapService.submitRequest = jasmine.createSpy('submitRequest').and.returnValue(
+      defer(() => Promise.resolve(response))
+    );
+
+    component.onSearch(new PcapRequest());
+
+    tick();
+    fixture.detectChanges();
+
+    expect(
+      fixture.debugElement.query(By.css('[data-qe-id="error"]'))
+      .nativeElement
+      .textContent.trim()
+    ).toBe(response.description);
+  }));
+
+  it('should render the error message if the search request fails', fakeAsync(() => {
+
+    pcapService.submitRequest = jasmine.createSpy('submitRequest').and.returnValue(
+      defer(() => Promise.reject(new Error('search error')))
+    );
+
+    component.onSearch(new PcapRequest());
+
+    tick();
+    fixture.detectChanges();
+
+    expect(
+      fixture.debugElement.query(By.css('[data-qe-id="error"]'))
+      .nativeElement
+      .textContent.trim()
+    ).toBe('Response message: search error. Something went wrong with your query submission!');
+  }));
+
+  it('should render the error message if the poll status request fails', fakeAsync(() => {
+
+    const response = new PcapStatusResponse();
+    response.jobId = '42';
+    pcapService.submitRequest = jasmine.createSpy('submitRequest').and.returnValue(
+      defer(() => Promise.resolve(response))
+    );
+
+    pcapService.pollStatus = jasmine.createSpy('pollStatus').and.returnValue(
+      defer(() => Promise.reject(new Error('poll error')))
+    );
+
+    component.onSearch(new PcapRequest());
+
+    tick();
+    fixture.detectChanges();
+
+    expect(
+      fixture.debugElement.query(By.css('[data-qe-id="error"]'))
+      .nativeElement
+      .textContent.trim()
+    ).toBe('Response message: poll error. Something went wrong with your status request!');
+  }));
+
+  it('should render the error message if the poll response`s job status is "failed"', fakeAsync(() => {
+    const searchResponse = new PcapStatusResponse();
+    searchResponse.jobId = '42';
+
+    pcapService.submitRequest = jasmine.createSpy('submitRequest').and.returnValue(
+      defer(() => Promise.resolve(searchResponse))
+    );
+
+    const pollResponse = new PcapStatusResponse();
+    pollResponse.jobStatus = 'FAILED';
+    pcapService.pollStatus = jasmine.createSpy('pollStatus').and.returnValue(
+      defer(() => Promise.resolve(pollResponse))
+    );
+
+    component.onSearch(new PcapRequest());
+
+    tick();
+    fixture.detectChanges();
+
+    expect(
+      fixture.debugElement.query(By.css('[data-qe-id="error"]'))
+      .nativeElement
+      .textContent.trim()
+    ).toBe(`Query status: ${pollResponse.jobStatus}. Check your filter criteria and try again!`);
+  }));
+
+  it('should render the progress if the poll status is neither "succeded" nor "failed"', fakeAsync(() => {
+    const searchResponse = new PcapStatusResponse();
+    searchResponse.jobId = '42';
+
+    pcapService.submitRequest = jasmine.createSpy('submitRequest').and.returnValue(
+      defer(() => Promise.resolve(searchResponse))
+    );
+
+    const pollResponse = new PcapStatusResponse();
+    pollResponse.percentComplete = 86;
+    pcapService.pollStatus = jasmine.createSpy('pollStatus').and.returnValue(
+      defer(() => Promise.resolve(pollResponse))
+    );
+
+    component.progressWidth = 98;
+
+    component.onSearch(new PcapRequest());
+
+    tick();
+    fixture.detectChanges();
+
+    const progress = fixture.debugElement.query(By.css('.pcap-progress'));
+    expect(progress.nativeElement.textContent).toBe(pollResponse.percentComplete + '%');
+  }));
+
+  it('should render the pcap list if the poll status is "succeeded"', fakeAsync(() => {
+    const searchResponse = new PcapStatusResponse();
+    searchResponse.jobId = '42';
+
+    pcapService.submitRequest = jasmine.createSpy('submitRequest').and.returnValue(
+      defer(() => Promise.resolve(searchResponse))
+    );
+
+    const pollResponse = new PcapStatusResponse();
+    pcapService.pollStatus = jasmine.createSpy('pollStatus').and.returnValue(
+      defer(() => Promise.resolve(pollResponse))
+    );
+
+    const myPdml = new Pdml();
+    pcapService.getPackets = jasmine.createSpy('getPackets').and.returnValue(
+      defer(() => Promise.resolve(myPdml))
+    );
+
+    component.onSearch(new PcapRequest());
+
+    expect(fixture.debugElement.query(By.css('app-pcap-list'))).toBeFalsy();
+
+    tick();
+    fixture.detectChanges();
+
+    expect(fixture.debugElement.query(By.css('app-pcap-list'))).toBeDefined();
+  }));
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
index 8e7f2f1..82121d4 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
@@ -53,34 +53,44 @@ export class PcapPanelComponent {
   }
 
   onSearch(pcapRequest) {
+    this.queryRunning = true;
     this.savedPcapRequest = pcapRequest;
     this.pagination.selectedPage = 1;
     this.pdml = null;
     this.progressWidth = 0;
+    this.errorMsg = null;
     this.pcapService.submitRequest(pcapRequest).subscribe((submitResponse: PcapStatusResponse) => {
-      this.queryId = submitResponse.jobId;
-      this.queryRunning = true;
-      this.errorMsg = null;
-      this.statusSubscription = this.pcapService.pollStatus(submitResponse.jobId).subscribe((statusResponse: PcapStatusResponse) => {
-        if ('SUCCEEDED' === statusResponse.jobStatus) {
-          this.pagination.total = statusResponse.pageTotal;
-          this.statusSubscription.unsubscribe();
-          this.queryRunning = false;
-          this.pcapService.getPackets(submitResponse.jobId, this.pagination.selectedPage).toPromise().then(pdml => {
-            this.pdml = pdml;
-          });
-        } else if ('FAILED' === statusResponse.jobStatus) {
+      let id = submitResponse.jobId;
+      if (!id) {
+        this.errorMsg = submitResponse.description;
+        this.queryRunning = false;
+      } else {
+        this.queryId = id;
+        this.errorMsg = null;
+        this.statusSubscription = this.pcapService.pollStatus(id).subscribe((statusResponse: PcapStatusResponse) => {
+          if ('SUCCEEDED' === statusResponse.jobStatus) {
+            this.pagination.total = statusResponse.pageTotal;
+            this.statusSubscription.unsubscribe();
+            this.queryRunning = false;
+            this.pcapService.getPackets(submitResponse.jobId, this.pagination.selectedPage).toPromise().then(pdml => {
+              this.pdml = pdml;
+            });
+          } else if ('FAILED' === statusResponse.jobStatus) {
+            this.statusSubscription.unsubscribe();
+            this.queryRunning = false;
+            this.errorMsg = `Query status: ${statusResponse.jobStatus}. Check your filter criteria and try again!`;
+          } else if (this.progressWidth < 100) {
+            this.progressWidth = Math.trunc(statusResponse.percentComplete);
+          }
+        }, (error: any) => {
           this.statusSubscription.unsubscribe();
           this.queryRunning = false;
-          this.errorMsg = `Query status: ${statusResponse.jobStatus}. Check your filter criteria and try again!`;
-        } else if (this.progressWidth < 100) {
-          this.progressWidth = Math.trunc(statusResponse.percentComplete);
-        }
-      }, (error: any) => {
-        this.statusSubscription.unsubscribe();
-        this.queryRunning = false;
-        this.errorMsg = `Response status: ${error.responseCode}. Something went wrong with your status request!`;
-      });
+          this.errorMsg = `Response message: ${error.message}. Something went wrong with your status request!`;
+        });
+      }
+    }, (error: any) => {
+      this.queryRunning = false;
+      this.errorMsg = `Response message: ${error.message}. Something went wrong with your query submission!`;
     });
   }
 


[05/50] [abbrv] metron git commit: METRON-1691: REST should limit the number of Pcap jobs a user can submit (merrimanr via mmiklavc) closes apache/metron#1129

Posted by ni...@apache.org.
METRON-1691: REST should limit the number of Pcap jobs a user can submit (merrimanr via mmiklavc) closes apache/metron#1129


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/6c90724d
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/6c90724d
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/6c90724d

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 6c90724d8d4f06c453128f860eff51037f1870d8
Parents: f1f5dda
Author: merrimanr <me...@gmail.com>
Authored: Wed Jul 25 10:58:05 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Wed Jul 25 10:58:05 2018 -0600

----------------------------------------------------------------------
 .../apache/metron/rest/model/pcap/Field.java    | 36 ++++-------
 .../rest/model/pcap/FixedPcapRequest.java       | 30 ---------
 .../apache/metron/rest/model/pcap/Packet.java   |  8 +--
 .../metron/rest/model/pcap/PcapStatus.java      | 24 +++----
 .../org/apache/metron/rest/model/pcap/Pdml.java | 21 +++---
 .../apache/metron/rest/model/pcap/Proto.java    | 24 +++----
 .../rest/model/pcap/QueryPcapRequest.java       | 18 ------
 metron-interface/metron-rest/README.md          | 27 ++++++--
 .../apache/metron/rest/MetronRestConstants.java |  1 +
 .../metron/rest/controller/PcapController.java  | 13 ++++
 .../apache/metron/rest/service/PcapService.java |  6 ++
 .../rest/service/impl/PcapServiceImpl.java      | 64 ++++++++++++++++--
 .../src/main/resources/application.yml          |  1 +
 .../PcapControllerIntegrationTest.java          | 46 +++++++++++++
 .../rest/service/impl/PcapServiceImplTest.java  | 68 ++++++++++++++++++++
 .../metron/job/manager/InMemoryJobManager.java  |  4 ++
 16 files changed, 261 insertions(+), 130 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Field.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Field.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Field.java
index 9c2878b..4ed71c3 100644
--- a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Field.java
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Field.java
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 public class Field {
 
@@ -132,33 +133,22 @@ public class Field {
   public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
-
     Field field = (Field) o;
-
-    return (getName() != null ? getName().equals(field.getName()) : field.getName() != null) &&
-            (getPos() != null ? getPos().equals(field.getPos()) : field.getPos() == null) &&
-            (getShowname() != null ? getShowname().equals(field.getShowname()) : field.getShowname() == null) &&
-            (getSize() != null ? getSize().equals(field.getSize()) : field.getSize() == null) &&
-            (getValue() != null ? getValue().equals(field.getValue()) : field.getValue() == null) &&
-            (getShow() != null ? getShow().equals(field.getShow()) : field.getShow() == null) &&
-            (getUnmaskedvalue() != null ? getUnmaskedvalue().equals(field.getUnmaskedvalue()) : field.getUnmaskedvalue() == null) &&
-            (getHide() != null ? getHide().equals(field.getHide()) : field.getHide() == null) &&
-            (getFields() != null ? getFields().equals(field.getFields()) : field.getFields() == null) &&
-            (getProtos() != null ? getProtos().equals(field.getProtos()) : field.getProtos() == null);
+    return Objects.equals(name, field.name) &&
+            Objects.equals(pos, field.pos) &&
+            Objects.equals(showname, field.showname) &&
+            Objects.equals(size, field.size) &&
+            Objects.equals(value, field.value) &&
+            Objects.equals(show, field.show) &&
+            Objects.equals(unmaskedvalue, field.unmaskedvalue) &&
+            Objects.equals(hide, field.hide) &&
+            Objects.equals(fields, field.fields) &&
+            Objects.equals(protos, field.protos);
   }
 
   @Override
   public int hashCode() {
-    int result = getName() != null ? getName().hashCode() : 0;
-    result = 31 * result + (getPos() != null ? getPos().hashCode() : 0);
-    result = 31 * result + (getShowname() != null ? getShowname().hashCode() : 0);
-    result = 31 * result + (getSize() != null ? getSize().hashCode() : 0);
-    result = 31 * result + (getValue() != null ? getValue().hashCode() : 0);
-    result = 31 * result + (getShow() != null ? getShow().hashCode() : 0);
-    result = 31 * result + (getUnmaskedvalue() != null ? getUnmaskedvalue().hashCode() : 0);
-    result = 31 * result + (getHide() != null ? getHide().hashCode() : 0);
-    result = 31 * result + (getFields() != null ? getFields().hashCode() : 0);
-    result = 31 * result + (getProtos() != null ? getProtos().hashCode() : 0);
-    return result;
+
+    return Objects.hash(name, pos, showname, size, value, show, unmaskedvalue, hide, fields, protos);
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java
index d91aac7..38d05b7 100644
--- a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java
@@ -113,34 +113,4 @@ public class FixedPcapRequest extends PcapRequest {
     }
     PcapOptions.FIELDS.put(this, fields);
   }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-
-    FixedPcapRequest fixedPcapRequest = (FixedPcapRequest) o;
-
-    return (super.equals(o)) &&
-            (getIpSrcAddr() != null ? getIpSrcAddr().equals(fixedPcapRequest.getIpSrcAddr()) : fixedPcapRequest.getIpSrcAddr() != null) &&
-            (getIpDstAddr() != null ? getIpDstAddr().equals(fixedPcapRequest.getIpDstAddr()) : fixedPcapRequest.getIpDstAddr() != null) &&
-            (getIpSrcPort() != null ? getIpSrcPort().equals(fixedPcapRequest.getIpSrcPort()) : fixedPcapRequest.getIpSrcPort() != null) &&
-            (getIpDstPort() != null ? getIpDstPort().equals(fixedPcapRequest.getIpDstPort()) : fixedPcapRequest.getIpDstPort() != null) &&
-            (getProtocol() != null ? getProtocol().equals(fixedPcapRequest.getProtocol()) : fixedPcapRequest.getProtocol() != null) &&
-            (getPacketFilter() != null ? getPacketFilter().equals(fixedPcapRequest.getPacketFilter()) : fixedPcapRequest.getPacketFilter() != null) &&
-            (getIncludeReverse() != null ? getIncludeReverse().equals(fixedPcapRequest.getIncludeReverse()) : fixedPcapRequest.getIncludeReverse() != null);
-  }
-
-  @Override
-  public int hashCode() {
-    int result = super.hashCode();
-    result = 31 * result + (getIpSrcAddr() != null ? getIpSrcAddr().hashCode() : 0);
-    result = 31 * result + (getIpDstAddr() != null ? getIpDstAddr().hashCode() : 0);
-    result = 31 * result + (getIpSrcPort() != null ? getIpSrcPort().hashCode() : 0);
-    result = 31 * result + (getIpDstPort() != null ? getIpDstPort().hashCode() : 0);
-    result = 31 * result + (getProtocol() != null ? getProtocol().hashCode() : 0);
-    result = 31 * result + (getPacketFilter() != null ? getPacketFilter().hashCode() : 0);
-    result = 31 * result + (getIncludeReverse() != null ? getIncludeReverse().hashCode() : 0);
-    return result;
-  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Packet.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Packet.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Packet.java
index de21e6b..1773272 100644
--- a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Packet.java
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Packet.java
@@ -21,6 +21,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 
 import java.util.List;
+import java.util.Objects;
 
 public class Packet {
 
@@ -40,14 +41,13 @@ public class Packet {
   public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
-
     Packet packet = (Packet) o;
-
-    return (getProtos() != null ? getProtos().equals(packet.getProtos()) : packet.getProtos() == null);
+    return Objects.equals(protos, packet.protos);
   }
 
   @Override
   public int hashCode() {
-    return getProtos() != null ? getProtos().hashCode() : 0;
+
+    return Objects.hash(protos);
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapStatus.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapStatus.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapStatus.java
index f004eb5..43c77fd 100644
--- a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapStatus.java
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapStatus.java
@@ -17,6 +17,8 @@
  */
 package org.apache.metron.rest.model.pcap;
 
+import java.util.Objects;
+
 public class PcapStatus {
 
   private String jobId;
@@ -69,23 +71,17 @@ public class PcapStatus {
   public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
-
-    PcapStatus pcapStatus = (PcapStatus) o;
-
-    return (getJobId() != null ? getJobId().equals(pcapStatus.getJobId()) : pcapStatus.getJobId() != null) &&
-            (getJobStatus() != null ? getJobStatus().equals(pcapStatus.getJobStatus()) : pcapStatus.getJobStatus() != null) &&
-            (getDescription() != null ? getDescription().equals(pcapStatus.getDescription()) : pcapStatus.getDescription() != null) &&
-            (getPercentComplete() != null ? getPercentComplete().equals(pcapStatus.getPercentComplete()) : pcapStatus.getPercentComplete() != null) &&
-            (getPageTotal() != null ? getPageTotal().equals(pcapStatus.getPageTotal()) : pcapStatus.getPageTotal() != null);
+    PcapStatus that = (PcapStatus) o;
+    return Objects.equals(jobId, that.jobId) &&
+            Objects.equals(jobStatus, that.jobStatus) &&
+            Objects.equals(description, that.description) &&
+            Objects.equals(percentComplete, that.percentComplete) &&
+            Objects.equals(pageTotal, that.pageTotal);
   }
 
   @Override
   public int hashCode() {
-    int result = (getJobId() != null ? getJobId().hashCode() : 0);
-    result = 31 * result + (getJobStatus() != null ? getJobStatus().hashCode() : 0);
-    result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
-    result = 31 * result + (getPercentComplete() != null ? getPercentComplete().hashCode() : 0);
-    result = 31 * result + (getPageTotal() != null ? getPageTotal().hashCode() : 0);
-    return result;
+
+    return Objects.hash(jobId, jobStatus, description, percentComplete, pageTotal);
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Pdml.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Pdml.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Pdml.java
index f44f96b..f59586a 100644
--- a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Pdml.java
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Pdml.java
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 
 import java.util.List;
+import java.util.Objects;
 
 public class Pdml {
 
@@ -81,23 +82,17 @@ public class Pdml {
   public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
-
     Pdml pdml = (Pdml) o;
-
-    return (getVersion() != null ? getVersion().equals(pdml.getVersion()) : pdml.getVersion() != null) &&
-            (getCreator() != null ? getCreator().equals(pdml.getCreator()) : pdml.getCreator() == null) &&
-            (getTime() != null ? getTime().equals(pdml.getTime()) : pdml.getTime() == null) &&
-            (getCaptureFile() != null ? getCaptureFile().equals(pdml.getCaptureFile()) : pdml.getCaptureFile() == null) &&
-            (getPackets() != null ? getPackets().equals(pdml.getPackets()) : pdml.getPackets() == null);
+    return Objects.equals(version, pdml.version) &&
+            Objects.equals(creator, pdml.creator) &&
+            Objects.equals(time, pdml.time) &&
+            Objects.equals(captureFile, pdml.captureFile) &&
+            Objects.equals(packets, pdml.packets);
   }
 
   @Override
   public int hashCode() {
-    int result = getVersion() != null ? getVersion().hashCode() : 0;
-    result = 31 * result + (getCreator() != null ? getCreator().hashCode() : 0);
-    result = 31 * result + (getTime() != null ? getTime().hashCode() : 0);
-    result = 31 * result + (getCaptureFile() != null ? getCaptureFile().hashCode() : 0);
-    result = 31 * result + (getPackets() != null ? getPackets().hashCode() : 0);
-    return result;
+
+    return Objects.hash(version, creator, time, captureFile, packets);
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Proto.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Proto.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Proto.java
index bdd5c1f..2c145a3 100644
--- a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Proto.java
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/Proto.java
@@ -21,6 +21,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 
 import java.util.List;
+import java.util.Objects;
 
 public class Proto {
 
@@ -90,25 +91,18 @@ public class Proto {
   public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
-
     Proto proto = (Proto) o;
-
-    return (getName() != null ? getName().equals(proto.getName()) : proto.getName() != null) &&
-            (getPos() != null ? getPos().equals(proto.getPos()) : proto.getPos() == null) &&
-            (getShowname() != null ? getShowname().equals(proto.getShowname()) : proto.getShowname() == null) &&
-            (getSize() != null ? getSize().equals(proto.getSize()) : proto.getSize() == null) &&
-            (getHide() != null ? getHide().equals(proto.getHide()) : proto.getHide() == null) &&
-            (getFields() != null ? getFields().equals(proto.getFields()) : proto.getFields() == null);
+    return Objects.equals(name, proto.name) &&
+            Objects.equals(pos, proto.pos) &&
+            Objects.equals(showname, proto.showname) &&
+            Objects.equals(size, proto.size) &&
+            Objects.equals(hide, proto.hide) &&
+            Objects.equals(fields, proto.fields);
   }
 
   @Override
   public int hashCode() {
-    int result = getName() != null ? getName().hashCode() : 0;
-    result = 31 * result + (getPos() != null ? getPos().hashCode() : 0);
-    result = 31 * result + (getShowname() != null ? getShowname().hashCode() : 0);
-    result = 31 * result + (getSize() != null ? getSize().hashCode() : 0);
-    result = 31 * result + (getHide() != null ? getHide().hashCode() : 0);
-    result = 31 * result + (getFields() != null ? getFields().hashCode() : 0);
-    return result;
+
+    return Objects.hash(name, pos, showname, size, hide, fields);
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapRequest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapRequest.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapRequest.java
index 0da3e69..1a03fda 100644
--- a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapRequest.java
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapRequest.java
@@ -38,22 +38,4 @@ public class QueryPcapRequest extends PcapRequest {
   public void setFields() {
     PcapOptions.FIELDS.put(this, getQuery());
   }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-
-    QueryPcapRequest queryPcapRequest = (QueryPcapRequest) o;
-
-    return (super.equals(o)) &&
-            (getQuery() != null ? getQuery().equals(queryPcapRequest.getQuery()) : queryPcapRequest.getQuery() != null);
-  }
-
-  @Override
-  public int hashCode() {
-    int result = super.hashCode();
-    result = 31 * result + (getQuery() != null ? getQuery().hashCode() : 0);
-    return result;
-  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md
index 53f5978..2d9a535 100644
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@ -212,7 +212,7 @@ METRON_SPRING_PROFILES_ACTIVE="vagrant,dev"
 
 ## Pcap Query
 
-The REST application exposes endpoints for querying Pcap data.  For more information about filtering options see [Query Filter Utility](/metron-platform/metron-pcap-backend#query-filter-utility).
+The REST application exposes endpoints for querying Pcap data.  For more information about filtering options see [Query Filter Utility](../../metron-platform/metron-pcap-backend#query-filter-utility).
 
 There is an endpoint available that will return Pcap data in [PDML](https://wiki.wireshark.org/PDML) format.  [Wireshark](https://www.wireshark.org/) must be installed for this feature to work.
 Installing wireshark in CentOS can be done with `yum -y install wireshark`.
@@ -253,8 +253,9 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
 | [ `GET /api/v1/metaalert/add/alert`](#get-apiv1metaalertaddalert)|
 | [ `GET /api/v1/metaalert/remove/alert`](#get-apiv1metaalertremovealert)|
 | [ `GET /api/v1/metaalert/update/status/{guid}/{status}`](#get-apiv1metaalertupdatestatusguidstatus)|
-| [ `GET /api/v1/pcap/fixed`](#get-apiv1pcapfixed)|
-| [ `GET /api/v1/pcap/query`](#get-apiv1pcapquery)|
+| [ `POST /api/v1/pcap/fixed`](#post-apiv1pcapfixed)|
+| [ `POST /api/v1/pcap/query`](#post-apiv1pcapquery)|
+| [ `GET /api/v1/pcap`](#get-apiv1pcap)|
 | [ `GET /api/v1/pcap/{jobId}`](#get-apiv1pcapjobid)|
 | [ `GET /api/v1/pcap/{jobId}/pdml`](#get-apiv1pcapjobidpdml)|
 | [ `GET /api/v1/pcap/{jobId}/raw`](#get-apiv1pcapjobidraw)|
@@ -516,7 +517,14 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
   * Returns:
     * 200 - Returns a job status with job ID.
     
-### `POST /api/v1/pcap/{jobId}`
+### `GET /api/v1/pcap`
+  * Description: Gets a list of job statuses for Pcap query jobs that match the requested state.
+  * Input:
+    * state - Job state
+  * Returns:
+    * 200 - Returns a list of job statuses for jobs that match the requested state.  
+ 
+### `GET /api/v1/pcap/{jobId}`
   * Description: Gets job status for Pcap query job.
   * Input:
     * jobId - Job ID of submitted job
@@ -524,7 +532,7 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
     * 200 - Returns a job status for the Job ID.
     * 404 - Job is missing.
     
-### `POST /api/v1/pcap/{jobId}/pdml`
+### `GET /api/v1/pcap/{jobId}/pdml`
   * Description: Gets Pcap Results for a page in PDML format.
   * Input:
     * jobId - Job ID of submitted job
@@ -533,7 +541,7 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
     * 200 - Returns PDML in json format.
     * 404 - Job or page is missing.
     
-### `POST /api/v1/pcap/{jobId}/raw`
+### `GET /api/v1/pcap/{jobId}/raw`
   * Description: Download Pcap Results for a page.
   * Input:
     * jobId - Job ID of submitted job
@@ -541,6 +549,13 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
   * Returns:
     * 200 - Returns Pcap as a file download.
     * 404 - Job or page is missing.
+    
+### `DELETE /api/v1/pcap/kill/{jobId}`
+  * Description: Kills running job.
+  * Input:
+    * jobId - Job ID of submitted job
+  * Returns:
+    * 200 - Kills passed job.
 
 ### `POST /api/v1/search/search`
   * Description: Searches the indexing store. GUIDs must be quoted to ensure correct results.

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
index b65d037..d38aedb 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
@@ -72,6 +72,7 @@ public class MetronRestConstants {
 
   public static final String USER_SETTINGS_HBASE_TABLE_SPRING_PROPERTY = "user.settings.table";
   public static final String USER_SETTINGS_HBASE_CF_SPRING_PROPERTY = "user.settings.cf";
+  public static final String USER_JOB_LIMIT_SPRING_PROPERTY = "user.job.limit";
 
   public static final String LOGGING_SYSTEM_PROPERTY = "org.springframework.boot.logging.LoggingSystem";
 

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
index be95718..13a623a 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
@@ -22,6 +22,7 @@ import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 import org.apache.commons.io.IOUtils;
+import org.apache.metron.job.JobStatus;
 import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.model.pcap.FixedPcapRequest;
 import org.apache.metron.rest.model.pcap.PcapStatus;
@@ -44,6 +45,7 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.List;
 
 @RestController
 @RequestMapping("/api/v1/pcap")
@@ -87,6 +89,17 @@ public class PcapController {
     }
   }
 
+  @ApiOperation(value = "Gets a list of job statuses for Pcap query jobs that match the requested state.")
+  @ApiResponses(value = {
+          @ApiResponse(message = "Returns a list of job statuses for jobs that match the requested state.", code = 200)
+  })
+  @RequestMapping(method = RequestMethod.GET)
+  ResponseEntity<List<PcapStatus>> getStatuses(@ApiParam(name="state", value="Job state", required=true)@RequestParam String state) throws RestException {
+
+    List<PcapStatus> jobs = pcapQueryService.getJobStatus(SecurityUtils.getCurrentUser(), JobStatus.State.valueOf(state));
+    return new ResponseEntity<>(jobs, HttpStatus.OK);
+  }
+
   @ApiOperation(value = "Gets Pcap Results for a page in PDML format.")
   @ApiResponses(value = {
           @ApiResponse(message = "Returns PDML in json format.", code = 200),

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
index 2ae34a3..00efab9 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
@@ -18,12 +18,14 @@
 package org.apache.metron.rest.service;
 
 import org.apache.hadoop.fs.Path;
+import org.apache.metron.job.JobStatus;
 import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.model.pcap.PcapRequest;
 import org.apache.metron.rest.model.pcap.PcapStatus;
 import org.apache.metron.rest.model.pcap.Pdml;
 
 import java.io.InputStream;
+import java.util.List;
 
 public interface PcapService {
 
@@ -31,6 +33,8 @@ public interface PcapService {
 
   PcapStatus getJobStatus(String username, String jobId) throws RestException;
 
+  List<PcapStatus> getJobStatus(String username, JobStatus.State state) throws RestException;
+
   PcapStatus killJob(String username, String jobId) throws RestException;
 
   Path getPath(String username, String jobId, Integer page) throws RestException;
@@ -38,4 +42,6 @@ public interface PcapService {
   Pdml getPdml(String username, String jobId, Integer page) throws RestException;
 
   InputStream getRawPcap(String username, String jobId, Integer page) throws RestException;
+
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
index 77a8934..ff80c8f 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
@@ -41,6 +41,9 @@ import org.springframework.stereotype.Service;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
 
 @Service
 public class PcapServiceImpl implements PcapService {
@@ -62,6 +65,14 @@ public class PcapServiceImpl implements PcapService {
 
   @Override
   public PcapStatus submit(String username, PcapRequest pcapRequest) throws RestException {
+    List<PcapStatus> runningJobs = getJobStatus(username, JobStatus.State.RUNNING);
+    Integer userJobLimit = environment.getProperty(MetronRestConstants.USER_JOB_LIMIT_SPRING_PROPERTY, Integer.class, 1);
+    if (runningJobs != null && runningJobs.size() >= userJobLimit) {
+      String jobIds = runningJobs.stream().map(PcapStatus::getJobId).collect(Collectors.joining(", "));
+      String message = String.format("Cannot submit job because a job is already running.  " +
+              "Please contact the administrator to cancel job(s) with id(s) %s", jobIds);
+      throw new RestException(message);
+    }
     try {
       setPcapOptions(username, pcapRequest);
       pcapRequest.setFields();
@@ -79,13 +90,7 @@ public class PcapServiceImpl implements PcapService {
     try {
       Statusable<Path> statusable = jobManager.getJob(username, jobId);
       if (statusable != null) {
-        pcapStatus = jobStatusToPcapStatus(statusable.getStatus());
-        if (statusable.isDone()) {
-          Pageable<Path> pageable = statusable.get();
-          if (pageable != null) {
-            pcapStatus.setPageTotal(pageable.getSize());
-          }
-        }
+        pcapStatus = statusableToPcapStatus(statusable);
       }
     } catch (JobNotFoundException | InterruptedException e) {
       // do nothing and return null pcapStatus
@@ -96,6 +101,40 @@ public class PcapServiceImpl implements PcapService {
   }
 
   @Override
+  public List<PcapStatus> getJobStatus(String username, JobStatus.State state) throws RestException {
+    List<PcapStatus> pcapStatuses = new ArrayList<>();
+    try {
+      List<Statusable<Path>> statusables = jobManager.getJobs(username);
+      if (statusables != null) {
+        pcapStatuses = statusables.stream()
+                .filter(statusable -> {
+                  try {
+                    return statusable.getStatus().getState() == state;
+                  } catch (JobException e) {
+                    return JobStatus.State.FAILED == state;
+                  }
+                })
+                .map(statusable -> {
+                  try {
+                    return statusableToPcapStatus(statusable);
+                  } catch (JobException | InterruptedException e) {
+                    PcapStatus pcapStatus = new PcapStatus();
+                    pcapStatus.setJobStatus(JobStatus.State.FAILED.toString());
+                    pcapStatus.setDescription(e.getMessage());
+                    return pcapStatus;
+                  }
+                })
+                .collect(Collectors.toList());
+      }
+    } catch (JobNotFoundException e) {
+      // do nothing and return null pcapStatus
+    } catch (JobException e) {
+      throw new RestException(e);
+    }
+    return pcapStatuses;
+  }
+
+  @Override
   public PcapStatus killJob(String username, String jobId) throws RestException {
     try {
       jobManager.killJob(username, jobId);
@@ -183,6 +222,17 @@ public class PcapServiceImpl implements PcapService {
     return FileSystem.get(configuration);
   }
 
+  protected PcapStatus statusableToPcapStatus(Statusable<Path> statusable) throws JobException, InterruptedException {
+    PcapStatus pcapStatus = jobStatusToPcapStatus(statusable.getStatus());
+    if (statusable.isDone()) {
+      Pageable<Path> pageable = statusable.get();
+      if (pageable != null) {
+        pcapStatus.setPageTotal(pageable.getSize());
+      }
+    }
+    return pcapStatus;
+  }
+
   protected PcapStatus jobStatusToPcapStatus(JobStatus jobStatus) {
     PcapStatus pcapStatus = new PcapStatus();
     pcapStatus.setJobId(jobStatus.getJobId());

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest/src/main/resources/application.yml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/resources/application.yml b/metron-interface/metron-rest/src/main/resources/application.yml
index 5fd9d72..866109e 100644
--- a/metron-interface/metron-rest/src/main/resources/application.yml
+++ b/metron-interface/metron-rest/src/main/resources/application.yml
@@ -72,6 +72,7 @@ user:
   settings:
     table: user_settings
     cf: cf
+  job.limit: 1
 
 pcap:
   base.path: /apps/metron/pcap/input

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
index 25956e4..5d30e72 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
@@ -23,6 +23,7 @@ import org.apache.hadoop.fs.Path;
 import org.apache.metron.common.Constants;
 import org.apache.metron.job.JobStatus;
 import org.apache.metron.job.Pageable;
+import org.apache.metron.job.manager.InMemoryJobManager;
 import org.apache.metron.pcap.PcapHelper;
 import org.apache.metron.pcap.PcapPages;
 import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
@@ -127,6 +128,8 @@ public class PcapControllerIntegrationTest {
   @Before
   public void setup() throws Exception {
     this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).apply(springSecurity()).build();
+    InMemoryJobManager jobManager = (InMemoryJobManager) wac.getBean("jobManager");
+    jobManager.clear();
   }
 
   @Test
@@ -216,6 +219,25 @@ public class PcapControllerIntegrationTest {
   }
 
   @Test
+  public void testTooManyJobs() throws Exception {
+    MockPcapJob mockPcapJob = (MockPcapJob) wac.getBean("mockPcapJob");
+
+    mockPcapJob.setStatus(new JobStatus().withJobId("jobId").withState(JobStatus.State.RUNNING));
+
+    this.mockMvc.perform(post(pcapUrl + "/fixed").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content(fixedJson))
+            .andExpect(status().isOk())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(jsonPath("$.jobId").value("jobId"))
+            .andExpect(jsonPath("$.jobStatus").value("RUNNING"));
+
+    this.mockMvc.perform(post(pcapUrl + "/fixed").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content(fixedJson))
+            .andExpect(status().isInternalServerError())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(jsonPath("$.message").value("Cannot submit job because a job is already running.  Please contact the administrator to cancel job(s) with id(s) jobId"));
+
+  }
+
+  @Test
   public void testGetStatus() throws Exception {
     MockPcapJob mockPcapJob = (MockPcapJob) wac.getBean("mockPcapJob");
 
@@ -265,6 +287,30 @@ public class PcapControllerIntegrationTest {
   }
 
   @Test
+  public void testGetStatusList() throws Exception {
+    MockPcapJob mockPcapJob = (MockPcapJob) wac.getBean("mockPcapJob");
+
+    mockPcapJob.setStatus(new JobStatus().withJobId("jobId").withState(JobStatus.State.RUNNING));
+
+    this.mockMvc.perform(post(pcapUrl + "/fixed").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content(fixedJson))
+            .andExpect(status().isOk())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(jsonPath("$.jobId").value("jobId"))
+            .andExpect(jsonPath("$.jobStatus").value("RUNNING"));
+
+    this.mockMvc.perform(get(pcapUrl + "?state=RUNNING").with(httpBasic(user, password)))
+            .andExpect(status().isOk())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(jsonPath("$[0].jobId").value("jobId"))
+            .andExpect(jsonPath("$[0].jobStatus").value("RUNNING"));
+
+    this.mockMvc.perform(get(pcapUrl + "?state=SUCCEEDED").with(httpBasic(user, password)))
+            .andExpect(status().isOk())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(content().json("[]"));
+  }
+
+  @Test
   public void testKillJob() throws Exception {
     MockPcapJob mockPcapJob = (MockPcapJob) wac.getBean("mockPcapJob");
 

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
index e9f8f9f..f99ab93 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
@@ -28,6 +28,7 @@ import org.apache.metron.job.JobException;
 import org.apache.metron.job.JobNotFoundException;
 import org.apache.metron.job.JobStatus;
 import org.apache.metron.job.Pageable;
+import org.apache.metron.job.Statusable;
 import org.apache.metron.job.manager.InMemoryJobManager;
 import org.apache.metron.job.manager.JobManager;
 import org.apache.metron.pcap.PcapHelper;
@@ -58,6 +59,8 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -189,6 +192,7 @@ public class PcapServiceImplTest {
     when(environment.getProperty(MetronRestConstants.PCAP_FINAL_OUTPUT_PATH_SPRING_PROPERTY)).thenReturn("/final/output/path");
     when(environment.getProperty(MetronRestConstants.PCAP_PAGE_SIZE_SPRING_PROPERTY)).thenReturn("100");
     when(environment.getProperty(MetronRestConstants.PCAP_PDML_SCRIPT_PATH_SPRING_PROPERTY)).thenReturn("/path/to/pdml/script");
+    when(environment.getProperty(MetronRestConstants.USER_JOB_LIMIT_SPRING_PROPERTY, Integer.class, 1)).thenReturn(1);
   }
 
   @Test
@@ -334,6 +338,26 @@ public class PcapServiceImplTest {
   }
 
   @Test
+  public void submitShouldThrowExceptionOnRunningJobFound() throws Exception {
+    exception.expect(RestException.class);
+    exception.expectMessage("Cannot submit job because a job is already running.  Please contact the administrator to cancel job(s) with id(s) jobId");
+
+    PcapStatus runningStatus1 = new PcapStatus();
+    runningStatus1.setJobStatus("RUNNING");
+    runningStatus1.setJobId("jobId1");
+    PcapStatus runningStatus2 = new PcapStatus();
+    runningStatus2.setJobStatus("RUNNING");
+    runningStatus2.setJobId("jobId2");
+
+    PcapServiceImpl pcapService = spy(new PcapServiceImpl(environment, configuration, mockPcapJobSupplier, new InMemoryJobManager<>(), pcapToPdmlScriptWrapper));
+    doReturn(Arrays.asList(runningStatus1, runningStatus2)).when(pcapService).getJobStatus("user", JobStatus.State.RUNNING);
+    when(environment.getProperty(MetronRestConstants.USER_JOB_LIMIT_SPRING_PROPERTY, Integer.class, 1)).thenReturn(2);
+
+    pcapService.submit("user", new FixedPcapRequest());
+  }
+
+
+  @Test
   public void fixedShouldThrowRestException() throws Exception {
     exception.expect(RestException.class);
     exception.expectMessage("some job exception");
@@ -397,6 +421,50 @@ public class PcapServiceImplTest {
   }
 
   @Test
+  public void getStatusForStateShouldProperlyReturnJobs() throws Exception {
+    MockPcapJob mockPcapJob = mock(MockPcapJob.class);
+    JobManager jobManager = mock(JobManager.class);
+    Statusable<Path> runningJob = mock(Statusable.class);
+    JobStatus runningStatus = mock(JobStatus.class);
+    when(runningStatus.getJobId()).thenReturn("runningJob");
+    when(runningStatus.getState()).thenReturn(JobStatus.State.RUNNING);
+    when(runningJob.getStatus()).thenReturn(runningStatus);
+
+    Statusable<Path> failedJob = mock(Statusable.class);
+    when(failedJob.getStatus()).thenThrow(new JobException("job exception"));
+
+    Statusable<Path> succeededJob = mock(Statusable.class);
+    JobStatus succeededStatus = mock(JobStatus.class);
+    when(succeededStatus.getJobId()).thenReturn("succeededJob");
+    when(succeededStatus.getState()).thenReturn(JobStatus.State.SUCCEEDED);
+    when(succeededJob.isDone()).thenReturn(true);
+    when(succeededJob.getStatus()).thenReturn(succeededStatus);
+    Pageable<Path> succeededPageable = mock(Pageable.class);
+    when(succeededPageable.getSize()).thenReturn(5);
+    when(succeededJob.get()).thenReturn(succeededPageable);
+
+    when(jobManager.getJobs("user")).thenReturn(Arrays.asList(runningJob, failedJob, succeededJob));
+
+    PcapServiceImpl pcapService = new PcapServiceImpl(environment, configuration, mockPcapJobSupplier, jobManager, pcapToPdmlScriptWrapper);
+
+    PcapStatus expectedRunningPcapStatus = new PcapStatus();
+    expectedRunningPcapStatus.setJobId("runningJob");
+    expectedRunningPcapStatus.setJobStatus(JobStatus.State.RUNNING.name());
+    Assert.assertEquals(expectedRunningPcapStatus, pcapService.getJobStatus("user", JobStatus.State.RUNNING).get(0));
+
+    PcapStatus expectedFailedPcapStatus = new PcapStatus();
+    expectedFailedPcapStatus.setJobStatus(JobStatus.State.FAILED.name());
+    expectedFailedPcapStatus.setDescription("job exception");
+    Assert.assertEquals(expectedFailedPcapStatus, pcapService.getJobStatus("user", JobStatus.State.FAILED).get(0));
+
+    PcapStatus expectedSucceededPcapStatus = new PcapStatus();
+    expectedSucceededPcapStatus.setJobId("succeededJob");
+    expectedSucceededPcapStatus.setJobStatus(JobStatus.State.SUCCEEDED.name());
+    expectedSucceededPcapStatus.setPageTotal(5);
+    Assert.assertEquals(expectedSucceededPcapStatus, pcapService.getJobStatus("user", JobStatus.State.SUCCEEDED).get(0));
+  }
+
+  @Test
   public void killJobShouldKillJobAndReportStatus() throws Exception {
     MockPcapJob mockPcapJob = mock(MockPcapJob.class);
     JobManager jobManager = mock(JobManager.class);

http://git-wip-us.apache.org/repos/asf/metron/blob/6c90724d/metron-platform/metron-job/src/main/java/org/apache/metron/job/manager/InMemoryJobManager.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-job/src/main/java/org/apache/metron/job/manager/InMemoryJobManager.java b/metron-platform/metron-job/src/main/java/org/apache/metron/job/manager/InMemoryJobManager.java
index 807af4d..ba436b3 100644
--- a/metron-platform/metron-job/src/main/java/org/apache/metron/job/manager/InMemoryJobManager.java
+++ b/metron-platform/metron-job/src/main/java/org/apache/metron/job/manager/InMemoryJobManager.java
@@ -87,4 +87,8 @@ public class InMemoryJobManager<PAGE_T> implements JobManager<PAGE_T> {
     return new ArrayList<>(getUserJobs(username).values());
   }
 
+  public void clear() {
+    jobs.clear();
+  }
+
 }


[02/50] [abbrv] metron git commit: METRON-1661 Create Pcap Query Filter endpoint (merrimanr) closes apache/metron#1125

Posted by ni...@apache.org.
METRON-1661 Create Pcap Query Filter endpoint (merrimanr) closes apache/metron#1125


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/d0e87576
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/d0e87576
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/d0e87576

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: d0e87576944b6370eb1d9a0c98d0450f2c51f052
Parents: e78bc53
Author: merrimanr <me...@gmail.com>
Authored: Mon Jul 23 09:09:37 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Mon Jul 23 09:09:37 2018 -0500

----------------------------------------------------------------------
 .../rest/model/pcap/FixedPcapRequest.java       |  1 +
 .../metron/rest/model/pcap/PcapRequest.java     |  4 +-
 .../rest/model/pcap/QueryPcapOptions.java       | 35 +++++++++
 .../rest/model/pcap/QueryPcapRequest.java       | 59 ++++++++++++++
 metron-interface/metron-rest/README.md          | 10 ++-
 .../metron/rest/controller/PcapController.java  | 15 +++-
 .../apache/metron/rest/service/PcapService.java |  8 +-
 .../rest/service/impl/PcapServiceImpl.java      | 13 ++--
 .../PcapControllerIntegrationTest.java          | 81 +++++++++++++-------
 .../apache/metron/rest/mock/MockPcapJob.java    | 13 ++--
 .../rest/service/impl/PcapServiceImplTest.java  | 55 +++++++++++--
 11 files changed, 240 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java
index a2d345b..d91aac7 100644
--- a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/FixedPcapRequest.java
@@ -87,6 +87,7 @@ public class FixedPcapRequest extends PcapRequest {
     FixedPcapOptions.INCLUDE_REVERSE.put(this, includeReverse);
   }
 
+  @Override
   public void setFields() {
     Map<String, String> fields = new HashMap<>();
     if (getIpSrcAddr() != null) {

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapRequest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapRequest.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapRequest.java
index 64ed932..44721fa 100644
--- a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapRequest.java
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/PcapRequest.java
@@ -22,7 +22,7 @@ import org.apache.metron.pcap.config.PcapOptions;
 
 import java.util.HashMap;
 
-public class PcapRequest extends AbstractMapDecorator<String, Object> {
+public abstract class PcapRequest extends AbstractMapDecorator<String, Object> {
 
   public PcapRequest() {
     super(new HashMap<>());
@@ -78,4 +78,6 @@ public class PcapRequest extends AbstractMapDecorator<String, Object> {
   public void setNumReducers(Integer numReducers) {
     PcapOptions.NUM_REDUCERS.put(this, numReducers);
   }
+
+  public abstract void setFields();
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapOptions.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapOptions.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapOptions.java
new file mode 100644
index 0000000..1ae3da6
--- /dev/null
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapOptions.java
@@ -0,0 +1,35 @@
+/**
+ * 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.
+ */
+package org.apache.metron.rest.model.pcap;
+
+import org.apache.metron.common.configuration.ConfigOption;
+
+public enum QueryPcapOptions implements ConfigOption {
+  QUERY("query");
+
+  String key;
+
+  QueryPcapOptions(String key) {
+    this.key = key;
+  }
+
+  @Override
+  public String getKey() {
+    return key;
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapRequest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapRequest.java b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapRequest.java
new file mode 100644
index 0000000..0da3e69
--- /dev/null
+++ b/metron-interface/metron-rest-client/src/main/java/org/apache/metron/rest/model/pcap/QueryPcapRequest.java
@@ -0,0 +1,59 @@
+/**
+ * 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.
+ */
+package org.apache.metron.rest.model.pcap;
+
+import org.apache.metron.pcap.config.PcapOptions;
+import org.apache.metron.pcap.filter.query.QueryPcapFilter;
+
+public class QueryPcapRequest extends PcapRequest {
+
+  public QueryPcapRequest() {
+    PcapOptions.FILTER_IMPL.put(this, new QueryPcapFilter.Configurator());
+  }
+
+  public String getQuery() {
+    return QueryPcapOptions.QUERY.get(this, String.class);
+  }
+
+  public void setQuery(String query) {
+    QueryPcapOptions.QUERY.put(this, query);
+  }
+
+  @Override
+  public void setFields() {
+    PcapOptions.FIELDS.put(this, getQuery());
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    QueryPcapRequest queryPcapRequest = (QueryPcapRequest) o;
+
+    return (super.equals(o)) &&
+            (getQuery() != null ? getQuery().equals(queryPcapRequest.getQuery()) : queryPcapRequest.getQuery() != null);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result + (getQuery() != null ? getQuery().hashCode() : 0);
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md
index 4a7102f..53f5978 100644
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@ -254,6 +254,7 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
 | [ `GET /api/v1/metaalert/remove/alert`](#get-apiv1metaalertremovealert)|
 | [ `GET /api/v1/metaalert/update/status/{guid}/{status}`](#get-apiv1metaalertupdatestatusguidstatus)|
 | [ `GET /api/v1/pcap/fixed`](#get-apiv1pcapfixed)|
+| [ `GET /api/v1/pcap/query`](#get-apiv1pcapquery)|
 | [ `GET /api/v1/pcap/{jobId}`](#get-apiv1pcapjobid)|
 | [ `GET /api/v1/pcap/{jobId}/pdml`](#get-apiv1pcapjobidpdml)|
 | [ `GET /api/v1/pcap/{jobId}/raw`](#get-apiv1pcapjobidraw)|
@@ -502,12 +503,19 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
     * 200 - Returns 'true' if the status changed and 'false' if it did not.
 
 ### `POST /api/v1/pcap/fixed`
-  * Description: Executes a Fixed Pcap Query.
+  * Description: Executes a Fixed Filter Pcap Query.
   * Input:
     * fixedPcapRequest - A Fixed Pcap Request which includes fixed filter fields like ip source address and protocol
   * Returns:
     * 200 - Returns a job status with job ID.
     
+### `POST /api/v1/pcap/query`
+  * Description: Executes a Query Filter Pcap Query.
+  * Input:
+    * queryPcapRequest - A Query Pcap Request which includes Stellar query field
+  * Returns:
+    * 200 - Returns a job status with job ID.
+    
 ### `POST /api/v1/pcap/{jobId}`
   * Description: Gets job status for Pcap query job.
   * Input:

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
index 23bb0b9..be95718 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/PcapController.java
@@ -22,11 +22,11 @@ import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
 import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.model.pcap.FixedPcapRequest;
 import org.apache.metron.rest.model.pcap.PcapStatus;
 import org.apache.metron.rest.model.pcap.Pdml;
+import org.apache.metron.rest.model.pcap.QueryPcapRequest;
 import org.apache.metron.rest.security.SecurityUtils;
 import org.apache.metron.rest.service.PcapService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -54,12 +54,21 @@ public class PcapController {
   @Autowired
   private PcapService pcapQueryService;
 
-  @ApiOperation(value = "Executes a Fixed Pcap Query.")
+  @ApiOperation(value = "Executes a Fixed Filter Pcap Query.")
   @ApiResponses(value = { @ApiResponse(message = "Returns a job status with job ID.", code = 200)})
   @RequestMapping(value = "/fixed", method = RequestMethod.POST)
   ResponseEntity<PcapStatus> fixed(@ApiParam(name="fixedPcapRequest", value="A Fixed Pcap Request"
           + " which includes fixed filter fields like ip source address and protocol", required=true)@RequestBody FixedPcapRequest fixedPcapRequest) throws RestException {
-    PcapStatus pcapStatus = pcapQueryService.fixed(SecurityUtils.getCurrentUser(), fixedPcapRequest);
+    PcapStatus pcapStatus = pcapQueryService.submit(SecurityUtils.getCurrentUser(), fixedPcapRequest);
+    return new ResponseEntity<>(pcapStatus, HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Executes a Query Filter Pcap Query.")
+  @ApiResponses(value = { @ApiResponse(message = "Returns a job status with job ID.", code = 200)})
+  @RequestMapping(value = "/query", method = RequestMethod.POST)
+  ResponseEntity<PcapStatus> query(@ApiParam(name="queryPcapRequest", value="A Query Pcap Request"
+          + " which includes Stellar query field", required=true)@RequestBody QueryPcapRequest queryPcapRequest) throws RestException {
+    PcapStatus pcapStatus = pcapQueryService.submit(SecurityUtils.getCurrentUser(), queryPcapRequest);
     return new ResponseEntity<>(pcapStatus, HttpStatus.OK);
   }
 

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
index f84735d..2ae34a3 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/PcapService.java
@@ -19,15 +19,15 @@ package org.apache.metron.rest.service;
 
 import org.apache.hadoop.fs.Path;
 import org.apache.metron.rest.RestException;
-import org.apache.metron.rest.model.pcap.FixedPcapRequest;
-
-import java.io.InputStream;
+import org.apache.metron.rest.model.pcap.PcapRequest;
 import org.apache.metron.rest.model.pcap.PcapStatus;
 import org.apache.metron.rest.model.pcap.Pdml;
 
+import java.io.InputStream;
+
 public interface PcapService {
 
-  PcapStatus fixed(String username, FixedPcapRequest fixedPcapRequest) throws RestException;
+  PcapStatus submit(String username, PcapRequest pcapRequest) throws RestException;
 
   PcapStatus getJobStatus(String username, String jobId) throws RestException;
 

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
index e341184..77a8934 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
@@ -18,7 +18,6 @@
 package org.apache.metron.rest.service.impl;
 
 import com.fasterxml.jackson.dataformat.xml.XmlMapper;
-import org.apache.commons.io.IOUtils;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
@@ -32,7 +31,6 @@ import org.apache.metron.pcap.config.PcapOptions;
 import org.apache.metron.rest.MetronRestConstants;
 import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.config.PcapJobSupplier;
-import org.apache.metron.rest.model.pcap.FixedPcapRequest;
 import org.apache.metron.rest.model.pcap.PcapRequest;
 import org.apache.metron.rest.model.pcap.PcapStatus;
 import org.apache.metron.rest.model.pcap.Pdml;
@@ -43,8 +41,6 @@ import org.springframework.stereotype.Service;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
 
 @Service
 public class PcapServiceImpl implements PcapService {
@@ -65,11 +61,11 @@ public class PcapServiceImpl implements PcapService {
   }
 
   @Override
-  public PcapStatus fixed(String username, FixedPcapRequest fixedPcapRequest) throws RestException {
+  public PcapStatus submit(String username, PcapRequest pcapRequest) throws RestException {
     try {
-      setPcapOptions(username, fixedPcapRequest);
-      fixedPcapRequest.setFields();
-      pcapJobSupplier.setPcapRequest(fixedPcapRequest);
+      setPcapOptions(username, pcapRequest);
+      pcapRequest.setFields();
+      pcapJobSupplier.setPcapRequest(pcapRequest);
       JobStatus jobStatus = jobManager.submit(pcapJobSupplier, username);
       return jobStatusToPcapStatus(jobStatus);
     } catch (IOException | JobException e) {
@@ -149,6 +145,7 @@ public class PcapServiceImpl implements PcapService {
     return pdml;
   }
 
+  @Override
   public InputStream getRawPcap(String username, String jobId, Integer page) throws RestException {
     InputStream inputStream = null;
     Path path = getPath(username, jobId, page);

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
index 6189d2c..25956e4 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/PcapControllerIntegrationTest.java
@@ -17,37 +17,17 @@
  */
 package org.apache.metron.rest.controller;
 
-import static org.apache.metron.rest.MetronRestConstants.TEST_PROFILE;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
-import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
 import org.adrianwalker.multilinestring.Multiline;
-import org.apache.hadoop.fs.Path;
 import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.fs.Path;
 import org.apache.metron.common.Constants;
 import org.apache.metron.job.JobStatus;
 import org.apache.metron.job.Pageable;
-import org.apache.metron.common.utils.JSONUtils;
-import org.apache.metron.job.JobStatus;
-import org.apache.metron.job.Pageable;
 import org.apache.metron.pcap.PcapHelper;
 import org.apache.metron.pcap.PcapPages;
 import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
+import org.apache.metron.pcap.filter.query.QueryPcapFilter;
 import org.apache.metron.rest.mock.MockPcapJob;
-import org.apache.metron.rest.mock.MockPcapToPdmlScriptWrapper;
-import org.apache.metron.rest.model.PcapResponse;
 import org.apache.metron.rest.service.PcapService;
 import org.junit.Assert;
 import org.junit.Before;
@@ -62,6 +42,22 @@ import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.context.WebApplicationContext;
 
+import java.io.File;
+import java.util.Arrays;
+import java.util.Map;
+
+import static org.apache.metron.rest.MetronRestConstants.TEST_PROFILE;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
 @RunWith(SpringRunner.class)
 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
 @ActiveProfiles(TEST_PROFILE)
@@ -101,6 +97,20 @@ public class PcapControllerIntegrationTest {
   @Multiline
   public static String fixedWithDefaultsJson;
 
+  /**
+   {
+   "basePath": "/base/path",
+   "baseInterimResultPath": "/base/interim/result/path",
+   "finalOutputPath": "/final/output/path",
+   "startTimeMs": 10,
+   "endTimeMs": 20,
+   "numReducers": 2,
+   "query": "query"
+   }
+   */
+  @Multiline
+  public static String queryJson;
+
   @Autowired
   private PcapService pcapService;
 
@@ -123,17 +133,16 @@ public class PcapControllerIntegrationTest {
   public void testSecurity() throws Exception {
     this.mockMvc.perform(post(pcapUrl + "/fixed").with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content(fixedJson))
             .andExpect(status().isUnauthorized());
+
+    this.mockMvc.perform(post(pcapUrl + "/query").with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content(queryJson))
+            .andExpect(status().isUnauthorized());
   }
 
   @Test
   public void testFixedRequest() throws Exception {
     MockPcapJob mockPcapJob = (MockPcapJob) wac.getBean("mockPcapJob");
-    List<byte[]> results = Arrays.asList("pcap1".getBytes(), "pcap2".getBytes());
-    mockPcapJob.setResults(results);
     mockPcapJob.setStatus(new JobStatus().withState(JobStatus.State.RUNNING));
 
-    PcapResponse expectedReponse = new PcapResponse();
-    expectedReponse.setPcaps(results);
     this.mockMvc.perform(post(pcapUrl + "/fixed").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content(fixedJson))
             .andExpect(status().isOk())
             .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
@@ -187,6 +196,26 @@ public class PcapControllerIntegrationTest {
   }
 
   @Test
+  public void testQueryRequest() throws Exception {
+    MockPcapJob mockPcapJob = (MockPcapJob) wac.getBean("mockPcapJob");
+    mockPcapJob.setStatus(new JobStatus().withState(JobStatus.State.RUNNING));
+
+    this.mockMvc.perform(post(pcapUrl + "/query").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content(queryJson))
+            .andExpect(status().isOk())
+            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+            .andExpect(jsonPath("$.jobStatus").value("RUNNING"));
+
+    Assert.assertEquals("/base/path", mockPcapJob.getBasePath());
+    Assert.assertEquals("/base/interim/result/path", mockPcapJob.getBaseInterrimResultPath());
+    Assert.assertEquals("/final/output/path", mockPcapJob.getFinalOutputPath());
+    Assert.assertEquals(10000000, mockPcapJob.getStartTimeNs());
+    Assert.assertEquals(20000000, mockPcapJob.getEndTimeNs());
+    Assert.assertEquals(2, mockPcapJob.getNumReducers());
+    Assert.assertTrue(mockPcapJob.getFilterImpl() instanceof QueryPcapFilter.Configurator);
+    Assert.assertEquals("query", mockPcapJob.getQuery());
+  }
+
+  @Test
   public void testGetStatus() throws Exception {
     MockPcapJob mockPcapJob = (MockPcapJob) wac.getBean("mockPcapJob");
 

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
index df65635..6a954e8 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
@@ -45,11 +45,10 @@ public class MockPcapJob extends PcapJob<Path> {
   private Map<String, String> fixedFields;
   private PcapFilterConfigurator filterImpl;
   private int recPerFile;
-  private SequenceFileIterable sequenceFileIterable;
+  private String query;
   private Statusable<Path> statusable;
 
   public MockPcapJob() {
-    sequenceFileIterable = mock(SequenceFileIterable.class);
     statusable = mock(Statusable.class);
   }
 
@@ -64,6 +63,8 @@ public class MockPcapJob extends PcapJob<Path> {
     Object fields = PcapOptions.FIELDS.get(configuration, Object.class);
     if (fields instanceof Map) {
       this.fixedFields = (Map<String, String>) fields;
+    } else {
+      this.query = (String) fields;
     }
     this.filterImpl = PcapOptions.FILTER_IMPL.get(configuration, PcapFilterConfigurator.class);
     this.recPerFile = PcapOptions.NUM_RECORDS_PER_FILE.get(configuration, Integer.class);
@@ -92,10 +93,6 @@ public class MockPcapJob extends PcapJob<Path> {
     when(statusable.isDone()).thenReturn(isDone);
   }
 
-  public void setResults(List<byte[]> pcaps) {
-    when(sequenceFileIterable.iterator()).thenReturn(pcaps.iterator());
-  }
-
   public String getBasePath() {
     return basePath;
   }
@@ -136,6 +133,10 @@ public class MockPcapJob extends PcapJob<Path> {
     return fixedFields;
   }
 
+  public String getQuery() {
+    return query;
+  }
+
   public PcapFilterConfigurator getFilterImpl() {
     return filterImpl;
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/d0e87576/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
index 3c6d506..e9f8f9f 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
@@ -32,12 +32,14 @@ import org.apache.metron.job.manager.InMemoryJobManager;
 import org.apache.metron.job.manager.JobManager;
 import org.apache.metron.pcap.PcapHelper;
 import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
+import org.apache.metron.pcap.filter.query.QueryPcapFilter;
 import org.apache.metron.rest.MetronRestConstants;
 import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.config.PcapJobSupplier;
 import org.apache.metron.rest.mock.MockPcapJob;
 import org.apache.metron.rest.mock.MockPcapJobSupplier;
 import org.apache.metron.rest.model.pcap.FixedPcapRequest;
+import org.apache.metron.rest.model.pcap.QueryPcapRequest;
 import org.apache.metron.rest.model.pcap.PcapStatus;
 import org.apache.metron.rest.model.pcap.Pdml;
 import org.hamcrest.CoreMatchers;
@@ -190,7 +192,7 @@ public class PcapServiceImplTest {
   }
 
   @Test
-  public void fixedShouldProperlyCallPcapJobQuery() throws Exception {
+  public void submitShouldProperlySubmitFixedPcapRequest() throws Exception {
     FixedPcapRequest fixedPcapRequest = new FixedPcapRequest();
     fixedPcapRequest.setBasePath("basePath");
     fixedPcapRequest.setBaseInterimResultPath("baseOutputPath");
@@ -232,7 +234,7 @@ public class PcapServiceImplTest {
     expectedPcapStatus.setJobStatus(JobStatus.State.RUNNING.name());
     expectedPcapStatus.setDescription("description");
 
-    Assert.assertEquals(expectedPcapStatus, pcapService.fixed("user", fixedPcapRequest));
+    Assert.assertEquals(expectedPcapStatus, pcapService.submit("user", fixedPcapRequest));
     Assert.assertEquals(expectedPcapStatus, pcapService.jobStatusToPcapStatus(jobManager.getJob("user", "jobId").getStatus()));
     Assert.assertEquals("basePath", mockPcapJob.getBasePath());
     Assert.assertEquals("baseOutputPath", mockPcapJob.getBaseInterrimResultPath());
@@ -253,7 +255,7 @@ public class PcapServiceImplTest {
   }
 
   @Test
-  public void fixedShouldProperlyCallPcapJobQueryWithDefaults() throws Exception {
+  public void submitShouldProperlySubmitWithDefaults() throws Exception {
     long beforeJobTime = System.currentTimeMillis();
 
     FixedPcapRequest fixedPcapRequest = new FixedPcapRequest();
@@ -275,7 +277,7 @@ public class PcapServiceImplTest {
     expectedPcapStatus.setJobStatus(JobStatus.State.RUNNING.name());
     expectedPcapStatus.setDescription("description");
 
-    Assert.assertEquals(expectedPcapStatus, pcapService.fixed("user", fixedPcapRequest));
+    Assert.assertEquals(expectedPcapStatus, pcapService.submit("user", fixedPcapRequest));
     Assert.assertEquals("/base/path", mockPcapJob.getBasePath());
     Assert.assertEquals("/base/interim/result/path", mockPcapJob.getBaseInterrimResultPath());
     Assert.assertEquals("/final/output/path", mockPcapJob.getFinalOutputPath());
@@ -289,6 +291,49 @@ public class PcapServiceImplTest {
   }
 
   @Test
+  public void submitShouldProperlySubmitQueryPcapRequest() throws Exception {
+    QueryPcapRequest queryPcapRequest = new QueryPcapRequest();
+    queryPcapRequest.setBasePath("basePath");
+    queryPcapRequest.setBaseInterimResultPath("baseOutputPath");
+    queryPcapRequest.setFinalOutputPath("finalOutputPath");
+    queryPcapRequest.setStartTimeMs(1L);
+    queryPcapRequest.setEndTimeMs(2L);
+    queryPcapRequest.setNumReducers(2);
+    queryPcapRequest.setQuery("query");
+    MockPcapJob mockPcapJob = new MockPcapJob();
+    mockPcapJobSupplier.setMockPcapJob(mockPcapJob);
+    JobManager jobManager = new InMemoryJobManager<>();
+
+    PcapServiceImpl pcapService = spy(new PcapServiceImpl(environment, configuration, mockPcapJobSupplier, jobManager, pcapToPdmlScriptWrapper));
+    FileSystem fileSystem = mock(FileSystem.class);
+    doReturn(fileSystem).when(pcapService).getFileSystem();
+    mockPcapJob.setStatus(new JobStatus()
+            .withJobId("jobId")
+            .withDescription("description")
+            .withPercentComplete(0L)
+            .withState(JobStatus.State.RUNNING));
+
+    String expectedFields = "query";
+    PcapStatus expectedPcapStatus = new PcapStatus();
+    expectedPcapStatus.setJobId("jobId");
+    expectedPcapStatus.setJobStatus(JobStatus.State.RUNNING.name());
+    expectedPcapStatus.setDescription("description");
+
+    Assert.assertEquals(expectedPcapStatus, pcapService.submit("user", queryPcapRequest));
+    Assert.assertEquals(expectedPcapStatus, pcapService.jobStatusToPcapStatus(jobManager.getJob("user", "jobId").getStatus()));
+    Assert.assertEquals("basePath", mockPcapJob.getBasePath());
+    Assert.assertEquals("baseOutputPath", mockPcapJob.getBaseInterrimResultPath());
+    Assert.assertEquals("finalOutputPath", mockPcapJob.getFinalOutputPath());
+    Assert.assertEquals(1000000, mockPcapJob.getStartTimeNs());
+    Assert.assertEquals(2000000, mockPcapJob.getEndTimeNs());
+    Assert.assertEquals(2, mockPcapJob.getNumReducers());
+    Assert.assertEquals(100, mockPcapJob.getRecPerFile());
+    Assert.assertTrue(mockPcapJob.getFilterImpl() instanceof QueryPcapFilter.Configurator);
+    Map<String, String> actualFixedFields = mockPcapJob.getFixedFields();
+    Assert.assertEquals("query", mockPcapJob.getQuery());
+  }
+
+  @Test
   public void fixedShouldThrowRestException() throws Exception {
     exception.expect(RestException.class);
     exception.expectMessage("some job exception");
@@ -301,7 +346,7 @@ public class PcapServiceImplTest {
     doReturn(fileSystem).when(pcapService).getFileSystem();
     when(jobManager.submit(pcapJobSupplier, "user")).thenThrow(new JobException("some job exception"));
 
-    pcapService.fixed("user", fixedPcapRequest);
+    pcapService.submit("user", fixedPcapRequest);
   }
 
   @Test


[37/50] [abbrv] metron git commit: METRON-1732: Fix job status liveness bug and parallelize finalizer file writing (mmiklavc via mmiklavc) closes apache/metron#1157

Posted by ni...@apache.org.
METRON-1732: Fix job status liveness bug and parallelize finalizer file writing (mmiklavc via mmiklavc) closes apache/metron#1157


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/d9e1f381
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/d9e1f381
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/d9e1f381

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: d9e1f381cc1ae9edfbffc7216abffdbd8f942c09
Parents: 8a926dd
Author: mmiklavc <mi...@gmail.com>
Authored: Wed Aug 15 11:30:10 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Wed Aug 15 11:30:10 2018 -0600

----------------------------------------------------------------------
 .../CURRENT/configuration/metron-rest-env.xml   | 14 +++
 .../package/scripts/params/params_linux.py      |  1 +
 .../METRON/CURRENT/package/templates/metron.j2  |  1 +
 .../METRON/CURRENT/themes/metron_theme.json     | 10 +++
 metron-interface/metron-rest/README.md          | 10 +++
 .../src/main/config/rest_application.yml        |  1 +
 .../apache/metron/rest/MetronRestConstants.java |  1 +
 .../rest/service/impl/PcapServiceImpl.java      | 25 +++---
 .../apache/metron/rest/mock/MockPcapJob.java    | 18 ++--
 .../rest/service/impl/PcapServiceImplTest.java  | 49 ++++++-----
 .../org/apache/metron/pcap/query/CliParser.java | 10 ++-
 .../PcapTopologyIntegrationTest.java            |  1 +
 .../apache/metron/pcap/query/PcapCliTest.java   | 21 ++---
 .../java/org/apache/metron/pcap/PcapPages.java  |  2 +-
 .../apache/metron/pcap/config/PcapConfig.java   |  6 ++
 .../metron/pcap/config/PcapGlobalDefaults.java  |  1 +
 .../apache/metron/pcap/config/PcapOptions.java  |  3 +-
 .../metron/pcap/finalizer/PcapFinalizer.java    | 70 +++++++++++++--
 .../java/org/apache/metron/pcap/mr/PcapJob.java | 90 ++++++++++++++------
 .../org/apache/metron/pcap/mr/PcapJobTest.java  | 16 +++-
 20 files changed, 258 insertions(+), 92 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
index 895c091..767afa3 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/configuration/metron-rest-env.xml
@@ -201,5 +201,19 @@
             <empty-value-valid>true</empty-value-valid>
         </value-attributes>
     </property>
+    <property>
+        <name>pcap_finalizer_threadpool_size</name>
+        <display-name>Pcap Finalizer Threadpool Size</display-name>
+        <description>The number of threads to use when finalizing Pcap jobs. This affects parallelism
+          around writing out paged files to their final location.
+          If it's a string and ends with "C", then strip the C and treat it as an integral multiple of
+          the number of cores. If it's a string and does not end with a C, then treat it as a number in
+          string form.
+        </description>
+        <value>1</value>
+        <value-attributes>
+            <empty-value-valid>false</empty-value-valid>
+        </value-attributes>
+    </property>
 
 </configuration>

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
index 4f8a9a7..115a54c 100755
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
@@ -391,6 +391,7 @@ pcap_base_interim_result_path = config['configurations']['metron-rest-env']['pca
 pcap_final_output_path = config['configurations']['metron-rest-env']['pcap_final_output_path']
 pcap_page_size = config['configurations']['metron-rest-env']['pcap_page_size']
 pcap_yarn_queue = config['configurations']['metron-rest-env']['pcap_yarn_queue']
+pcap_finalizer_threadpool_size= config['configurations']['metron-rest-env']['pcap_finalizer_threadpool_size']
 pcap_configured_flag_file = status_params.pcap_configured_flag_file
 
 # MapReduce

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2 b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
index 55422d0..a7d01e5 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/templates/metron.j2
@@ -45,3 +45,4 @@ PCAP_BASE_INTERIM_RESULT_PATH="{{pcap_base_interim_result_path}}"
 PCAP_FINAL_OUTPUT_PATH="{{pcap_final_output_path}}"
 PCAP_PAGE_SIZE="{{pcap_page_size}}"
 PCAP_YARN_QUEUE="{{pcap_yarn_queue}}"
+PCAP_FINALIZER_THREADPOOL_SIZE="{{pcap_finalizer_threadpool_size}}"

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
index db06b61..2b64f8f 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/themes/metron_theme.json
@@ -828,6 +828,10 @@
           "subsection-name": "subsection-rest"
         },
         {
+          "config": "metron-rest-env/pcap_finalizer_threadpool_size",
+          "subsection-name": "subsection-rest"
+        },
+        {
           "config": "metron-management-ui-env/metron_management_ui_port",
           "subsection-name": "subsection-management-ui"
         },
@@ -1441,6 +1445,12 @@
         }
       },
       {
+        "config": "metron-rest-env/pcap_finalizer_threadpool_size",
+        "widget": {
+          "type": "text-field"
+        }
+      },
+      {
         "config": "metron-management-ui-env/metron_management_ui_port",
         "widget": {
           "type": "text-field"

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md
index d19d8c3..080422d 100644
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@ -223,6 +223,16 @@ REST will supply the script with raw pcap data through standard in and expects P
 
 Pcap query jobs can be configured for submission to a YARN queue.  This setting is exposed as the Spring property `pcap.yarn.queue`.  If configured, the REST application will set the `mapreduce.job.queuename` Hadoop property to that value.
 
+Queries can also be configured on a global level for setting the number of results per page via a Spring property `pcap.page.size`. By default, this value is set to 10 pcaps per page, but you may choose to set this value higher
+based on observing frequenetly-run query result sizes. This setting works in conjunction with the property for setting finalizer threadpool size when optimizing query performance.
+
+Pcap query jobs have a finalization routine that writes their results out to HDFS in pages. Depending on the size of your pcaps, the number or results typically returned, page sizing (described above), and available CPU cores for running
+your REST application, your performance can be improved by adjusting the number of files that can be written to HDFS in parallel. To this end, there is a threadpool used for this finalization step that can be configured to use a specified
+number of threads. This setting is exposed as the Spring property `pcap.finalizer.threadpool.size`. A default value of "1" is used if not specified by the user. Generally speaking, you should see a performance gain when this value is set
+to anything higher than 1. A sizeable increase in performance can be achieved, especially for larger numbers of files of smaller size, by increasing the number of threads. It should be noted that this property is parsed as a String to allow
+for more complex parallelism values. In addition to normal integer values, you can specify a multiple of the number of cores. If it's a string and ends with "C", then strip the C and treat it as an integral multiple of the number of cores.
+If it's a string and does not end with a C, then treat it as a number in string form.
+
 ## API
 
 Request and Response objects are JSON formatted.  The JSON schemas are available in the Swagger UI.

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-interface/metron-rest/src/main/config/rest_application.yml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/config/rest_application.yml b/metron-interface/metron-rest/src/main/config/rest_application.yml
index e25ad82..84efc01 100644
--- a/metron-interface/metron-rest/src/main/config/rest_application.yml
+++ b/metron-interface/metron-rest/src/main/config/rest_application.yml
@@ -61,3 +61,4 @@ pcap:
   final.output.path: ${PCAP_FINAL_OUTPUT_PATH}
   page.size: ${PCAP_PAGE_SIZE}
   yarn.queue: ${PCAP_YARN_QUEUE}
+  finalizer.threadpool.size: ${PCAP_FINALIZER_THREADPOOL_SIZE}

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
index 02655298..e3bf698 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestConstants.java
@@ -82,4 +82,5 @@ public class MetronRestConstants {
   public static final String PCAP_PAGE_SIZE_SPRING_PROPERTY = "pcap.page.size";
   public static final String PCAP_PDML_SCRIPT_PATH_SPRING_PROPERTY = "pcap.pdml.script.path";
   public static final String PCAP_YARN_QUEUE_SPRING_PROPERTY = "pcap.yarn.queue";
+  public static final String PCAP_FINALIZER_THREADPOOL_SIZE_SPRING_PROPERTY = "pcap.finalizer.threadpool.size";
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
index db2e17b..d5468d4 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
@@ -17,7 +17,17 @@
  */
 package org.apache.metron.rest.service.impl;
 
+import static org.apache.metron.rest.MetronRestConstants.PCAP_YARN_QUEUE_SPRING_PROPERTY;
+
 import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
@@ -31,7 +41,6 @@ import org.apache.metron.job.manager.JobManager;
 import org.apache.metron.pcap.config.PcapOptions;
 import org.apache.metron.pcap.filter.PcapFilterConfigurator;
 import org.apache.metron.pcap.filter.fixed.FixedPcapFilter;
-import org.apache.metron.pcap.filter.query.QueryPcapFilter;
 import org.apache.metron.rest.MetronRestConstants;
 import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.config.PcapJobSupplier;
@@ -47,19 +56,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.env.Environment;
 import org.springframework.stereotype.Service;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-
-import static org.apache.metron.rest.MetronRestConstants.PCAP_YARN_QUEUE_SPRING_PROPERTY;
-
 @Service
 public class PcapServiceImpl implements PcapService {
 
@@ -274,6 +270,7 @@ public class PcapServiceImpl implements PcapService {
     }
 
     PcapOptions.NUM_RECORDS_PER_FILE.put(pcapRequest, Integer.parseInt(environment.getProperty(MetronRestConstants.PCAP_PAGE_SIZE_SPRING_PROPERTY)));
+    PcapOptions.FINALIZER_THREADPOOL_SIZE.put(pcapRequest, environment.getProperty(MetronRestConstants.PCAP_FINALIZER_THREADPOOL_SIZE_SPRING_PROPERTY));
   }
 
   protected FileSystem getFileSystem() throws IOException {

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
index c977faa..1fdf45e 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/mock/MockPcapJob.java
@@ -17,6 +17,11 @@
  */
 package org.apache.metron.rest.mock;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.mapreduce.MRJobConfig;
@@ -29,12 +34,6 @@ import org.apache.metron.pcap.config.PcapOptions;
 import org.apache.metron.pcap.filter.PcapFilterConfigurator;
 import org.apache.metron.pcap.mr.PcapJob;
 
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 public class MockPcapJob extends PcapJob<Path> {
 
   private String basePath;
@@ -46,6 +45,7 @@ public class MockPcapJob extends PcapJob<Path> {
   private Map<String, String> fixedFields;
   private PcapFilterConfigurator filterImpl;
   private int recPerFile;
+  private String finalizerThreadpoolSize;
   private String query;
   private String yarnQueue;
   private Statusable<Path> statusable;
@@ -72,6 +72,7 @@ public class MockPcapJob extends PcapJob<Path> {
     this.filterImpl = PcapOptions.FILTER_IMPL.get(configuration, PcapFilterConfigurator.class);
     this.recPerFile = PcapOptions.NUM_RECORDS_PER_FILE.get(configuration, Integer.class);
     this.yarnQueue = PcapOptions.HADOOP_CONF.get(configuration, Configuration.class).get(MRJobConfig.QUEUE_NAME);
+    this.finalizerThreadpoolSize = PcapOptions.FINALIZER_THREADPOOL_SIZE.get(configuration, String.class);
     return statusable;
   }
 
@@ -152,4 +153,9 @@ public class MockPcapJob extends PcapJob<Path> {
   public String getYarnQueue() {
     return yarnQueue;
   }
+
+  public String getFinalizerThreadpoolSize() {
+    return finalizerThreadpoolSize;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
index 6635598..8cd363a 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
@@ -17,12 +17,31 @@
  */
 package org.apache.metron.rest.service.impl;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyVararg;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.doReturn;
+import static org.powermock.api.mockito.PowerMockito.whenNew;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
 import org.adrianwalker.multilinestring.Multiline;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FSDataInputStream;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
-import org.apache.hadoop.mapreduce.MRJobConfig;
 import org.apache.metron.common.Constants;
 import org.apache.metron.common.utils.JSONUtils;
 import org.apache.metron.job.JobException;
@@ -43,10 +62,10 @@ import org.apache.metron.rest.mock.MockPcapJob;
 import org.apache.metron.rest.mock.MockPcapJobSupplier;
 import org.apache.metron.rest.model.pcap.FixedPcapOptions;
 import org.apache.metron.rest.model.pcap.FixedPcapRequest;
-import org.apache.metron.rest.model.pcap.QueryPcapOptions;
-import org.apache.metron.rest.model.pcap.QueryPcapRequest;
 import org.apache.metron.rest.model.pcap.PcapStatus;
 import org.apache.metron.rest.model.pcap.Pdml;
+import org.apache.metron.rest.model.pcap.QueryPcapOptions;
+import org.apache.metron.rest.model.pcap.QueryPcapRequest;
 import org.hamcrest.CoreMatchers;
 import org.junit.Assert;
 import org.junit.Before;
@@ -59,28 +78,6 @@ import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.springframework.core.env.Environment;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.anyVararg;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.powermock.api.mockito.PowerMockito.doReturn;
-import static org.powermock.api.mockito.PowerMockito.whenNew;
-
 @SuppressWarnings("ALL")
 @RunWith(PowerMockRunner.class)
 @PrepareForTest({PcapToPdmlScriptWrapper.class, ProcessBuilder.class})
@@ -195,6 +192,7 @@ public class PcapServiceImplTest {
     when(environment.getProperty(MetronRestConstants.PCAP_BASE_INTERIM_RESULT_PATH_SPRING_PROPERTY)).thenReturn("/base/interim/result/path");
     when(environment.getProperty(MetronRestConstants.PCAP_FINAL_OUTPUT_PATH_SPRING_PROPERTY)).thenReturn("/final/output/path");
     when(environment.getProperty(MetronRestConstants.PCAP_PAGE_SIZE_SPRING_PROPERTY)).thenReturn("100");
+    when(environment.getProperty(MetronRestConstants.PCAP_FINALIZER_THREADPOOL_SIZE_SPRING_PROPERTY)).thenReturn("2C");
     when(environment.getProperty(MetronRestConstants.PCAP_PDML_SCRIPT_PATH_SPRING_PROPERTY)).thenReturn("/path/to/pdml/script");
     when(environment.getProperty(MetronRestConstants.USER_JOB_LIMIT_SPRING_PROPERTY, Integer.class, 1)).thenReturn(1);
   }
@@ -255,6 +253,7 @@ public class PcapServiceImplTest {
     Assert.assertEquals(2, mockPcapJob.getNumReducers());
     Assert.assertEquals(100, mockPcapJob.getRecPerFile());
     Assert.assertEquals("pcap", mockPcapJob.getYarnQueue());
+    Assert.assertEquals("2C", mockPcapJob.getFinalizerThreadpoolSize());
     Assert.assertTrue(mockPcapJob.getFilterImpl() instanceof FixedPcapFilter.Configurator);
     Map<String, String> actualFixedFields = mockPcapJob.getFixedFields();
     Assert.assertEquals("ip_src_addr", actualFixedFields.get(Constants.Fields.SRC_ADDR.getName()));

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
index b9a2a50..9c338cb 100644
--- a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
+++ b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
@@ -18,8 +18,9 @@
 
 package org.apache.metron.pcap.query;
 
-import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INTERIM_RESULT_PATH_DEFAULT;
 import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INPUT_PATH_DEFAULT;
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INTERIM_RESULT_PATH_DEFAULT;
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.NUM_FINALIZER_THREADS_DEFAULT;
 import static org.apache.metron.pcap.config.PcapGlobalDefaults.NUM_RECORDS_PER_FILE_DEFAULT;
 import static org.apache.metron.pcap.config.PcapGlobalDefaults.NUM_REDUCERS_DEFAULT;
 
@@ -56,6 +57,7 @@ public class CliParser {
     options.addOption(newOption("et", "end_time", true, "Packet end time range. Default is current system time."));
     options.addOption(newOption("df", "date_format", true, "Date format to use for parsing start_time and end_time. Default is to use time in millis since the epoch."));
     options.addOption(newOption("yq", "yarn_queue", true, "Yarn queue this job will be submitted to"));
+    options.addOption(newOption("ft", "finalizer_threads", true, "Number of threads to use for the final output writing."));
     return options;
   }
 
@@ -129,6 +131,12 @@ public class CliParser {
     if (commandLine.hasOption("yarn_queue")) {
       config.setYarnQueue(commandLine.getOptionValue("yarn_queue"));
     }
+    if (commandLine.hasOption("finalizer_threads")) {
+      String numThreads = commandLine.getOptionValue("finalizer_threads");
+      config.setFinalizerThreadpoolSize(numThreads);
+    } else {
+      config.setFinalizerThreadpoolSize(NUM_FINALIZER_THREADS_DEFAULT);
+    }
   }
 
   public void printHelp(String msg, Options opts) {

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
index c30267d..a8f0676 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
@@ -306,6 +306,7 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
     PcapOptions.NUM_REDUCERS.put(configuration, 10);
     PcapOptions.NUM_RECORDS_PER_FILE.put(configuration, 1);
     PcapOptions.FINAL_OUTPUT_PATH.put(configuration, new Path(outputDir.getAbsolutePath()));
+    PcapOptions.FINALIZER_THREADPOOL_SIZE.put(configuration, 4);
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
index 3e7aad2..ec06c52 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
@@ -17,8 +17,8 @@
  */
 package org.apache.metron.pcap.query;
 
-import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INTERIM_RESULT_PATH_DEFAULT;
 import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INPUT_PATH_DEFAULT;
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INTERIM_RESULT_PATH_DEFAULT;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Matchers.argThat;
@@ -33,17 +33,11 @@ import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
-
 import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.mapreduce.MRJobConfig;
 import org.apache.metron.common.Constants;
 import org.apache.metron.common.system.Clock;
@@ -172,7 +166,8 @@ public class PcapCliTest {
             "-protocol", "6",
             "-include_reverse",
             "-num_reducers", "10",
-            "-records_per_file", "1000"
+            "-records_per_file", "1000",
+            "-finalizer_threads", "10"
     };
     Map<String, String> query = new HashMap<String, String>() {{
       put(Constants.Fields.SRC_ADDR.getName(), "192.168.1.1");
@@ -191,6 +186,7 @@ public class PcapCliTest {
     PcapOptions.END_TIME_MS.put(config, 1000L);
     PcapOptions.NUM_RECORDS_PER_FILE.put(config, 1000);
     PcapOptions.PRINT_JOB_STATUS.put(config, true);
+    PcapOptions.FINALIZER_THREADPOOL_SIZE.put(config, "10");
 
     when(jobRunner.submit(isA(Finalizer.class), argThat(mapContaining(config)))).thenReturn(jobRunner);
 
@@ -216,7 +212,8 @@ public class PcapCliTest {
             "-include_reverse",
             "-num_reducers", "10",
             "-records_per_file", "1000",
-            "-yq", "pcap"
+            "-yq", "pcap",
+            "-finalizer_threads", "10"
     };
     Map<String, String> query = new HashMap<String, String>() {{
       put(Constants.Fields.SRC_ADDR.getName(), "192.168.1.1");
@@ -242,6 +239,7 @@ public class PcapCliTest {
     PcapOptions.HADOOP_CONF.put(config, new HashMap<String, Object>() {{
       put(MRJobConfig.QUEUE_NAME, "pcap");
     }});
+    PcapOptions.FINALIZER_THREADPOOL_SIZE.put(config, "10");
 
     when(jobRunner.submit(isA(Finalizer.class), argThat(mapContaining(config)))).thenReturn(jobRunner);
 
@@ -275,6 +273,7 @@ public class PcapCliTest {
     PcapOptions.FIELDS.put(config, query);
     PcapOptions.NUM_REDUCERS.put(config, 10);
     PcapOptions.START_TIME_MS.put(config, 500L);
+    PcapOptions.FINALIZER_THREADPOOL_SIZE.put(config, "1");
 
     when(jobRunner.submit(isA(Finalizer.class), argThat(mapContaining(config)))).thenReturn(jobRunner);
 
@@ -293,7 +292,8 @@ public class PcapCliTest {
             "-base_path", "/base/path",
             "-base_output_path", "/base/output/path",
             "-query", "some query string",
-            "-records_per_file", "1000"
+            "-records_per_file", "1000",
+            "-finalizer_threads", "10"
     };
 
     String query = "some query string";
@@ -306,6 +306,7 @@ public class PcapCliTest {
     PcapOptions.END_TIME_MS.put(config, 1000L);  // needed bc defaults in config
     PcapOptions.NUM_RECORDS_PER_FILE.put(config, 1000);
     PcapOptions.PRINT_JOB_STATUS.put(config, true);
+    PcapOptions.FINALIZER_THREADPOOL_SIZE.put(config, "10");
 
     when(jobRunner.submit(isA(Finalizer.class), argThat(mapContaining(config)))).thenReturn(jobRunner);
 

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapPages.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapPages.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapPages.java
index c98e681..3dbf92d 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapPages.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapPages.java
@@ -64,7 +64,7 @@ public class PcapPages implements Pageable<Path> {
     return new PcapIterator(files.iterator());
   }
 
-  private class PcapIterator implements Iterator<Path> {
+  private static class PcapIterator implements Iterator<Path> {
 
     private Iterator<Path> delegateIt;
 

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
index abf35d0..f2ad653 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
@@ -29,6 +29,7 @@ import java.util.Optional;
 import java.util.function.Function;
 
 public class PcapConfig extends AbstractMapDecorator<String, Object>{
+
   public interface PrefixStrategy extends Function<Clock, String>{}
 
   private boolean showHelp;
@@ -147,4 +148,9 @@ public class PcapConfig extends AbstractMapDecorator<String, Object>{
   public Optional<String> getYarnQueue() {
     return Optional.ofNullable(yarnQueue);
   }
+
+  public void setFinalizerThreadpoolSize(String numThreads) {
+    PcapOptions.FINALIZER_THREADPOOL_SIZE.put(this, numThreads);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapGlobalDefaults.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapGlobalDefaults.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapGlobalDefaults.java
index b8c674c..ebfdad5 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapGlobalDefaults.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapGlobalDefaults.java
@@ -25,4 +25,5 @@ public class PcapGlobalDefaults {
   public static final String FINAL_OUTPUT_PATH_DEFAULT = BASE_PCAP_PATH_DEFAULT + "/output";
   public static final int NUM_REDUCERS_DEFAULT = 10;
   public static final int NUM_RECORDS_PER_FILE_DEFAULT = 10000;
+  public static final String NUM_FINALIZER_THREADS_DEFAULT = "1";
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java
index 203c800..c5852d1 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java
@@ -41,7 +41,8 @@ public enum PcapOptions implements ConfigOption {
   FILTER_IMPL("filterImpl"),
   HADOOP_CONF("hadoopConf"),
   FILESYSTEM("fileSystem"),
-  PRINT_JOB_STATUS("printJobStatus");
+  PRINT_JOB_STATUS("printJobStatus"),
+  FINALIZER_THREADPOOL_SIZE("finalizerThreadpoolSize");
 
   public static final BiFunction<String, Object, Path> STRING_TO_PATH =
       (s, o) -> o == null ? null : new Path(o.toString());

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
index 5a61f9b..2eeab3f 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
@@ -18,6 +18,7 @@
 
 package org.apache.metron.pcap.finalizer;
 
+import static java.lang.String.format;
 import static org.apache.metron.pcap.config.PcapGlobalDefaults.NUM_RECORDS_PER_FILE_DEFAULT;
 
 import com.google.common.collect.Iterables;
@@ -25,8 +26,11 @@ import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.LocatedFileStatus;
@@ -66,8 +70,11 @@ public abstract class PcapFinalizer implements Finalizer<Path> {
     Configuration hadoopConfig = PcapOptions.HADOOP_CONF.get(config, Configuration.class);
     int recPerFile = PcapOptions.NUM_RECORDS_PER_FILE
         .getOrDefault(config, Integer.class, NUM_RECORDS_PER_FILE_DEFAULT);
-    Path interimResultPath = PcapOptions.INTERIM_RESULT_PATH.get(config, PcapOptions.STRING_TO_PATH, Path.class);
+    Path interimResultPath = PcapOptions.INTERIM_RESULT_PATH
+        .get(config, PcapOptions.STRING_TO_PATH, Path.class);
     FileSystem fs = PcapOptions.FILESYSTEM.get(config, FileSystem.class);
+    int parallelism = getNumThreads(PcapOptions.FINALIZER_THREADPOOL_SIZE.get(config, String.class));
+    LOG.info("Finalizer running with parallelism set to " + parallelism);
 
     SequenceFileIterable interimResults = null;
     try {
@@ -78,15 +85,14 @@ public abstract class PcapFinalizer implements Finalizer<Path> {
     List<Path> outFiles = new ArrayList<>();
     try {
       Iterable<List<byte[]>> partitions = Iterables.partition(interimResults, recPerFile);
+      Map<Path, List<byte[]>> toWrite = new HashMap<>();
       int part = 1;
       if (partitions.iterator().hasNext()) {
         for (List<byte[]> data : partitions) {
           Path outputPath = getOutputPath(config, part++);
-          if (data.size() > 0) {
-            write(resultsWriter, hadoopConfig, data, outputPath);
-            outFiles.add(outputPath);
-          }
+          toWrite.put(outputPath, data);
         }
+        outFiles = writeParallel(hadoopConfig, toWrite, parallelism);
       } else {
         LOG.info("No results returned.");
       }
@@ -99,10 +105,62 @@ public abstract class PcapFinalizer implements Finalizer<Path> {
         LOG.warn("Unable to cleanup files in HDFS", e);
       }
     }
+    LOG.info("Done finalizing results");
     return new PcapPages(outFiles);
   }
 
-  protected abstract void write(PcapResultsWriter resultsWriter, Configuration hadoopConfig, List<byte[]> data, Path outputPath) throws IOException;
+  /**
+   * Figure out how many threads to use in the thread pool. If it's a string and ends with "C",
+   * then strip the C and treat it as an integral multiple of the number of cores.  If it's a
+   * string and does not end with a C, then treat it as a number in string form.
+   */
+  private static int getNumThreads(String numThreads) throws JobException {
+    String numThreadsStr = ((String) numThreads).trim().toUpperCase();
+    try {
+      if (numThreadsStr.endsWith("C")) {
+        Integer factor = Integer.parseInt(numThreadsStr.replace("C", ""));
+        return factor * Runtime.getRuntime().availableProcessors();
+      } else {
+        return Integer.parseInt(numThreadsStr);
+      }
+    } catch (NumberFormatException e) {
+      throw new JobException(
+          format("Unable to set number of threads for finalizing from property value '%s'",
+              numThreads));
+    }
+  }
+
+  protected List<Path> writeParallel(Configuration hadoopConfig, Map<Path, List<byte[]>> toWrite,
+      int parallelism) throws IOException {
+    List<Path> outFiles = Collections.synchronizedList(new ArrayList<>());
+    ForkJoinPool tp = new ForkJoinPool(parallelism);
+    try {
+      tp.submit(() -> {
+        toWrite.entrySet().parallelStream().forEach(e -> {
+          Path path = e.getKey();
+          List<byte[]> data = e.getValue();
+          if (data.size() > 0) {
+            try {
+              write(getResultsWriter(), hadoopConfig, data, path);
+            } catch (IOException ioe) {
+              throw new RuntimeException(
+                  String.format("Failed to write results to path '%s'", path.toString()), ioe);
+            }
+            outFiles.add(path);
+          }
+        });
+      }).get();
+    } catch (InterruptedException | ExecutionException  e) {
+      throw new IOException("Error finalizing results.", e);
+    } catch (RuntimeException e) {
+      throw new IOException(e.getMessage(), e.getCause());
+    }
+    outFiles.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
+    return outFiles;
+  }
+
+  protected abstract void write(PcapResultsWriter resultsWriter, Configuration hadoopConfig,
+      List<byte[]> data, Path outputPath) throws IOException;
 
   protected abstract Path getOutputPath(Map<String, Object> config, int partition);
 

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
index bf780af..5e82904 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
@@ -83,8 +83,8 @@ public class PcapJob<T> implements Statusable<Path> {
   private static final long THREE_SECONDS = 3000;
   private static final long ONE_SECOND = 1000;
   private final OutputDirFormatter outputDirFormatter;
-  private volatile Job mrJob; // store a running MR job reference for async status check
-  private volatile JobStatus jobStatus; // overall job status, including finalization step
+  private Job mrJob; // store a running MR job reference for async status check
+  private JobStatus jobStatus; // overall job status, including finalization step
   private Finalizer<Path> finalizer;
   private Map<String, Object> configuration;
   private Pageable<Path> finalResults;
@@ -212,10 +212,10 @@ public class PcapJob<T> implements Statusable<Path> {
   }
 
   @Override
-  public Statusable<Path> submit(Finalizer<Path> finalizer, Map<String, Object> configuration)
+  public Statusable<Path> submit(Finalizer<Path> finalizer, Map<String, Object> config)
       throws JobException {
     this.finalizer = finalizer;
-    this.configuration = configuration;
+    this.configuration = config;
     Optional<String> jobName = Optional.ofNullable(PcapOptions.JOB_NAME.get(configuration, String.class));
     Configuration hadoopConf = PcapOptions.HADOOP_CONF.get(configuration, Configuration.class);
     FileSystem fileSystem = PcapOptions.FILESYSTEM.get(configuration, FileSystem.class);
@@ -307,8 +307,15 @@ public class PcapJob<T> implements Statusable<Path> {
       }
       return this;
     }
-    mrJob.submit();
-    jobStatus.withState(State.SUBMITTED).withDescription("Job submitted").withJobId(mrJob.getJobID().toString());
+    synchronized (this) {
+      // this block synchronized for proper variable visibility across threads once the status timer
+      // is started. mrJob and jobStatus need to be synchronized so that their references and internal
+      // state are made available to the timer thread. The references to these variables above need
+      // not be synchronized because the job will exit when only 1 thread will have had to use them.
+      mrJob.submit();
+      jobStatus.withState(State.SUBMITTED).withDescription("Job submitted")
+          .withJobId(mrJob.getJobID().toString());
+    }
     startJobStatusTimerThread(statusInterval);
     return this;
   }
@@ -337,45 +344,67 @@ public class PcapJob<T> implements Statusable<Path> {
    *
    * @return true if should continue updating status, false otherwise.
    */
-  private synchronized boolean updateStatus() {
+  private boolean updateStatus() {
+    JobStatus tempStatus = null;
+    final float mrJobFraction = 0.75f; // fraction of total job progress calculation we're allocating to the MR job vs finalization
+    synchronized (this) {
+      tempStatus = new JobStatus(jobStatus);
+    }
+    boolean keepUpdating = true;
     try {
-      org.apache.hadoop.mapreduce.JobStatus mrJobStatus = mrJob.getStatus();
-      org.apache.hadoop.mapreduce.JobStatus.State mrJobState = mrJob.getStatus().getState();
-      if (mrJob.isComplete()) {
-        jobStatus.withPercentComplete(100.0);
+      boolean mrJobComplete = false;
+      org.apache.hadoop.mapreduce.JobStatus.State mrJobState = null;
+      String mrJobFailureInfo = null;
+      float mapProg = 0.0f;
+      float reduceProg = 0.0f;
+      synchronized (this) {
+        mrJobComplete = mrJob.isComplete();
+        org.apache.hadoop.mapreduce.JobStatus mrJobStatus = mrJob.getStatus();
+        mrJobState = mrJobStatus.getState();
+        mrJobFailureInfo = mrJobStatus.getFailureInfo();
+        mapProg = mrJob.mapProgress();
+        reduceProg = mrJob.reduceProgress();
+      }
+      if (mrJobComplete) {
         switch (mrJobState) {
           case SUCCEEDED:
-            jobStatus.withState(State.FINALIZING).withDescription("Finalizing job.");
+            tempStatus.withPercentComplete(100.0 * mrJobFraction).withState(State.FINALIZING).withDescription("Finalizing job.");
             try {
+              synchronized (this) {
+                // want to update the description while the job is finalizing
+                jobStatus = new JobStatus(tempStatus);
+              }
               setFinalResults(finalizer, configuration);
-              jobStatus.withState(State.SUCCEEDED).withDescription("Job completed.");
+              tempStatus.withPercentComplete(100.0).withState(State.SUCCEEDED).withDescription("Job completed.");
             } catch (JobException je) {
-              jobStatus.withState(State.FAILED).withDescription("Job finalize failed.")
+              tempStatus.withPercentComplete(100.0).withState(State.FAILED).withDescription("Job finalize failed.")
                   .withFailureException(je);
             }
             break;
           case FAILED:
-            jobStatus.withState(State.FAILED).withDescription(mrJob.getStatus().getFailureInfo());
+            tempStatus.withPercentComplete(100.0).withState(State.FAILED).withDescription(mrJobFailureInfo);
             break;
           case KILLED:
-            jobStatus.withState(State.KILLED).withDescription(mrJob.getStatus().getFailureInfo());
+            tempStatus.withPercentComplete(100.0).withState(State.KILLED).withDescription(mrJobFailureInfo);
             break;
         }
-        return false;
+        keepUpdating = false;
       } else {
-        float mapProg = mrJob.mapProgress();
-        float reduceProg = mrJob.reduceProgress();
-        float totalProgress = ((mapProg / 2) + (reduceProg / 2)) * 100;
+        float mrJobProgress = ((mapProg / 2) + (reduceProg / 2)) * 100;
+        float totalProgress = mrJobProgress * mrJobFraction;
         String description = String
             .format("map: %s%%, reduce: %s%%", mapProg * 100, reduceProg * 100);
-        jobStatus.withPercentComplete(totalProgress).withState(State.RUNNING)
+        tempStatus.withPercentComplete(totalProgress).withState(State.RUNNING)
             .withDescription(description);
       }
     } catch (InterruptedException | IOException e) {
-      jobStatus.withPercentComplete(100.0).withState(State.FAILED).withFailureException(e);
-      return false;
+      tempStatus.withPercentComplete(100.0).withState(State.FAILED).withFailureException(e);
+      keepUpdating = false;
     }
-    return true;
+    synchronized (this) {
+      jobStatus = new JobStatus(tempStatus);
+    }
+    return keepUpdating;
   }
 
   /**
@@ -489,6 +518,8 @@ public class PcapJob<T> implements Statusable<Path> {
           || status.getState() == State.KILLED
           || status.getState() == State.FAILED) {
         return getFinalResults();
+      } else {
+        LOG.info("Percent complete: {}, description: {}", status.getPercentComplete(), status.getDescription());
       }
       Thread.sleep(completeCheckInterval);
     }
@@ -499,8 +530,11 @@ public class PcapJob<T> implements Statusable<Path> {
   }
 
   @Override
-  public synchronized boolean isDone() {
-    State jobState = jobStatus.getState();
+  public boolean isDone() {
+    State jobState = null;
+    synchronized (this) {
+      jobState = jobStatus.getState();
+    }
     return (jobState == State.SUCCEEDED
         || jobState == State.KILLED
         || jobState == State.FAILED);
@@ -509,7 +543,9 @@ public class PcapJob<T> implements Statusable<Path> {
   @Override
   public void kill() throws JobException {
     try {
-      mrJob.killJob();
+      synchronized (this) {
+        mrJob.killJob();
+      }
     } catch (IOException e) {
       throw new JobException("Unable to kill pcap job.", e);
     }

http://git-wip-us.apache.org/repos/asf/metron/blob/d9e1f381/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
index d5ef2dc..bbac79a 100644
--- a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
+++ b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
@@ -244,13 +244,27 @@ public class PcapJobTest {
     JobStatus status = statusable.getStatus();
     Assert.assertThat(status.getState(), equalTo(State.RUNNING));
     Assert.assertThat(status.getDescription(), equalTo("map: 50.0%, reduce: 0.0%"));
-    Assert.assertThat(status.getPercentComplete(), equalTo(25.0));
+    Assert.assertThat(status.getPercentComplete(), equalTo(25.0 * 0.75));
     when(mrJob.mapProgress()).thenReturn(1.0f);
     when(mrJob.reduceProgress()).thenReturn(0.5f);
     timer.updateJobStatus();
     status = statusable.getStatus();
     Assert.assertThat(status.getDescription(), equalTo("map: 100.0%, reduce: 50.0%"));
+    Assert.assertThat(status.getPercentComplete(), equalTo(75.0 * 0.75));
+    when(mrJob.mapProgress()).thenReturn(1.0f);
+    when(mrJob.reduceProgress()).thenReturn(1.0f);
+    timer.updateJobStatus();
+    status = statusable.getStatus();
+    Assert.assertThat(status.getDescription(), equalTo("map: 100.0%, reduce: 100.0%"));
     Assert.assertThat(status.getPercentComplete(), equalTo(75.0));
+    when(mrJob.isComplete()).thenReturn(true);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
+    when(mrJob.mapProgress()).thenReturn(1.0f);
+    when(mrJob.reduceProgress()).thenReturn(1.0f);
+    timer.updateJobStatus();
+    status = statusable.getStatus();
+    Assert.assertThat(status.getDescription(), equalTo("Job completed."));
+    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
   }
 
   @Test


[08/50] [abbrv] metron git commit: METRON-1671 Create PCAP UI (tiborm via merrimanr) closes apache/metron#1103

Posted by ni...@apache.org.
http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
new file mode 100644
index 0000000..244a3ea
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
@@ -0,0 +1,1752 @@
+/**
+ * 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 { TestBed, async, inject } from '@angular/core/testing';
+import {
+  BaseRequestOptions,
+  HttpModule,
+  Http,
+  Response,
+  ResponseOptions
+} from '@angular/http';
+import { MockBackend } from '@angular/http/testing';
+
+import { PcapService } from './pcap.service';
+import { PcapRequest } from '../model/pcap.request';
+
+describe('PcapService', () => {
+  beforeEach(() => {
+
+    TestBed.configureTestingModule({
+      imports: [HttpModule],
+      providers: [
+        PcapService,
+        {
+          provide: Http,
+          useFactory: (mockBackend, options) => {
+            return new Http(mockBackend, options);
+          },
+          deps: [MockBackend, BaseRequestOptions]
+        },
+        MockBackend,
+        BaseRequestOptions
+      ]
+    });
+  });
+  describe('getPackets()', () => {
+    it('should return an Observable<Response>',
+      inject([PcapService, MockBackend], (pcapService, mockBackend) => {
+
+        let request: PcapRequest = {
+          startTimeMs: 0,
+          endTimeMs: 0,
+          ipSrcAddr: '0.0.0.0',
+          ipSrcPort: '80',
+          ipDstAddr: '0.0.0.0',
+          ipDstPort: '80',
+          protocol: '*',
+          packetFilter: '*',
+          includeReverse: false,
+        };
+
+        mockBackend.connections.subscribe((connection) => {
+          connection.mockRespond(new Response(new ResponseOptions({body: pdml_json()})));
+        });
+        let packets;
+        pcapService.getPackets(request).subscribe(r => packets = r)
+        expect(packets).toBeTruthy()
+        expect(packets.pdml).toBeTruthy()
+        expect(packets.pdml.packet.length).toBe(1)
+        expect(packets.pdml.packet[0].proto.length).toBeGreaterThan(3)
+
+        console.log(packets)
+      }))
+
+
+    it('should ...', inject([PcapService], (service: PcapService) => {
+      expect(service).toBeTruthy();
+    }));
+
+  })
+
+});
+
+
+
+
+function pdml_json() {
+  return `{
+  "pdml": {
+    "$": {
+      "version": "0",
+      "creator": "wireshark/2.4.2",
+      "time": "Tue Mar 27 21:55:25 2018",
+      "capture_file": "./metron-platform/metron-api/src/test/resources/test-tcp-packet.pcap"
+    },
+    "packet": [
+      {
+        "proto": [
+          {
+            "$": {
+              "name": "geninfo",
+              "pos": "0",
+              "showname": "General information",
+              "size": "104"
+            },
+            "field": [
+              {
+                "$": {
+                  "name": "num",
+                  "pos": "0",
+                  "show": "1",
+                  "showname": "Number",
+                  "value": "1",
+                  "size": "104"
+                }
+              },
+              {
+                "$": {
+                  "name": "len",
+                  "pos": "0",
+                  "show": "104",
+                  "showname": "Frame Length",
+                  "value": "68",
+                  "size": "104"
+                }
+              },
+              {
+                "$": {
+                  "name": "caplen",
+                  "pos": "0",
+                  "show": "104",
+                  "showname": "Captured Length",
+                  "value": "68",
+                  "size": "104"
+                }
+              },
+              {
+                "$": {
+                  "name": "timestamp",
+                  "pos": "0",
+                  "show": "Mar 26, 2014 19:59:40.024362000 GMT",
+                  "showname": "Captured Time",
+                  "value": "1395863980.024362000",
+                  "size": "104"
+                }
+              }
+            ]
+          },
+          {
+            "$": {
+              "name": "frame",
+              "showname": "Frame 1: 104 bytes on wire (832 bits), 104 bytes captured (832 bits)",
+              "size": "104",
+              "pos": "0"
+            },
+            "field": [
+              {
+                "$": {
+                  "name": "frame.encap_type",
+                  "showname": "Encapsulation type: Ethernet (1)",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "1"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.time",
+                  "showname": "Arrival Time: Mar 26, 2014 19:59:40.024362000 GMT",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "Mar 26, 2014 19:59:40.024362000 GMT"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.offset_shift",
+                  "showname": "Time shift for this packet: 0.000000000 seconds",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "0.000000000"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.time_epoch",
+                  "showname": "Epoch Time: 1395863980.024362000 seconds",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "1395863980.024362000"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.time_delta",
+                  "showname": "Time delta from previous captured frame: 0.000000000 seconds",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "0.000000000"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.time_delta_displayed",
+                  "showname": "Time delta from previous displayed frame: 0.000000000 seconds",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "0.000000000"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.time_relative",
+                  "showname": "Time since reference or first frame: 0.000000000 seconds",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "0.000000000"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.number",
+                  "showname": "Frame Number: 1",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "1"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.len",
+                  "showname": "Frame Length: 104 bytes (832 bits)",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "104"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.cap_len",
+                  "showname": "Capture Length: 104 bytes (832 bits)",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "104"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.marked",
+                  "showname": "Frame is marked: False",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "0"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.ignored",
+                  "showname": "Frame is ignored: False",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "0"
+                }
+              },
+              {
+                "$": {
+                  "name": "frame.protocols",
+                  "showname": "Protocols in frame: eth:ethertype:ip:tcp:smtp",
+                  "size": "0",
+                  "pos": "0",
+                  "show": "eth:ethertype:ip:tcp:smtp"
+                }
+              }
+            ]
+          },
+          {
+            "$": {
+              "name": "eth",
+              "showname": "Ethernet II, Src: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02), Dst: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)",
+              "size": "14",
+              "pos": "0"
+            },
+            "field": [
+              {
+                "$": {
+                  "name": "eth.dst",
+                  "showname": "Destination: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)",
+                  "size": "6",
+                  "pos": "0",
+                  "show": "02:1a:c5:05:00:02",
+                  "value": "021ac5050002"
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "eth.dst_resolved",
+                      "showname": "Destination (resolved): MS-NLB-PhysServer-26_c5:05:00:02",
+                      "hide": "yes",
+                      "size": "6",
+                      "pos": "0",
+                      "show": "MS-NLB-PhysServer-26_c5:05:00:02",
+                      "value": "021ac5050002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "eth.addr",
+                      "showname": "Address: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)",
+                      "size": "6",
+                      "pos": "0",
+                      "show": "02:1a:c5:05:00:02",
+                      "value": "021ac5050002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "eth.addr_resolved",
+                      "showname": "Address (resolved): MS-NLB-PhysServer-26_c5:05:00:02",
+                      "hide": "yes",
+                      "size": "6",
+                      "pos": "0",
+                      "show": "MS-NLB-PhysServer-26_c5:05:00:02",
+                      "value": "021ac5050002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "eth.lg",
+                      "showname": ".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)",
+                      "size": "3",
+                      "pos": "0",
+                      "show": "1",
+                      "value": "1",
+                      "unmaskedvalue": "021ac5"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "eth.ig",
+                      "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
+                      "size": "3",
+                      "pos": "0",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "021ac5"
+                    }
+                  }
+                ]
+              },
+              {
+                "$": {
+                  "name": "eth.src",
+                  "showname": "Source: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02)",
+                  "size": "6",
+                  "pos": "6",
+                  "show": "02:1a:c5:01:00:02",
+                  "value": "021ac5010002"
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "eth.src_resolved",
+                      "showname": "Source (resolved): MS-NLB-PhysServer-26_c5:01:00:02",
+                      "hide": "yes",
+                      "size": "6",
+                      "pos": "6",
+                      "show": "MS-NLB-PhysServer-26_c5:01:00:02",
+                      "value": "021ac5010002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "eth.addr",
+                      "showname": "Address: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02)",
+                      "size": "6",
+                      "pos": "6",
+                      "show": "02:1a:c5:01:00:02",
+                      "value": "021ac5010002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "eth.addr_resolved",
+                      "showname": "Address (resolved): MS-NLB-PhysServer-26_c5:01:00:02",
+                      "hide": "yes",
+                      "size": "6",
+                      "pos": "6",
+                      "show": "MS-NLB-PhysServer-26_c5:01:00:02",
+                      "value": "021ac5010002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "eth.lg",
+                      "showname": ".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)",
+                      "size": "3",
+                      "pos": "6",
+                      "show": "1",
+                      "value": "1",
+                      "unmaskedvalue": "021ac5"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "eth.ig",
+                      "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
+                      "size": "3",
+                      "pos": "6",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "021ac5"
+                    }
+                  }
+                ]
+              },
+              {
+                "$": {
+                  "name": "eth.type",
+                  "showname": "Type: IPv4 (0x0800)",
+                  "size": "2",
+                  "pos": "12",
+                  "show": "0x00000800",
+                  "value": "0800"
+                }
+              },
+              {
+                "$": {
+                  "name": "eth.fcs",
+                  "showname": "Frame check sequence: 0x26469e92 [correct]",
+                  "size": "4",
+                  "pos": "100",
+                  "show": "0x26469e92",
+                  "value": "26469e92"
+                }
+              },
+              {
+                "$": {
+                  "name": "eth.fcs.status",
+                  "showname": "FCS Status: Good",
+                  "size": "0",
+                  "pos": "100",
+                  "show": "1"
+                }
+              }
+            ]
+          },
+          {
+            "$": {
+              "name": "ip",
+              "showname": "Internet Protocol Version 4, Src: 24.0.0.2, Dst: 24.128.0.2",
+              "size": "20",
+              "pos": "14"
+            },
+            "field": [
+              {
+                "$": {
+                  "name": "ip.version",
+                  "showname": "0100 .... = Version: 4",
+                  "size": "1",
+                  "pos": "14",
+                  "show": "4",
+                  "value": "4",
+                  "unmaskedvalue": "45"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.hdr_len",
+                  "showname": ".... 0101 = Header Length: 20 bytes (5)",
+                  "size": "1",
+                  "pos": "14",
+                  "show": "20",
+                  "value": "45"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.dsfield",
+                  "showname": "Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)",
+                  "size": "1",
+                  "pos": "15",
+                  "show": "0x00000000",
+                  "value": "00"
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "ip.dsfield.dscp",
+                      "showname": "0000 00.. = Differentiated Services Codepoint: Default (0)",
+                      "size": "1",
+                      "pos": "15",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "00"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.dsfield.ecn",
+                      "showname": ".... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)",
+                      "size": "1",
+                      "pos": "15",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "00"
+                    }
+                  }
+                ]
+              },
+              {
+                "$": {
+                  "name": "ip.len",
+                  "showname": "Total Length: 86",
+                  "size": "2",
+                  "pos": "16",
+                  "show": "86",
+                  "value": "0056"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.id",
+                  "showname": "Identification: 0xcff6 (53238)",
+                  "size": "2",
+                  "pos": "18",
+                  "show": "0x0000cff6",
+                  "value": "cff6"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.flags",
+                  "showname": "Flags: 0x02 (Don't Fragment)",
+                  "size": "1",
+                  "pos": "20",
+                  "show": "0x00000002",
+                  "value": "40"
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "ip.flags.rb",
+                      "showname": "0... .... = Reserved bit: Not set",
+                      "size": "1",
+                      "pos": "20",
+                      "show": "0",
+                      "value": "40"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.flags.df",
+                      "showname": ".1.. .... = Don't fragment: Set",
+                      "size": "1",
+                      "pos": "20",
+                      "show": "1",
+                      "value": "40"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.flags.mf",
+                      "showname": "..0. .... = More fragments: Not set",
+                      "size": "1",
+                      "pos": "20",
+                      "show": "0",
+                      "value": "40"
+                    }
+                  }
+                ]
+              },
+              {
+                "$": {
+                  "name": "ip.frag_offset",
+                  "showname": "Fragment offset: 0",
+                  "size": "2",
+                  "pos": "20",
+                  "show": "0",
+                  "value": "4000"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.ttl",
+                  "showname": "Time to live: 32",
+                  "size": "1",
+                  "pos": "22",
+                  "show": "32",
+                  "value": "20"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.proto",
+                  "showname": "Protocol: TCP (6)",
+                  "size": "1",
+                  "pos": "23",
+                  "show": "6",
+                  "value": "06"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.checksum",
+                  "showname": "Header checksum: 0x5a28 [validation disabled]",
+                  "size": "2",
+                  "pos": "24",
+                  "show": "0x00005a28",
+                  "value": "5a28"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.checksum.status",
+                  "showname": "Header checksum status: Unverified",
+                  "size": "0",
+                  "pos": "24",
+                  "show": "2"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.src",
+                  "showname": "Source: 24.0.0.2",
+                  "size": "4",
+                  "pos": "26",
+                  "show": "24.0.0.2",
+                  "value": "18000002"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.addr",
+                  "showname": "Source or Destination Address: 24.0.0.2",
+                  "hide": "yes",
+                  "size": "4",
+                  "pos": "26",
+                  "show": "24.0.0.2",
+                  "value": "18000002"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.src_host",
+                  "showname": "Source Host: 24.0.0.2",
+                  "hide": "yes",
+                  "size": "4",
+                  "pos": "26",
+                  "show": "24.0.0.2",
+                  "value": "18000002"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.host",
+                  "showname": "Source or Destination Host: 24.0.0.2",
+                  "hide": "yes",
+                  "size": "4",
+                  "pos": "26",
+                  "show": "24.0.0.2",
+                  "value": "18000002"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.dst",
+                  "showname": "Destination: 24.128.0.2",
+                  "size": "4",
+                  "pos": "30",
+                  "show": "24.128.0.2",
+                  "value": "18800002"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.addr",
+                  "showname": "Source or Destination Address: 24.128.0.2",
+                  "hide": "yes",
+                  "size": "4",
+                  "pos": "30",
+                  "show": "24.128.0.2",
+                  "value": "18800002"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.dst_host",
+                  "showname": "Destination Host: 24.128.0.2",
+                  "hide": "yes",
+                  "size": "4",
+                  "pos": "30",
+                  "show": "24.128.0.2",
+                  "value": "18800002"
+                }
+              },
+              {
+                "$": {
+                  "name": "ip.host",
+                  "showname": "Source or Destination Host: 24.128.0.2",
+                  "hide": "yes",
+                  "size": "4",
+                  "pos": "30",
+                  "show": "24.128.0.2",
+                  "value": "18800002"
+                }
+              },
+              {
+                "$": {
+                  "name": "",
+                  "show": "Source GeoIP: United States, Woodbridge, NJ, AS7922 Comcast Cable Communications, LLC, United States, Woodbridge, NJ, AS7922 Comcast Cable Communications, LLC, 40.557598, -74.284599",
+                  "size": "4",
+                  "pos": "26",
+                  "value": "18000002"
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "ip.geoip.src_country",
+                      "showname": "Source GeoIP Country: United States",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "United States",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.country",
+                      "showname": "Source or Destination GeoIP Country: United States",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "United States",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.src_city",
+                      "showname": "Source GeoIP City: Woodbridge, NJ",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "Woodbridge, NJ",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.city",
+                      "showname": "Source or Destination GeoIP City: Woodbridge, NJ",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "Woodbridge, NJ",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.src_asnum",
+                      "showname": "Source GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "AS7922 Comcast Cable Communications, LLC",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.asnum",
+                      "showname": "Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "AS7922 Comcast Cable Communications, LLC",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.src_country",
+                      "showname": "Source GeoIP Country: United States",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "United States",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.country",
+                      "showname": "Source or Destination GeoIP Country: United States",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "United States",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.src_city",
+                      "showname": "Source GeoIP City: Woodbridge, NJ",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "Woodbridge, NJ",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.city",
+                      "showname": "Source or Destination GeoIP City: Woodbridge, NJ",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "Woodbridge, NJ",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.src_asnum",
+                      "showname": "Source GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "AS7922 Comcast Cable Communications, LLC",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.asnum",
+                      "showname": "Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "AS7922 Comcast Cable Communications, LLC",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.src_lat",
+                      "showname": "Source GeoIP Latitude: 40.557598",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "40.557598",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.lat",
+                      "showname": "Source or Destination GeoIP Latitude: 40.557598",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "40.557598",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.src_lon",
+                      "showname": "Source GeoIP Longitude: -74.284599",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "-74.284599",
+                      "value": "18000002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.lon",
+                      "showname": "Source or Destination GeoIP Longitude: -74.284599",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "26",
+                      "show": "-74.284599",
+                      "value": "18000002"
+                    }
+                  }
+                ]
+              },
+              {
+                "$": {
+                  "name": "",
+                  "show": "Destination GeoIP: United States, Groton, CT, AS7922 Comcast Cable Communications, LLC, United States, Groton, CT, AS7922 Comcast Cable Communications, LLC, 41.353199, -72.038597",
+                  "size": "4",
+                  "pos": "30",
+                  "value": "18800002"
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "ip.geoip.dst_country",
+                      "showname": "Destination GeoIP Country: United States",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "United States",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.country",
+                      "showname": "Source or Destination GeoIP Country: United States",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "United States",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.dst_city",
+                      "showname": "Destination GeoIP City: Groton, CT",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "Groton, CT",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.city",
+                      "showname": "Source or Destination GeoIP City: Groton, CT",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "Groton, CT",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.dst_asnum",
+                      "showname": "Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "AS7922 Comcast Cable Communications, LLC",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.asnum",
+                      "showname": "Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "AS7922 Comcast Cable Communications, LLC",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.dst_country",
+                      "showname": "Destination GeoIP Country: United States",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "United States",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.country",
+                      "showname": "Source or Destination GeoIP Country: United States",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "United States",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.dst_city",
+                      "showname": "Destination GeoIP City: Groton, CT",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "Groton, CT",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.city",
+                      "showname": "Source or Destination GeoIP City: Groton, CT",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "Groton, CT",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.dst_asnum",
+                      "showname": "Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "AS7922 Comcast Cable Communications, LLC",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.asnum",
+                      "showname": "Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "AS7922 Comcast Cable Communications, LLC",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.dst_lat",
+                      "showname": "Destination GeoIP Latitude: 41.353199",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "41.353199",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.lat",
+                      "showname": "Source or Destination GeoIP Latitude: 41.353199",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "41.353199",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.dst_lon",
+                      "showname": "Destination GeoIP Longitude: -72.038597",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "-72.038597",
+                      "value": "18800002"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "ip.geoip.lon",
+                      "showname": "Source or Destination GeoIP Longitude: -72.038597",
+                      "hide": "yes",
+                      "size": "4",
+                      "pos": "30",
+                      "show": "-72.038597",
+                      "value": "18800002"
+                    }
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "$": {
+              "name": "tcp",
+              "showname": "Transmission Control Protocol, Src Port: 2137, Dst Port: 25, Seq: 1, Ack: 1, Len: 34",
+              "size": "32",
+              "pos": "34"
+            },
+            "field": [
+              {
+                "$": {
+                  "name": "tcp.srcport",
+                  "showname": "Source Port: 2137",
+                  "size": "2",
+                  "pos": "34",
+                  "show": "2137",
+                  "value": "0859"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.dstport",
+                  "showname": "Destination Port: 25",
+                  "size": "2",
+                  "pos": "36",
+                  "show": "25",
+                  "value": "0019"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.port",
+                  "showname": "Source or Destination Port: 2137",
+                  "hide": "yes",
+                  "size": "2",
+                  "pos": "34",
+                  "show": "2137",
+                  "value": "0859"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.port",
+                  "showname": "Source or Destination Port: 25",
+                  "hide": "yes",
+                  "size": "2",
+                  "pos": "36",
+                  "show": "25",
+                  "value": "0019"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.stream",
+                  "showname": "Stream index: 0",
+                  "size": "0",
+                  "pos": "34",
+                  "show": "0"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.len",
+                  "showname": "TCP Segment Len: 34",
+                  "size": "1",
+                  "pos": "46",
+                  "show": "34",
+                  "value": "80"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.seq",
+                  "showname": "Sequence number: 1    (relative sequence number)",
+                  "size": "4",
+                  "pos": "38",
+                  "show": "1",
+                  "value": "f88900ce"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.nxtseq",
+                  "showname": "Next sequence number: 35    (relative sequence number)",
+                  "size": "0",
+                  "pos": "34",
+                  "show": "35"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.ack",
+                  "showname": "Acknowledgment number: 1    (relative ack number)",
+                  "size": "4",
+                  "pos": "42",
+                  "show": "1",
+                  "value": "365aa74f"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.hdr_len",
+                  "showname": "1000 .... = Header Length: 32 bytes (8)",
+                  "size": "1",
+                  "pos": "46",
+                  "show": "32",
+                  "value": "80"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.flags",
+                  "showname": "Flags: 0x018 (PSH, ACK)",
+                  "size": "2",
+                  "pos": "46",
+                  "show": "0x00000018",
+                  "value": "18",
+                  "unmaskedvalue": "8018"
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "tcp.flags.res",
+                      "showname": "000. .... .... = Reserved: Not set",
+                      "size": "1",
+                      "pos": "46",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "80"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.ns",
+                      "showname": "...0 .... .... = Nonce: Not set",
+                      "size": "1",
+                      "pos": "46",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "80"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.cwr",
+                      "showname": ".... 0... .... = Congestion Window Reduced (CWR): Not set",
+                      "size": "1",
+                      "pos": "47",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "18"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.ecn",
+                      "showname": ".... .0.. .... = ECN-Echo: Not set",
+                      "size": "1",
+                      "pos": "47",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "18"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.urg",
+                      "showname": ".... ..0. .... = Urgent: Not set",
+                      "size": "1",
+                      "pos": "47",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "18"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.ack",
+                      "showname": ".... ...1 .... = Acknowledgment: Set",
+                      "size": "1",
+                      "pos": "47",
+                      "show": "1",
+                      "value": "1",
+                      "unmaskedvalue": "18"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.push",
+                      "showname": ".... .... 1... = Push: Set",
+                      "size": "1",
+                      "pos": "47",
+                      "show": "1",
+                      "value": "1",
+                      "unmaskedvalue": "18"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.reset",
+                      "showname": ".... .... .0.. = Reset: Not set",
+                      "size": "1",
+                      "pos": "47",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "18"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.syn",
+                      "showname": ".... .... ..0. = Syn: Not set",
+                      "size": "1",
+                      "pos": "47",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "18"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.fin",
+                      "showname": ".... .... ...0 = Fin: Not set",
+                      "size": "1",
+                      "pos": "47",
+                      "show": "0",
+                      "value": "0",
+                      "unmaskedvalue": "18"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.flags.str",
+                      "showname": "TCP Flags: \\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7AP\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7",
+                      "size": "2",
+                      "pos": "46",
+                      "show": "\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7AP\\\\xc2\\\\xb7\\\\xc2\\\\xb7\\\\xc2\\\\xb7",
+                      "value": "8018"
+                    }
+                  }
+                ]
+              },
+              {
+                "$": {
+                  "name": "tcp.window_size_value",
+                  "showname": "Window size value: 7240",
+                  "size": "2",
+                  "pos": "48",
+                  "show": "7240",
+                  "value": "1c48"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.window_size",
+                  "showname": "Calculated window size: 7240",
+                  "size": "2",
+                  "pos": "48",
+                  "show": "7240",
+                  "value": "1c48"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.window_size_scalefactor",
+                  "showname": "Window size scaling factor: -1 (unknown)",
+                  "size": "2",
+                  "pos": "48",
+                  "show": "-1",
+                  "value": "1c48"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.checksum",
+                  "showname": "Checksum: 0x681f [unverified]",
+                  "size": "2",
+                  "pos": "50",
+                  "show": "0x0000681f",
+                  "value": "681f"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.checksum.status",
+                  "showname": "Checksum Status: Unverified",
+                  "size": "0",
+                  "pos": "50",
+                  "show": "2"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.urgent_pointer",
+                  "showname": "Urgent pointer: 0",
+                  "size": "2",
+                  "pos": "52",
+                  "show": "0",
+                  "value": "0000"
+                }
+              },
+              {
+                "$": {
+                  "name": "tcp.options",
+                  "showname": "Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps",
+                  "size": "12",
+                  "pos": "54",
+                  "show": "01:01:08:0a:eb:83:4b:08:e8:8c:de:cb",
+                  "value": "0101080aeb834b08e88cdecb"
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "tcp.options.nop",
+                      "showname": "TCP Option - No-Operation (NOP)",
+                      "size": "1",
+                      "pos": "54",
+                      "show": "01",
+                      "value": "01"
+                    },
+                    "field": [
+                      {
+                        "$": {
+                          "name": "tcp.option_kind",
+                          "showname": "Kind: No-Operation (1)",
+                          "size": "1",
+                          "pos": "54",
+                          "show": "1",
+                          "value": "01"
+                        }
+                      }
+                    ]
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.options.nop",
+                      "showname": "TCP Option - No-Operation (NOP)",
+                      "size": "1",
+                      "pos": "55",
+                      "show": "01",
+                      "value": "01"
+                    },
+                    "field": [
+                      {
+                        "$": {
+                          "name": "tcp.option_kind",
+                          "showname": "Kind: No-Operation (1)",
+                          "size": "1",
+                          "pos": "55",
+                          "show": "1",
+                          "value": "01"
+                        }
+                      }
+                    ]
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.options.timestamp",
+                      "showname": "TCP Option - Timestamps: TSval 3951250184, TSecr 3901546187",
+                      "size": "10",
+                      "pos": "56",
+                      "show": "08:0a:eb:83:4b:08:e8:8c:de:cb",
+                      "value": "080aeb834b08e88cdecb"
+                    },
+                    "field": [
+                      {
+                        "$": {
+                          "name": "tcp.option_kind",
+                          "showname": "Kind: Time Stamp Option (8)",
+                          "size": "1",
+                          "pos": "56",
+                          "show": "8",
+                          "value": "08"
+                        }
+                      },
+                      {
+                        "$": {
+                          "name": "tcp.option_len",
+                          "showname": "Length: 10",
+                          "size": "1",
+                          "pos": "57",
+                          "show": "10",
+                          "value": "0a"
+                        }
+                      },
+                      {
+                        "$": {
+                          "name": "tcp.options.timestamp.tsval",
+                          "showname": "Timestamp value: 3951250184",
+                          "size": "4",
+                          "pos": "58",
+                          "show": "3951250184",
+                          "value": "eb834b08"
+                        }
+                      },
+                      {
+                        "$": {
+                          "name": "tcp.options.timestamp.tsecr",
+                          "showname": "Timestamp echo reply: 3901546187",
+                          "size": "4",
+                          "pos": "62",
+                          "show": "3901546187",
+                          "value": "e88cdecb"
+                        }
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "$": {
+                  "name": "tcp.analysis",
+                  "showname": "SEQ/ACK analysis",
+                  "size": "0",
+                  "pos": "34",
+                  "show": "",
+                  "value": ""
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "tcp.analysis.bytes_in_flight",
+                      "showname": "Bytes in flight: 34",
+                      "size": "0",
+                      "pos": "34",
+                      "show": "34"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "tcp.analysis.push_bytes_sent",
+                      "showname": "Bytes sent since last PSH flag: 34",
+                      "size": "0",
+                      "pos": "34",
+                      "show": "34"
+                    }
+                  }
+                ]
+              },
+              {
+                "$": {
+                  "name": "tcp.payload",
+                  "showname": "TCP payload (34 bytes)",
+                  "size": "34",
+                  "pos": "66",
+                  "show": "45:48:4c:4f:20:63:6c:69:65:6e:74:2d:31:38:30:30:30:30:30:33:2e:65:78:61:6d:70:6c:65:2e:69:6e:74:0d:0a",
+                  "value": "45484c4f20636c69656e742d31383030303030332e6578616d706c652e696e740d0a"
+                }
+              }
+            ]
+          },
+          {
+            "$": {
+              "name": "smtp",
+              "showname": "Simple Mail Transfer Protocol",
+              "size": "34",
+              "pos": "66"
+            },
+            "field": [
+              {
+                "$": {
+                  "name": "smtp.req",
+                  "showname": "Request: True",
+                  "hide": "yes",
+                  "size": "0",
+                  "pos": "66",
+                  "show": "1"
+                }
+              },
+              {
+                "$": {
+                  "name": "smtp.command_line",
+                  "showname": "Command Line: EHLO client-18000003.example.int\\\\r\\\\n",
+                  "size": "34",
+                  "pos": "66",
+                  "show": "EHLO client-18000003.example.int\\\\xd\\\\xa",
+                  "value": "45484c4f20636c69656e742d31383030303030332e6578616d706c652e696e740d0a"
+                },
+                "field": [
+                  {
+                    "$": {
+                      "name": "smtp.req.command",
+                      "showname": "Command: EHLO",
+                      "size": "4",
+                      "pos": "66",
+                      "show": "EHLO",
+                      "value": "45484c4f"
+                    }
+                  },
+                  {
+                    "$": {
+                      "name": "smtp.req.parameter",
+                      "showname": "Request parameter: client-18000003.example.int",
+                      "size": "27",
+                      "pos": "71",
+                      "show": "client-18000003.example.int",
+                      "value": "636c69656e742d31383030303030332e6578616d706c652e696e74"
+                    }
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
+`
+}
+
+function pdml() {
+  return `<?xml version="1.0" encoding="utf-8"?>
+  <?xml-stylesheet type="text/xsl" href="pdml2html.xsl"?>
+  <!-- You can find pdml2html.xsl in /usr/share/wireshark or at https://code.wireshark.org/review/gitweb?p=wireshark.git;a=blob_plain;f=pdml2html.xsl. -->
+  <pdml version="0" creator="wireshark/2.4.2" time="Tue Mar 27 21:55:25 2018" capture_file="./metron-platform/metron-api/src/test/resources/test-tcp-packet.pcap">
+  <packet>
+    <proto name="geninfo" pos="0" showname="General information" size="104">
+      <field name="num" pos="0" show="1" showname="Number" value="1" size="104"/>
+      <field name="len" pos="0" show="104" showname="Frame Length" value="68" size="104"/>
+      <field name="caplen" pos="0" show="104" showname="Captured Length" value="68" size="104"/>
+      <field name="timestamp" pos="0" show="Mar 26, 2014 19:59:40.024362000 GMT" showname="Captured Time" value="1395863980.024362000" size="104"/>
+    </proto>
+    <proto name="frame" showname="Frame 1: 104 bytes on wire (832 bits), 104 bytes captured (832 bits)" size="104" pos="0">
+      <field name="frame.encap_type" showname="Encapsulation type: Ethernet (1)" size="0" pos="0" show="1"/>
+      <field name="frame.time" showname="Arrival Time: Mar 26, 2014 19:59:40.024362000 GMT" size="0" pos="0" show="Mar 26, 2014 19:59:40.024362000 GMT"/>
+      <field name="frame.offset_shift" showname="Time shift for this packet: 0.000000000 seconds" size="0" pos="0" show="0.000000000"/>
+      <field name="frame.time_epoch" showname="Epoch Time: 1395863980.024362000 seconds" size="0" pos="0" show="1395863980.024362000"/>
+      <field name="frame.time_delta" showname="Time delta from previous captured frame: 0.000000000 seconds" size="0" pos="0" show="0.000000000"/>
+      <field name="frame.time_delta_displayed" showname="Time delta from previous displayed frame: 0.000000000 seconds" size="0" pos="0" show="0.000000000"/>
+      <field name="frame.time_relative" showname="Time since reference or first frame: 0.000000000 seconds" size="0" pos="0" show="0.000000000"/>
+      <field name="frame.number" showname="Frame Number: 1" size="0" pos="0" show="1"/>
+      <field name="frame.len" showname="Frame Length: 104 bytes (832 bits)" size="0" pos="0" show="104"/>
+      <field name="frame.cap_len" showname="Capture Length: 104 bytes (832 bits)" size="0" pos="0" show="104"/>
+      <field name="frame.marked" showname="Frame is marked: False" size="0" pos="0" show="0"/>
+      <field name="frame.ignored" showname="Frame is ignored: False" size="0" pos="0" show="0"/>
+      <field name="frame.protocols" showname="Protocols in frame: eth:ethertype:ip:tcp:smtp" size="0" pos="0" show="eth:ethertype:ip:tcp:smtp"/>
+    </proto>
+    <proto name="eth" showname="Ethernet II, Src: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02), Dst: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)" size="14" pos="0">
+      <field name="eth.dst" showname="Destination: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)" size="6" pos="0" show="02:1a:c5:05:00:02" value="021ac5050002">
+        <field name="eth.dst_resolved" showname="Destination (resolved): MS-NLB-PhysServer-26_c5:05:00:02" hide="yes" size="6" pos="0" show="MS-NLB-PhysServer-26_c5:05:00:02" value="021ac5050002"/>
+        <field name="eth.addr" showname="Address: MS-NLB-PhysServer-26_c5:05:00:02 (02:1a:c5:05:00:02)" size="6" pos="0" show="02:1a:c5:05:00:02" value="021ac5050002"/>
+        <field name="eth.addr_resolved" showname="Address (resolved): MS-NLB-PhysServer-26_c5:05:00:02" hide="yes" size="6" pos="0" show="MS-NLB-PhysServer-26_c5:05:00:02" value="021ac5050002"/>
+        <field name="eth.lg" showname=".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)" size="3" pos="0" show="1" value="1" unmaskedvalue="021ac5"/>
+        <field name="eth.ig" showname=".... ...0 .... .... .... .... = IG bit: Individual address (unicast)" size="3" pos="0" show="0" value="0" unmaskedvalue="021ac5"/>
+      </field>
+      <field name="eth.src" showname="Source: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02)" size="6" pos="6" show="02:1a:c5:01:00:02" value="021ac5010002">
+        <field name="eth.src_resolved" showname="Source (resolved): MS-NLB-PhysServer-26_c5:01:00:02" hide="yes" size="6" pos="6" show="MS-NLB-PhysServer-26_c5:01:00:02" value="021ac5010002"/>
+        <field name="eth.addr" showname="Address: MS-NLB-PhysServer-26_c5:01:00:02 (02:1a:c5:01:00:02)" size="6" pos="6" show="02:1a:c5:01:00:02" value="021ac5010002"/>
+        <field name="eth.addr_resolved" showname="Address (resolved): MS-NLB-PhysServer-26_c5:01:00:02" hide="yes" size="6" pos="6" show="MS-NLB-PhysServer-26_c5:01:00:02" value="021ac5010002"/>
+        <field name="eth.lg" showname=".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)" size="3" pos="6" show="1" value="1" unmaskedvalue="021ac5"/>
+        <field name="eth.ig" showname=".... ...0 .... .... .... .... = IG bit: Individual address (unicast)" size="3" pos="6" show="0" value="0" unmaskedvalue="021ac5"/>
+      </field>
+      <field name="eth.type" showname="Type: IPv4 (0x0800)" size="2" pos="12" show="0x00000800" value="0800"/>
+      <field name="eth.fcs" showname="Frame check sequence: 0x26469e92 [correct]" size="4" pos="100" show="0x26469e92" value="26469e92"/>
+      <field name="eth.fcs.status" showname="FCS Status: Good" size="0" pos="100" show="1"/>
+    </proto>
+    <proto name="ip" showname="Internet Protocol Version 4, Src: 24.0.0.2, Dst: 24.128.0.2" size="20" pos="14">
+      <field name="ip.version" showname="0100 .... = Version: 4" size="1" pos="14" show="4" value="4" unmaskedvalue="45"/>
+      <field name="ip.hdr_len" showname=".... 0101 = Header Length: 20 bytes (5)" size="1" pos="14" show="20" value="45"/>
+      <field name="ip.dsfield" showname="Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)" size="1" pos="15" show="0x00000000" value="00">
+        <field name="ip.dsfield.dscp" showname="0000 00.. = Differentiated Services Codepoint: Default (0)" size="1" pos="15" show="0" value="0" unmaskedvalue="00"/>
+        <field name="ip.dsfield.ecn" showname=".... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)" size="1" pos="15" show="0" value="0" unmaskedvalue="00"/>
+      </field>
+      <field name="ip.len" showname="Total Length: 86" size="2" pos="16" show="86" value="0056"/>
+      <field name="ip.id" showname="Identification: 0xcff6 (53238)" size="2" pos="18" show="0x0000cff6" value="cff6"/>
+      <field name="ip.flags" showname="Flags: 0x02 (Don&#x27;t Fragment)" size="1" pos="20" show="0x00000002" value="40">
+        <field name="ip.flags.rb" showname="0... .... = Reserved bit: Not set" size="1" pos="20" show="0" value="40"/>
+        <field name="ip.flags.df" showname=".1.. .... = Don&#x27;t fragment: Set" size="1" pos="20" show="1" value="40"/>
+        <field name="ip.flags.mf" showname="..0. .... = More fragments: Not set" size="1" pos="20" show="0" value="40"/>
+      </field>
+      <field name="ip.frag_offset" showname="Fragment offset: 0" size="2" pos="20" show="0" value="4000"/>
+      <field name="ip.ttl" showname="Time to live: 32" size="1" pos="22" show="32" value="20"/>
+      <field name="ip.proto" showname="Protocol: TCP (6)" size="1" pos="23" show="6" value="06"/>
+      <field name="ip.checksum" showname="Header checksum: 0x5a28 [validation disabled]" size="2" pos="24" show="0x00005a28" value="5a28"/>
+      <field name="ip.checksum.status" showname="Header checksum status: Unverified" size="0" pos="24" show="2"/>
+      <field name="ip.src" showname="Source: 24.0.0.2" size="4" pos="26" show="24.0.0.2" value="18000002"/>
+      <field name="ip.addr" showname="Source or Destination Address: 24.0.0.2" hide="yes" size="4" pos="26" show="24.0.0.2" value="18000002"/>
+      <field name="ip.src_host" showname="Source Host: 24.0.0.2" hide="yes" size="4" pos="26" show="24.0.0.2" value="18000002"/>
+      <field name="ip.host" showname="Source or Destination Host: 24.0.0.2" hide="yes" size="4" pos="26" show="24.0.0.2" value="18000002"/>
+      <field name="ip.dst" showname="Destination: 24.128.0.2" size="4" pos="30" show="24.128.0.2" value="18800002"/>
+      <field name="ip.addr" showname="Source or Destination Address: 24.128.0.2" hide="yes" size="4" pos="30" show="24.128.0.2" value="18800002"/>
+      <field name="ip.dst_host" showname="Destination Host: 24.128.0.2" hide="yes" size="4" pos="30" show="24.128.0.2" value="18800002"/>
+      <field name="ip.host" showname="Source or Destination Host: 24.128.0.2" hide="yes" size="4" pos="30" show="24.128.0.2" value="18800002"/>
+      <field name="" show="Source GeoIP: United States, Woodbridge, NJ, AS7922 Comcast Cable Communications, LLC, United States, Woodbridge, NJ, AS7922 Comcast Cable Communications, LLC, 40.557598, -74.284599" size="4" pos="26" value="18000002">
+        <field name="ip.geoip.src_country" showname="Source GeoIP Country: United States" size="4" pos="26" show="United States" value="18000002"/>
+        <field name="ip.geoip.country" showname="Source or Destination GeoIP Country: United States" hide="yes" size="4" pos="26" show="United States" value="18000002"/>
+        <field name="ip.geoip.src_city" showname="Source GeoIP City: Woodbridge, NJ" size="4" pos="26" show="Woodbridge, NJ" value="18000002"/>
+        <field name="ip.geoip.city" showname="Source or Destination GeoIP City: Woodbridge, NJ" hide="yes" size="4" pos="26" show="Woodbridge, NJ" value="18000002"/>
+        <field name="ip.geoip.src_asnum" showname="Source GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" size="4" pos="26" show="AS7922 Comcast Cable Communications, LLC" value="18000002"/>
+        <field name="ip.geoip.asnum" showname="Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" hide="yes" size="4" pos="26" show="AS7922 Comcast Cable Communications, LLC" value="18000002"/>
+        <field name="ip.geoip.src_country" showname="Source GeoIP Country: United States" size="4" pos="26" show="United States" value="18000002"/>
+        <field name="ip.geoip.country" showname="Source or Destination GeoIP Country: United States" hide="yes" size="4" pos="26" show="United States" value="18000002"/>
+        <field name="ip.geoip.src_city" showname="Source GeoIP City: Woodbridge, NJ" size="4" pos="26" show="Woodbridge, NJ" value="18000002"/>
+        <field name="ip.geoip.city" showname="Source or Destination GeoIP City: Woodbridge, NJ" hide="yes" size="4" pos="26" show="Woodbridge, NJ" value="18000002"/>
+        <field name="ip.geoip.src_asnum" showname="Source GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" size="4" pos="26" show="AS7922 Comcast Cable Communications, LLC" value="18000002"/>
+        <field name="ip.geoip.asnum" showname="Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" hide="yes" size="4" pos="26" show="AS7922 Comcast Cable Communications, LLC" value="18000002"/>
+        <field name="ip.geoip.src_lat" showname="Source GeoIP Latitude: 40.557598" size="4" pos="26" show="40.557598" value="18000002"/>
+        <field name="ip.geoip.lat" showname="Source or Destination GeoIP Latitude: 40.557598" hide="yes" size="4" pos="26" show="40.557598" value="18000002"/>
+        <field name="ip.geoip.src_lon" showname="Source GeoIP Longitude: -74.284599" size="4" pos="26" show="-74.284599" value="18000002"/>
+        <field name="ip.geoip.lon" showname="Source or Destination GeoIP Longitude: -74.284599" hide="yes" size="4" pos="26" show="-74.284599" value="18000002"/>
+      </field>
+      <field name="" show="Destination GeoIP: United States, Groton, CT, AS7922 Comcast Cable Communications, LLC, United States, Groton, CT, AS7922 Comcast Cable Communications, LLC, 41.353199, -72.038597" size="4" pos="30" value="18800002">
+        <field name="ip.geoip.dst_country" showname="Destination GeoIP Country: United States" size="4" pos="30" show="United States" value="18800002"/>
+        <field name="ip.geoip.country" showname="Source or Destination GeoIP Country: United States" hide="yes" size="4" pos="30" show="United States" value="18800002"/>
+        <field name="ip.geoip.dst_city" showname="Destination GeoIP City: Groton, CT" size="4" pos="30" show="Groton, CT" value="18800002"/>
+        <field name="ip.geoip.city" showname="Source or Destination GeoIP City: Groton, CT" hide="yes" size="4" pos="30" show="Groton, CT" value="18800002"/>
+        <field name="ip.geoip.dst_asnum" showname="Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" size="4" pos="30" show="AS7922 Comcast Cable Communications, LLC" value="18800002"/>
+        <field name="ip.geoip.asnum" showname="Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" hide="yes" size="4" pos="30" show="AS7922 Comcast Cable Communications, LLC" value="18800002"/>
+        <field name="ip.geoip.dst_country" showname="Destination GeoIP Country: United States" size="4" pos="30" show="United States" value="18800002"/>
+        <field name="ip.geoip.country" showname="Source or Destination GeoIP Country: United States" hide="yes" size="4" pos="30" show="United States" value="18800002"/>
+        <field name="ip.geoip.dst_city" showname="Destination GeoIP City: Groton, CT" size="4" pos="30" show="Groton, CT" value="18800002"/>
+        <field name="ip.geoip.city" showname="Source or Destination GeoIP City: Groton, CT" hide="yes" size="4" pos="30" show="Groton, CT" value="18800002"/>
+        <field name="ip.geoip.dst_asnum" showname="Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" size="4" pos="30" show="AS7922 Comcast Cable Communications, LLC" value="18800002"/>
+        <field name="ip.geoip.asnum" showname="Source or Destination GeoIP AS Number: AS7922 Comcast Cable Communications, LLC" hide="yes" size="4" pos="30" show="AS7922 Comcast Cable Communications, LLC" value="18800002"/>
+        <field name="ip.geoip.dst_lat" showname="Destination GeoIP Latitude: 41.353199" size="4" pos="30" show="41.353199" value="18800002"/>
+        <field name="ip.geoip.lat" showname="Source or Destination GeoIP Latitude: 41.353199" hide="yes" size="4" pos="30" show="41.353199" value="18800002"/>
+        <field name="ip.geoip.dst_lon" showname="Destination GeoIP Longitude: -72.038597" size="4" pos="30" show="-72.038597" value="18800002"/>
+        <field name="ip.geoip.lon" showname="Source or Destination GeoIP Longitude: -72.038597" hide="yes" size="4" pos="30" show="-72.038597" value="18800002"/>
+      </field>
+    </proto>
+    <proto name="tcp" showname="Transmission Control Protocol, Src Port: 2137, Dst Port: 25, Seq: 1, Ack: 1, Len: 34" size="32" pos="34">
+      <field name="tcp.srcport" showname="Source Port: 2137" size="2" pos="34" show="2137" value="0859"/>
+      <field name="tcp.dstport" showname="Destination Port: 25" size="2" pos="36" show="25" value="0019"/>
+      <field name="tcp.port" showname="Source or Destination Port: 2137" hide="yes" size="2" pos="34" show="2137" value="0859"/>
+      <field name="tcp.port" showname="Source or Destination Port: 25" hide="yes" size="2" pos="36" show="25" value="0019"/>
+      <field name="tcp.stream" showname="Stream index: 0" size="0" pos="34" show="0"/>
+      <field name="tcp.len" showname="TCP Segment Len: 34" size="1" pos="46" show="34" value="80"/>
+      <field name="tcp.seq" showname="Sequence number: 1    (relative sequence number)" size="4" pos="38" show="1" value="f88900ce"/>
+      <field name="tcp.nxtseq" showname="Next sequence number: 35    (relative sequence number)" size="0" pos="34" show="35"/>
+      <field name="tcp.ack" showname="Acknowledgment number: 1    (relative ack number)" size="4" pos="42" show="1" value="365aa74f"/>
+      <field name="tcp.hdr_len" showname="1000 .... = Header Length: 32 bytes (8)" size="1" pos="46" show="32" value="80"/>
+      <field name="tcp.flags" showname="Flags: 0x018 (PSH, ACK)" size="2" pos="46" show="0x00000018" value="18" unmaskedvalue="8018">
+        <field name="tcp.flags.res" showname="000. .... .... = Reserved: Not set" size="1" pos="46" show="0" value="0" unmaskedvalue="80"/>
+        <field name="tcp.flags.ns" showname="...0 .... .... = Nonce: Not set" size="1" pos="46" show="0" value="0" unmaskedvalue="80"/>
+        <field name="tcp.flags.cwr" showname=".... 0... .... = Congestion Window Reduced (CWR): Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
+        <field name="tcp.flags.ecn" showname=".... .0.. .... = ECN-Echo: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
+        <field name="tcp.flags.urg" showname=".... ..0. .... = Urgent: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
+        <field name="tcp.flags.ack" showname=".... ...1 .... = Acknowledgment: Set" size="1" pos="47" show="1" value="1" unmaskedvalue="18"/>
+        <field name="tcp.flags.push" showname=".... .... 1... = Push: Set" size="1" pos="47" show="1" value="1" unmaskedvalue="18"/>
+        <field name="tcp.flags.reset" showname=".... .... .0.. = Reset: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
+        <field name="tcp.flags.syn" showname=".... .... ..0. = Syn: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
+        <field name="tcp.flags.fin" showname=".... .... ...0 = Fin: Not set" size="1" pos="47" show="0" value="0" unmaskedvalue="18"/>
+        <field name="tcp.flags.str" showname="TCP Flags: \\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7AP\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7" size="2" pos="46" show="\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7AP\\xc2\\xb7\\xc2\\xb7\\xc2\\xb7" value="8018"/>
+      </field>
+      <field name="tcp.window_size_value" showname="Window size value: 7240" size="2" pos="48" show="7240" value="1c48"/>
+      <field name="tcp.window_size" showname="Calculated window size: 7240" size="2" pos="48" show="7240" value="1c48"/>
+      <field name="tcp.window_size_scalefactor" showname="Window size scaling factor: -1 (unknown)" size="2" pos="48" show="-1" value="1c48"/>
+      <field name="tcp.checksum" showname="Checksum: 0x681f [unverified]" size="2" pos="50" show="0x0000681f" value="681f"/>
+      <field name="tcp.checksum.status" showname="Checksum Status: Unverified" size="0" pos="50" show="2"/>
+      <field name="tcp.urgent_pointer" showname="Urgent pointer: 0" size="2" pos="52" show="0" value="0000"/>
+      <field name="tcp.options" showname="Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps" size="12" pos="54" show="01:01:08:0a:eb:83:4b:08:e8:8c:de:cb" value="0101080aeb834b08e88cdecb">
+        <field name="tcp.options.nop" showname="TCP Option - No-Operation (NOP)" size="1" pos="54" show="01" value="01">
+          <field name="tcp.option_kind" showname="Kind: No-Operation (1)" size="1" pos="54" show="1" value="01"/>
+        </field>
+        <field name="tcp.options.nop" showname="TCP Option - No-Operation (NOP)" size="1" pos="55" show="01" value="01">
+          <field name="tcp.option_kind" showname="Kind: No-Operation (1)" size="1" pos="55" show="1" value="01"/>
+        </field>
+        <field name="tcp.options.timestamp" showname="TCP Option - Timestamps: TSval 3951250184, TSecr 3901546187" size="10" pos="56" show="08:0a:eb:83:4b:08:e8:8c:de:cb" value="080aeb834b08e88cdecb">
+          <field name="tcp.option_kind" showname="Kind: Time Stamp Option (8)" size="1" pos="56" show="8" value="08"/>
+          <field name="tcp.option_len" showname="Length: 10" size="1" pos="57" show="10" value="0a"/>
+          <field name="tcp.options.timestamp.tsval" showname="Timestamp value: 3951250184" size="4" pos="58" show="3951250184" value="eb834b08"/>
+          <field name="tcp.options.timestamp.tsecr" showname="Timestamp echo reply: 3901546187" size="4" pos="62" show="3901546187" value="e88cdecb"/>
+        </field>
+      </field>
+      <field name="tcp.analysis" showname="SEQ/ACK analysis" size="0" pos="34" show="" value="">
+        <field name="tcp.analysis.bytes_in_flight" showname="Bytes in flight: 34" size="0" pos="34" show="34"/>
+        <field name="tcp.analysis.push_bytes_sent" showname="Bytes sent since last PSH flag: 34" size="0" pos="34" show="34"/>
+      </field>
+      <field name="tcp.payload" showname="TCP payload (34 bytes)" size="34" pos="66" show="45:48:4c:4f:20:63:6c:69:65:6e:74:2d:31:38:30:30:30:30:30:33:2e:65:78:61:6d:70:6c:65:2e:69:6e:74:0d:0a" value="45484c4f20636c69656e742d31383030303030332e6578616d706c652e696e740d0a"/>
+    </proto>
+    <proto name="smtp" showname="Simple Mail Transfer Protocol" size="34" pos="66">
+      <field name="smtp.req" showname="Request: True" hide="yes" size="0" pos="66" show="1"/>
+      <field name="smtp.command_line" showname="Command Line: EHLO client-18000003.example.int\\r\\n" size="34" pos="66" show="EHLO client-18000003.example.int\\xd\\xa" value="45484c4f20636c69656e742d31383030303030332e6578616d706c652e696e740d0a">
+        <field name="smtp.req.command" showname="Command: EHLO" size="4" pos="66" show="EHLO" value="45484c4f"/>
+        <field name="smtp.req.parameter" showname="Request parameter: client-18000003.example.int" size="27" pos="71" show="client-18000003.example.int" value="636c69656e742d31383030303030332e6578616d706c652e696e74"/>
+      </field>
+    </proto>
+  </packet>
+
+
+  </pdml>`
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
new file mode 100644
index 0000000..5f6f33c
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
@@ -0,0 +1,68 @@
+/**
+ * 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 {Injectable, NgZone} from '@angular/core';
+import {Observable, Subject} from 'rxjs/Rx';
+import {Http, Headers, RequestOptions, Response} from '@angular/http';
+import {HttpUtil} from '../../utils/httpUtil';
+
+import 'rxjs/add/operator/map';
+
+import {PcapRequest} from '../model/pcap.request';
+import {Pdml} from '../model/pdml';
+
+export class PcapStatusResponse {
+  jobStatus: string;
+  percentComplete: number;
+  pageTotal: number;
+}
+
+@Injectable()
+export class PcapService {
+
+    private statusInterval = 4;
+    defaultHeaders = {'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest'};
+
+    constructor(private http: Http, private ngZone: NgZone) {
+    }
+
+    public pollStatus(id: string): Observable<{}> {
+      return Observable.interval(this.statusInterval * 1000).switchMap(() => {
+        return this.getStatus(id);
+      });
+    }
+
+    public submitRequest(pcapRequest: PcapRequest): Observable<string> {
+      return this.http.post('/api/v1/pcap/fixed', pcapRequest, new RequestOptions({headers: new Headers(this.defaultHeaders)}))
+          .map(result => JSON.parse(result.text()).jobId)
+          .catch(HttpUtil.handleError)
+          .onErrorResumeNext();
+    }
+
+    public getStatus(id: string): Observable<PcapStatusResponse> {
+      return this.http.get(`/api/v1/pcap/${id}`,
+          new RequestOptions({headers: new Headers(this.defaultHeaders)}))
+          .map(HttpUtil.extractData)
+          .catch(HttpUtil.handleError);
+  }
+    public getPackets(id: string, pageId: number): Observable<Pdml> {
+        return this.http.get(`/api/v1/pcap/${id}/pdml?page=${pageId}`, new RequestOptions({headers: new Headers(this.defaultHeaders)}))
+            .map(HttpUtil.extractData)
+            .catch(HttpUtil.handleError)
+            .onErrorResumeNext();
+    }
+}


[44/50] [abbrv] metron git commit: METRON-1752 Prevent package.lock from changing during build (sardell via merrimanr) closes apache/metron#1177

Posted by ni...@apache.org.
http://git-wip-us.apache.org/repos/asf/metron/blob/09826da8/metron-interface/metron-config/pom.xml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-config/pom.xml b/metron-interface/metron-config/pom.xml
index 14acdc6..59c416e 100644
--- a/metron-interface/metron-config/pom.xml
+++ b/metron-interface/metron-config/pom.xml
@@ -1,15 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-  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. 
+<!--
+  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.
   -->
 
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -25,8 +25,8 @@
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-        <node.version>v6.2.0</node.version>
-        <npm.version>3.8.9</npm.version>
+        <node.version>v9.11.1</node.version>
+        <npm.version>6.2.0</npm.version>
     </properties>
     <dependencies>
     </dependencies>
@@ -53,12 +53,12 @@
               </execution>
               <execution>
                 <phase>generate-resources</phase>
-                <id>npm install</id>
+                <id>npm ci</id>
                 <goals>
                   <goal>npm</goal>
                 </goals>
                 <configuration>
-                  <arguments>install</arguments>
+                  <arguments>ci</arguments>
                 </configuration>
               </execution>
               <execution>


[36/50] [abbrv] metron git commit: METRON-1735 Empty print status option causes NPE (merrimanr) closes apache/metron#1160

Posted by ni...@apache.org.
METRON-1735 Empty print status option causes NPE (merrimanr) closes apache/metron#1160


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/8a926dd5
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/8a926dd5
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/8a926dd5

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 8a926dd5a4501df8c7ef51b526ad5e4d752a5e19
Parents: bce9b90
Author: merrimanr <me...@gmail.com>
Authored: Tue Aug 14 12:59:38 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Tue Aug 14 12:59:38 2018 -0500

----------------------------------------------------------------------
 .../org/apache/metron/pcap/query/CliParser.java  |  4 ----
 .../apache/metron/pcap/query/PcapCliTest.java    |  7 ++-----
 .../apache/metron/pcap/config/PcapConfig.java    |  2 +-
 .../java/org/apache/metron/pcap/mr/PcapJob.java  | 18 +++++++++++++-----
 .../org/apache/metron/pcap/mr/PcapJobTest.java   | 19 +++++++++++++++++++
 5 files changed, 35 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/8a926dd5/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
index 5040f90..b9a2a50 100644
--- a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
+++ b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
@@ -55,7 +55,6 @@ public class CliParser {
     options.addOption(newOption("rpf", "records_per_file", true, String.format("Number of records to include in each output pcap file (defaults to %s)", NUM_RECORDS_PER_FILE_DEFAULT)));
     options.addOption(newOption("et", "end_time", true, "Packet end time range. Default is current system time."));
     options.addOption(newOption("df", "date_format", true, "Date format to use for parsing start_time and end_time. Default is to use time in millis since the epoch."));
-    options.addOption(newOption("ps", "print_status", false, "Print the status of the job as it runs"));
     options.addOption(newOption("yq", "yarn_queue", true, "Yarn queue this job will be submitted to"));
     return options;
   }
@@ -127,9 +126,6 @@ public class CliParser {
         //no-op
       }
     }
-    if (commandLine.hasOption("print_status")) {
-      config.setPrintJobStatus(true);
-    }
     if (commandLine.hasOption("yarn_queue")) {
       config.setYarnQueue(commandLine.getOptionValue("yarn_queue"));
     }

http://git-wip-us.apache.org/repos/asf/metron/blob/8a926dd5/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
index a71e997..3e7aad2 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
@@ -172,8 +172,7 @@ public class PcapCliTest {
             "-protocol", "6",
             "-include_reverse",
             "-num_reducers", "10",
-            "-records_per_file", "1000",
-            "-ps"
+            "-records_per_file", "1000"
     };
     Map<String, String> query = new HashMap<String, String>() {{
       put(Constants.Fields.SRC_ADDR.getName(), "192.168.1.1");
@@ -217,7 +216,6 @@ public class PcapCliTest {
             "-include_reverse",
             "-num_reducers", "10",
             "-records_per_file", "1000",
-            "-ps",
             "-yq", "pcap"
     };
     Map<String, String> query = new HashMap<String, String>() {{
@@ -295,8 +293,7 @@ public class PcapCliTest {
             "-base_path", "/base/path",
             "-base_output_path", "/base/output/path",
             "-query", "some query string",
-            "-records_per_file", "1000",
-            "-ps"
+            "-records_per_file", "1000"
     };
 
     String query = "some query string";

http://git-wip-us.apache.org/repos/asf/metron/blob/8a926dd5/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
index 4a08e14..abf35d0 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
@@ -42,7 +42,7 @@ public class PcapConfig extends AbstractMapDecorator<String, Object>{
   public PcapConfig(PrefixStrategy prefixStrategy) {
     this();
     setShowHelp(false);
-    setPrintJobStatus(false);
+    setPrintJobStatus(true);
     setBasePath("");
     setBaseInterimResultPath("");
     setStartTimeMs(-1L);

http://git-wip-us.apache.org/repos/asf/metron/blob/8a926dd5/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
index 0f5ad4d..bf780af 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
@@ -463,12 +463,20 @@ public class PcapJob<T> implements Statusable<Path> {
     return new JobStatus(jobStatus);
   }
 
+  protected void setJobStatus(JobStatus jobStatus) {
+    this.jobStatus = jobStatus;
+  }
+
+  protected void setMrJob(Job mrJob) {
+    this.mrJob = mrJob;
+  }
+
   /**
    * Synchronous call blocks until completion.
    */
   @Override
   public Pageable<Path> get() throws JobException, InterruptedException {
-    if (PcapOptions.PRINT_JOB_STATUS.get(configuration, Boolean.class)) {
+    if (PcapOptions.PRINT_JOB_STATUS.getOrDefault(configuration, Boolean.class, false) && mrJob != null) {
       try {
         mrJob.monitorAndPrintJob();
       } catch (IOException e) {
@@ -486,10 +494,6 @@ public class PcapJob<T> implements Statusable<Path> {
     }
   }
 
-  public void monitorJob() throws IOException, InterruptedException {
-    mrJob.monitorAndPrintJob();
-  }
-
   private synchronized Pageable<Path> getFinalResults() {
     return new PcapPages(finalResults);
   }
@@ -521,4 +525,8 @@ public class PcapJob<T> implements Statusable<Path> {
   public Map<String, Object> getConfiguration() {
     return new HashMap<>(this.configuration);
   }
+
+  protected void setConfiguration(Map<String, Object> configuration) {
+    this.configuration = configuration;
+  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/8a926dd5/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
index 0f555d0..d5ef2dc 100644
--- a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
+++ b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/mr/PcapJobTest.java
@@ -21,6 +21,9 @@ package org.apache.metron.pcap.mr;
 import static java.lang.Long.toUnsignedString;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import java.io.IOException;
@@ -287,4 +290,20 @@ public class PcapJobTest {
     Assert.assertThat(status.getJobId(), equalTo(jobIdVal));
   }
 
+  @Test
+  public void get_should_print_status() throws Exception {
+    Map<String, Object> configuration = new HashMap<>();
+    testJob.setConfiguration(configuration);
+    testJob.setMrJob(mrJob);
+    testJob.setJobStatus(new JobStatus().withState(State.SUCCEEDED));
+
+    testJob.get();
+    verify(mrJob, times(0)).monitorAndPrintJob();
+
+    PcapOptions.PRINT_JOB_STATUS.put(configuration, true);
+    testJob.get();
+    verify(mrJob, times(1)).monitorAndPrintJob();
+    verifyNoMoreInteractions(mrJob);
+  }
+
 }


[22/50] [abbrv] metron git commit: METRON-1726: Refactor PcapTopologyIntegrationTest (mmiklavc via mmiklavc) closes apache/metron#1140

Posted by ni...@apache.org.
METRON-1726: Refactor PcapTopologyIntegrationTest (mmiklavc via mmiklavc) closes apache/metron#1140


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/7967f358
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/7967f358
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/7967f358

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 7967f358c6c4b8437935c43e54179126e48e248f
Parents: 3e77859
Author: mmiklavc <mi...@gmail.com>
Authored: Tue Aug 7 15:02:20 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Tue Aug 7 15:02:20 2018 -0600

----------------------------------------------------------------------
 .../org/apache/metron/pcap/query/PcapCli.java   |   2 +
 .../PcapTopologyIntegrationTest.java            | 892 ++++++++++---------
 .../metron/pcap/finalizer/PcapCliFinalizer.java |   5 +-
 3 files changed, 473 insertions(+), 426 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/7967f358/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
index 0b06b0c..c23f037 100644
--- a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
+++ b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
@@ -87,6 +87,7 @@ public class PcapCli {
       try {
         config = fixedParser.parse(otherArgs);
         commonConfig = config;
+        PcapOptions.FINAL_OUTPUT_PATH.put(commonConfig, new Path(execDir));
       } catch (ParseException | java.text.ParseException e) {
         System.err.println(e.getMessage());
         System.err.flush();
@@ -112,6 +113,7 @@ public class PcapCli {
       try {
         config = queryParser.parse(otherArgs);
         commonConfig = config;
+        PcapOptions.FINAL_OUTPUT_PATH.put(commonConfig, new Path(execDir));
       } catch (ParseException | java.text.ParseException e) {
         System.err.println(e.getMessage());
         queryParser.printHelp();

http://git-wip-us.apache.org/repos/asf/metron/blob/7967f358/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
index 108fd2b..c30267d 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
@@ -58,6 +58,7 @@ import org.apache.metron.integration.components.MRComponent;
 import org.apache.metron.integration.components.ZKServerComponent;
 import org.apache.metron.integration.utils.KafkaUtil;
 import org.apache.metron.job.JobStatus;
+import org.apache.metron.job.Pageable;
 import org.apache.metron.job.Statusable;
 import org.apache.metron.pcap.PacketInfo;
 import org.apache.metron.pcap.PcapHelper;
@@ -73,7 +74,10 @@ import org.apache.metron.spout.pcap.Endianness;
 import org.apache.metron.spout.pcap.deserializer.Deserializers;
 import org.apache.metron.test.utils.UnitTestHelper;
 import org.json.simple.JSONObject;
+import org.junit.AfterClass;
 import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -85,12 +89,19 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
   private static String OUTPUT_DIR = BASE_DIR + "/output";
   private static final int MAX_RETRIES = 30;
   private static final int SLEEP_MS = 500;
-  private String topologiesDir = "src/main/flux";
-  private String targetDir = "target";
+  private static String topologiesDir = "src/main/flux";
+  private static String targetDir = "target";
+  private static ComponentRunner runner;
+  private static File inputDir;
+  private static File interimResultDir;
+  private static File outputDir;
+  private static List<Map.Entry<byte[], byte[]>> pcapEntries;
+  private static boolean withHeaders;
+  private FixedPcapConfig configuration;
 
   private static void clearOutDirs(File... dirs) throws IOException {
-    for(File dir: dirs) {
-      for(File f : dir.listFiles()) {
+    for (File dir : dirs) {
+      for (File f : dir.listFiles()) {
         if (f.isDirectory()) {
           FileUtils.deleteDirectory(f);
         } else {
@@ -99,8 +110,8 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
       }
     }
   }
-  private static int numFiles(File outDir, Configuration config) {
 
+  private static int numFiles(File outDir, Configuration config) {
     return outDir.list(new FilenameFilter() {
       @Override
       public boolean accept(File dir, String name) {
@@ -109,11 +120,12 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
     }).length;
   }
 
-  // This will eventually be completely deprecated.  As it takes a significant amount of testing, the test is being disabled.
+  // This will eventually be completely deprecated.
+  // As it takes a significant amount of testing, the test is being disabled.
   @Ignore
   @Test
   public void testTimestampInPacket() throws Exception {
-    testTopology(new Function<Properties, Void>() {
+    setupTopology(new Function<Properties, Void>() {
       @Nullable
       @Override
       public Void apply(@Nullable Properties input) {
@@ -129,9 +141,14 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
                );
   }
 
-  @Test
-  public void testTimestampInKey() throws Exception {
-    testTopology(new Function<Properties, Void>() {
+  /**
+   * Sets up component infrastructure once for all tests.
+   */
+  @BeforeClass
+  public static void setupAll() throws Exception {
+    System.out.println("Setting up test components");
+    withHeaders = false;
+    setupTopology(new Function<Properties, Void>() {
       @Nullable
       @Override
       public Void apply(@Nullable Properties input) {
@@ -154,7 +171,30 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
           System.out.println("Wrote " + pcapEntries.size() + " to kafka");
         }
       }
-    }, false);
+    }, withHeaders);
+    System.out.println("Done with setup.");
+  }
+
+  private static File getDir(String targetDir, String childDir) {
+    File directory = new File(new File(targetDir), childDir);
+    if (!directory.exists()) {
+      directory.mkdirs();
+    }
+    return directory;
+  }
+
+  /**
+   * Cleans up component infrastructure after all tests finish running.
+   */
+  @AfterClass
+  public static void teardownAll() throws Exception {
+    System.out.println("Tearing down test infrastructure");
+    System.out.println("Stopping runner");
+    runner.stop();
+    System.out.println("Done stopping runner");
+    System.out.println("Clearing output directories");
+    clearOutDirs(inputDir, interimResultDir, outputDir);
+    System.out.println("Finished");
   }
 
   private static long getTimestamp(int offset, List<Map.Entry<byte[], byte[]>> entries) {
@@ -165,27 +205,27 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
     public void send(KafkaComponent kafkaComponent, List<Map.Entry<byte[], byte[]>> entries) throws Exception;
   }
 
-  public void testTopology(Function<Properties, Void> updatePropertiesCallback
+  public static void setupTopology(Function<Properties, Void> updatePropertiesCallback
                           ,SendEntries sendPcapEntriesCallback
                           ,boolean withHeaders
                           )
-          throws Exception
-  {
+          throws Exception {
     if (!new File(topologiesDir).exists()) {
       topologiesDir = UnitTestHelper.findDir("topologies");
     }
     targetDir = UnitTestHelper.findDir("target");
-    final File inputDir = getDir(targetDir, DATA_DIR);
-    final File interimResultDir = getDir(targetDir, INTERIM_RESULT);
-    final File outputDir = getDir(targetDir, OUTPUT_DIR);
+    inputDir = getDir(targetDir, DATA_DIR);
+    interimResultDir = getDir(targetDir, INTERIM_RESULT);
+    outputDir = getDir(targetDir, OUTPUT_DIR);
     clearOutDirs(inputDir, interimResultDir, outputDir);
 
     File baseDir = new File(new File(targetDir), BASE_DIR);
     //Assert.assertEquals(0, numFiles(outDir));
     Assert.assertNotNull(topologiesDir);
     Assert.assertNotNull(targetDir);
-    Path pcapFile = new Path("../metron-integration-test/src/main/sample/data/SampleInput/PCAPExampleOutput");
-    final List<Map.Entry<byte[], byte[]>> pcapEntries = Lists.newArrayList(readPcaps(pcapFile, withHeaders));
+    Path pcapFile = new Path(
+        "../metron-integration-test/src/main/sample/data/SampleInput/PCAPExampleOutput");
+    pcapEntries = Lists.newArrayList(readPcaps(pcapFile, withHeaders));
     Assert.assertTrue(Iterables.size(pcapEntries) > 0);
     final Properties topologyProperties = new Properties() {{
       setProperty("topology.workers", "1");
@@ -206,415 +246,428 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
 
     final ZKServerComponent zkServerComponent = getZKServerComponent(topologyProperties);
 
-    final KafkaComponent kafkaComponent = getKafkaComponent(topologyProperties, Collections.singletonList(
+    final KafkaComponent kafkaComponent = getKafkaComponent(topologyProperties,
+        Collections.singletonList(
             new KafkaComponent.Topic(KAFKA_TOPIC, 1)));
 
-
     final MRComponent mr = new MRComponent().withBasePath(baseDir.getAbsolutePath());
 
     FluxTopologyComponent fluxComponent = new FluxTopologyComponent.Builder()
-            .withTopologyLocation(new File(topologiesDir + "/pcap/remote.yaml"))
-            .withTopologyName("pcap")
-            .withTopologyProperties(topologyProperties)
-            .build();
+        .withTopologyLocation(new File(topologiesDir + "/pcap/remote.yaml"))
+        .withTopologyName("pcap")
+        .withTopologyProperties(topologyProperties)
+        .build();
     //UnitTestHelper.verboseLogging();
-    ComponentRunner runner = new ComponentRunner.Builder()
-            .withComponent("mr", mr)
-            .withComponent("zk",zkServerComponent)
-            .withComponent("kafka", kafkaComponent)
-            .withComponent("storm", fluxComponent)
-            .withMaxTimeMS(-1)
-            .withMillisecondsBetweenAttempts(2000)
-            .withNumRetries(10)
-            .withCustomShutdownOrder(new String[]{"storm","kafka","zk","mr"})
-            .build();
-    try {
-      runner.start();
-
-      fluxComponent.submitTopology();
-      sendPcapEntriesCallback.send(kafkaComponent, pcapEntries);
-      runner.process(new Processor<Void>() {
-        @Override
-        public ReadinessState process(ComponentRunner runner) {
-          int numFiles = numFiles(inputDir, mr.getConfiguration());
-          int expectedNumFiles = pcapEntries.size() / 2;
-          if (numFiles == expectedNumFiles) {
-            return ReadinessState.READY;
-          } else {
-            return ReadinessState.NOT_READY;
-          }
-        }
-
-        @Override
-        public ProcessorResult<Void> getResult() {
-          return null;
+    runner = new ComponentRunner.Builder()
+        .withComponent("mr", mr)
+        .withComponent("zk", zkServerComponent)
+        .withComponent("kafka", kafkaComponent)
+        .withComponent("storm", fluxComponent)
+        .withMaxTimeMS(-1)
+        .withMillisecondsBetweenAttempts(2000)
+        .withNumRetries(10)
+        .withCustomShutdownOrder(new String[]{"storm", "kafka", "zk", "mr"})
+        .build();
+    runner.start();
+
+    fluxComponent.submitTopology();
+    sendPcapEntriesCallback.send(kafkaComponent, pcapEntries);
+    runner.process(new Processor<Void>() {
+      @Override
+      public ReadinessState process(ComponentRunner runner) {
+        int numFiles = numFiles(inputDir, mr.getConfiguration());
+        int expectedNumFiles = pcapEntries.size() / 2;
+        if (numFiles == expectedNumFiles) {
+          return ReadinessState.READY;
+        } else {
+          return ReadinessState.NOT_READY;
         }
-      });
-
-      FixedPcapConfig configuration = new FixedPcapConfig(PcapCli.PREFIX_STRATEGY);
-      Configuration hadoopConf = new Configuration();
-      PcapOptions.JOB_NAME.put(configuration, "jobName");
-      PcapOptions.HADOOP_CONF.put(configuration, hadoopConf);
-      PcapOptions.FILESYSTEM.put(configuration, FileSystem.get(hadoopConf));
-      PcapOptions.BASE_PATH.put(configuration, new Path(inputDir.getAbsolutePath()));
-      PcapOptions.BASE_INTERIM_RESULT_PATH.put(configuration, new Path(interimResultDir.getAbsolutePath()));
-      PcapOptions.START_TIME_NS.put(configuration, getTimestamp(4, pcapEntries));
-      PcapOptions.END_TIME_NS.put(configuration, getTimestamp(5, pcapEntries));
-      PcapOptions.NUM_REDUCERS.put(configuration, 10);
-      PcapOptions.NUM_RECORDS_PER_FILE.put(configuration, 2);
-      PcapOptions.FINAL_OUTPUT_PATH.put(configuration, new Path(outputDir.getAbsolutePath()));
-      {
-        //Ensure that only two pcaps are returned when we look at 4 and 5
-        PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, new HashMap());
-        PcapJob<Map<String, String>> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(results.get().getSize(), 1);
-      }
-      {
-        // Ensure that only two pcaps are returned when we look at 4 and 5
-        // test with empty query filter
-        PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, "");
-        PcapJob<String> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(results.get().getSize(), 1);
-      }
-      {
-        //ensure that none get returned since date range has no results
-        PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, new HashMap<>());
-        PcapOptions.START_TIME_NS.put(configuration, 0);
-        PcapOptions.END_TIME_NS.put(configuration, 1);
-        PcapJob<Map<String, String>> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Assert.assertEquals(100.0, results.getStatus().getPercentComplete(), 0.0);
-        Assert.assertEquals("No results in specified date range.",
-            results.getStatus().getDescription());
-        Assert.assertEquals(results.get().getSize(), 0);
-      }
-      {
-        //ensure that none get returned since that destination IP address isn't in the dataset
-        PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, new HashMap<String, String>() {{
-          put(Constants.Fields.DST_ADDR.getName(), "207.28.210.1");
-        }});
-        PcapJob<Map<String, String>> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(results.get().getSize(), 0);
-      }
-      {
-        // ensure that none get returned since that destination IP address isn't in the dataset
-        // test with query filter
-        PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, "ip_dst_addr == '207.28.210.1'");
-        PcapJob<String> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(results.get().getSize(), 0);
       }
-      {
-        //same with protocol as before with the destination addr
-        PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, new HashMap<String, String>() {{
-          put(Constants.Fields.PROTOCOL.getName(), "foo");
-        }});
-        PcapJob<Map<String, String>> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(results.get().getSize(), 0);
+
+      @Override
+      public ProcessorResult<Void> getResult() {
+        return null;
       }
-      {
-        //same with protocol as before with the destination addr
-        //test with query filter
-        PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, "protocol == 'foo'");
-        PcapJob<String> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(results.get().getSize(), 0);
+    });
+  }
+
+  /**
+   * This is executed before each individual test.
+   */
+  @Before
+  public void setup() throws IOException {
+    configuration = new FixedPcapConfig(PcapCli.PREFIX_STRATEGY);
+    Configuration hadoopConf = new Configuration();
+    PcapOptions.JOB_NAME.put(configuration, "jobName");
+    PcapOptions.HADOOP_CONF.put(configuration, hadoopConf);
+    PcapOptions.FILESYSTEM.put(configuration, FileSystem.get(hadoopConf));
+    PcapOptions.BASE_PATH.put(configuration, new Path(inputDir.getAbsolutePath()));
+    PcapOptions.BASE_INTERIM_RESULT_PATH.put(configuration, new Path(interimResultDir.getAbsolutePath()));
+    PcapOptions.NUM_REDUCERS.put(configuration, 10);
+    PcapOptions.NUM_RECORDS_PER_FILE.put(configuration, 1);
+    PcapOptions.FINAL_OUTPUT_PATH.put(configuration, new Path(outputDir.getAbsolutePath()));
+  }
+
+  @Test
+  public void filters_pcaps_by_start_end_ns_with_fixed_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(4, pcapEntries));
+    PcapOptions.END_TIME_NS.put(configuration, getTimestamp(5, pcapEntries));
+    PcapOptions.FIELDS.put(configuration, new HashMap());
+    PcapJob<Map<String, String>> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Pageable<Path> resultPages = results.get();
+    Iterable<byte[]> bytes = Iterables.transform(resultPages, path -> {
+      try {
+        return HDFSUtils.readBytes(path);
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
       }
-      {
-        //make sure I get them all.
-        PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, new HashMap<>());
-        PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
-        PcapOptions.END_TIME_NS.put(configuration, getTimestamp(pcapEntries.size()-1, pcapEntries) + 1);
-        PcapJob<Map<String, String>> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(10, results.get().getSize());
+    });
+    assertInOrder(bytes);
+    Assert.assertEquals("Expected 2 records returned.", 2, resultPages.getSize());
+    Assert.assertEquals("Expected 1 record in first file.", 1,
+        PcapHelper.toPacketInfo(Iterables.get(bytes, 0)).size());
+    Assert.assertEquals("Expected 1 record in second file.", 1,
+        PcapHelper.toPacketInfo(Iterables.get(bytes, 1)).size());
+  }
+
+  @Test
+  public void filters_pcaps_by_start_end_ns_with_empty_query_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(4, pcapEntries));
+    PcapOptions.END_TIME_NS.put(configuration, getTimestamp(5, pcapEntries));
+    PcapOptions.FIELDS.put(configuration, "");
+    PcapJob<String> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Pageable<Path> resultPages = results.get();
+    Iterable<byte[]> bytes = Iterables.transform(resultPages, path -> {
+      try {
+        return HDFSUtils.readBytes(path);
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
       }
-      {
-        //make sure I get them all.
-        //with query filter
-        PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, "");
-        PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
-        PcapOptions.END_TIME_NS.put(configuration, getTimestamp(pcapEntries.size()-1, pcapEntries) + 1);
-        PcapJob<String> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(10, results.get().getSize());
+    });
+    assertInOrder(bytes);
+    Assert.assertEquals("Expected 2 records returned.", 2, resultPages.getSize());
+    Assert.assertEquals("Expected 1 record in first file.", 1,
+        PcapHelper.toPacketInfo(Iterables.get(bytes, 0)).size());
+    Assert.assertEquals("Expected 1 record in second file.", 1,
+        PcapHelper.toPacketInfo(Iterables.get(bytes, 1)).size());
+  }
+
+  @Test
+  public void date_range_filters_out_all_results() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
+    PcapOptions.FIELDS.put(configuration, new HashMap<>());
+    PcapOptions.START_TIME_NS.put(configuration, 0);
+    PcapOptions.END_TIME_NS.put(configuration, 1);
+    PcapJob<Map<String, String>> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Assert.assertEquals(100.0, results.getStatus().getPercentComplete(), 0.0);
+    Assert.assertEquals("No results in specified date range.",
+        results.getStatus().getDescription());
+    Assert.assertEquals(results.get().getSize(), 0);
+  }
+
+  @Test
+  public void ip_address_filters_out_all_results_with_fixed_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS.put(configuration, getTimestamp(1, pcapEntries));
+    PcapOptions.FIELDS.put(configuration, new HashMap<String, String>() {{
+      put(Constants.Fields.DST_ADDR.getName(), "207.28.210.1");
+    }});
+    PcapJob<Map<String, String>> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Assert.assertEquals(results.get().getSize(), 0);
+  }
+
+  @Test
+  public void ip_address_filters_out_all_results_with_query_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS.put(configuration, getTimestamp(1, pcapEntries));
+    PcapOptions.FIELDS.put(configuration, "ip_dst_addr == '207.28.210.1'");
+    PcapJob<String> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Assert.assertEquals(results.get().getSize(), 0);
+  }
+
+  @Test
+  public void protocol_filters_out_all_results_with_fixed_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS.put(configuration, getTimestamp(1, pcapEntries));
+    PcapOptions.FIELDS.put(configuration, new HashMap<String, String>() {{
+      put(Constants.Fields.PROTOCOL.getName(), "foo");
+    }});
+    PcapJob<Map<String, String>> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Assert.assertEquals(results.get().getSize(), 0);
+  }
+
+  @Test
+  public void protocol_filters_out_all_results_with_query_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS.put(configuration, getTimestamp(1, pcapEntries));
+    PcapOptions.FIELDS.put(configuration, "protocol == 'foo'");
+    PcapJob<String> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Assert.assertEquals(results.get().getSize(), 0);
+  }
+
+  @Test
+  public void fixed_filter_returns_all_results_for_full_date_range() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS
+        .put(configuration, getTimestamp(pcapEntries.size() - 1, pcapEntries) + 1);
+    PcapOptions.FIELDS.put(configuration, new HashMap<>());
+    PcapJob<Map<String, String>> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Pageable<Path> resultPages = results.get();
+    Iterable<byte[]> bytes = Iterables.transform(resultPages, path -> {
+      try {
+        return HDFSUtils.readBytes(path);
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
       }
-      {
-        PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, new HashMap<String, String>() {{
-          put(Constants.Fields.DST_PORT.getName(), "22");
-        }});
-        PcapOptions.NUM_RECORDS_PER_FILE.put(configuration, 1);
-        PcapJob<Map<String, String>> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertTrue(results.get().getSize() > 0);
-        Assert.assertEquals(Iterables.size(bytes)
-                , Iterables.size(filterPcaps(pcapEntries, new Predicate<JSONObject>() {
-                          @Override
-                          public boolean apply(@Nullable JSONObject input) {
-                            Object prt = input.get(Constants.Fields.DST_PORT.getName());
-                            return prt != null && prt.toString().equals("22");
-                          }
-                        }, withHeaders)
-                )
-        );
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        PcapMerger.merge(baos, HDFSUtils.readBytes(results.get().getPage(0)));
-        Assert.assertTrue(baos.toByteArray().length > 0);
+    });
+    assertInOrder(bytes);
+    Assert.assertEquals(pcapEntries.size(), resultPages.getSize());
+  }
+
+  @Test
+  public void query_filter_returns_all_results_for_full_date_range() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS
+        .put(configuration, getTimestamp(pcapEntries.size() - 1, pcapEntries) + 1);
+    PcapOptions.FIELDS.put(configuration, "");
+    PcapJob<String> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Pageable<Path> resultPages = results.get();
+    Iterable<byte[]> bytes = Iterables.transform(resultPages, path -> {
+      try {
+        return HDFSUtils.readBytes(path);
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
       }
-      {
-        //same with protocol as before with the destination addr
-        //test with query filter
-        PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, "ip_dst_port == 22");
-        PcapJob<String> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(Iterables.size(bytes)
-                , Iterables.size(filterPcaps(pcapEntries, new Predicate<JSONObject>() {
-                          @Override
-                          public boolean apply(@Nullable JSONObject input) {
-                            Object prt = input.get(Constants.Fields.DST_PORT.getName());
-                            return prt != null && prt.toString().equals("22");
-                          }
-                        }, withHeaders)
-                )
-        );
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        PcapMerger.merge(baos, HDFSUtils.readBytes(results.get().getPage(0)));
-        Assert.assertTrue(baos.toByteArray().length > 0);
+    });
+    assertInOrder(bytes);
+    Assert.assertEquals(pcapEntries.size(), resultPages.getSize());
+  }
+
+  @Test
+  public void filters_results_by_dst_port_with_fixed_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS
+        .put(configuration, getTimestamp(pcapEntries.size() - 1, pcapEntries) + 1);
+    PcapOptions.FIELDS.put(configuration, new HashMap<String, String>() {{
+      put(Constants.Fields.DST_PORT.getName(), "22");
+    }});
+    PcapOptions.NUM_RECORDS_PER_FILE.put(configuration, 1);
+    PcapJob<Map<String, String>> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Pageable<Path> resultPages = results.get();
+    Iterable<byte[]> bytes = Iterables.transform(resultPages, path -> {
+      try {
+        return HDFSUtils.readBytes(path);
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
       }
-      {
-        // test with query filter ip_dst_port > 20 and ip_dst_port < 55792
-        PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, "ip_dst_port > 20 and ip_dst_port < 55792");
-        PcapJob<String> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
+    });
+    assertInOrder(bytes);
+    Assert.assertTrue(resultPages.getSize() > 0);
+    Assert.assertEquals(Iterables.size(filterPcaps(pcapEntries, new Predicate<JSONObject>() {
+          @Override
+          public boolean apply(@Nullable JSONObject input) {
+            Object prt = input.get(Constants.Fields.DST_PORT.getName());
+            return prt != null && prt.toString().equals("22");
           }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(Iterables.size(bytes)
-                , Iterables.size(filterPcaps(pcapEntries, new Predicate<JSONObject>() {
-                          @Override
-                          public boolean apply(@Nullable JSONObject input) {
-                            Object prt = input.get(Constants.Fields.DST_PORT.getName());
-                            return prt != null && ((Long) prt > 20 && (Long) prt < 55792);
-                          }
-                        }, withHeaders)
-                )
-        );
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        PcapMerger.merge(baos, HDFSUtils.readBytes(results.get().getPage(0)));
-        Assert.assertTrue(baos.toByteArray().length > 0);
+        }, withHeaders)
+        ), resultPages.getSize()
+    );
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    PcapMerger.merge(baos, HDFSUtils.readBytes(resultPages.getPage(0)));
+    Assert.assertTrue(baos.toByteArray().length > 0);
+  }
+
+  @Test
+  public void filters_results_by_dst_port_with_query_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS
+        .put(configuration, getTimestamp(pcapEntries.size() - 1, pcapEntries) + 1);
+    PcapOptions.FIELDS.put(configuration, "ip_dst_port == 22");
+    PcapJob<String> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Pageable<Path> resultPages = results.get();
+    Iterable<byte[]> bytes = Iterables.transform(resultPages, path -> {
+      try {
+        return HDFSUtils.readBytes(path);
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
       }
-      {
-        //test with query filter ip_dst_port > 55790
-        PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, "ip_dst_port > 55790");
-        PcapJob<String> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(Iterables.size(bytes)
-                , Iterables.size(filterPcaps(pcapEntries, new Predicate<JSONObject>() {
-                          @Override
-                          public boolean apply(@Nullable JSONObject input) {
-                            Object prt = input.get(Constants.Fields.DST_PORT.getName());
-                            return prt != null && (Long) prt > 55790;
-                          }
-                        }, withHeaders)
-                )
-        );
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        PcapMerger.merge(baos, HDFSUtils.readBytes(results.get().getPage(0)));
-        Assert.assertTrue(baos.toByteArray().length > 0);
+    });
+    assertInOrder(bytes);
+    Assert.assertEquals(Iterables.size(filterPcaps(pcapEntries, new Predicate<JSONObject>() {
+              @Override
+              public boolean apply(@Nullable JSONObject input) {
+                Object prt = input.get(Constants.Fields.DST_PORT.getName());
+                return prt != null && prt.toString().equals("22");
+              }
+            }, withHeaders)
+        ), resultPages.getSize()
+    );
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    PcapMerger.merge(baos, HDFSUtils.readBytes(resultPages.getPage(0)));
+    Assert.assertTrue(baos.toByteArray().length > 0);
+  }
+
+  @Test
+  public void filters_results_by_dst_port_range_with_query_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS
+        .put(configuration, getTimestamp(pcapEntries.size() - 1, pcapEntries) + 1);
+    PcapOptions.FIELDS.put(configuration, "ip_dst_port > 20 and ip_dst_port < 55792");
+    PcapJob<String> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Pageable<Path> resultPages = results.get();
+    Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
+      try {
+        return HDFSUtils.readBytes(path);
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
       }
-      {
-        //test with query filter and byte array matching
-        PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
-        PcapOptions.FIELDS.put(configuration, "BYTEARRAY_MATCHER('2f56abd814bc56420489ca38e7faf8cec3d4', packet)");
-        PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
-        PcapOptions.END_TIME_NS.put(configuration, getTimestamp(pcapEntries.size()-1, pcapEntries) + 1);
-        PcapJob<String> job = new PcapJob<>();
-        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
-        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        waitForJob(results);
-
-        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
-        Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
-          try {
-            return HDFSUtils.readBytes(path);
-          } catch (IOException e) {
-            throw new IllegalStateException(e);
-          }
-        });
-        assertInOrder(bytes);
-        Assert.assertEquals(1, results.get().getSize());
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        PcapMerger.merge(baos, HDFSUtils.readBytes(results.get().getPage(0)));
-        Assert.assertTrue(baos.toByteArray().length > 0);
+    });
+    assertInOrder(bytes);
+    Assert.assertEquals(Iterables.size(filterPcaps(pcapEntries, new Predicate<JSONObject>() {
+              @Override
+              public boolean apply(@Nullable JSONObject input) {
+                Object prt = input.get(Constants.Fields.DST_PORT.getName());
+                return prt != null && ((Long) prt > 20 && (Long) prt < 55792);
+              }
+            }, withHeaders)
+        ), resultPages.getSize()
+    );
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    PcapMerger.merge(baos, HDFSUtils.readBytes(resultPages.getPage(0)));
+    Assert.assertTrue(baos.toByteArray().length > 0);
+  }
+
+  @Test
+  public void filters_results_by_dst_port_greater_than_value_with_query_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS
+        .put(configuration, getTimestamp(pcapEntries.size() - 1, pcapEntries) + 1);
+    PcapOptions.FIELDS.put(configuration, "ip_dst_port > 55790");
+    PcapJob<String> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Pageable<Path> resultPages = results.get();
+    Iterable<byte[]> bytes = Iterables.transform(resultPages, path -> {
+      try {
+        return HDFSUtils.readBytes(path);
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
       }
+    });
+    assertInOrder(bytes);
+    Assert.assertEquals(Iterables.size(filterPcaps(pcapEntries, new Predicate<JSONObject>() {
+              @Override
+              public boolean apply(@Nullable JSONObject input) {
+                Object prt = input.get(Constants.Fields.DST_PORT.getName());
+                return prt != null && (Long) prt > 55790;
+              }
+            }, withHeaders)
+        ), resultPages.getSize()
+    );
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    PcapMerger.merge(baos, HDFSUtils.readBytes(resultPages.getPage(0)));
+    Assert.assertTrue(baos.toByteArray().length > 0);
+  }
 
-      System.out.println("Ended");
-    } finally {
-      runner.stop();
-      clearOutDirs(inputDir, interimResultDir, outputDir);
-    }
+  @Test
+  public void filters_results_by_BYTEARRAY_MATCHER_with_query_filter() throws Exception {
+    PcapOptions.FILTER_IMPL.put(configuration, new QueryPcapFilter.Configurator());
+    PcapOptions.FIELDS.put(configuration, "BYTEARRAY_MATCHER('2f56abd814bc56420489ca38e7faf8cec3d4', packet)");
+    PcapOptions.START_TIME_NS.put(configuration, getTimestamp(0, pcapEntries));
+    PcapOptions.END_TIME_NS
+        .put(configuration, getTimestamp(pcapEntries.size() - 1, pcapEntries) + 1);
+    PcapJob<String> job = new PcapJob<>();
+    Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+    Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+    waitForJob(results);
+
+    Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+    Iterable<byte[]> bytes = Iterables.transform(results.get(), path -> {
+      try {
+        return HDFSUtils.readBytes(path);
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
+      }
+    });
+    assertInOrder(bytes);
+    Assert.assertEquals(1, results.get().getSize());
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    PcapMerger.merge(baos, HDFSUtils.readBytes(results.get().getPage(0)));
+    Assert.assertTrue(baos.toByteArray().length > 0);
   }
 
   private void waitForJob(Statusable statusable) throws Exception {
@@ -628,14 +681,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
     throw new Exception("Job did not complete within " + (MAX_RETRIES * SLEEP_MS) + " seconds");
   }
 
-  private File getDir(String targetDir, String childDir) {
-    File directory = new File(new File(targetDir), childDir);
-    if (!directory.exists()) {
-      directory.mkdirs();
-    }
-    return directory;
-  }
-
   private static Iterable<Map.Entry<byte[], byte[]>> readPcaps(Path pcapFile, boolean withHeaders) throws IOException {
     SequenceFile.Reader reader = new SequenceFile.Reader(new Configuration(),
         SequenceFile.Reader.file(pcapFile)
@@ -655,28 +700,27 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
       long calculatedTs = PcapHelper.getTimestamp(pcapWithHeader);
       {
         List<PacketInfo> info = PcapHelper.toPacketInfo(pcapWithHeader);
-        for(PacketInfo pi : info) {
+        for (PacketInfo pi : info) {
           Assert.assertEquals(calculatedTs, pi.getPacketTimeInNanos());
           //IF you are debugging and want to see the packets, uncomment the following.
           //System.out.println( Long.toUnsignedString(calculatedTs) + " => " + pi.getJsonDoc());
         }
       }
-      if(withHeaders) {
+      if (withHeaders) {
         ret.add(new AbstractMap.SimpleImmutableEntry<>(Bytes.toBytes(calculatedTs), pcapWithHeader));
-      }
-      else {
+      } else {
         byte[] pcapRaw = new byte[pcapWithHeader.length - PcapHelper.GLOBAL_HEADER_SIZE - PcapHelper.PACKET_HEADER_SIZE];
         System.arraycopy(pcapWithHeader, PcapHelper.GLOBAL_HEADER_SIZE + PcapHelper.PACKET_HEADER_SIZE, pcapRaw, 0, pcapRaw.length);
         ret.add(new AbstractMap.SimpleImmutableEntry<>(Bytes.toBytes(calculatedTs), pcapRaw));
       }
     }
-    return Iterables.limit(ret, 2*(ret.size()/2));
+    return Iterables.limit(ret, 2 * (ret.size() / 2));
   }
 
   public static void assertInOrder(Iterable<byte[]> packets) {
     long previous = 0;
-    for(byte[] packet : packets) {
-      for(JSONObject json : TO_JSONS.apply(packet)) {
+    for (byte[] packet : packets) {
+      for (JSONObject json : TO_JSONS.apply(packet)) {
         Long current = Long.parseLong(json.get("ts_micro").toString());
         Assert.assertNotNull(current);
         Assert.assertTrue(Long.compareUnsigned(current, previous) >= 0);

http://git-wip-us.apache.org/repos/asf/metron/blob/7967f358/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java
index c912e58..e4e9b95 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java
@@ -37,7 +37,7 @@ public class PcapCliFinalizer extends PcapFinalizer {
    * as a formatted timestamp + uuid. A final sample format will look as follows:
    * /base/output/path/pcap-data-201807181911-09855b4ae3204dee8b63760d65198da3+0001.pcap
    */
-  private static final String PCAP_CLI_FILENAME_FORMAT = "pcap-data-%s+%04d.pcap";
+  private static final String PCAP_CLI_FILENAME_FORMAT = "%s/pcap-data-%s+%04d.pcap";
 
   @Override
   protected void write(PcapResultsWriter resultsWriter, Configuration hadoopConfig,
@@ -47,8 +47,9 @@ public class PcapCliFinalizer extends PcapFinalizer {
 
   @Override
   protected Path getOutputPath(Map<String, Object> config, int partition) {
+    Path finalOutputPath = PcapOptions.FINAL_OUTPUT_PATH.get(config, PcapOptions.STRING_TO_PATH, Path.class);
     String prefix = PcapOptions.FINAL_FILENAME_PREFIX.get(config, String.class);
-    return new Path(String.format(PCAP_CLI_FILENAME_FORMAT, prefix, partition));
+    return new Path(String.format(PCAP_CLI_FILENAME_FORMAT, finalOutputPath, prefix, partition));
   }
 
 }


[03/50] [abbrv] metron git commit: METRON-1690: Add more context to PcapJob JobStatus (mmiklavc via mmiklavc) closes apache/metron#1128

Posted by ni...@apache.org.
METRON-1690: Add more context to PcapJob JobStatus (mmiklavc via mmiklavc) closes apache/metron#1128


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/9d4842f3
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/9d4842f3
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/9d4842f3

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 9d4842f3d6f355d449d51f3c6ac02f2c904c295d
Parents: d0e8757
Author: mmiklavc <mi...@gmail.com>
Authored: Wed Jul 25 08:21:32 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Wed Jul 25 08:21:32 2018 -0600

----------------------------------------------------------------------
 .../metron-job_state_statechart_diagram.svg     |  18 +-
 .../metron-job_state_statechart_diagram.xml     |  18 +-
 .../java/org/apache/metron/job/JobStatus.java   |  42 ++++-
 .../org/apache/metron/job/JobStatusTest.java    |  55 ++++++
 .../job/manager/InMemoryJobManagerTest.java     |  16 ++
 .../org/apache/metron/pcap/PcapJobTest.java     |  80 +++++---
 .../PcapTopologyIntegrationTest.java            |  30 +--
 .../java/org/apache/metron/pcap/mr/PcapJob.java | 186 +++++++++----------
 site-book/bin/generate-md.sh                    |   2 +
 9 files changed, 294 insertions(+), 153 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/9d4842f3/metron-platform/metron-job/metron-job_state_statechart_diagram.svg
----------------------------------------------------------------------
diff --git a/metron-platform/metron-job/metron-job_state_statechart_diagram.svg b/metron-platform/metron-job/metron-job_state_statechart_diagram.svg
index a99c5ad..c801eae 100644
--- a/metron-platform/metron-job/metron-job_state_statechart_diagram.svg
+++ b/metron-platform/metron-job/metron-job_state_statechart_diagram.svg
@@ -1,14 +1,14 @@
 <!--
   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
+       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.
   -->
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="661px" height="291px" version="1.1" style="background-color: rgb(255, 255, 255);"><defs/><g transform="translate(0.5,0.5)"><ellipse cx="15" cy="30" rx="11" ry="11" fill="#000000" stroke="#ff0000" transform="rotate(90,15,30)" pointer-events="none"/><rect x="110" y="0" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(123.5,23.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="92" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 92px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-dec
 oration:inherit;">NOT_RUNNING</div></div></foreignObject><text x="46" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">NOT_RUNNING</text></switch></g><path d="M 440 30 L 537.76 30" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 529.88 34.5 L 538.88 30 L 529.88 25.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><rect x="320" y="0" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(350.5,23.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="58" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 60px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://ww
 w.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">RUNNING</div></div></foreignObject><text x="29" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">RUNNING</text></switch></g><rect x="540" y="0" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(563.5,23.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="72" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 74px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">FINALIZING</div></div></foreignObject><text x="36" y="12" fill="#0000
 00" text-anchor="middle" font-size="12px" font-family="Verdana">FINALIZING</text></switch></g><path d="M 30 30 L 107.76 30" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 99.88 34.5 L 108.88 30 L 99.88 25.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 230 30 L 317.76 30" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 309.88 34.5 L 318.88 30 L 309.88 25.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><rect x="320" y="120" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(357.5,143.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="44" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0,
  0, 0); line-height: 1.2; vertical-align: top; width: 44px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">KILLED</div></div></foreignObject><text x="22" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">KILLED</text></switch></g><rect x="320" y="230" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(357.5,253.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="44" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 44px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml"
  style="display:inline-block;text-align:inherit;text-decoration:inherit;">FAILED</div></div></foreignObject><text x="22" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">FAILED</text></switch></g><rect x="540" y="120" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(562.5,143.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="75" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 76px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">SUCCEEDED</div></div></foreignObject><text x="38" y="12" fill="#000000" text-anchor="mid
 dle" font-size="12px" font-family="Verdana">SUCCEEDED</text></switch></g><path d="M 380 60 L 380 117.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 375.5 109.88 L 380 118.88 L 384.5 109.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 600 60 L 600 117.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 595.5 109.88 L 600 118.88 L 604.5 109.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 540 45 L 500 45 Q 490 45 490 55 L 490 250 Q 490 260 480 260 L 442.24 260" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 450.12 255.5 L 441.12 260 L 450.12 264.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 320 45 L 290 45 Q 280 45 280 55 L 280 250 Q 280 260 290 260 L 317.76 260" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path 
 d="M 309.88 264.5 L 318.88 260 L 309.88 255.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 540 45 L 500 45 Q 490 45 490 55 L 490 140 Q 490 150 480 150 L 442.24 150" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 450.12 145.5 L 441.12 150 L 450.12 154.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/></g></svg>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="656px" height="291px" version="1.1"><defs/><g transform="translate(0.5,0.5)"><ellipse cx="15" cy="150" rx="11" ry="11" fill="#000000" stroke="#ff0000" transform="rotate(90,15,150)" pointer-events="none"/><rect x="102" y="120" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(115.5,143.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="92" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 92px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">NOT_RUNNING</div></div>
 </foreignObject><text x="46" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">NOT_RUNNING</text></switch></g><path d="M 435 30 L 532.76 30" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 524.88 34.5 L 533.88 30 L 524.88 25.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><rect x="314.5" y="0" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(345.5,23.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="58" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 60px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inl
 ine-block;text-align:inherit;text-decoration:inherit;">RUNNING</div></div></foreignObject><text x="29" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">RUNNING</text></switch></g><rect x="534.5" y="0" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(558.5,23.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="72" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 74px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">FINALIZING</div></div></foreignObject><text x="36" y="12" fill="#000000" text-anchor="middle" font-size="1
 2px" font-family="Verdana">FINALIZING</text></switch></g><path d="M 30 150 L 99.76 150" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 91.88 154.5 L 100.88 150 L 91.88 145.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><rect x="314.5" y="120" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(352.5,143.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="44" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 44px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">KILLED</div></div></foreig
 nObject><text x="22" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">KILLED</text></switch></g><rect x="314.5" y="230" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(352.5,253.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="44" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 44px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">FAILED</div></div></foreignObject><text x="22" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">FAILED</text></switch></g><rect x="534.5" y="120" width="
 120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(556.5,143.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="75" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 76px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">SUCCEEDED</div></div></foreignObject><text x="38" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">SUCCEEDED</text></switch></g><path d="M 375 60 L 375 117.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 370.5 109.88 L 375 118.88 L 379.5 109.88" fill="none" stroke="#ff00
 00" stroke-miterlimit="10" pointer-events="none"/><path d="M 595 60 L 595 117.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 590.5 109.88 L 595 118.88 L 599.5 109.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 535 45 L 495 45 Q 485 45 485 55 L 485 250 Q 485 260 475 260 L 437.24 260" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 445.12 255.5 L 436.12 260 L 445.12 264.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 315 45 L 285 45 Q 275 45 275 55 L 275 250 Q 275 260 285 260 L 312.76 260" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 304.88 264.5 L 313.88 260 L 304.88 255.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 535 45 L 495 45 Q 485 45 485 55 L 485 140 Q 485 150 475 150 L 437.24 150" fill="none" stroke="#ff0000" stroke-miterlimit="10"
  pointer-events="none"/><path d="M 445.12 145.5 L 436.12 150 L 445.12 154.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><rect x="102" y="0" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/><g transform="translate(125.5,23.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="72" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 73px; white-space: nowrap; word-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">SUBMITTED</div></div></foreignObject><text x="36" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Verdana">SUBMITTED</text></switch></g><path d
 ="M 162 120 L 162 62.24" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 166.5 70.12 L 162 61.12 L 157.5 70.12" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 222 30 L 312.76 30" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 304.88 34.5 L 313.88 30 L 304.88 25.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/></g></svg>

http://git-wip-us.apache.org/repos/asf/metron/blob/9d4842f3/metron-platform/metron-job/metron-job_state_statechart_diagram.xml
----------------------------------------------------------------------
diff --git a/metron-platform/metron-job/metron-job_state_statechart_diagram.xml b/metron-platform/metron-job/metron-job_state_statechart_diagram.xml
index b9ee8aa..9c33323 100644
--- a/metron-platform/metron-job/metron-job_state_statechart_diagram.xml
+++ b/metron-platform/metron-job/metron-job_state_statechart_diagram.xml
@@ -1,14 +1,14 @@
 <!--
   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
+       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.
   -->
 <!-- This is a draw.io diagram.  You can load it from http://www.draw.io -->
-<mxfile userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36" version="8.9.5" editor="www.draw.io" type="google"><diagram id="58cdce13-f638-feb5-8d6f-7d28b1aa9fa0" name="Page-1">7VrbctowEP0aHtvxTdg8BkJSpintlKZN+tJRbGFrIixGiAL9+kq2fAfiOhjIFF6wdqWVtOfsauVxxxzM1rcMzoNP1EOkY2jeumNedwzD0GxN/EnJJpb0eiAW+Ax7sUjPBBP8BymhGucvsYcWhY6cUsLxvCh0aRgilxdkkDG6KnabUlKcdQ59VBFMXEiq0h/Y40EsdYCWyT8g7AfJzLqmNE/QffYZXYZqvo5hTqNfrJ7BxJbqvwigR1c5kTnsmANGKY+fZusBItK3idvicTc7tOm6GQp5rQGOY1sAOK6NtCdNs98pC78hWaJkC9FC+SZxjrAgcBCNfsBnRMh08Sg2Mpf6BYeMTzjkUj/FhAwooSwaaGrRT3bmjD6jnGY6VZrIdcjLjMbekSqXzrCrngl8QqSf+jqxFNIwmpaG/AbOMJHs+46YB0OoxIpquqHa21YHCfZDIXOFD5FQ9j3MBMkwlcIFXUoI+8pNiHG03ul7PUVURAqiM8TZRnRRAxzFARUjaXuVMc5UoiBHtkQGFcf91HCGs3hQUNeE3ajAPv787dfX+/F4NL6tMKCI0irAHE3m0JXalUgIRWZA5iqvG1aVEzI23P2cOBMWHABxvVeCvAsqkOvGFsy7bWBuViPbE1lQNSnjAfVpCMkwk+ZxRWvMH3LPjxKV90C2QrG0BwVS1Mh0+zFDoXcl07ecf47CWKLQcvaSpCbaCaoETXmKqdx2AVER5MxFO9xmqfMI
 Mh/xHX1AbWYwRCDHv4vzHxRmqxLal7A+bFhb2jmFNajgfTMaX92Nfl4gPxzkXeOcIO++LpO//Zyc3BH25WTjjHKyfTl6m8Bs1IDZOjzMaugXioXFNAWYpWLOLBfm8VbUqPwd7AVDhlYyFO+3YihiXbqfZkR0KofFx9Hd3fD6clC0VBuYzikPil61NrgaXeBuD26rd0q4k9dpObwn94PBcHh9gby1UvC0Ea7rFWAblRaqnFDFhV4oLbJK47EWaG+htKhzq3eOVVoAs0QpCzQrLbovGWqxtNC3vDn+Jya+Gb7tjpkmTAQ1mJgk9vapaNslBoGmVLR729PkMaj42ledCRX1zpnfqlIqFm6GNmiRi71jUbFbuXA1pWLZUO+IVLQOlRX/MyrWOaCPR8XyuWo0vPuXT3pQ5vQOKgok4SbXbS47LHYv2OyWFqxre9dV7g8MUIqEeAWHiwtwSdGtpeijFa4nj4uXqSia2ecVcffsGxZz+Bc=</diagram></mxfile>
+<mxfile userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" version="8.9.8" editor="www.draw.io" type="google"><diagram>7Zpdd5owGMc/jbc9hIDiZbW286x1O7Pd2t3sZBCB00g8MVbdp1+Q8GagZSjU7tibJk9eCPn/nicv2IHD+eaGoYV3Rx1MOrrmbDrwqqPrutbTxL/Qso0s/b4ZGVzmO5EJpIap/wdLo2znrnwHL3MVOaWE+4u80aZBgG2esyHG6DpfbUZJ/qkL5GLFMLURUa0/fId7kdUytdT+CfuuFz8ZaLJkjuLK0rD0kEPXGRMcdeCQUcqj1HwzxCScvHheonbXJaXJwBgOeJUGetTgBZGVfDc5Lr6NX1Y0EPMqMgOPz4mwAZEU416E5UuOGJ9yxMPymU/IkBLKdg2htvsLK3NGn3GmZDaTJYyuAgc7aafRZIRFNp37tkwT9BuTAbKf3V2DuKeABrvH0oBfo7lPQpq+Y+agAEmzRAfoMl80OkR8NxA2W0wZFoUDx2cCGp+GxiVdhYoN5DRhxvGmdKpBIqAgH9M55mwrqsgGfStqIZnXgWR+nRIEJRVeBp7YhiSzbtJxKqtISGWLVYaKypMv97++PUwm48mNInhelLXnczxdIDssXQt/zoOAmC0nWTdUBGbiz34dgRMR/QgC67GXb/fyGYWBXiBx9wgSG6rfOiJmySxl3KMuDRAZpdasjHjj88dM+ikU4cIMc4EYyaPUZJdJy16XCAfOZRhsw+cvcBBZpDjWq0xUFDcWkeAZTyQMXzsnoHBhZkuTdDkRtFwcT35lmRkmiPsv+d4P0cxU3PLsksd1SQPoF2bOKa0WfbKr6Hs9nlzejn+eJT6exF34nhL3Dgu7Hy6A
 6moAhe8XQC3FwT6Pb29HV2fnaix+trqp6asB9HJ81rdJfSFoUd/4WRmBpw/D4Wh0dda4wQWyVR8GQFGy1slEnkbk2QTkTibpQeWpkkonuLAWnEys4y+ssulX6oseE0JMY58QsK98NFTZLnuftNdV9+2uoldUutpxlLxTNbQKLqv+Ca0PA1C5E1RAq6uiBbS22OpZChC9umz1QVkga4AteCS2QOfEr00StnJXPz2zNlz91tjSFLbMmmypXSU+0gBbh17WfZgrucPYKlgTW2OrYCGDZj221OUV7mNawpbQCm0z1RZhhWX5oGFPGbScxLKxqS2gkftiJBLRKGrDbp4Dac1A2toG8CRgr8OWesM6fRjcje/vz+fHxj5rtXm9Cg68Xy3eN5/g/r7kJFJxhw/V2AEa+K5V7PFii5/jw6i5BQNGvh/Q4AbMOg5W//nXUlCwJpltYQXjE1iMw/5ZrypXxlsd1eZKZNNf6kTV0987wdFf</diagram></mxfile>

http://git-wip-us.apache.org/repos/asf/metron/blob/9d4842f3/metron-platform/metron-job/src/main/java/org/apache/metron/job/JobStatus.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-job/src/main/java/org/apache/metron/job/JobStatus.java b/metron-platform/metron-job/src/main/java/org/apache/metron/job/JobStatus.java
index 5a2f485..9af549c 100644
--- a/metron-platform/metron-job/src/main/java/org/apache/metron/job/JobStatus.java
+++ b/metron-platform/metron-job/src/main/java/org/apache/metron/job/JobStatus.java
@@ -23,8 +23,11 @@ package org.apache.metron.job;
  */
 public class JobStatus {
 
+  private Throwable failureReason;
+
   public enum State {
     NOT_RUNNING,
+    SUBMITTED,
     RUNNING,
     SUCCEEDED,
     FINALIZING,
@@ -33,11 +36,32 @@ public class JobStatus {
   }
 
   private String jobId;
-  private State state = State.NOT_RUNNING;
-  private double percentComplete = 0.0;
+  private State state;
+  private double percentComplete;
   private String description;
   private long completionTime;
 
+  public JobStatus() {
+    jobId = "";
+    state = State.NOT_RUNNING;
+    percentComplete = 0.0;
+    description = "Not started";
+    completionTime = 0L;
+  }
+
+  /**
+   * Copy constructor instead of clone. Effective for thread safety, per Goetz JCIP.
+   *
+   * @param jobStatus Existing JobStatus object to copy state from.
+   */
+  public JobStatus(JobStatus jobStatus) {
+    this.jobId = jobStatus.jobId;
+    this.state = jobStatus.state;
+    this.percentComplete = jobStatus.percentComplete;
+    this.description = jobStatus.description;
+    this.completionTime = jobStatus.completionTime;
+  }
+
   public JobStatus withJobId(String jobId) {
     this.jobId = jobId;
     return this;
@@ -63,6 +87,11 @@ public class JobStatus {
     return this;
   }
 
+  public JobStatus withFailureException(Throwable failureReason) {
+    this.failureReason = failureReason;
+    return this;
+  }
+
   public String getJobId() {
     return jobId;
   }
@@ -83,4 +112,13 @@ public class JobStatus {
     return completionTime;
   }
 
+  /**
+   * Null if no failure reason available.
+   *
+   * @return Throwable indicating failure.
+   */
+  public Throwable getFailureReason() {
+    return failureReason;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/9d4842f3/metron-platform/metron-job/src/test/java/org/apache/metron/job/JobStatusTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-job/src/test/java/org/apache/metron/job/JobStatusTest.java b/metron-platform/metron-job/src/test/java/org/apache/metron/job/JobStatusTest.java
new file mode 100644
index 0000000..67c5c52
--- /dev/null
+++ b/metron-platform/metron-job/src/test/java/org/apache/metron/job/JobStatusTest.java
@@ -0,0 +1,55 @@
+/**
+ * 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.
+ */
+
+package org.apache.metron.job;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import org.apache.metron.job.JobStatus.State;
+import org.junit.Test;
+
+public class JobStatusTest {
+
+  @Test
+  public void constructor_copies_from_existing_instance() {
+    JobStatus original = new JobStatus()
+        .withState(State.SUCCEEDED)
+        .withCompletionTime(5000)
+        .withJobId("abc123")
+        .withDescription("All done")
+        .withPercentComplete(100.0);
+    JobStatus copied = new JobStatus(original);
+    assertThat(copied.getState(), equalTo(State.SUCCEEDED));
+    assertThat(copied.getCompletionTime(), equalTo(5000L));
+    assertThat(copied.getJobId(), equalTo("abc123"));
+    assertThat(copied.getDescription(), equalTo("All done"));
+    assertThat(copied.getPercentComplete(), equalTo(100.0));
+  }
+
+  @Test
+  public void failure_info_provided() {
+    JobException e = new JobException("The job blew up.");
+    JobStatus original = new JobStatus()
+        .withState(State.FAILED)
+        .withDescription("Failed")
+        .withFailureException(e);
+    assertThat(original.getFailureReason(), equalTo(e));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/9d4842f3/metron-platform/metron-job/src/test/java/org/apache/metron/job/manager/InMemoryJobManagerTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-job/src/test/java/org/apache/metron/job/manager/InMemoryJobManagerTest.java b/metron-platform/metron-job/src/test/java/org/apache/metron/job/manager/InMemoryJobManagerTest.java
index f3a3978..b1fab4e 100644
--- a/metron-platform/metron-job/src/test/java/org/apache/metron/job/manager/InMemoryJobManagerTest.java
+++ b/metron-platform/metron-job/src/test/java/org/apache/metron/job/manager/InMemoryJobManagerTest.java
@@ -65,6 +65,7 @@ public class InMemoryJobManagerTest {
   private String jobId1;
   private String jobId2;
   private String jobId3;
+  private String emptyJobId;
   private String basePath;
 
   @Before
@@ -77,6 +78,7 @@ public class InMemoryJobManagerTest {
     jobId1 = "job_abc_123";
     jobId2 = "job_def_456";
     jobId3 = "job_ghi_789";
+    emptyJobId = "";
     basePath = tempDir.getRoot().getAbsolutePath();
     when(job1.getJobType()).thenReturn(JobType.MAP_REDUCE);
     when(job2.getJobType()).thenReturn(JobType.MAP_REDUCE);
@@ -135,6 +137,20 @@ public class InMemoryJobManagerTest {
   }
 
   @Test
+  public void empty_result_set_with_empty_jobId_shows_status() throws JobException {
+    when(job1.getStatus()).thenReturn(new JobStatus().withState(State.SUCCEEDED).withJobId(emptyJobId));
+
+    // user submits 1 job with empty results
+    jm.submit(newSupplier(job1), username1);
+    assertThat(jm.getJob(username1, emptyJobId), equalTo(job1));
+
+    // user submits another job with empty results
+    when(job2.getStatus()).thenReturn(new JobStatus().withState(State.SUCCEEDED).withJobId(emptyJobId));
+    jm.submit(newSupplier(job2), username1);
+    assertThat(jm.getJob(username1, emptyJobId), equalTo(job2));
+  }
+
+  @Test
   public void returns_job_status() throws JobException {
     JobStatus expected = new JobStatus().withState(State.SUCCEEDED).withJobId(jobId1);
     when(job1.getStatus()).thenReturn(expected);

http://git-wip-us.apache.org/repos/asf/metron/blob/9d4842f3/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
index b28c428..14963fd 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
@@ -28,6 +28,8 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Timer;
+import java.util.TimerTask;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
@@ -62,6 +64,7 @@ public class PcapJobTest {
   private JobID jobId;
   @Mock
   private Finalizer<Path> finalizer;
+  private TestTimer timer;
   private Pageable<Path> pageableResult;
   private FixedPcapConfig config;
   private Configuration hadoopConfig;
@@ -77,7 +80,6 @@ public class PcapJobTest {
   private Map<String, String> fixedFields;
   private PcapJob<Map<String, String>> testJob;
 
-
   @Before
   public void setup() throws IOException {
     MockitoAnnotations.initMocks(this);
@@ -94,7 +96,9 @@ public class PcapJobTest {
     finalOutputPath = new Path("finaloutpath");
     when(jobId.toString()).thenReturn(jobIdVal);
     when(mrStatus.getJobID()).thenReturn(jobId);
+    when(mrJob.getJobID()).thenReturn(jobId);
     pageableResult = new PcapPages();
+    timer = new TestTimer();
     // handles setting the file name prefix under the hood
     config = new FixedPcapConfig(clock -> "clockprefix");
     PcapOptions.HADOOP_CONF.put(config, hadoopConfig);
@@ -108,28 +112,20 @@ public class PcapJobTest {
     PcapOptions.FILTER_IMPL.put(config, new FixedPcapFilter.Configurator());
     PcapOptions.NUM_RECORDS_PER_FILE.put(config, numRecordsPerFile);
     PcapOptions.FINAL_OUTPUT_PATH.put(config, finalOutputPath);
-    testJob = new TestJob<>();
-    testJob.setStatusInterval(10);
-    testJob.setCompleteCheckInterval(10);
-  }
-
-  @Test
-  public void partition_gives_value_in_range() throws Exception {
-    long start = 1473897600000000000L;
-    long end = TimestampConverters.MILLISECONDS.toNanoseconds(1473995927455L);
-    Configuration conf = new Configuration();
-    conf.set(PcapJob.START_TS_CONF, toUnsignedString(start));
-    conf.set(PcapJob.END_TS_CONF, toUnsignedString(end));
-    conf.set(PcapJob.WIDTH_CONF, "" + PcapJob.findWidth(start, end, 10));
-    PcapJob.PcapPartitioner partitioner = new PcapJob.PcapPartitioner();
-    partitioner.setConf(conf);
-    Assert.assertThat("Partition not in range",
-        partitioner.getPartition(new LongWritable(1473978789181189000L), new BytesWritable(), 10),
-        equalTo(8));
+    testJob = new TestJob<>(mrJob);
+    testJob.setStatusInterval(1);
+    testJob.setCompleteCheckInterval(1);
+    testJob.setTimer(timer);
   }
 
   private class TestJob<T> extends PcapJob<T> {
 
+    private final Job mrJob;
+
+    public TestJob(Job mrJob) {
+      this.mrJob = mrJob;
+    }
+
     @Override
     public Job createJob(Optional<String> jobName,
         Path basePath,
@@ -145,6 +141,36 @@ public class PcapJobTest {
     }
   }
 
+  private class TestTimer extends Timer {
+
+    private TimerTask task;
+
+    @Override
+    public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
+      this.task = task;
+    }
+
+    public void updateJobStatus() {
+      task.run();
+    }
+
+  }
+
+  @Test
+  public void partition_gives_value_in_range() throws Exception {
+    long start = 1473897600000000000L;
+    long end = TimestampConverters.MILLISECONDS.toNanoseconds(1473995927455L);
+    Configuration conf = new Configuration();
+    conf.set(PcapJob.START_TS_CONF, toUnsignedString(start));
+    conf.set(PcapJob.END_TS_CONF, toUnsignedString(end));
+    conf.set(PcapJob.WIDTH_CONF, "" + PcapJob.findWidth(start, end, 10));
+    PcapJob.PcapPartitioner partitioner = new PcapJob.PcapPartitioner();
+    partitioner.setConf(conf);
+    Assert.assertThat("Partition not in range",
+        partitioner.getPartition(new LongWritable(1473978789181189000L), new BytesWritable(), 10),
+        equalTo(8));
+  }
+
   @Test
   public void job_succeeds_synchronously() throws Exception {
     pageableResult = new PcapPages(
@@ -154,6 +180,7 @@ public class PcapJobTest {
     when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
     when(mrJob.getStatus()).thenReturn(mrStatus);
     Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
     Pageable<Path> results = statusable.get();
     Assert.assertThat(results.getSize(), equalTo(3));
     JobStatus status = statusable.getStatus();
@@ -168,6 +195,7 @@ public class PcapJobTest {
     when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.FAILED);
     when(mrJob.getStatus()).thenReturn(mrStatus);
     Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
     Pageable<Path> results = statusable.get();
     JobStatus status = statusable.getStatus();
     Assert.assertThat(status.getState(), equalTo(State.FAILED));
@@ -181,6 +209,7 @@ public class PcapJobTest {
     when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.KILLED);
     when(mrJob.getStatus()).thenReturn(mrStatus);
     Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
     Pageable<Path> results = statusable.get();
     JobStatus status = statusable.getStatus();
     Assert.assertThat(status.getState(), equalTo(State.KILLED));
@@ -190,13 +219,11 @@ public class PcapJobTest {
 
   @Test
   public void job_succeeds_asynchronously() throws Exception {
-    // not complete a few times to make sure cancel works as expected
-    when(mrJob.isComplete()).thenReturn(false, false, false, true);
+    when(mrJob.isComplete()).thenReturn(true);
     when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
     when(mrJob.getStatus()).thenReturn(mrStatus);
     Statusable<Path> statusable = testJob.submit(finalizer, config);
-    while (!statusable.isDone()) {
-    }
+    timer.updateJobStatus();
     JobStatus status = statusable.getStatus();
     Assert.assertThat(status.getState(), equalTo(State.SUCCEEDED));
     Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
@@ -207,18 +234,20 @@ public class PcapJobTest {
     when(mrJob.isComplete()).thenReturn(false);
     when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.RUNNING);
     when(mrJob.getStatus()).thenReturn(mrStatus);
-    Statusable<Path> statusable = testJob.submit(finalizer, config);
     when(mrJob.mapProgress()).thenReturn(0.5f);
     when(mrJob.reduceProgress()).thenReturn(0f);
+    Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
     JobStatus status = statusable.getStatus();
     Assert.assertThat(status.getState(), equalTo(State.RUNNING));
     Assert.assertThat(status.getDescription(), equalTo("map: 50.0%, reduce: 0.0%"));
     Assert.assertThat(status.getPercentComplete(), equalTo(25.0));
     when(mrJob.mapProgress()).thenReturn(1.0f);
     when(mrJob.reduceProgress()).thenReturn(0.5f);
+    timer.updateJobStatus();
     status = statusable.getStatus();
-    Assert.assertThat(status.getPercentComplete(), equalTo(75.0));
     Assert.assertThat(status.getDescription(), equalTo("map: 100.0%, reduce: 50.0%"));
+    Assert.assertThat(status.getPercentComplete(), equalTo(75.0));
   }
 
   @Test
@@ -230,6 +259,7 @@ public class PcapJobTest {
     statusable.kill();
     when(mrJob.isComplete()).thenReturn(true);
     when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.KILLED);
+    timer.updateJobStatus();
     JobStatus status = statusable.getStatus();
     Assert.assertThat(status.getState(), equalTo(State.KILLED));
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/9d4842f3/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
index 0be33d6..108fd2b 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/integration/PcapTopologyIntegrationTest.java
@@ -270,7 +270,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<Map<String, String>> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -292,7 +291,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<String> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -307,6 +305,23 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         Assert.assertEquals(results.get().getSize(), 1);
       }
       {
+        //ensure that none get returned since date range has no results
+        PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
+        PcapOptions.FIELDS.put(configuration, new HashMap<>());
+        PcapOptions.START_TIME_NS.put(configuration, 0);
+        PcapOptions.END_TIME_NS.put(configuration, 1);
+        PcapJob<Map<String, String>> job = new PcapJob<>();
+        Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
+        Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
+        waitForJob(results);
+
+        Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
+        Assert.assertEquals(100.0, results.getStatus().getPercentComplete(), 0.0);
+        Assert.assertEquals("No results in specified date range.",
+            results.getStatus().getDescription());
+        Assert.assertEquals(results.get().getSize(), 0);
+      }
+      {
         //ensure that none get returned since that destination IP address isn't in the dataset
         PcapOptions.FILTER_IMPL.put(configuration, new FixedPcapFilter.Configurator());
         PcapOptions.FIELDS.put(configuration, new HashMap<String, String>() {{
@@ -315,7 +330,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<Map<String, String>> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -337,7 +351,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<String> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -360,7 +373,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<Map<String, String>> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -382,7 +394,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<String> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -405,7 +416,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<Map<String, String>> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -429,7 +439,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<String> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -452,7 +461,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<Map<String, String>> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -487,7 +495,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<String> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -520,7 +527,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<String> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -553,7 +559,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<String> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());
@@ -588,7 +593,6 @@ public class PcapTopologyIntegrationTest extends BaseIntegrationTest {
         PcapJob<String> job = new PcapJob<>();
         Statusable<Path> results = job.submit(PcapFinalizerStrategies.CLI, configuration);
         Assert.assertEquals(Statusable.JobType.MAP_REDUCE, results.getJobType());
-        Assert.assertEquals(JobStatus.State.RUNNING, results.getStatus().getState());
         waitForJob(results);
 
         Assert.assertEquals(JobStatus.State.SUCCEEDED, results.getStatus().getState());

http://git-wip-us.apache.org/repos/asf/metron/blob/9d4842f3/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
index 1dd670d..a26e5ff 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
@@ -79,8 +79,8 @@ public class PcapJob<T> implements Statusable<Path> {
   public static final String WIDTH_CONF = "width";
   private static final long THREE_SECONDS = 3000;
   private static final long ONE_SECOND = 1000;
-  private Job mrJob; // store a running MR job reference for async status check
-  private State jobState; // overall job state, including finalization step
+  private volatile Job mrJob; // store a running MR job reference for async status check
+  private volatile JobStatus jobStatus; // overall job status, including finalization step
   private Finalizer<Path> finalizer;
   private Map<String, Object> configuration;
   private Pageable<Path> finalResults;
@@ -181,8 +181,9 @@ public class PcapJob<T> implements Statusable<Path> {
   }
 
   public PcapJob() {
-    jobState = State.NOT_RUNNING;
+    jobStatus = new JobStatus();
     finalResults = new PcapPages();
+    timer = new Timer();
     statusInterval = THREE_SECONDS;
     completeCheckInterval = ONE_SECOND;
   }
@@ -286,73 +287,112 @@ public class PcapJob<T> implements Statusable<Path> {
         , fs
         , filterImpl
     );
+    if (mrJob == null) {
+      LOG.info("No files to process with specified date range.");
+      try {
+        setFinalResults(input -> new PcapPages(), configuration);
+        jobStatus.withState(State.SUCCEEDED).withDescription("No results in specified date range.")
+            .withPercentComplete(100.0);
+      } catch (JobException e) {
+        // This should not cause an error as we simply set results to an empty result set.
+        jobStatus.withState(State.FAILED).withDescription("Unable to finalize empty job.")
+            .withFailureException(e);
+      }
+      return this;
+    }
     mrJob.submit();
-    jobState = State.RUNNING;
+    jobStatus.withState(State.SUBMITTED).withDescription("Job submitted").withJobId(mrJob.getJobID().toString());
     startJobStatusTimerThread(statusInterval);
     return this;
   }
 
   private void startJobStatusTimerThread(long interval) {
-    timer = new Timer();
-    timer.scheduleAtFixedRate(new TimerTask() {
+    getTimer().scheduleAtFixedRate(new TimerTask() {
       @Override
       public void run() {
-        try {
-          synchronized (this) {
-            if (jobState == State.RUNNING) {
-              if (mrJob.isComplete()) {
-                switch (mrJob.getStatus().getState()) {
-                  case SUCCEEDED:
-                    jobState = State.FINALIZING;
-                    if (setFinalResults(finalizer, configuration)) {
-                      jobState = State.SUCCEEDED;
-                    } else {
-                      jobState = State.FAILED;
-                    }
-                    break;
-                  case FAILED:
-                    jobState = State.FAILED;
-                    break;
-                  case KILLED:
-                    jobState = State.KILLED;
-                    break;
-                }
-                cancel(); // be gone, ye!
-              }
-            }
-          }
-        } catch (InterruptedException | IOException e) {
-          jobState = State.FAILED;
-          cancel();
+        if (!updateStatus()) {
+          cancel(); // be gone, ye!
         }
       }
     }, interval, interval);
   }
 
+  public void setTimer(Timer timer) {
+    this.timer = timer;
+  }
+
+  private Timer getTimer() {
+    return timer;
+  }
+
+  /**
+   * Update job status info. Will finalize job when underlying MR job completes.
+   *
+   * @return true if should continue updating status, false otherwise.
+   */
+  private synchronized boolean updateStatus() {
+    try {
+      org.apache.hadoop.mapreduce.JobStatus mrJobStatus = mrJob.getStatus();
+      org.apache.hadoop.mapreduce.JobStatus.State mrJobState = mrJob.getStatus().getState();
+      if (mrJob.isComplete()) {
+        jobStatus.withPercentComplete(100.0);
+        switch (mrJobState) {
+          case SUCCEEDED:
+            jobStatus.withState(State.FINALIZING).withDescription("Finalizing job.");
+            try {
+              setFinalResults(finalizer, configuration);
+              jobStatus.withState(State.SUCCEEDED).withDescription("Job completed.");
+            } catch (JobException je) {
+              jobStatus.withState(State.FAILED).withDescription("Job finalize failed.")
+                  .withFailureException(je);
+            }
+            break;
+          case FAILED:
+            jobStatus.withState(State.FAILED).withDescription(mrJob.getStatus().getFailureInfo());
+            break;
+          case KILLED:
+            jobStatus.withState(State.KILLED).withDescription(mrJob.getStatus().getFailureInfo());
+            break;
+        }
+        return false;
+      } else {
+        float mapProg = mrJob.mapProgress();
+        float reduceProg = mrJob.reduceProgress();
+        float totalProgress = ((mapProg / 2) + (reduceProg / 2)) * 100;
+        String description = String
+            .format("map: %s%%, reduce: %s%%", mapProg * 100, reduceProg * 100);
+        jobStatus.withPercentComplete(totalProgress).withState(State.RUNNING)
+            .withDescription(description);
+      }
+    } catch (InterruptedException | IOException e) {
+      jobStatus.withPercentComplete(100.0).withState(State.FAILED).withFailureException(e);
+      return false;
+    }
+    return true;
+  }
+
   /**
-   * Writes results using finalizer. Returns true on success, false otherwise.
+   * Writes results using finalizer. Returns true on success, false otherwise. If no results
+   * to finalize, returns empty Pageable.
    *
    * @param finalizer Writes results.
    * @param configuration Configure the finalizer.
    * @return Returns true on success, false otherwise.
    */
-  private boolean setFinalResults(Finalizer<Path> finalizer, Map<String, Object> configuration) {
-    boolean success = true;
-    Pageable<Path> results = new PcapPages();
-    try {
-      results = finalizer.finalizeJob(configuration);
-    } catch (JobException e) {
-      LOG.error("Failed to finalize job.", e);
-      success = false;
+  private void setFinalResults(Finalizer<Path> finalizer, Map<String, Object> configuration)
+      throws JobException {
+    Pageable<Path> results = finalizer.finalizeJob(configuration);
+    if (results == null) {
+      results = new PcapPages();
     }
     synchronized (this) {
       finalResults = results;
     }
-    return success;
   }
 
   /**
-   * Creates, but does not submit the job. This is the core MapReduce mrJob.
+   * Creates, but does not submit the job. This is the core MapReduce mrJob. Empty input path
+   * results in a null to be returned instead of creating the job.
    */
   public Job createJob(Optional<String> jobName
                       ,Path basePath
@@ -366,6 +406,11 @@ public class PcapJob<T> implements Statusable<Path> {
                       , PcapFilterConfigurator<T> filterImpl
                       ) throws IOException
   {
+    Iterable<String> filteredPaths = FileFilterUtil.getPathsInTimeRange(beginNS, endNS, listFiles(fs, basePath));
+    String inputPaths = Joiner.on(',').join(filteredPaths);
+    if (StringUtils.isEmpty(inputPaths)) {
+      return null;
+    }
     conf.set(START_TS_CONF, Long.toUnsignedString(beginNS));
     conf.set(END_TS_CONF, Long.toUnsignedString(endNS));
     conf.set(WIDTH_CONF, "" + findWidth(beginNS, endNS, numReducers));
@@ -381,11 +426,6 @@ public class PcapJob<T> implements Statusable<Path> {
     job.setPartitionerClass(PcapPartitioner.class);
     job.setOutputKeyClass(LongWritable.class);
     job.setOutputValueClass(BytesWritable.class);
-    Iterable<String> filteredPaths = FileFilterUtil.getPathsInTimeRange(beginNS, endNS, listFiles(fs, basePath));
-    String inputPaths = Joiner.on(',').join(filteredPaths);
-    if (StringUtils.isEmpty(inputPaths)) {
-      return null;
-    }
     SequenceFileInputFormat.addInputPaths(job, inputPaths);
     job.setInputFormatClass(SequenceFileInputFormat.class);
     job.setOutputFormatClass(SequenceFileOutputFormat.class);
@@ -411,54 +451,9 @@ public class PcapJob<T> implements Statusable<Path> {
     return JobType.MAP_REDUCE;
   }
 
-  /**
-   * Synchronized for mrJob and jobState
-   */
   @Override
   public synchronized JobStatus getStatus() throws JobException {
-    JobStatus status = new JobStatus();
-    if (mrJob == null) {
-      status.withPercentComplete(100).withState(State.SUCCEEDED);
-    } else {
-      try {
-        org.apache.hadoop.mapreduce.JobStatus mrJobStatus = mrJob.getStatus();
-        status.withJobId(mrJobStatus.getJobID().toString());
-        if (jobState == State.SUCCEEDED) {
-          status.withPercentComplete(100).withState(State.SUCCEEDED)
-              .withDescription("Job complete");
-        } else {
-          if (mrJob.isComplete()) {
-            status.withPercentComplete(100);
-            switch (mrJobStatus.getState()) {
-              case SUCCEEDED:
-                status.withState(State.FINALIZING).withDescription(State.FINALIZING.toString());
-                break;
-              case FAILED:
-                status.withState(State.FAILED).withDescription(State.FAILED.toString());
-                break;
-              case KILLED:
-                status.withState(State.KILLED).withDescription(State.KILLED.toString());
-                break;
-              default:
-                throw new IllegalStateException(
-                    "Unknown job state reported as 'complete' by mapreduce framework: "
-                        + mrJobStatus.getState());
-            }
-          } else {
-            float mapProg = mrJob.mapProgress();
-            float reduceProg = mrJob.reduceProgress();
-            float totalProgress = ((mapProg / 2) + (reduceProg / 2)) * 100;
-            String description = String
-                .format("map: %s%%, reduce: %s%%", mapProg * 100, reduceProg * 100);
-            status.withPercentComplete(totalProgress).withState(State.RUNNING)
-                .withDescription(description);
-          }
-        }
-      } catch (Exception e) {
-        throw new JobException("Error occurred while attempting to retrieve job status.", e);
-      }
-    }
-    return status;
+    return new JobStatus(jobStatus);
   }
 
   /**
@@ -483,6 +478,7 @@ public class PcapJob<T> implements Statusable<Path> {
 
   @Override
   public synchronized boolean isDone() {
+    State jobState = jobStatus.getState();
     return (jobState == State.SUCCEEDED
         || jobState == State.KILLED
         || jobState == State.FAILED);

http://git-wip-us.apache.org/repos/asf/metron/blob/9d4842f3/site-book/bin/generate-md.sh
----------------------------------------------------------------------
diff --git a/site-book/bin/generate-md.sh b/site-book/bin/generate-md.sh
index 464cb69..83f41cc 100755
--- a/site-book/bin/generate-md.sh
+++ b/site-book/bin/generate-md.sh
@@ -63,6 +63,7 @@ RESOURCE_LIST=(
     metron-deployment/readme-images/enable-kerberos-configure-kerberos.png
     metron-deployment/readme-images/enable-kerberos-started.png
     metron-deployment/readme-images/enable-kerberos.png
+    metron-platform/metron-job/metron-job_state_statechart_diagram.svg
     metron-platform/metron-parsers/parser_arch.png
     metron-platform/metron-indexing/indexing_arch.png
     metron-platform/metron-enrichment/enrichment_arch.png
@@ -86,6 +87,7 @@ HREF_REWRITE_LIST=(
     metron-deployment/Kerberos-ambari-setup.md 's#(readme-images/enable-kerberos.png)#(../images/enable-kerberos.png)#g'
     metron-platform/metron-enrichment/README.md 's#(enrichment_arch.png)#(../../images/enrichment_arch.png)#g'
     metron-platform/metron-indexing/README.md 's#(indexing_arch.png)#(../../images/indexing_arch.png)#g'
+    metron-platform/metron-job/README.md 's#(metron-job_state_statechart_diagram.svg)#(../../images/metron-job_state_statechart_diagram.svg)#g'
     metron-platform/metron-parsers/README.md 's#(parser_arch.png)#(../../images/parser_arch.png)#g'
     metron-analytics/metron-maas-service/README.md 's#(maas_arch.png)#(../../images/maas_arch.png)#g'
 )


[49/50] [abbrv] metron git commit: METRON-1751 Storm Profiler dies when consuming null message (nickwallen) closes apache/metron#1176

Posted by ni...@apache.org.
METRON-1751 Storm Profiler dies when consuming null message (nickwallen) closes apache/metron#1176


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/d32bd50d
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/d32bd50d
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/d32bd50d

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: d32bd50d43aae87af9ec12d2daea83b2f4eca342
Parents: 661e23e
Author: nickwallen <ni...@nickallen.org>
Authored: Wed Aug 29 14:55:58 2018 -0400
Committer: nickallen <ni...@apache.org>
Committed: Wed Aug 29 14:55:58 2018 -0400

----------------------------------------------------------------------
 .../profiler/bolt/ProfileSplitterBolt.java      | 29 +++++++++++---------
 .../profiler/bolt/ProfileSplitterBoltTest.java  | 16 +++++++++++
 2 files changed, 32 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/d32bd50d/metron-analytics/metron-profiler/src/main/java/org/apache/metron/profiler/bolt/ProfileSplitterBolt.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler/src/main/java/org/apache/metron/profiler/bolt/ProfileSplitterBolt.java b/metron-analytics/metron-profiler/src/main/java/org/apache/metron/profiler/bolt/ProfileSplitterBolt.java
index f28411f..87f1ba9 100644
--- a/metron-analytics/metron-profiler/src/main/java/org/apache/metron/profiler/bolt/ProfileSplitterBolt.java
+++ b/metron-analytics/metron-profiler/src/main/java/org/apache/metron/profiler/bolt/ProfileSplitterBolt.java
@@ -144,9 +144,9 @@ public class ProfileSplitterBolt extends ConfiguredProfilerBolt {
     try {
       doExecute(input);
 
-    } catch (IllegalArgumentException | ParseException | UnsupportedEncodingException e) {
-      LOG.error("Unexpected error", e);
-      collector.reportError(e);
+    } catch (Throwable t) {
+      LOG.error("Unexpected error", t);
+      collector.reportError(t);
 
     } finally {
       collector.ack(input);
@@ -157,22 +157,25 @@ public class ProfileSplitterBolt extends ConfiguredProfilerBolt {
 
     // retrieve the input message
     byte[] data = input.getBinary(0);
+    if(data == null) {
+      LOG.debug("Received null message. Nothing to do.");
+      return;
+    }
     JSONObject message = (JSONObject) parser.parse(new String(data, "UTF8"));
 
     // ensure there is a valid profiler configuration
     ProfilerConfig config = getProfilerConfig();
-    if(config != null && config.getProfiles().size() > 0) {
-
-      // what time is it?
-      Clock clock = clockFactory.createClock(config);
-      Optional<Long> timestamp = clock.currentTimeMillis(message);
+    if(config == null || getProfilerConfig().getProfiles().size() == 0) {
+      LOG.debug("No Profiler configuration found. Nothing to do.");
+      return;
+    }
 
-      // route the message.  if a message does not contain the timestamp field, it cannot be routed.
-      timestamp.ifPresent(ts -> routeMessage(input, message, config, ts));
+    // what time is it?
+    Clock clock = clockFactory.createClock(config);
+    Optional<Long> timestamp = clock.currentTimeMillis(message);
 
-    } else {
-      LOG.debug("No Profiler configuration found.  Nothing to do.");
-    }
+    // route the message.  if a message does not contain the timestamp field, it cannot be routed.
+    timestamp.ifPresent(ts -> routeMessage(input, message, config, ts));
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/metron/blob/d32bd50d/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/bolt/ProfileSplitterBoltTest.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/bolt/ProfileSplitterBoltTest.java b/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/bolt/ProfileSplitterBoltTest.java
index bf81923..72e2b72 100644
--- a/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/bolt/ProfileSplitterBoltTest.java
+++ b/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/bolt/ProfileSplitterBoltTest.java
@@ -404,6 +404,22 @@ public class ProfileSplitterBoltTest extends BaseBoltTest {
             .emit(any(Values.class));
   }
 
+  @Test
+  public void testWithNullMessage() throws Exception {
+
+    // ensure the tuple returns null to mimic a null message in kafka
+    when(tuple.getBinary(0)).thenReturn(null);
+
+    ProfilerConfig config = toProfilerConfig(profileWithOnlyIfInvalid);
+    ProfileSplitterBolt bolt = createBolt(config);
+    bolt.execute(tuple);
+
+    // a tuple should NOT be emitted for the downstream profile builder
+    verify(outputCollector, times(0))
+            .emit(any(Values.class));
+
+  }
+
   /**
    * Creates a ProfilerConfig based on a string containing JSON.
    *


[30/50] [abbrv] metron git commit: METRON-1722 PcapCLI should print progress to stdout (merrimanr) closes apache/metron#1138

Posted by ni...@apache.org.
METRON-1722 PcapCLI should print progress to stdout (merrimanr) closes apache/metron#1138


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/b29833cc
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/b29833cc
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/b29833cc

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: b29833cc5db4efca2a6e9025042baf705fb347c6
Parents: d8a5922
Author: merrimanr <me...@gmail.com>
Authored: Fri Aug 10 08:25:13 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Fri Aug 10 08:25:13 2018 -0500

----------------------------------------------------------------------
 metron-platform/metron-pcap-backend/README.md           |  2 ++
 .../java/org/apache/metron/pcap/query/CliParser.java    |  4 ++++
 .../java/org/apache/metron/pcap/query/PcapCliTest.java  | 12 +++++++++---
 .../java/org/apache/metron/pcap/config/PcapConfig.java  |  9 +++++++++
 .../java/org/apache/metron/pcap/config/PcapOptions.java |  3 ++-
 .../main/java/org/apache/metron/pcap/mr/PcapJob.java    | 11 +++++++++++
 6 files changed, 37 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/b29833cc/metron-platform/metron-pcap-backend/README.md
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/README.md b/metron-platform/metron-pcap-backend/README.md
index e7960e3..2ff20d8 100644
--- a/metron-platform/metron-pcap-backend/README.md
+++ b/metron-platform/metron-pcap-backend/README.md
@@ -132,6 +132,7 @@ usage: Fixed filter options
  -nr,--num_reducers <arg>        The number of reducers to use.  Default
                                  is 10.
  -h,--help                       Display help
+ -ps,--print_status              Print the status of the job as it runs
  -ir,--include_reverse           Indicates if filter should check swapped
                                  src/dest addresses and IPs
  -p,--protocol <arg>             IP Protocol
@@ -154,6 +155,7 @@ usage: Query filter options
  -nr,--num_reducers <arg>        The number of reducers to use.  Default
                                  is 10.
  -h,--help                       Display help
+ -ps,--print_status              Print the status of the job as it runs
  -q,--query <arg>                Query string to use as a filter
  -st,--start_time <arg>          (required) Packet start time range.
 ```

http://git-wip-us.apache.org/repos/asf/metron/blob/b29833cc/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
index 2d15e8b..69c725c 100644
--- a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
+++ b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
@@ -55,6 +55,7 @@ public class CliParser {
     options.addOption(newOption("rpf", "records_per_file", true, String.format("Number of records to include in each output pcap file (defaults to %s)", NUM_RECORDS_PER_FILE_DEFAULT)));
     options.addOption(newOption("et", "end_time", true, "Packet end time range. Default is current system time."));
     options.addOption(newOption("df", "date_format", true, "Date format to use for parsing start_time and end_time. Default is to use time in millis since the epoch."));
+    options.addOption(newOption("ps", "print_status", false, "Print the status of the job as it runs"));
     return options;
   }
 
@@ -125,6 +126,9 @@ public class CliParser {
         //no-op
       }
     }
+    if (commandLine.hasOption("print_status")) {
+      config.setPrintJobStatus(true);
+    }
   }
 
   public void printHelp(String msg, Options opts) {

http://git-wip-us.apache.org/repos/asf/metron/blob/b29833cc/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
index 96ca354..7c75224 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
@@ -147,7 +147,8 @@ public class PcapCliTest {
             "-protocol", "6",
             "-include_reverse",
             "-num_reducers", "10",
-            "-records_per_file", "1000"
+            "-records_per_file", "1000",
+            "-ps"
     };
     Map<String, String> query = new HashMap<String, String>() {{
       put(Constants.Fields.SRC_ADDR.getName(), "192.168.1.1");
@@ -165,6 +166,7 @@ public class PcapCliTest {
     PcapOptions.START_TIME_MS.put(config, 500L);
     PcapOptions.END_TIME_MS.put(config, 1000L);
     PcapOptions.NUM_RECORDS_PER_FILE.put(config, 1000);
+    PcapOptions.PRINT_JOB_STATUS.put(config, true);
 
     when(jobRunner.submit(isA(Finalizer.class), argThat(mapContaining(config)))).thenReturn(jobRunner);
 
@@ -189,7 +191,8 @@ public class PcapCliTest {
             "-protocol", "6",
             "-include_reverse",
             "-num_reducers", "10",
-            "-records_per_file", "1000"
+            "-records_per_file", "1000",
+            "-ps"
     };
     Map<String, String> query = new HashMap<String, String>() {{
       put(Constants.Fields.SRC_ADDR.getName(), "192.168.1.1");
@@ -211,6 +214,7 @@ public class PcapCliTest {
     PcapOptions.START_TIME_MS.put(config, startAsNanos / 1000000L); // needed bc defaults in config
     PcapOptions.END_TIME_MS.put(config, endAsNanos / 1000000L);  // needed bc defaults in config
     PcapOptions.NUM_RECORDS_PER_FILE.put(config, 1000);
+    PcapOptions.PRINT_JOB_STATUS.put(config, true);
 
     when(jobRunner.submit(isA(Finalizer.class), argThat(mapContaining(config)))).thenReturn(jobRunner);
 
@@ -262,7 +266,8 @@ public class PcapCliTest {
             "-base_path", "/base/path",
             "-base_output_path", "/base/output/path",
             "-query", "some query string",
-            "-records_per_file", "1000"
+            "-records_per_file", "1000",
+            "-ps"
     };
 
     String query = "some query string";
@@ -274,6 +279,7 @@ public class PcapCliTest {
     PcapOptions.START_TIME_MS.put(config, 500L); // needed bc defaults in config
     PcapOptions.END_TIME_MS.put(config, 1000L);  // needed bc defaults in config
     PcapOptions.NUM_RECORDS_PER_FILE.put(config, 1000);
+    PcapOptions.PRINT_JOB_STATUS.put(config, true);
 
     when(jobRunner.submit(isA(Finalizer.class), argThat(mapContaining(config)))).thenReturn(jobRunner);
 

http://git-wip-us.apache.org/repos/asf/metron/blob/b29833cc/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
index 26509be..cbb8170 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapConfig.java
@@ -40,6 +40,7 @@ public class PcapConfig extends AbstractMapDecorator<String, Object>{
   public PcapConfig(PrefixStrategy prefixStrategy) {
     this();
     setShowHelp(false);
+    setPrintJobStatus(false);
     setBasePath("");
     setBaseInterimResultPath("");
     setStartTimeMs(-1L);
@@ -73,6 +74,14 @@ public class PcapConfig extends AbstractMapDecorator<String, Object>{
     this.showHelp = showHelp;
   }
 
+  public boolean printJobStatus() {
+    return PcapOptions.PRINT_JOB_STATUS.get(this, Boolean.class);
+  }
+
+  public void setPrintJobStatus(boolean printJobStatus) {
+    PcapOptions.PRINT_JOB_STATUS.put(this, printJobStatus);
+  }
+
   public String getBasePath() {
     return PcapOptions.BASE_PATH.get(this, String.class);
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/b29833cc/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java
index 3d7c4f6..203c800 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapOptions.java
@@ -40,7 +40,8 @@ public enum PcapOptions implements ConfigOption {
   FIELDS("fields"),
   FILTER_IMPL("filterImpl"),
   HADOOP_CONF("hadoopConf"),
-  FILESYSTEM("fileSystem");
+  FILESYSTEM("fileSystem"),
+  PRINT_JOB_STATUS("printJobStatus");
 
   public static final BiFunction<String, Object, Path> STRING_TO_PATH =
       (s, o) -> o == null ? null : new Path(o.toString());

http://git-wip-us.apache.org/repos/asf/metron/blob/b29833cc/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
index 23bd510..10f31b4 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
@@ -467,6 +467,13 @@ public class PcapJob<T> implements Statusable<Path> {
    */
   @Override
   public Pageable<Path> get() throws JobException, InterruptedException {
+    if (PcapOptions.PRINT_JOB_STATUS.get(configuration, Boolean.class)) {
+      try {
+        mrJob.monitorAndPrintJob();
+      } catch (IOException e) {
+        throw new JobException("Could not monitor job status", e);
+      }
+    }
     for (; ; ) {
       JobStatus status = getStatus();
       if (status.getState() == State.SUCCEEDED
@@ -478,6 +485,10 @@ public class PcapJob<T> implements Statusable<Path> {
     }
   }
 
+  public void monitorJob() throws IOException, InterruptedException {
+    mrJob.monitorAndPrintJob();
+  }
+
   private synchronized Pageable<Path> getFinalResults() {
     return new PcapPages(finalResults);
   }


[31/50] [abbrv] metron git commit: METRON-1702 Reload a running job in the UI (merrimanr) closes apache/metron#1156

Posted by ni...@apache.org.
METRON-1702 Reload a running job in the UI (merrimanr) closes apache/metron#1156


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/05316a4d
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/05316a4d
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/05316a4d

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 05316a4d989d7546058e73b3a75eef435f2608a0
Parents: b29833c
Author: merrimanr <me...@gmail.com>
Authored: Fri Aug 10 08:27:06 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Fri Aug 10 08:27:06 2018 -0500

----------------------------------------------------------------------
 .../src/app/pcap/model/pcap.request.ts          |  4 +-
 .../pcap-filters/pcap-filters.component.spec.ts | 60 +++++++++++++-
 .../pcap/pcap-filters/pcap-filters.component.ts | 26 ++++--
 .../pcap/pcap-panel/pcap-panel.component.html   |  2 +-
 .../pcap-panel/pcap-panel.component.spec.ts     | 37 +++++++++
 .../app/pcap/pcap-panel/pcap-panel.component.ts | 87 +++++++++++++-------
 .../src/app/pcap/service/pcap.service.spec.ts   | 44 ++++++++++
 .../src/app/pcap/service/pcap.service.ts        | 15 ++++
 .../rest/service/impl/PcapServiceImpl.java      | 10 +--
 .../rest/service/impl/PcapServiceImplTest.java  | 36 ++++----
 10 files changed, 254 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
index d00a6ac..8afc963 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
@@ -20,9 +20,9 @@ export class PcapRequest {
   startTimeMs: number = 0;
   endTimeMs: number = 150000000000000000;
   ipSrcAddr: string = '';
-  ipSrcPort: number = 0;
+  ipSrcPort: number;
   ipDstAddr: string = '';
-  ipDstPort: number = 0;
+  ipDstPort: number;
   protocol: string = '';
   packetFilter: string = '';
   includeReverse: boolean = false;

http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
index 4336b22..c1d8664 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
@@ -21,7 +21,7 @@ import { By } from '@angular/platform-browser';
 
 import { PcapFiltersComponent } from './pcap-filters.component';
 import { FormsModule } from '../../../../node_modules/@angular/forms';
-import { Component, Input, Output, EventEmitter, DebugElement } from '@angular/core';
+import { Component, Input, Output, EventEmitter, DebugElement, SimpleChange } from '@angular/core';
 import { PcapRequest } from '../model/pcap.request';
 
 @Component({
@@ -171,6 +171,14 @@ describe('PcapFiltersComponent', () => {
     component.onSubmit();
   });
 
+  it('Port fields should be missing by default', () => {
+    component.search.emit = (model: PcapRequest) => {
+      expect(model.ipSrcPort).toBeFalsy();
+      expect(model.ipDstPort).toBeFalsy();
+    };
+    component.onSubmit();
+  });
+
   it('Filter should have an output called search', () => {
     component.search.subscribe((filterModel) => {
       expect(filterModel).toBeDefined();
@@ -194,14 +202,60 @@ describe('PcapFiltersComponent', () => {
     expect(fixture.componentInstance.model.hasOwnProperty('startTimeMs')).toBeTruthy();
     expect(fixture.componentInstance.model.hasOwnProperty('endTimeMs')).toBeTruthy();
     expect(fixture.componentInstance.model.hasOwnProperty('ipSrcAddr')).toBeTruthy();
-    expect(fixture.componentInstance.model.hasOwnProperty('ipSrcPort')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('ipSrcPort')).toBeFalsy();
     expect(fixture.componentInstance.model.hasOwnProperty('ipDstAddr')).toBeTruthy();
-    expect(fixture.componentInstance.model.hasOwnProperty('ipDstPort')).toBeTruthy();
+    expect(fixture.componentInstance.model.hasOwnProperty('ipDstPort')).toBeFalsy();
     expect(fixture.componentInstance.model.hasOwnProperty('protocol')).toBeTruthy();
     expect(fixture.componentInstance.model.hasOwnProperty('packetFilter')).toBeTruthy();
     expect(fixture.componentInstance.model.hasOwnProperty('includeReverse')).toBeTruthy();
   });
 
+  it('should update request on changes', () => {
+
+    let startTimeStr = '2220-12-12 12:12:12';
+    let endTimeStr = '2320-03-13 13:13:13';
+
+    let newModel = {
+      startTimeMs: new Date(startTimeStr).getTime(),
+      endTimeMs: new Date(endTimeStr).getTime(),
+      ipSrcPort: 9345,
+      ipDstPort: 8989
+    };
+    component.model.startTimeMs = new Date(startTimeStr).getTime();
+    component.model.endTimeMs = new Date(endTimeStr).getTime();
+
+    component.ngOnChanges({
+      model: new SimpleChange(null, newModel, false)
+    });
+
+    expect(component.startTimeStr).toBe(startTimeStr);
+    expect(component.endTimeStr).toBe(endTimeStr);
+    expect(component.ipSrcPort).toBe('9345');
+    expect(component.ipDstPort).toBe('8989');
+  });
+
+  it('should update request on changes with missing port filters', () => {
+
+    let startTimeStr = '2220-12-12 12:12:12';
+    let endTimeStr = '2320-03-13 13:13:13';
+
+    let newModel = {
+      startTimeMs: new Date(startTimeStr).getTime(),
+      endTimeMs: new Date(endTimeStr).getTime()
+    };
+    component.model.startTimeMs = new Date(startTimeStr).getTime();
+    component.model.endTimeMs = new Date(endTimeStr).getTime();
+
+    component.ngOnChanges({
+      model: new SimpleChange(null, newModel, false)
+    });
+
+    expect(component.startTimeStr).toBe(startTimeStr);
+    expect(component.endTimeStr).toBe(endTimeStr);
+    expect(component.ipSrcPort).toBe('');
+    expect(component.ipDstPort).toBe('');
+  });
+
   describe('Filter validation', () => {
 
     function setup() {

http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
index 5bbdb67..20a8c2b 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
@@ -15,7 +15,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
+import {Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges} from '@angular/core';
 import * as moment from 'moment/moment';
 import { DEFAULT_TIMESTAMP_FORMAT } from '../../utils/constants';
 
@@ -26,9 +26,10 @@ import { PcapRequest } from '../model/pcap.request';
   templateUrl: './pcap-filters.component.html',
   styleUrls: ['./pcap-filters.component.scss']
 })
-export class PcapFiltersComponent implements OnInit {
+export class PcapFiltersComponent implements OnInit, OnChanges {
 
   @Input() queryRunning: boolean = true;
+  @Input() model: PcapRequest = new PcapRequest();
   @Output() search: EventEmitter<PcapRequest> = new EventEmitter<PcapRequest>();
 
   startTimeStr: string;
@@ -36,8 +37,6 @@ export class PcapFiltersComponent implements OnInit {
   ipSrcPort: string = '';
   ipDstPort: string = '';
 
-  model = new PcapRequest();
-
   constructor() { }
 
   ngOnInit() {
@@ -48,11 +47,26 @@ export class PcapFiltersComponent implements OnInit {
     this.endTimeStr = moment(endTime).format(DEFAULT_TIMESTAMP_FORMAT);
   }
 
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes['model']) {
+      this.startTimeStr = moment(changes['model'].currentValue.startTimeMs).format(DEFAULT_TIMESTAMP_FORMAT);
+      this.endTimeStr = moment(changes['model'].currentValue.endTimeMs).format(DEFAULT_TIMESTAMP_FORMAT);
+      let newIpSrcPort = changes['model'].currentValue.ipSrcPort;
+      this.ipSrcPort = newIpSrcPort ? newIpSrcPort.toString() : '';
+      let newIpDstPort = changes['model'].currentValue.ipDstPort;
+      this.ipDstPort = newIpDstPort ? newIpDstPort.toString() : '';
+    }
+  }
+
   onSubmit() {
     this.model.startTimeMs = new Date(this.startTimeStr).getTime();
     this.model.endTimeMs = new Date(this.endTimeStr).getTime();
-    this.model.ipSrcPort = +this.ipSrcPort;
-    this.model.ipDstPort = +this.ipDstPort;
+    if (this.ipSrcPort !== '') {
+      this.model.ipSrcPort = +this.ipSrcPort;
+    }
+    if (this.ipDstPort !== '') {
+      this.model.ipDstPort = +this.ipDstPort;
+    }
 
     this.search.emit(this.model);
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
index 0dda268..6b18a5f 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
@@ -13,7 +13,7 @@
   -->
 <div class="panel my-4">
   <div class="panel-header">
-    <app-pcap-filters [queryRunning]="queryRunning" (search)="onSearch($event)"></app-pcap-filters>
+    <app-pcap-filters [queryRunning]="queryRunning" [model]="pcapRequest" (search)="onSearch($event)"></app-pcap-filters>
   </div>
   <div *ngIf="queryRunning" class="pcap-progress-wrapper">
     <div class="progress pcap-progress-background">

http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
index 9dacc7f..fe4df1e 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
@@ -26,6 +26,7 @@ import { PcapPagination } from '../model/pcap-pagination';
 import { By } from '../../../../node_modules/@angular/platform-browser';
 import { PcapRequest } from '../model/pcap.request';
 import { defer } from 'rxjs/observable/defer';
+import {Observable} from "rxjs/Observable";
 import {RestError} from "../../model/rest-error";
 
 @Component({
@@ -34,6 +35,7 @@ import {RestError} from "../../model/rest-error";
 })
 class FakeFilterComponent {
   @Input() queryRunning: boolean;
+  @Input() model: PcapRequest;
 }
 
 @Component({
@@ -46,6 +48,9 @@ class FakePcapListComponent {
 }
 
 class FakePcapService {
+
+  getRunningJob() {}
+
   getDownloadUrl() {
     return '';
   }
@@ -76,6 +81,8 @@ describe('PcapPanelComponent', () => {
 
   beforeEach(() => {
     pcapService = TestBed.get(PcapService);
+    pcapService.getRunningJob = jasmine.createSpy('getRunningJob')
+            .and.returnValue(Observable.of([]));
     fixture = TestBed.createComponent(PcapPanelComponent);
     component = fixture.componentInstance;
     fixture.detectChanges();
@@ -517,4 +524,34 @@ describe('PcapPanelComponent', () => {
 
     expect(component.errorMsg).toEqual('Response message: error message. Something went wrong retrieving pdml results!');
   }));
+
+  it('should load running job on init', fakeAsync(() => {
+    const searchResponse = new PcapStatusResponse();
+    searchResponse.jobId = '42';
+
+    pcapService.getRunningJob = jasmine.createSpy('getRunningJob').and.returnValue(
+            defer(() => Promise.resolve([searchResponse]))
+    );
+    component.updateStatus = jasmine.createSpy('updateStatus');
+    component.startPolling = jasmine.createSpy('startPolling');
+
+    const pcapRequest = new PcapRequest();
+    pcapRequest.ipSrcAddr = 'ip_src_addr';
+    pcapService.getPcapRequest = jasmine.createSpy('getPcapRequest').and.returnValue(
+            defer(() => Promise.resolve(pcapRequest))
+    );
+
+    expect(component.queryRunning).toEqual(false);
+    expect(component.pcapRequest).toEqual(new PcapRequest());
+
+    component.ngOnInit();
+
+    tick();
+
+    expect(component.queryRunning).toEqual(true);
+    expect(component.updateStatus).toHaveBeenCalled();
+    expect(component.startPolling).toHaveBeenCalledWith('42');
+    expect(pcapService.getPcapRequest).toHaveBeenCalledWith('42');
+    expect(component.pcapRequest).toEqual(pcapRequest)
+  }));
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
index b11d9df..7c88007 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
@@ -15,7 +15,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, Input, OnDestroy } from '@angular/core';
+import { Component, OnInit, OnDestroy, Input } from '@angular/core';
 
 import { PcapService } from '../service/pcap.service';
 import { PcapStatusResponse } from '../model/pcap-status-response';
@@ -23,14 +23,14 @@ import { PcapRequest } from '../model/pcap.request';
 import { Pdml } from '../model/pdml';
 import { Subscription } from 'rxjs/Rx';
 import { PcapPagination } from '../model/pcap-pagination';
-import {RestError} from "../../model/rest-error";
+import { RestError } from "../../model/rest-error";
 
 @Component({
   selector: 'app-pcap-panel',
   templateUrl: './pcap-panel.component.html',
   styleUrls: ['./pcap-panel.component.scss']
 })
-export class PcapPanelComponent implements OnDestroy {
+export class PcapPanelComponent implements OnInit, OnDestroy {
 
   @Input() pdml: Pdml = null;
   @Input() pcapRequest: PcapRequest;
@@ -49,6 +49,22 @@ export class PcapPanelComponent implements OnDestroy {
 
   constructor(private pcapService: PcapService) { }
 
+  ngOnInit() {
+    this.pcapRequest = new PcapRequest();
+    this.pcapService.getRunningJob().subscribe((statusResponses: PcapStatusResponse[]) => {
+      if (statusResponses.length > 0) {
+        // Assume the first job in the list is the running job
+        this.queryRunning = true;
+        let statusResponse = statusResponses[0];
+        this.updateStatus(statusResponse);
+        this.startPolling(statusResponse.jobId);
+        this.pcapService.getPcapRequest(statusResponse.jobId).subscribe((pcapRequest: PcapRequest) => {
+          this.pcapRequest = pcapRequest;
+        });
+      }
+    });
+  }
+
   changePage(page) {
     this.pagination.selectedPage = page;
     this.pcapService.getPackets(this.queryId, this.pagination.selectedPage).toPromise().then(pdml => {
@@ -70,41 +86,48 @@ export class PcapPanelComponent implements OnDestroy {
         this.errorMsg = submitResponse.description;
         this.queryRunning = false;
       } else {
-        this.queryId = id;
-        this.errorMsg = null;
-        this.statusSubscription = this.pcapService.pollStatus(id).subscribe((statusResponse: PcapStatusResponse) => {
-          if ('SUCCEEDED' === statusResponse.jobStatus) {
-            this.pagination.total = statusResponse.pageTotal;
-            this.statusSubscription.unsubscribe();
-            this.queryRunning = false;
-            this.pcapService.getPackets(id, this.pagination.selectedPage).toPromise().then(pdml => {
-              this.pdml = pdml;
-            }, (error: RestError) => {
-              if (error.responseCode === 404) {
-                this.errorMsg = 'No results returned';
-              } else {
-                this.errorMsg = `Response message: ${error.message}. Something went wrong retrieving pdml results!`;
-              }
-            });
-          } else if ('FAILED' === statusResponse.jobStatus) {
-            this.statusSubscription.unsubscribe();
-            this.queryRunning = false;
-            this.errorMsg = `Query status: ${statusResponse.jobStatus}. Check your filter criteria and try again!`;
-          } else if (this.progressWidth < 100) {
-            this.progressWidth = Math.trunc(statusResponse.percentComplete);
-          }
-        }, (error: any) => {
-          this.statusSubscription.unsubscribe();
-          this.queryRunning = false;
-          this.errorMsg = `Response message: ${error.message}. Something went wrong with your status request!`;
-        });
+        this.startPolling(id);
       }
     }, (error: any) => {
-      this.queryRunning = false;
       this.errorMsg = `Response message: ${error.message}. Something went wrong with your query submission!`;
     });
   }
 
+  startPolling(id: string) {
+    this.queryId = id;
+    this.errorMsg = null;
+    this.statusSubscription = this.pcapService.pollStatus(id).subscribe((statusResponse: PcapStatusResponse) => {
+      this.updateStatus(statusResponse);
+    }, (error: any) => {
+      this.statusSubscription.unsubscribe();
+      this.queryRunning = false;
+      this.errorMsg = `Response message: ${error.message}. Something went wrong with your status request!`;
+    });
+  }
+
+  updateStatus(statusResponse: PcapStatusResponse) {
+    if ('SUCCEEDED' === statusResponse.jobStatus) {
+      this.pagination.total = statusResponse.pageTotal;
+      this.statusSubscription.unsubscribe();
+      this.queryRunning = false;
+      this.pcapService.getPackets(this.queryId, this.pagination.selectedPage).toPromise().then(pdml => {
+        this.pdml = pdml;
+      }, (error: RestError) => {
+        if (error.responseCode === 404) {
+          this.errorMsg = 'No results returned';
+        } else {
+          this.errorMsg = `Response message: ${error.message}. Something went wrong retrieving pdml results!`;
+        }
+      });
+    } else if ('FAILED' === statusResponse.jobStatus) {
+      this.statusSubscription.unsubscribe();
+      this.queryRunning = false;
+      this.errorMsg = `Query status: ${statusResponse.jobStatus}. Check your filter criteria and try again!`;
+    } else if (this.progressWidth < 100) {
+      this.progressWidth = Math.trunc(statusResponse.percentComplete);
+    }
+  }
+
   getDownloadUrl() {
     return this.pcapService.getDownloadUrl(this.queryId, this.pagination.selectedPage);
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
index c8cb937..e7ac5ff 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
@@ -140,6 +140,50 @@ describe('PcapService', () => {
     ));
   });
 
+  describe('getRunningJob()', () => {
+    it('should return an Observable<PcapStatusResponse>', inject(
+            [PcapService, XHRBackend],
+            (pcapService, mockBackend) => {
+              const responseMock: PcapStatusResponse = fakePcapStatusResponse;
+              let response;
+
+              mockBackend.connections.subscribe(connection => {
+                expect(connection.request.url).toMatch(
+                        /\/api\/v1\/pcap\?state=RUNNING/
+                );
+                connection.mockRespond(
+                        new Response(new ResponseOptions({ body: responseMock }))
+                );
+              });
+
+              pcapService.getRunningJob().subscribe(r => (response = r));
+              expect(response).toBeTruthy();
+            }
+    ));
+  });
+
+  describe('getPcapRequest()', () => {
+    it('should return an Observable<PcapRequest>', inject(
+            [PcapService, XHRBackend],
+            (pcapService, mockBackend) => {
+              const responseMock: PcapRequest = fakePcapRequest;
+              let response;
+
+              mockBackend.connections.subscribe(connection => {
+                expect(connection.request.url).toMatch(
+                        /\/api\/v1\/pcap\/job_1234567890123_4567\/config/
+                );
+                connection.mockRespond(
+                        new Response(new ResponseOptions({ body: responseMock }))
+                );
+              });
+
+              pcapService.getPcapRequest(jobId).subscribe(r => (response = r));
+              expect(response).toBeTruthy();
+            }
+    ));
+  });
+
   describe('getDownloadUrl()', () => {
     it('should return a url with the correct page to download the pdml', inject(
       [PcapService],

http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
index 85ed9cc..d4efc42 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
@@ -53,12 +53,27 @@ export class PcapService {
           .map(HttpUtil.extractData)
           .catch(HttpUtil.handleError);
     }
+
+    public getRunningJob(): Observable<PcapStatusResponse[]> {
+      return this.http.get(`/api/v1/pcap?state=RUNNING`,
+              new RequestOptions({headers: new Headers(this.defaultHeaders)}))
+              .map(HttpUtil.extractData)
+              .catch(HttpUtil.handleError);
+    }
+
     public getPackets(id: string, pageId: number): Observable<Pdml> {
         return this.http.get(`/api/v1/pcap/${id}/pdml?page=${pageId}`, new RequestOptions({headers: new Headers(this.defaultHeaders)}))
             .map(HttpUtil.extractData)
             .catch(HttpUtil.handleError);
     }
 
+    public getPcapRequest(id: string): Observable<PcapRequest> {
+      return this.http.get(`/api/v1/pcap/${id}/config`,
+              new RequestOptions({headers: new Headers(this.defaultHeaders)}))
+              .map(HttpUtil.extractData)
+              .catch(HttpUtil.handleError);
+    }
+
     public getDownloadUrl(id: string, pageId: number) {
       return `/api/v1/pcap/${id}/raw?page=${pageId}`;
     }

http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
index f3af0dd..ae3f807 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/PcapServiceImpl.java
@@ -222,19 +222,19 @@ public class PcapServiceImpl implements PcapService {
         Map<String, Object> jobConfiguration = statusable.getConfiguration();
         configuration.put(PcapOptions.BASE_PATH.getKey(), PcapOptions.BASE_PATH.get(jobConfiguration, String.class));
         configuration.put(PcapOptions.FINAL_OUTPUT_PATH.getKey(), PcapOptions.FINAL_OUTPUT_PATH.get(jobConfiguration, String.class));
-        configuration.put(PcapOptions.START_TIME_MS.getKey(), PcapOptions.START_TIME_MS.get(jobConfiguration, String.class));
-        configuration.put(PcapOptions.END_TIME_MS.getKey(), PcapOptions.END_TIME_MS.get(jobConfiguration, String.class));
+        configuration.put(PcapOptions.START_TIME_MS.getKey(), PcapOptions.START_TIME_MS.get(jobConfiguration, Long.class));
+        configuration.put(PcapOptions.END_TIME_MS.getKey(), PcapOptions.END_TIME_MS.get(jobConfiguration, Long.class));
         configuration.put(PcapOptions.NUM_REDUCERS.getKey(), PcapOptions.NUM_REDUCERS.get(jobConfiguration, Integer.class));
 
         boolean isFixedFilter = PcapOptions.FILTER_IMPL.get(jobConfiguration, PcapFilterConfigurator.class) instanceof FixedPcapFilter.Configurator;
         if (isFixedFilter) {
           configuration.put(FixedPcapOptions.IP_SRC_ADDR.getKey(), FixedPcapOptions.IP_SRC_ADDR.get(jobConfiguration, String.class));
           configuration.put(FixedPcapOptions.IP_DST_ADDR.getKey(), FixedPcapOptions.IP_DST_ADDR.get(jobConfiguration, String.class));
-          configuration.put(FixedPcapOptions.IP_SRC_PORT.getKey(), FixedPcapOptions.IP_SRC_PORT.get(jobConfiguration, String.class));
-          configuration.put(FixedPcapOptions.IP_DST_PORT.getKey(), FixedPcapOptions.IP_DST_PORT.get(jobConfiguration, String.class));
+          configuration.put(FixedPcapOptions.IP_SRC_PORT.getKey(), FixedPcapOptions.IP_SRC_PORT.get(jobConfiguration, Integer.class));
+          configuration.put(FixedPcapOptions.IP_DST_PORT.getKey(), FixedPcapOptions.IP_DST_PORT.get(jobConfiguration, Integer.class));
           configuration.put(FixedPcapOptions.PROTOCOL.getKey(), FixedPcapOptions.PROTOCOL.get(jobConfiguration, String.class));
           configuration.put(FixedPcapOptions.PACKET_FILTER.getKey(), FixedPcapOptions.PACKET_FILTER.get(jobConfiguration, String.class));
-          configuration.put(FixedPcapOptions.INCLUDE_REVERSE.getKey(), FixedPcapOptions.INCLUDE_REVERSE.get(jobConfiguration, String.class));
+          configuration.put(FixedPcapOptions.INCLUDE_REVERSE.getKey(), FixedPcapOptions.INCLUDE_REVERSE.get(jobConfiguration, Boolean.class));
         } else {
           configuration.put(QueryPcapOptions.QUERY.getKey(), QueryPcapOptions.QUERY.get(jobConfiguration, String.class));
         }

http://git-wip-us.apache.org/repos/asf/metron/blob/05316a4d/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
index 2552df7..d539c71 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/PcapServiceImplTest.java
@@ -671,18 +671,18 @@ public class PcapServiceImplTest {
     pcapService.submit("user", fixedPcapRequest);
 
     Map<String, Object> configuration = pcapService.getConfiguration("user", "jobId");
-    Assert.assertEquals("basePath", PcapOptions.BASE_PATH.get(configuration, String.class));
-    Assert.assertEquals("finalOutputPath", PcapOptions.FINAL_OUTPUT_PATH.get(configuration, String.class));
-    Assert.assertEquals(1L, PcapOptions.START_TIME_MS.get(configuration, Long.class).longValue());
-    Assert.assertEquals(2L, PcapOptions.END_TIME_MS.get(configuration, Long.class).longValue());
-    Assert.assertEquals(2, PcapOptions.NUM_REDUCERS.get(configuration, Integer.class).intValue());
-    Assert.assertEquals("ip_src_addr", FixedPcapOptions.IP_SRC_ADDR.get(configuration, String.class));
-    Assert.assertEquals("ip_dst_addr", FixedPcapOptions.IP_DST_ADDR.get(configuration, String.class));
-    Assert.assertEquals(1000, FixedPcapOptions.IP_SRC_PORT.get(configuration, Integer.class).intValue());
-    Assert.assertEquals(2000, FixedPcapOptions.IP_DST_PORT.get(configuration, Integer.class).intValue());
-    Assert.assertEquals("tcp", FixedPcapOptions.PROTOCOL.get(configuration, String.class));
-    Assert.assertEquals("filter", FixedPcapOptions.PACKET_FILTER.get(configuration, String.class));
-    Assert.assertEquals(true, FixedPcapOptions.INCLUDE_REVERSE.get(configuration, Boolean.class));
+    Assert.assertEquals("basePath", configuration.get(PcapOptions.BASE_PATH.getKey()));
+    Assert.assertEquals("finalOutputPath", configuration.get(PcapOptions.FINAL_OUTPUT_PATH.getKey()));
+    Assert.assertEquals(1L, configuration.get(PcapOptions.START_TIME_MS.getKey()));
+    Assert.assertEquals(2L, configuration.get(PcapOptions.END_TIME_MS.getKey()));
+    Assert.assertEquals(2, configuration.get(PcapOptions.NUM_REDUCERS.getKey()));
+    Assert.assertEquals("ip_src_addr", configuration.get(FixedPcapOptions.IP_SRC_ADDR.getKey()));
+    Assert.assertEquals("ip_dst_addr", configuration.get(FixedPcapOptions.IP_DST_ADDR.getKey()));
+    Assert.assertEquals(1000, configuration.get(FixedPcapOptions.IP_SRC_PORT.getKey()));
+    Assert.assertEquals(2000, configuration.get(FixedPcapOptions.IP_DST_PORT.getKey()));
+    Assert.assertEquals("tcp", configuration.get(FixedPcapOptions.PROTOCOL.getKey()));
+    Assert.assertEquals("filter", configuration.get(FixedPcapOptions.PACKET_FILTER.getKey()));
+    Assert.assertEquals(true, configuration.get(FixedPcapOptions.INCLUDE_REVERSE.getKey()));
   }
 
   @Test
@@ -708,12 +708,12 @@ public class PcapServiceImplTest {
     pcapService.submit("user", queryPcapRequest);
 
     Map<String, Object> configuration = pcapService.getConfiguration("user", "jobId");
-    Assert.assertEquals("basePath", PcapOptions.BASE_PATH.get(configuration, String.class));
-    Assert.assertEquals("finalOutputPath", PcapOptions.FINAL_OUTPUT_PATH.get(configuration, String.class));
-    Assert.assertEquals(1L, PcapOptions.START_TIME_MS.get(configuration, Long.class).longValue());
-    Assert.assertEquals(2L, PcapOptions.END_TIME_MS.get(configuration, Long.class).longValue());
-    Assert.assertEquals(2, PcapOptions.NUM_REDUCERS.get(configuration, Integer.class).intValue());
-    Assert.assertEquals("query", QueryPcapOptions.QUERY.get(configuration, String.class));
+    Assert.assertEquals("basePath", configuration.get(PcapOptions.BASE_PATH.getKey()));
+    Assert.assertEquals("finalOutputPath", configuration.get(PcapOptions.FINAL_OUTPUT_PATH.getKey()));
+    Assert.assertEquals(1L, configuration.get(PcapOptions.START_TIME_MS.getKey()));
+    Assert.assertEquals(2L, configuration.get(PcapOptions.END_TIME_MS.getKey()));
+    Assert.assertEquals(2, configuration.get(PcapOptions.NUM_REDUCERS.getKey()));
+    Assert.assertEquals("query", configuration.get(QueryPcapOptions.QUERY.getKey()));
   }
 
   @Test


[14/50] [abbrv] metron git commit: METRON-1662 PCAP UI - Downloading PCAP page files (tiborm via merrimanr) closes apache/metron#1118

Posted by ni...@apache.org.
METRON-1662 PCAP UI - Downloading PCAP page files (tiborm via merrimanr) closes apache/metron#1118


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/b445bfe2
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/b445bfe2
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/b445bfe2

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: b445bfe242123e544c1fef41e2ab5bdc07dbc56e
Parents: 2b6959b
Author: tiborm <ti...@gmail.com>
Authored: Fri Aug 3 08:13:11 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Fri Aug 3 08:13:11 2018 -0500

----------------------------------------------------------------------
 .../metron-alerts/src/app/pcap/model/pdml.ts    | 12 +++-----
 .../pcap/pcap-packet/pcap-packet.component.ts   |  9 ++----
 .../pcap/pcap-panel/pcap-panel.component.html   |  2 +-
 .../app/pcap/pcap-panel/pcap-panel.component.ts | 30 +++++++++++---------
 .../src/app/pcap/service/pcap.service.ts        | 15 ++++++----
 5 files changed, 33 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/b445bfe2/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts b/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
index 97fc347..34245c6 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
@@ -17,8 +17,7 @@
  */
  
  export class PdmlField {
-  //public $: any[]
-    public name: string;
+  public name: string;
   public fields?: PdmlField[]
   public show: string;
   public value: string;
@@ -26,9 +25,8 @@
 }
 
 export class PdmlProto {
-  //public $: any
-    public name: string;
-    public showname: string;
+  public name: string;
+  public showname: string;
   public fields: PdmlField[]
   
   public static findField(p: PdmlProto, name: string): PdmlField {
@@ -36,10 +34,8 @@ export class PdmlProto {
   }
 }
 
-
 export class PdmlPacket {
-  //public $: any
-    public name: string;
+  public name: string;
   public protos: PdmlProto[]
   public expanded: boolean = false
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/b445bfe2/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.ts
index f1bbe2d..ca28a39 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.ts
@@ -24,16 +24,11 @@ import { PdmlPacket } from '../model/pdml'
   templateUrl: './pcap-packet.component.html',
   styleUrls: ['./pcap-packet.component.scss']
 })
-export class PcapPacketComponent implements OnInit {
+export class PcapPacketComponent {
   @Input() packet: PdmlPacket
 
-  constructor() { }
-
-  ngOnInit() {
-  }
-
   toggle() {
-    this.packet.expanded = !this.packet.expanded
+    this.packet.expanded = !this.packet.expanded;
   }
 
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/b445bfe2/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
index b373914..1fd250f 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
@@ -27,5 +27,5 @@
   <div class="panel-body" *ngIf="pdml">
     <app-pcap-list [packets]="pdml.packets"></app-pcap-list>
   </div>
-
+  <a class="btn btn-primary" [attr.href]="getDownloadUrl()" *ngIf="pdml">Download PCAP</a>
 </div>

http://git-wip-us.apache.org/repos/asf/metron/blob/b445bfe2/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
index fd49ec7..4114e24 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
@@ -15,47 +15,45 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, OnInit, Input } from '@angular/core';
+import { Component, Input } from '@angular/core';
 
-import { PcapService, PcapStatusResponse } from '../service/pcap.service'
-import { PcapRequest } from '../model/pcap.request'
-import { Pdml } from '../model/pdml'
-import {Subscription} from "rxjs/Rx";
+import { PcapService, PcapStatusResponse } from '../service/pcap.service';
+import { PcapRequest } from '../model/pcap.request';
+import { Pdml } from '../model/pdml';
+import { Subscription } from 'rxjs/Rx';
 
 @Component({
   selector: 'app-pcap-panel',
   templateUrl: './pcap-panel.component.html',
   styleUrls: ['./pcap-panel.component.scss']
 })
-export class PcapPanelComponent implements OnInit {
+export class PcapPanelComponent {
 
   @Input() pdml: Pdml = null;
-
   @Input() pcapRequest: PcapRequest;
 
   statusSubscription: Subscription;
   queryRunning: boolean = false;
+  queryId: string;
   progressWidth: number = 0;
   selectedPage: number = 1;
   errorMsg: string;
 
-  constructor(private pcapService: PcapService ) { }
-
-  ngOnInit() {
-  }
+  constructor(private pcapService: PcapService) {}
 
   onSearch(pcapRequest) {
     console.log(pcapRequest);
     this.pdml = null;
     this.progressWidth = 0;
-    this.pcapService.submitRequest(pcapRequest).subscribe(id => {
+    this.pcapService.submitRequest(pcapRequest).subscribe((submitResponse: PcapStatusResponse) => {
+      this.queryId = submitResponse.jobId;
       this.queryRunning = true;
       this.errorMsg = null;
-      this.statusSubscription = this.pcapService.pollStatus(id).subscribe((statusResponse: PcapStatusResponse) => {
+      this.statusSubscription = this.pcapService.pollStatus(submitResponse.jobId).subscribe((statusResponse: PcapStatusResponse) => {
         if ('SUCCEEDED' === statusResponse.jobStatus) {
           this.statusSubscription.unsubscribe();
           this.queryRunning = false;
-          this.pcapService.getPackets(id, this.selectedPage).toPromise().then(pdml => {
+          this.pcapService.getPackets(submitResponse.jobId, this.selectedPage).toPromise().then(pdml => {
             this.pdml = pdml;
           });
         } else if ('FAILED' === statusResponse.jobStatus) {
@@ -72,4 +70,8 @@ export class PcapPanelComponent implements OnInit {
       });
     });
   }
+
+  getDownloadUrl() {
+    return this.pcapService.getDownloadUrl(this.queryId, this.selectedPage);
+  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/b445bfe2/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
index 5f6f33c..ba209c4 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
@@ -16,8 +16,8 @@
  * limitations under the License.
  */
 import {Injectable, NgZone} from '@angular/core';
-import {Observable, Subject} from 'rxjs/Rx';
-import {Http, Headers, RequestOptions, Response} from '@angular/http';
+import {Observable} from 'rxjs/Rx';
+import {Http, Headers, RequestOptions} from '@angular/http';
 import {HttpUtil} from '../../utils/httpUtil';
 
 import 'rxjs/add/operator/map';
@@ -26,9 +26,10 @@ import {PcapRequest} from '../model/pcap.request';
 import {Pdml} from '../model/pdml';
 
 export class PcapStatusResponse {
+  jobId: string;
   jobStatus: string;
   percentComplete: number;
-  pageTotal: number;
+  totalPages: number;
 }
 
 @Injectable()
@@ -46,9 +47,9 @@ export class PcapService {
       });
     }
 
-    public submitRequest(pcapRequest: PcapRequest): Observable<string> {
+    public submitRequest(pcapRequest: PcapRequest): Observable<PcapStatusResponse> {
       return this.http.post('/api/v1/pcap/fixed', pcapRequest, new RequestOptions({headers: new Headers(this.defaultHeaders)}))
-          .map(result => JSON.parse(result.text()).jobId)
+          .map(result => result.json() as PcapStatusResponse)
           .catch(HttpUtil.handleError)
           .onErrorResumeNext();
     }
@@ -65,4 +66,8 @@ export class PcapService {
             .catch(HttpUtil.handleError)
             .onErrorResumeNext();
     }
+
+    public getDownloadUrl(id: string, pageNo: number) {
+      return `/api/v1/pcap/${id}/raw?page=${pageNo}`;
+    }
 }


[21/50] [abbrv] metron git commit: METRON-1683 PCAP UI - Fix the download progress bar (sardell via merrimanr) closes apache/metron#1122

Posted by ni...@apache.org.
METRON-1683 PCAP UI - Fix the download progress bar (sardell via merrimanr) closes apache/metron#1122


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/3e778592
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/3e778592
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/3e778592

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 3e7785920e8f0c5aadf36e05dbcfa69252cc5574
Parents: 037b50b
Author: sardell <sa...@hortonworks.com>
Authored: Tue Aug 7 13:58:08 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Tue Aug 7 13:58:08 2018 -0500

----------------------------------------------------------------------
 .../metron-alerts/src/app/app.component.html    |    2 +-
 .../metron-alerts/src/app/app.component.ts      |    3 +
 .../src/app/pcap/model/pcap-status-response.ts  |    1 +
 .../src/app/pcap/model/pcap.mock.ts             |   39 +
 .../src/app/pcap/model/pdml.mock.ts             |   62 +
 .../metron-alerts/src/app/pcap/model/pdml.ts    |    6 +-
 .../app/pcap/pcap-list/pcap-list.component.html |    2 +-
 .../pcap/pcap-list/pcap-list.component.spec.ts  |   17 +
 .../pcap-packet-line.component.spec.ts          | 1336 +------------
 .../pcap/pcap-packet/pcap-packet.component.html |   11 +-
 .../pcap-packet/pcap-packet.component.spec.ts   | 1278 +------------
 .../pcap/pcap-panel/pcap-panel.component.html   |    6 +-
 .../pcap/pcap-panel/pcap-panel.component.scss   |   18 -
 .../pcap-panel/pcap-panel.component.spec.ts     |  262 ++-
 .../app/pcap/pcap-panel/pcap-panel.component.ts |   52 +-
 .../src/app/pcap/service/pcap.service.spec.ts   | 1809 +-----------------
 .../src/app/pcap/service/pcap.service.ts        |    7 +-
 .../src/environments/environment.e2e.ts         |    3 +-
 .../src/environments/environment.prod.ts        |    3 +-
 .../src/environments/environment.ts             |    3 +-
 metron-interface/metron-alerts/src/index.html   |    2 +-
 21 files changed, 674 insertions(+), 4248 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/app.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/app.component.html b/metron-interface/metron-alerts/src/app/app.component.html
index 1b992ca..c3a5d3c 100644
--- a/metron-interface/metron-alerts/src/app/app.component.html
+++ b/metron-interface/metron-alerts/src/app/app.component.html
@@ -11,7 +11,7 @@
 	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="container-fluid px-0">
+<div class="container-fluid px-0" [class.notransition]="noTransition">
     <nav class="navbar" *ngIf="loggedIn">
         <a class="" href="#">
             <img alt="" src="../assets/images/logo.png" width="135" height="45">

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/app.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/app.component.ts b/metron-interface/metron-alerts/src/app/app.component.ts
index 2a3c15e..52dee23 100644
--- a/metron-interface/metron-alerts/src/app/app.component.ts
+++ b/metron-interface/metron-alerts/src/app/app.component.ts
@@ -17,6 +17,7 @@
  */
 import { Component, OnInit } from '@angular/core';
 import {AuthenticationService} from './service/authentication.service';
+import { environment } from 'environments/environment';
 
 declare var $;
 
@@ -27,11 +28,13 @@ declare var $;
 })
 export class AppComponent implements OnInit {
   loggedIn = false;
+  noTransition = false;
 
   constructor(private authService: AuthenticationService) {
     this.authService.onLoginEvent.subscribe(result => {
       this.loggedIn = result;
     });
+    this.noTransition = environment.noTransition;
   }
 
   ngOnInit(): void {

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/model/pcap-status-response.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap-status-response.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap-status-response.ts
index d4d9a5e..09c4985 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pcap-status-response.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap-status-response.ts
@@ -18,6 +18,7 @@
 export class PcapStatusResponse {
   jobId: string;
   jobStatus: string;
+  description: string;
   percentComplete: number;
   pageTotal: number;
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
new file mode 100644
index 0000000..c867fe9
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
@@ -0,0 +1,39 @@
+/**
+ * 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 { PcapRequest } from './pcap.request';
+import { PcapStatusResponse } from '../model/pcap-status-response';
+
+export const fakePcapRequest = {
+  startTimeMs: 0,
+  endTimeMs: 0,
+  ipSrcAddr: '0.0.0.0',
+  ipSrcPort: '80',
+  ipDstAddr: '0.0.0.0',
+  ipDstPort: '80',
+  protocol: '*',
+  packetFilter: '*',
+  includeReverse: false
+} as PcapRequest;
+
+export const fakePcapStatusResponse = {
+  jobId: 'job_1234567890123_4567',
+  jobStatus: 'SUBMITTED',
+  description: 'Job submitted.',
+  percentComplete: 0.0,
+  pageTotal: 0
+} as PcapStatusResponse;

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/model/pdml.mock.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pdml.mock.ts b/metron-interface/metron-alerts/src/app/pcap/model/pdml.mock.ts
new file mode 100644
index 0000000..6316b92
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pdml.mock.ts
@@ -0,0 +1,62 @@
+/**
+ * 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 { PdmlPacket, PdmlProto, PdmlField } from './pdml';
+
+export const fakePdml = {
+  "pdml": {
+    "$": {
+      "version": "0",
+      "creator": "wireshark/2.4.2",
+      "time": "Tue Mar 27 21:55:25 2018",
+      "capture_file": "./metron-platform/metron-api/src/test/resources/test-tcp-packet.pcap"
+    },
+    "packet": [] as PdmlPacket[]
+  }
+};
+
+export const fakePacket = {
+  "name": '',
+  "expanded": false,
+  "protos": [
+    {
+      "name": "geninfo",
+      "showname": "",
+      "fields": [
+        { "name": "timestamp", "pos": "0", "showname": "Captured Time", "size": "722", "value": "1458240269.373968000", "show": "Mar 17, 2016 18:44:29.373968000 UTC", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField
+      ]
+    } as PdmlProto,
+    {
+      "name": "ip",
+      "showname": "",
+      "fields": [
+        { "name": "ip.proto", "pos": "23", "showname": "Protocol: TCP (6)", "size": "1", "value": "06", "show": "6", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField,
+        { "name": "ip.src", "pos": "26", "showname": "Source: 192.168.66.121 (192.168.66.121)", "size": "4", "value": "c0a84279", "show": "192.168.66.121", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField,
+        { "name": "ip.dst", "pos": "30", "showname": "Destination: 192.168.66.1 (192.168.66.1)", "size": "4", "value": "c0a84201", "show": "192.168.66.1", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField
+      ]
+    } as PdmlProto,
+    {
+      "name": "tcp",
+      "showname": "",
+      "fields": [
+        { "name": "tcp.srcport", "pos": "34", "showname": "Source port: ssh (22)", "size": "2", "value": "0016", "show": "22", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField,
+        { "name": "tcp.dstport", "pos": "36", "showname": "Destination port: 55791 (55791)", "size": "2", "value": "d9ef", "show": "55791", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField
+      ],
+    } as PdmlProto
+  ]
+} as PdmlPacket;

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts b/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
index 34245c6..d6b3b40 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
@@ -30,7 +30,11 @@ export class PdmlProto {
   public fields: PdmlField[]
   
   public static findField(p: PdmlProto, name: string): PdmlField {
-    return p.fields.find(f => f['name'] == name)
+    if (p && p.fields) {
+      return p.fields.find(f => f['name'] == name)
+    } else {
+      return new PdmlField();
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
index 4d74541..5337935 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
@@ -25,7 +25,7 @@
     </thead>
     <tbody>
       <ng-container *ngFor="let packet of packets">
-        <tr (click)="toggle(packet)" app-pcap-packet-line [packet]="packet"></tr>
+        <tr (click)="toggle(packet)" app-pcap-packet-line [packet]="packet" data-qe-id="pcap-packet-line"></tr>
         <tr *ngIf="packet.expanded">
           <td colspan="6" class="pcap-packet">
             <app-pcap-packet [packet]="packet"></app-pcap-packet>

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
index 1d0b996..b1e9611 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
 
 import { PcapListComponent } from './pcap-list.component';
 import { PcapPagination } from '../model/pcap-pagination';
@@ -64,6 +65,7 @@ describe('PcapListComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(PcapListComponent);
     component = fixture.componentInstance;
+    component.packets = [{name: 'test', protos: [], expanded: true}];
     component.pagination = new PcapPagination();
     component.pagination.total = 10;
     fixture.detectChanges();
@@ -78,4 +80,19 @@ describe('PcapListComponent', () => {
     component.onPageChange();
     expect(incrementSpy).toHaveBeenCalled();
   });
+
+  it('should toggle packet details expansion with toggle()', () => {
+    let packet = new PdmlPacket();
+    component.toggle(packet);
+    expect(packet.expanded).toBe(true);
+    component.toggle(packet);
+    expect(packet.expanded).toBe(false);
+  });
+
+  it('should execute toggle() when app-pcap-packet-line is clicked', () => {
+    const packetLineDe  = fixture.debugElement.query(By.css('[data-qe-id="pcap-packet-line"]'));
+    const incrementSpy = spyOn(component, 'toggle');
+    packetLineDe.triggerEventHandler('click', null);
+    expect(incrementSpy).toHaveBeenCalled();
+  });
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
index e01f9c6..d01c0cb 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
@@ -16,9 +16,9 @@
  * limitations under the License.
  */
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { fakePacket } from '../model/pdml.mock';
 
 import { PcapPacketLineComponent } from './pcap-packet-line.component';
-import { PdmlPacket } from '../model/pdml';
 
 describe('PcapPacketLineComponent', () => {
   let component: PcapPacketLineComponent;
@@ -34,1250 +34,102 @@ describe('PcapPacketLineComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(PcapPacketLineComponent);
     component = fixture.componentInstance;
-    component.packet = fakePacket as PdmlPacket;
+    component.packet = fakePacket;
     fixture.detectChanges();
   });
 
-  it('should create', () => {
+  it('should be created', () => {
     expect(component).toBeTruthy();
   });
-});
 
-const fakePacket = {
-  name: "something",
-  expanded: false,
-  protos: [
-    {
-      "name": "geninfo",
-      "pos": "0",
-      "showname": "General information",
-      "size": "722",
-      "hide": null,
-      "fields": [
-        {
-          "name": "num",
-          "pos": "0",
-          "showname": "Number",
-          "size": "722",
-          "value": "1",
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "len",
-          "pos": "0",
-          "showname": "Frame Length",
-          "size": "722",
-          "value": "2d2",
-          "show": "722",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "caplen",
-          "pos": "0",
-          "showname": "Captured Length",
-          "size": "722",
-          "value": "2d2",
-          "show": "722",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "timestamp",
-          "pos": "0",
-          "showname": "Captured Time",
-          "size": "722",
-          "value": "1458240269.373968000",
-          "show": "Mar 17, 2016 18:44:29.373968000 UTC",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "frame",
-      "pos": "0",
-      "showname": "Frame 1: 722 bytes on wire (5776 bits), 722 bytes captured (5776 bits)",
-      "size": "722",
-      "hide": null,
-      "fields": [
-        {
-          "name": "frame.dlt",
-          "pos": "0",
-          "showname": "WTAP_ENCAP: 1",
-          "size": "0",
-          "value": null,
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time",
-          "pos": "0",
-          "showname": "Arrival Time: Mar 17, 2016 18:44:29.373968000 UTC",
-          "size": "0",
-          "value": null,
-          "show": "Mar 17, 2016 18:44:29.373968000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.offset_shift",
-          "pos": "0",
-          "showname": "Time shift for this packet: 0.000000000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "0.000000000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time_epoch",
-          "pos": "0",
-          "showname": "Epoch Time: 1458240269.373968000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "1458240269.373968000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time_delta",
-          "pos": "0",
-          "showname": "Time delta from previous captured frame: 0.000000000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "0.000000000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time_delta_displayed",
-          "pos": "0",
-          "showname": "Time delta from previous displayed frame: 0.000000000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "0.000000000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.time_relative",
-          "pos": "0",
-          "showname": "Time since reference or first frame: 0.000000000 seconds",
-          "size": "0",
-          "value": null,
-          "show": "0.000000000",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.number",
-          "pos": "0",
-          "showname": "Frame Number: 1",
-          "size": "0",
-          "value": null,
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.len",
-          "pos": "0",
-          "showname": "Frame Length: 722 bytes (5776 bits)",
-          "size": "0",
-          "value": null,
-          "show": "722",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.cap_len",
-          "pos": "0",
-          "showname": "Capture Length: 722 bytes (5776 bits)",
-          "size": "0",
-          "value": null,
-          "show": "722",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.marked",
-          "pos": "0",
-          "showname": "Frame is marked: False",
-          "size": "0",
-          "value": null,
-          "show": "0",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.ignored",
-          "pos": "0",
-          "showname": "Frame is ignored: False",
-          "size": "0",
-          "value": null,
-          "show": "0",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "frame.protocols",
-          "pos": "0",
-          "showname": "Protocols in frame: eth:ip:tcp:ssh",
-          "size": "0",
-          "value": null,
-          "show": "eth:ip:tcp:ssh",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "eth",
-      "pos": "0",
-      "showname": "Ethernet II, Src: CadmusCo_96:a4:7a (08:00:27:96:a4:7a), Dst: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
-      "size": "14",
-      "hide": null,
-      "fields": [
-        {
-          "name": "eth.dst",
-          "pos": "0",
-          "showname": "Destination: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
-          "size": "6",
-          "value": "0a0027000000",
-          "show": "0a:00:27:00:00:00",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "eth.addr",
-              "pos": "0",
-              "showname": "Address: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
-              "size": "6",
-              "value": "0a0027000000",
-              "show": "0a:00:27:00:00:00",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "eth.lg",
-              "pos": "0",
-              "showname": ".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)",
-              "size": "3",
-              "value": "1",
-              "show": "1",
-              "unmaskedvalue": "0a0027",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "eth.ig",
-              "pos": "0",
-              "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
-              "size": "3",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "0a0027",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "eth.src",
-          "pos": "6",
-          "showname": "Source: CadmusCo_96:a4:7a (08:00:27:96:a4:7a)",
-          "size": "6",
-          "value": "08002796a47a",
-          "show": "08:00:27:96:a4:7a",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "eth.addr",
-              "pos": "6",
-              "showname": "Address: CadmusCo_96:a4:7a (08:00:27:96:a4:7a)",
-              "size": "6",
-              "value": "08002796a47a",
-              "show": "08:00:27:96:a4:7a",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "eth.lg",
-              "pos": "6",
-              "showname": ".... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)",
-              "size": "3",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "080027",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "eth.ig",
-              "pos": "6",
-              "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
-              "size": "3",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "080027",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "eth.type",
-          "pos": "12",
-          "showname": "Type: IP (0x0800)",
-          "size": "2",
-          "value": "0800",
-          "show": "0x0800",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "ip",
-      "pos": "14",
-      "showname": "Internet Protocol Version 4, Src: 192.168.66.121 (192.168.66.121), Dst: 192.168.66.1 (192.168.66.1)",
-      "size": "20",
-      "hide": null,
-      "fields": [
-        {
-          "name": "ip.version",
-          "pos": "14",
-          "showname": "Version: 4",
-          "size": "1",
-          "value": "45",
-          "show": "4",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.hdr_len",
-          "pos": "14",
-          "showname": "Header length: 20 bytes",
-          "size": "1",
-          "value": "45",
-          "show": "20",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.dsfield",
-          "pos": "15",
-          "showname": "Differentiated Services Field: 0x10 (DSCP 0x04: Unknown DSCP; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))",
-          "size": "1",
-          "value": "10",
-          "show": "16",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "ip.dsfield.dscp",
-              "pos": "15",
-              "showname": "0001 00.. = Differentiated Services Codepoint: Unknown (0x04)",
-              "size": "1",
-              "value": "4",
-              "show": "0x04",
-              "unmaskedvalue": "10",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "ip.dsfield.ecn",
-              "pos": "15",
-              "showname": ".... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)",
-              "size": "1",
-              "value": "0",
-              "show": "0x00",
-              "unmaskedvalue": "10",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "ip.len",
-          "pos": "16",
-          "showname": "Total Length: 708",
-          "size": "2",
-          "value": "02c4",
-          "show": "708",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.id",
-          "pos": "18",
-          "showname": "Identification: 0x7cd9 (31961)",
-          "size": "2",
-          "value": "7cd9",
-          "show": "0x7cd9",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.flags",
-          "pos": "20",
-          "showname": "Flags: 0x02 (Don't Fragment)",
-          "size": "1",
-          "value": "40",
-          "show": "0x02",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "ip.flags.rb",
-              "pos": "20",
-              "showname": "0... .... = Reserved bit: Not set",
-              "size": "1",
-              "value": "40",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "ip.flags.df",
-              "pos": "20",
-              "showname": ".1.. .... = Don't fragment: Set",
-              "size": "1",
-              "value": "40",
-              "show": "1",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "ip.flags.mf",
-              "pos": "20",
-              "showname": "..0. .... = More fragments: Not set",
-              "size": "1",
-              "value": "40",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "ip.frag_offset",
-          "pos": "20",
-          "showname": "Fragment offset: 0",
-          "size": "2",
-          "value": "4000",
-          "show": "0",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.ttl",
-          "pos": "22",
-          "showname": "Time to live: 64",
-          "size": "1",
-          "value": "40",
-          "show": "64",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.proto",
-          "pos": "23",
-          "showname": "Protocol: TCP (6)",
-          "size": "1",
-          "value": "06",
-          "show": "6",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.checksum",
-          "pos": "24",
-          "showname": "Header checksum: 0xb57f [correct]",
-          "size": "2",
-          "value": "b57f",
-          "show": "0xb57f",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "ip.checksum_good",
-              "pos": "24",
-              "showname": "Good: True",
-              "size": "2",
-              "value": "b57f",
-              "show": "1",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "ip.checksum_bad",
-              "pos": "24",
-              "showname": "Bad: False",
-              "size": "2",
-              "value": "b57f",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "ip.src",
-          "pos": "26",
-          "showname": "Source: 192.168.66.121 (192.168.66.121)",
-          "size": "4",
-          "value": "c0a84279",
-          "show": "192.168.66.121",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.addr",
-          "pos": "26",
-          "showname": "Source or Destination Address: 192.168.66.121 (192.168.66.121)",
-          "size": "4",
-          "value": "c0a84279",
-          "show": "192.168.66.121",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.src_host",
-          "pos": "26",
-          "showname": "Source Host: 192.168.66.121",
-          "size": "4",
-          "value": "c0a84279",
-          "show": "192.168.66.121",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.host",
-          "pos": "26",
-          "showname": "Source or Destination Host: 192.168.66.121",
-          "size": "4",
-          "value": "c0a84279",
-          "show": "192.168.66.121",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.dst",
-          "pos": "30",
-          "showname": "Destination: 192.168.66.1 (192.168.66.1)",
-          "size": "4",
-          "value": "c0a84201",
-          "show": "192.168.66.1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.addr",
-          "pos": "30",
-          "showname": "Source or Destination Address: 192.168.66.1 (192.168.66.1)",
-          "size": "4",
-          "value": "c0a84201",
-          "show": "192.168.66.1",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.dst_host",
-          "pos": "30",
-          "showname": "Destination Host: 192.168.66.1",
-          "size": "4",
-          "value": "c0a84201",
-          "show": "192.168.66.1",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "ip.host",
-          "pos": "30",
-          "showname": "Source or Destination Host: 192.168.66.1",
-          "size": "4",
-          "value": "c0a84201",
-          "show": "192.168.66.1",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "tcp",
-      "pos": "34",
-      "showname": "Transmission Control Protocol, Src Port: ssh (22), Dst Port: 55791 (55791), Seq: 1, Ack: 1, Len: 656",
-      "size": "32",
-      "hide": null,
-      "fields": [
-        {
-          "name": "tcp.srcport",
-          "pos": "34",
-          "showname": "Source port: ssh (22)",
-          "size": "2",
-          "value": "0016",
-          "show": "22",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.dstport",
-          "pos": "36",
-          "showname": "Destination port: 55791 (55791)",
-          "size": "2",
-          "value": "d9ef",
-          "show": "55791",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.port",
-          "pos": "34",
-          "showname": "Source or Destination Port: 22",
-          "size": "2",
-          "value": "0016",
-          "show": "22",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.port",
-          "pos": "36",
-          "showname": "Source or Destination Port: 55791",
-          "size": "2",
-          "value": "d9ef",
-          "show": "55791",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.stream",
-          "pos": "34",
-          "showname": "Stream index: 0",
-          "size": "0",
-          "value": null,
-          "show": "0",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.len",
-          "pos": "46",
-          "showname": "TCP Segment Len: 656",
-          "size": "1",
-          "value": "80",
-          "show": "656",
-          "unmaskedvalue": null,
-          "hide": "yes",
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.seq",
-          "pos": "38",
-          "showname": "Sequence number: 1    (relative sequence number)",
-          "size": "4",
-          "value": "12903044",
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.nxtseq",
-          "pos": "34",
-          "showname": "Next sequence number: 657    (relative sequence number)",
-          "size": "0",
-          "value": null,
-          "show": "657",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.ack",
-          "pos": "42",
-          "showname": "Acknowledgment number: 1    (relative ack number)",
-          "size": "4",
-          "value": "8b92f3e7",
-          "show": "1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.hdr_len",
-          "pos": "46",
-          "showname": "Header length: 32 bytes",
-          "size": "1",
-          "value": "80",
-          "show": "32",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.flags",
-          "pos": "46",
-          "showname": "Flags: 0x018 (PSH, ACK)",
-          "size": "2",
-          "value": "18",
-          "show": "0x0018",
-          "unmaskedvalue": "8018",
-          "hide": null,
-          "fields": [
-            {
-              "name": "tcp.flags.res",
-              "pos": "46",
-              "showname": "000. .... .... = Reserved: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "80",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.ns",
-              "pos": "46",
-              "showname": "...0 .... .... = Nonce: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "80",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.cwr",
-              "pos": "47",
-              "showname": ".... 0... .... = Congestion Window Reduced (CWR): Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.ecn",
-              "pos": "47",
-              "showname": ".... .0.. .... = ECN-Echo: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.urg",
-              "pos": "47",
-              "showname": ".... ..0. .... = Urgent: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.ack",
-              "pos": "47",
-              "showname": ".... ...1 .... = Acknowledgment: Set",
-              "size": "1",
-              "value": "1",
-              "show": "1",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.push",
-              "pos": "47",
-              "showname": ".... .... 1... = Push: Set",
-              "size": "1",
-              "value": "1",
-              "show": "1",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.reset",
-              "pos": "47",
-              "showname": ".... .... .0.. = Reset: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.syn",
-              "pos": "47",
-              "showname": ".... .... ..0. = Syn: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.flags.fin",
-              "pos": "47",
-              "showname": ".... .... ...0 = Fin: Not set",
-              "size": "1",
-              "value": "0",
-              "show": "0",
-              "unmaskedvalue": "18",
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "tcp.window_size_value",
-          "pos": "48",
-          "showname": "Window size value: 501",
-          "size": "2",
-          "value": "01f5",
-          "show": "501",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.window_size",
-          "pos": "48",
-          "showname": "Calculated window size: 501",
-          "size": "2",
-          "value": "01f5",
-          "show": "501",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.window_size_scalefactor",
-          "pos": "48",
-          "showname": "Window size scaling factor: -1 (unknown)",
-          "size": "2",
-          "value": "01f5",
-          "show": "-1",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        },
-        {
-          "name": "tcp.checksum",
-          "pos": "50",
-          "showname": "Checksum: 0x0882 [validation disabled]",
-          "size": "2",
-          "value": "0882",
-          "show": "0x0882",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "tcp.checksum_good",
-              "pos": "50",
-              "showname": "Good Checksum: False",
-              "size": "2",
-              "value": "0882",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            },
-            {
-              "name": "tcp.checksum_bad",
-              "pos": "50",
-              "showname": "Bad Checksum: False",
-              "size": "2",
-              "value": "0882",
-              "show": "0",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "tcp.options",
-          "pos": "54",
-          "showname": "Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps",
-          "size": "12",
-          "value": "0101080a0014f4f811bdb98f",
-          "show": "01:01:08:0a:00:14:f4:f8:11:bd:b9:8f",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "",
-              "pos": "54",
-              "showname": null,
-              "size": "1",
-              "value": "01",
-              "show": "No-Operation (NOP)",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": [
-                {
-                  "name": "ip.opt.type",
-                  "pos": "54",
-                  "showname": "Type: 1",
-                  "size": "1",
-                  "value": "01",
-                  "show": "1",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": [
-                    {
-                      "name": "ip.opt.type.copy",
-                      "pos": "54",
-                      "showname": "0... .... = Copy on fragmentation: No",
-                      "size": "1",
-                      "value": "0",
-                      "show": "0",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    },
-                    {
-                      "name": "ip.opt.type.class",
-                      "pos": "54",
-                      "showname": ".00. .... = Class: Control (0)",
-                      "size": "1",
-                      "value": "0",
-                      "show": "0",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    },
-                    {
-                      "name": "ip.opt.type.number",
-                      "pos": "54",
-                      "showname": "...0 0001 = Number: No-Operation (NOP) (1)",
-                      "size": "1",
-                      "value": "1",
-                      "show": "1",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    }
-                  ],
-                  "protos": null
-                }
-              ],
-              "protos": null
-            },
-            {
-              "name": "",
-              "pos": "55",
-              "showname": null,
-              "size": "1",
-              "value": "01",
-              "show": "No-Operation (NOP)",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": [
-                {
-                  "name": "ip.opt.type",
-                  "pos": "55",
-                  "showname": "Type: 1",
-                  "size": "1",
-                  "value": "01",
-                  "show": "1",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": [
-                    {
-                      "name": "ip.opt.type.copy",
-                      "pos": "55",
-                      "showname": "0... .... = Copy on fragmentation: No",
-                      "size": "1",
-                      "value": "0",
-                      "show": "0",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    },
-                    {
-                      "name": "ip.opt.type.class",
-                      "pos": "55",
-                      "showname": ".00. .... = Class: Control (0)",
-                      "size": "1",
-                      "value": "0",
-                      "show": "0",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    },
-                    {
-                      "name": "ip.opt.type.number",
-                      "pos": "55",
-                      "showname": "...0 0001 = Number: No-Operation (NOP) (1)",
-                      "size": "1",
-                      "value": "1",
-                      "show": "1",
-                      "unmaskedvalue": "01",
-                      "hide": null,
-                      "fields": null,
-                      "protos": null
-                    }
-                  ],
-                  "protos": null
-                }
-              ],
-              "protos": null
-            },
-            {
-              "name": "",
-              "pos": "56",
-              "showname": null,
-              "size": "10",
-              "value": "080a0014f4f811bdb98f",
-              "show": "Timestamps: TSval 1373432, TSecr 297646479",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": [
-                {
-                  "name": "tcp.option_kind",
-                  "pos": "56",
-                  "showname": "Kind: Timestamp (8)",
-                  "size": "1",
-                  "value": "08",
-                  "show": "8",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": null,
-                  "protos": null
-                },
-                {
-                  "name": "tcp.option_len",
-                  "pos": "57",
-                  "showname": "Length: 10",
-                  "size": "1",
-                  "value": "0a",
-                  "show": "10",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": null,
-                  "protos": null
-                },
-                {
-                  "name": "tcp.options.timestamp.tsval",
-                  "pos": "58",
-                  "showname": "Timestamp value: 1373432",
-                  "size": "4",
-                  "value": "0014f4f8",
-                  "show": "1373432",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": null,
-                  "protos": null
-                },
-                {
-                  "name": "tcp.options.timestamp.tsecr",
-                  "pos": "62",
-                  "showname": "Timestamp echo reply: 297646479",
-                  "size": "4",
-                  "value": "11bdb98f",
-                  "show": "297646479",
-                  "unmaskedvalue": null,
-                  "hide": null,
-                  "fields": null,
-                  "protos": null
-                }
-              ],
-              "protos": null
-            }
-          ],
-          "protos": null
-        },
-        {
-          "name": "tcp.analysis",
-          "pos": "34",
-          "showname": "SEQ/ACK analysis",
-          "size": "0",
-          "value": "",
-          "show": "",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": [
-            {
-              "name": "tcp.analysis.bytes_in_flight",
-              "pos": "34",
-              "showname": "Bytes in flight: 656",
-              "size": "0",
-              "value": null,
-              "show": "656",
-              "unmaskedvalue": null,
-              "hide": null,
-              "fields": null,
-              "protos": null
-            }
-          ],
-          "protos": null
-        }
-      ]
-    },
-    {
-      "name": "ssh",
-      "pos": "66",
-      "showname": "SSH Protocol",
-      "size": "656",
-      "hide": null,
-      "fields": [
-        {
-          "name": "ssh.encrypted_packet",
-          "pos": "66",
-          "showname": "Encrypted Packet: 5b2bfe1fa006867834412184af9f5b239737763adead7140...",
-          "size": "656",
-          "value": "5b2bfe1fa006867834412184af9f5b239737763adead71408fc01b88e548b2cc86f341a39771c6ed16f2b0bb3e6ab6109e73c7d68ca7545852f91930e4633c17fb9dc7aa794b0d820d0fa3ac65bf0f013e5449d5953d2943506657e2b76b548e67f5c9ce1a4c53db1b52465bde7208baf11f3fe01975418b4db186a38ad32947d1908b62e532da4b729353a932423d25f5f734484076aa4143c6a74937a4ea49448e261ae1ecb6b07bbdf5c98d0855940a19018c88263b8936f7b3e9a4b6cd98090fa10a10e37ad20fe5d833071ad6d5b2886ba85ec72affb83e316443dbe29dbf643e6aa05595c90765cf85f6da55cc1c09d8dccc7d05da022429ad602a559a044b7e2663b0c153a3011bf824ef8d1fa56cba957c5f5d2276a1c9e92de65782f406848c6e20f634c5d1fea843a8bf1a4058e85553f5838f7299958fbf54be84e46c5a3c3965f8bed7fe03a9a1168a892e0073adeb54deca171a318d11fc1a8179f91632310213da327965a40bc6fe18eae55e8da6b57d7ef9f3a05b42381bcb3db8f8efd6d0c638a2cdd46efb0b8f1274e98672f644b2275947e626b02e5166f86c2dd4a67b81e213f8c064927a396815db589f10e5e521ffedb13f8edbe2de01c6fc8bf0e12c82212e497794aa045e9b6fcca83b4cad0a3b5e6ca2d1feaf8887b4d64f22989396e
 cfa8f7f1835eed422580505109fed36797bdc10a9168d5148daef6a8710c3df1d6366c9763ab4ebd359d86a8ea14819252fb52ba423422d1f60b0179316b3729e479ba07e88cb886938c8daae65d470dde91e5336e0fc4221a72cc49057d878aa5924875d097483e94bc44a4ea93aee8780e56c50a405932841f50da156e1f90559a7c4f76999442fb433a26fc703dea656bbe03790ac3c9c5318ff5f81d87d483524bbfe7ff167",
-          "show": "5b:2b:fe:1f:a0:06:86:78:34:41:21:84:af:9f:5b:23:97:37:76:3a:de:ad:71:40:8f:c0:1b:88:e5:48:b2:cc:86:f3:41:a3:97:71:c6:ed:16:f2:b0:bb:3e:6a:b6:10:9e:73:c7:d6:8c:a7:54:58:52:f9:19:30:e4:63:3c:17:fb:9d:c7:aa:79:4b:0d:82:0d:0f:a3:ac:65:bf:0f:01:3e:54:49:d5:95:3d:29:43:50:66:57:e2:b7:6b:54:8e:67:f5:c9:ce:1a:4c:53:db:1b:52:46:5b:de:72:08:ba:f1:1f:3f:e0:19:75:41:8b:4d:b1:86:a3:8a:d3:29:47:d1:90:8b:62:e5:32:da:4b:72:93:53:a9:32:42:3d:25:f5:f7:34:48:40:76:aa:41:43:c6:a7:49:37:a4:ea:49:44:8e:26:1a:e1:ec:b6:b0:7b:bd:f5:c9:8d:08:55:94:0a:19:01:8c:88:26:3b:89:36:f7:b3:e9:a4:b6:cd:98:09:0f:a1:0a:10:e3:7a:d2:0f:e5:d8:33:07:1a:d6:d5:b2:88:6b:a8:5e:c7:2a:ff:b8:3e:31:64:43:db:e2:9d:bf:64:3e:6a:a0:55:95:c9:07:65:cf:85:f6:da:55:cc:1c:09:d8:dc:cc:7d:05:da:02:24:29:ad:60:2a:55:9a:04:4b:7e:26:63:b0:c1:53:a3:01:1b:f8:24:ef:8d:1f:a5:6c:ba:95:7c:5f:5d:22:76:a1:c9:e9:2d:e6:57:82:f4:06:84:8c:6e:20:f6:34:c5:d1:fe:a8:43:a8:bf:1a:40:58:e8:55:53:f5:83:8f:72:99:95:8f:bf:54:be:84:e4:6c:5a:3c:39:65:
 f8:be:d7:fe:03:a9:a1:16:8a:89:2e:00:73:ad:eb:54:de:ca:17:1a:31:8d:11:fc:1a:81:79:f9:16:32:31:02:13:da:32:79:65:a4:0b:c6:fe:18:ea:e5:5e:8d:a6:b5:7d:7e:f9:f3:a0:5b:42:38:1b:cb:3d:b8:f8:ef:d6:d0:c6:38:a2:cd:d4:6e:fb:0b:8f:12:74:e9:86:72:f6:44:b2:27:59:47:e6:26:b0:2e:51:66:f8:6c:2d:d4:a6:7b:81:e2:13:f8:c0:64:92:7a:39:68:15:db:58:9f:10:e5:e5:21:ff:ed:b1:3f:8e:db:e2:de:01:c6:fc:8b:f0:e1:2c:82:21:2e:49:77:94:aa:04:5e:9b:6f:cc:a8:3b:4c:ad:0a:3b:5e:6c:a2:d1:fe:af:88:87:b4:d6:4f:22:98:93:96:ec:fa:8f:7f:18:35:ee:d4:22:58:05:05:10:9f:ed:36:79:7b:dc:10:a9:16:8d:51:48:da:ef:6a:87:10:c3:df:1d:63:66:c9:76:3a:b4:eb:d3:59:d8:6a:8e:a1:48:19:25:2f:b5:2b:a4:23:42:2d:1f:60:b0:17:93:16:b3:72:9e:47:9b:a0:7e:88:cb:88:69:38:c8:da:ae:65:d4:70:dd:e9:1e:53:36:e0:fc:42:21:a7:2c:c4:90:57:d8:78:aa:59:24:87:5d:09:74:83:e9:4b:c4:4a:4e:a9:3a:ee:87:80:e5:6c:50:a4:05:93:28:41:f5:0d:a1:56:e1:f9:05:59:a7:c4:f7:69:99:44:2f:b4:33:a2:6f:c7:03:de:a6:56:bb:e0:37:90:ac:3c:9c:53:18:ff:5f:81:d8:7d:48:35:24:bb:fe:7f:f1:67",
-          "unmaskedvalue": null,
-          "hide": null,
-          "fields": null,
-          "protos": null
-        }
-      ]
-    }
-  ]
-};
+  it('should extract timestamp fields', () => {
+    expect(component.ip.timestamp).toEqual({ 
+      "name": "timestamp", 
+      "pos": "0", 
+      "showname": 
+      "Captured Time", 
+      "size": "722", 
+      "value": "1458240269.373968000", 
+      "show": "Mar 17, 2016 18:44:29.373968000 UTC", 
+      "unmaskedvalue": null, 
+      "hide": null, 
+      "fields": null, 
+      "protos": null
+    });
+  });
+
+  it('should extract ipSrcAddr fields', () => {
+    expect(component.ip.ipSrcAddr).toEqual({ 
+      "name": "ip.src", 
+      "pos": "26", 
+      "showname": "Source: 192.168.66.121 (192.168.66.121)", 
+      "size": "4", 
+      "value": "c0a84279", 
+      "show": "192.168.66.121", 
+      "unmaskedvalue": null, 
+      "hide": null, 
+      "fields": null, 
+      "protos": null 
+    });
+  });
+
+  it('should extract ipSrcPort fields', () => {
+    expect(component.ip.ipSrcPort).toEqual({ 
+      "name": "tcp.srcport", 
+      "pos": "34", 
+      "showname": "Source port: ssh (22)", 
+      "size": "2", 
+      "value": "0016", 
+      "show": "22", 
+      "unmaskedvalue": null, 
+      "hide": null, 
+      "fields": null, 
+      "protos": null 
+    });
+  });
+
+  it('should extract ipDestAddr fields', () => {
+    expect(component.ip.ipDestAddr).toEqual({ 
+      "name": "ip.dst", 
+      "pos": "30", 
+      "showname": "Destination: 192.168.66.1 (192.168.66.1)", 
+      "size": "4", 
+      "value": "c0a84201", 
+      "show": "192.168.66.1", 
+      "unmaskedvalue": null, 
+      "hide": null, 
+      "fields": null, 
+      "protos": null 
+    });
+  });
+  
+  it('should extract ipDestPort fields', () => {
+    expect(component.ip.ipDestPort).toEqual({ 
+      "name": "tcp.dstport", 
+      "pos": "36", 
+      "showname": "Destination port: 55791 (55791)", 
+      "size": "2", 
+      "value": "d9ef", 
+      "show": "55791", 
+      "unmaskedvalue": null, 
+      "hide": null, 
+      "fields": null, 
+      "protos": null 
+    });
+  });
+  
+  it('should extract protocol fields', () => {
+    expect(component.ip.protocol).toEqual({ 
+      "name": "ip.proto", 
+      "pos": "23", 
+      "showname": "Protocol: TCP (6)", 
+      "size": "1", 
+      "value": "06", 
+      "show": "6", 
+      "unmaskedvalue": null, 
+      "hide": null, 
+      "fields": null, 
+      "protos": null 
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/3e778592/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
index 750c315..f7d9dbe 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
@@ -11,11 +11,12 @@
 	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="proto" *ngFor="let proto of packet.protos" (click)="proto.expanded = !proto.expanded">
-  <div class="proto-header">{{ proto.showname }}</div>
-  <div class="proto-fields" *ngIf="proto.expanded">
-    <div *ngFor="let field of proto.fields">
-      <span class="field-name">{{ field.name}}</span> {{ field.showname}}
+<div data-qe-id="proto" class="proto" *ngFor="let proto of packet.protos" (click)="proto.expanded = !proto.expanded">
+  <div data-qe-id="proto-showname" class="proto-header">{{ proto.showname }}</div>
+  <div data-qe-id="proto-fields" class="proto-fields" *ngIf="proto.expanded">
+    <div data-qe-id="proto-field" *ngFor="let field of proto.fields">
+      <span data-qe-id="proto-field-name" class="field-name">{{ field.name }}</span>
+      <span data-qe-id="proto-field-showname">{{ field.showname }}</span>
     </div>
   </div>
 </div>


[12/50] [abbrv] metron git commit: METRON-1671 Create PCAP UI (tiborm via merrimanr) closes apache/metron#1103

Posted by ni...@apache.org.
METRON-1671 Create PCAP UI (tiborm via merrimanr) closes apache/metron#1103


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/d5eb56a9
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/d5eb56a9
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/d5eb56a9

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: d5eb56a967ac7b925d0a843c027a2b0dde66de06
Parents: 7dff4de
Author: tiborm <ti...@gmail.com>
Authored: Wed Aug 1 11:31:50 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Wed Aug 1 11:31:50 2018 -0500

----------------------------------------------------------------------
 .../metron-alerts/package-lock.json             | 3719 +++++++++---------
 .../metron-alerts/src/app/app-routing.module.ts |    3 +-
 .../metron-alerts/src/app/app.component.html    |    8 +
 .../metron-alerts/src/app/app.component.scss    |   18 +
 .../metron-alerts/src/app/app.module.ts         |    6 +-
 .../src/app/pcap/model/pcap.request.ts          |   29 +
 .../metron-alerts/src/app/pcap/model/pdml.ts    |   50 +
 .../pcap-filters/pcap-filters.component.html    |   52 +
 .../pcap-filters/pcap-filters.component.scss    |   69 +
 .../pcap-filters/pcap-filters.component.spec.ts |   46 +
 .../pcap/pcap-filters/pcap-filters.component.ts |   41 +
 .../app/pcap/pcap-list/pcap-list.component.html |   37 +
 .../app/pcap/pcap-list/pcap-list.component.scss |   21 +
 .../pcap/pcap-list/pcap-list.component.spec.ts  |   70 +
 .../app/pcap/pcap-list/pcap-list.component.ts   |   39 +
 .../pcap-packet-line.component.html             |   19 +
 .../pcap-packet-line.component.scss             |   17 +
 .../pcap-packet-line.component.spec.ts          | 1283 ++++++
 .../pcap-packet-line.component.ts               |   55 +
 .../pcap/pcap-packet/pcap-packet.component.html |   21 +
 .../pcap/pcap-packet/pcap-packet.component.scss |   36 +
 .../pcap-packet/pcap-packet.component.spec.ts   | 1283 ++++++
 .../pcap/pcap-packet/pcap-packet.component.ts   |   39 +
 .../pcap/pcap-panel/pcap-panel.component.html   |   31 +
 .../pcap/pcap-panel/pcap-panel.component.scss   |   52 +
 .../pcap-panel/pcap-panel.component.spec.ts     |   68 +
 .../app/pcap/pcap-panel/pcap-panel.component.ts |   75 +
 .../metron-alerts/src/app/pcap/pcap.module.ts   |   50 +
 .../metron-alerts/src/app/pcap/pcap.routing.ts  |   27 +
 .../src/app/pcap/service/pcap.service.spec.ts   | 1752 +++++++++
 .../src/app/pcap/service/pcap.service.ts        |   68 +
 31 files changed, 7304 insertions(+), 1780 deletions(-)
----------------------------------------------------------------------



[42/50] [abbrv] metron git commit: METRON-1554 Pcap Query Panel (merrimanr) closes apache/metron#1169

Posted by ni...@apache.org.
METRON-1554 Pcap Query Panel (merrimanr) closes apache/metron#1169


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/9fdccba3
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/9fdccba3
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/9fdccba3

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 9fdccba371c1f1e0dcb79e00a7207a934b79b64c
Parents: 308c2b2 14e80b3
Author: merrimanr <me...@gmail.com>
Authored: Fri Aug 17 10:26:51 2018 -0500
Committer: merrimanr <me...@gmail.com>
Committed: Fri Aug 17 10:28:09 2018 -0500

----------------------------------------------------------------------
 dependencies_with_url.csv                       |    4 +
 metron-deployment/amazon-ec2/conf/defaults.yml  |    1 -
 metron-deployment/amazon-ec2/playbook.yml       |    2 +-
 .../ansible/playbooks/metron_install.yml        |    7 -
 .../roles/ambari_config/vars/single_node_vm.yml |    1 +
 .../roles/metron_pcapservice/defaults/main.yml  |   28 -
 .../roles/metron_pcapservice/meta/main.yml      |   19 -
 .../metron_pcapservice/tasks/config-hbase.yml   |   26 -
 .../roles/metron_pcapservice/tasks/main.yml     |   25 -
 .../metron_pcapservice/tasks/pcapservice.yml    |   30 -
 .../templates/metron-pcapservice-logrotate.yml  |   27 -
 .../metron_pcapservice/templates/pcapservice    |   84 -
 .../ubuntu14/ansible/inventory/hosts            |    4 -
 .../CURRENT/configuration/metron-rest-env.xml   |   48 +
 .../package/scripts/params/params_linux.py      |   16 +
 .../package/scripts/params/status_params.py     |    7 +
 .../CURRENT/package/scripts/rest_commands.py    |   62 +
 .../CURRENT/package/scripts/rest_master.py      |    9 +
 .../METRON/CURRENT/package/templates/metron.j2  |    6 +
 .../METRON/CURRENT/themes/metron_theme.json     |   30 +
 .../docker/rpm-docker/SPECS/metron.spec         |    1 +
 .../metron-alerts/package-lock.json             | 3719 +++++++++---------
 .../metron-alerts/src/app/app-routing.module.ts |    3 +-
 .../metron-alerts/src/app/app.component.html    |   10 +-
 .../metron-alerts/src/app/app.component.scss    |   18 +
 .../metron-alerts/src/app/app.component.ts      |    3 +
 .../metron-alerts/src/app/app.module.ts         |    6 +-
 .../src/app/pcap/model/pcap-pagination.ts       |   21 +
 .../src/app/pcap/model/pcap-status-response.ts  |   24 +
 .../src/app/pcap/model/pcap.mock.ts             |   39 +
 .../src/app/pcap/model/pcap.request.ts          |   29 +
 .../src/app/pcap/model/pdml.mock.ts             |   93 +
 .../metron-alerts/src/app/pcap/model/pdml.ts    |   50 +
 .../pcap-filters/pcap-filters.component.html    |   62 +
 .../pcap-filters/pcap-filters.component.scss    |   69 +
 .../pcap-filters/pcap-filters.component.spec.ts |  463 +++
 .../pcap/pcap-filters/pcap-filters.component.ts |   77 +
 .../app/pcap/pcap-list/pcap-list.component.html |   38 +
 .../app/pcap/pcap-list/pcap-list.component.scss |   21 +
 .../pcap/pcap-list/pcap-list.component.spec.ts  |   98 +
 .../app/pcap/pcap-list/pcap-list.component.ts   |   43 +
 .../pcap-packet-line.component.html             |   19 +
 .../pcap-packet-line.component.scss             |   17 +
 .../pcap-packet-line.component.spec.ts          |  180 +
 .../pcap-packet-line.component.ts               |   56 +
 .../pcap/pcap-packet/pcap-packet.component.html |   22 +
 .../pcap/pcap-packet/pcap-packet.component.scss |   36 +
 .../pcap-packet/pcap-packet.component.spec.ts   |   79 +
 .../pcap/pcap-packet/pcap-packet.component.ts   |   34 +
 .../pcap-pagination.component.html              |   19 +
 .../pcap-pagination.component.scss              |   40 +
 .../pcap-pagination.component.spec.ts           |   78 +
 .../pcap-pagination.component.ts                |   41 +
 .../pcap/pcap-panel/pcap-panel.component.html   |   31 +
 .../pcap/pcap-panel/pcap-panel.component.scss   |   67 +
 .../pcap-panel/pcap-panel.component.spec.ts     |  557 +++
 .../app/pcap/pcap-panel/pcap-panel.component.ts |  164 +
 .../metron-alerts/src/app/pcap/pcap.module.ts   |   54 +
 .../metron-alerts/src/app/pcap/pcap.routing.ts  |   27 +
 .../src/app/pcap/service/pcap.service.spec.ts   |  197 +
 .../src/app/pcap/service/pcap.service.ts        |   88 +
 .../shared/directives/alert-search.directive.ts |    2 +-
 .../src/environments/environment.e2e.ts         |    3 +-
 .../src/environments/environment.prod.ts        |    3 +-
 .../src/environments/environment.ts             |    3 +-
 metron-interface/metron-alerts/src/index.html   |    2 +-
 metron-interface/metron-rest-client/pom.xml     |   10 +
 .../apache/metron/rest/model/PcapResponse.java  |   38 +
 .../apache/metron/rest/model/pcap/Field.java    |  154 +
 .../rest/model/pcap/FixedPcapOptions.java       |   42 +
 .../rest/model/pcap/FixedPcapRequest.java       |  116 +
 .../apache/metron/rest/model/pcap/Packet.java   |   53 +
 .../metron/rest/model/pcap/PcapRequest.java     |   83 +
 .../metron/rest/model/pcap/PcapStatus.java      |   87 +
 .../org/apache/metron/rest/model/pcap/Pdml.java |   98 +
 .../apache/metron/rest/model/pcap/Proto.java    |  108 +
 .../rest/model/pcap/QueryPcapOptions.java       |   35 +
 .../rest/model/pcap/QueryPcapRequest.java       |   41 +
 metron-interface/metron-rest/README.md          |  105 +
 metron-interface/metron-rest/pom.xml            |   44 +-
 .../src/main/config/rest_application.yml        |   11 +-
 .../apache/metron/rest/MetronRestConstants.java |    9 +
 .../apache/metron/rest/config/PcapConfig.java   |   49 +
 .../metron/rest/config/PcapJobSupplier.java     |   54 +
 .../metron/rest/controller/PcapController.java  |  180 +
 .../apache/metron/rest/service/PcapService.java |  116 +
 .../rest/service/impl/PcapServiceImpl.java      |  299 ++
 .../service/impl/PcapToPdmlScriptWrapper.java   |   59 +
 .../src/main/resources/application.yml          |    9 +-
 .../metron-rest/src/main/scripts/metron-rest.sh |    2 +-
 .../src/main/scripts/pcap_to_pdml.sh            |   19 +
 .../apache/metron/rest/config/TestConfig.java   |   47 +-
 .../PcapControllerIntegrationTest.java          |  474 +++
 .../apache/metron/rest/mock/MockPcapJob.java    |  161 +
 .../metron/rest/mock/MockPcapJobSupplier.java   |   36 +
 .../rest/mock/MockPcapToPdmlScriptWrapper.java  |   55 +
 .../rest/service/impl/PcapServiceImplTest.java  |  746 ++++
 metron-interface/pom.xml                        |    3 +
 metron-platform/metron-api/README.md            |   65 -
 metron-platform/metron-api/pom.xml              |  269 --
 .../apache/metron/api/ConfigurationManager.java |  135 -
 .../api/helper/service/PcapServiceCli.java      |  169 -
 .../metron/pcapservice/ConfigurationUtil.java   |   64 -
 .../pcapservice/PcapReceiverImplRestEasy.java   |  299 --
 .../metron/pcapservice/PcapsResponse.java       |  118 -
 .../metron/pcapservice/RestTestingUtil.java     |  329 --
 .../pcapservice/rest/JettyServiceRunner.java    |   44 -
 .../metron/pcapservice/rest/PcapService.java    |   56 -
 .../src/main/resources/META-INF/LICENSE         |  605 ---
 .../src/main/resources/META-INF/NOTICE          |   39 -
 .../pcapservice/ConfigurationUtilTest.java      |   43 -
 .../PcapReceiverImplRestEasyTest.java           |  255 --
 .../src/test/resources/log4j.properties         |   24 -
 .../src/test/resources/test-tcp-packet.pcap     |  Bin 144 -> 0 bytes
 .../common/configuration/ConfigOption.java      |   71 +
 .../apache/metron/common/utils/HDFSUtils.java   |   53 +-
 .../common/configuration/ConfigOptionTest.java  |  112 +
 .../metron/common/utils/HDFSUtilsTest.java      |   59 +
 metron-platform/metron-job/README.md            |   26 +
 .../metron-job_state_statechart_diagram.svg     |   14 +
 .../metron-job_state_statechart_diagram.xml     |   14 +
 metron-platform/metron-job/pom.xml              |   39 +
 .../java/org/apache/metron/job/Finalizer.java   |   38 +
 .../org/apache/metron/job/JobException.java     |   31 +
 .../apache/metron/job/JobNotFoundException.java |   30 +
 .../java/org/apache/metron/job/JobStatus.java   |  124 +
 .../java/org/apache/metron/job/Pageable.java    |   38 +
 .../apache/metron/job/RuntimeJobException.java  |   30 +
 .../java/org/apache/metron/job/Statusable.java  |   82 +
 .../metron/job/manager/InMemoryJobManager.java  |   94 +
 .../apache/metron/job/manager/JobManager.java   |   41 +
 .../org/apache/metron/job/JobStatusTest.java    |   55 +
 .../job/manager/InMemoryJobManagerTest.java     |  208 +
 metron-platform/metron-pcap-backend/README.md   |    4 +
 metron-platform/metron-pcap-backend/pom.xml     |    6 +
 .../src/main/config/pcap.properties             |    2 +-
 .../org/apache/metron/pcap/query/CliConfig.java |  128 -
 .../org/apache/metron/pcap/query/CliParser.java |   55 +-
 .../metron/pcap/query/FixedCliConfig.java       |   50 -
 .../metron/pcap/query/FixedCliParser.java       |   10 +-
 .../org/apache/metron/pcap/query/PcapCli.java   |  112 +-
 .../metron/pcap/query/QueryCliConfig.java       |   34 -
 .../metron/pcap/query/QueryCliParser.java       |   10 +-
 .../apache/metron/pcap/query/ResultsWriter.java |   48 -
 .../apache/metron/pcap/FixedPcapFilterTest.java |  286 --
 .../org/apache/metron/pcap/PcapJobTest.java     |   49 -
 .../apache/metron/pcap/QueryPcapFilterTest.java |  228 --
 .../PcapTopologyIntegrationTest.java            |  847 ++--
 .../apache/metron/pcap/query/PcapCliTest.java   |  229 +-
 metron-platform/metron-pcap/pom.xml             |    7 +-
 .../java/org/apache/metron/pcap/PcapHelper.java |   18 +-
 .../java/org/apache/metron/pcap/PcapPages.java  |   86 +
 .../metron/pcap/config/FixedPcapConfig.java     |   46 +
 .../apache/metron/pcap/config/PcapConfig.java   |  156 +
 .../metron/pcap/config/PcapGlobalDefaults.java  |   29 +
 .../apache/metron/pcap/config/PcapOptions.java  |   70 +
 .../metron/pcap/config/QueryPcapConfig.java     |   33 +
 .../pcap/filter/fixed/FixedPcapFilter.java      |   14 +-
 .../pcap/filter/query/QueryPcapFilter.java      |   17 +-
 .../metron/pcap/finalizer/PcapCliFinalizer.java |   55 +
 .../metron/pcap/finalizer/PcapFinalizer.java    |  190 +
 .../pcap/finalizer/PcapFinalizerStrategies.java |   47 +
 .../pcap/finalizer/PcapRestFinalizer.java       |   57 +
 .../metron/pcap/mr/OutputDirFormatter.java      |   37 +
 .../java/org/apache/metron/pcap/mr/PcapJob.java |  426 +-
 .../metron/pcap/writer/PcapResultsWriter.java   |   75 +
 .../org/apache/metron/pcap/PcapHelperTest.java  |   76 +
 .../org/apache/metron/pcap/PcapPagesTest.java   |   76 +
 .../pcap/filter/fixed/FixedPcapFilterTest.java  |  271 +-
 .../pcap/filter/query/QueryPcapFilterTest.java  |  207 +-
 .../metron/pcap/mr/OutputDirFormatterTest.java  |   62 +
 .../org/apache/metron/pcap/mr/PcapJobTest.java  |  323 ++
 metron-platform/pom.xml                         |    2 +-
 site-book/bin/generate-md.sh                    |    2 +
 174 files changed, 12620 insertions(+), 6018 deletions(-)
----------------------------------------------------------------------



[25/50] [abbrv] metron git commit: METRON-1723 PCAP UI - Unable to select/copy from packets details in PCAP query panel (sardell via merrimanr) closes apache/metron#1139

Posted by ni...@apache.org.
METRON-1723 PCAP UI - Unable to select/copy from packets details in PCAP query panel (sardell via merrimanr) closes apache/metron#1139


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/5b3e2c33
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/5b3e2c33
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/5b3e2c33

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 5b3e2c337b3772bcd7d18cedac75877bbe6f7735
Parents: 52de126
Author: sardell <sa...@hortonworks.com>
Authored: Thu Aug 9 07:44:15 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Thu Aug 9 07:44:15 2018 -0500

----------------------------------------------------------------------
 .../src/app/pcap/pcap-packet/pcap-packet.component.html          | 2 +-
 .../src/app/pcap/pcap-packet/pcap-packet.component.scss          | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/5b3e2c33/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
index f7d9dbe..dd9afa1 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
@@ -13,7 +13,7 @@
   -->
 <div data-qe-id="proto" class="proto" *ngFor="let proto of packet.protos" (click)="proto.expanded = !proto.expanded">
   <div data-qe-id="proto-showname" class="proto-header">{{ proto.showname }}</div>
-  <div data-qe-id="proto-fields" class="proto-fields" *ngIf="proto.expanded">
+  <div data-qe-id="proto-fields" class="proto-fields" (click)="$event.stopPropagation()" *ngIf="proto.expanded">
     <div data-qe-id="proto-field" *ngFor="let field of proto.fields">
       <span data-qe-id="proto-field-name" class="field-name">{{ field.name }}</span>
       <span data-qe-id="proto-field-showname">{{ field.showname }}</span>

http://git-wip-us.apache.org/repos/asf/metron/blob/5b3e2c33/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.scss
index e9d4662..02ce7b6 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.scss
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.scss
@@ -27,10 +27,10 @@
 }
 
 .proto-fields {
-  margin-left: 1em;
+  cursor: text;
+  padding-left: 1em;
 }
 
-
 .field-name {
   display: none
 }


[40/50] [abbrv] metron git commit: METRON-1727: Alerts are not populated on the alerts UI after enabling X-pack for Elastic search (MohanDV via mmiklavc) closes apache/metron#1141

Posted by ni...@apache.org.
METRON-1727: Alerts are not populated on the alerts UI after enabling X-pack for Elastic search (MohanDV via mmiklavc) closes apache/metron#1141


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/308c2b27
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/308c2b27
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/308c2b27

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 308c2b27b9b234c0c84c343dfc45b3617b027e64
Parents: 2b0060b
Author: MohanDV <mo...@gmail.com>
Authored: Wed Aug 15 16:16:26 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Wed Aug 15 16:20:54 2018 -0600

----------------------------------------------------------------------
 metron-deployment/Kerberos-manual-setup.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/308c2b27/metron-deployment/Kerberos-manual-setup.md
----------------------------------------------------------------------
diff --git a/metron-deployment/Kerberos-manual-setup.md b/metron-deployment/Kerberos-manual-setup.md
index e3de148..18891a4 100644
--- a/metron-deployment/Kerberos-manual-setup.md
+++ b/metron-deployment/Kerberos-manual-setup.md
@@ -580,7 +580,7 @@ X-Pack
     Submit the update to Zookeeper
 
     ```
-    $METRON_HOME/bin/zk_load_configs.sh -m PUSH -i METRON_HOME/config/zookeeper/ -z $ZOOKEEPER
+    $METRON_HOME/bin/zk_load_configs.sh -m PUSH -i $METRON_HOME/config/zookeeper/ -z $ZOOKEEPER
     ```
 
 1. The last step before restarting the topology is to create a custom X-Pack shaded and relocated jar. This is up to you because of licensing restrictions, but here is a sample Maven pom file that should help.
@@ -737,6 +737,8 @@ X-Pack
     $METRON_HOME/bin/start_elasticsearch_topology.sh
     ```
 
+1. Restart the metron-rest service, and make sure the elasticsearch-xpack-shaded-5.6.2.jar is in the METRON_REST_CLASSPATH when the metron-rest starts.
+
 Once you've performed these steps, you should be able to start seeing data in your ES indexes.
 
 ### X-Pack Common Problems


[34/50] [abbrv] metron git commit: METRON-1734 Src and Dst port filters are incorrect after changing to empty (merrimanr) closes apache/metron#1159

Posted by ni...@apache.org.
METRON-1734 Src and Dst port filters are incorrect after changing to empty (merrimanr) closes apache/metron#1159


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/ba0a6092
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/ba0a6092
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/ba0a6092

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: ba0a6092c97d23c118c2b5780c2559c2f849a220
Parents: 7a8c246
Author: merrimanr <me...@gmail.com>
Authored: Mon Aug 13 15:42:29 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Mon Aug 13 15:42:29 2018 -0500

----------------------------------------------------------------------
 .../pcap/pcap-filters/pcap-filters.component.spec.ts   | 13 +++++++++++++
 .../app/pcap/pcap-filters/pcap-filters.component.ts    |  4 ++++
 2 files changed, 17 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/ba0a6092/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
index c1d8664..3f1ab07 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
@@ -179,6 +179,19 @@ describe('PcapFiltersComponent', () => {
     component.onSubmit();
   });
 
+  it('Port fields should be removed from request when set to empty', () => {
+    component.model.ipSrcPort = 44;
+    component.model.ipDstPort = 44;
+    component.ipSrcPort = '';
+    component.ipDstPort = '';
+
+    component.search.emit = (model: PcapRequest) => {
+      expect(model.ipSrcPort).toBeFalsy();
+      expect(model.ipDstPort).toBeFalsy();
+    };
+    component.onSubmit();
+  });
+
   it('Filter should have an output called search', () => {
     component.search.subscribe((filterModel) => {
       expect(filterModel).toBeDefined();

http://git-wip-us.apache.org/repos/asf/metron/blob/ba0a6092/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
index 20a8c2b..df91ae3 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
@@ -63,9 +63,13 @@ export class PcapFiltersComponent implements OnInit, OnChanges {
     this.model.endTimeMs = new Date(this.endTimeStr).getTime();
     if (this.ipSrcPort !== '') {
       this.model.ipSrcPort = +this.ipSrcPort;
+    } else {
+      delete this.model.ipSrcPort;
     }
     if (this.ipDstPort !== '') {
       this.model.ipDstPort = +this.ipDstPort;
+    } else {
+      delete this.model.ipDstPort;
     }
 
     this.search.emit(this.model);


[09/50] [abbrv] metron git commit: METRON-1671 Create PCAP UI (tiborm via merrimanr) closes apache/metron#1103

Posted by ni...@apache.org.
http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.spec.ts
new file mode 100644
index 0000000..155e1f8
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.spec.ts
@@ -0,0 +1,1283 @@
+/**
+ * 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, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PcapPacketComponent } from './pcap-packet.component';
+import { PdmlPacket } from '../model/pdml';
+
+describe('PcapPacketComponent', () => {
+  let component: PcapPacketComponent;
+  let fixture: ComponentFixture<PcapPacketComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ PcapPacketComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PcapPacketComponent);
+    component = fixture.componentInstance;
+    component.packet = fakePacket as PdmlPacket;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
+
+const fakePacket = {
+  name: "something",
+  expanded: false,
+  protos: [
+    {
+      "name": "geninfo",
+      "pos": "0",
+      "showname": "General information",
+      "size": "722",
+      "hide": null,
+      "fields": [
+        {
+          "name": "num",
+          "pos": "0",
+          "showname": "Number",
+          "size": "722",
+          "value": "1",
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "len",
+          "pos": "0",
+          "showname": "Frame Length",
+          "size": "722",
+          "value": "2d2",
+          "show": "722",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "caplen",
+          "pos": "0",
+          "showname": "Captured Length",
+          "size": "722",
+          "value": "2d2",
+          "show": "722",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "timestamp",
+          "pos": "0",
+          "showname": "Captured Time",
+          "size": "722",
+          "value": "1458240269.373968000",
+          "show": "Mar 17, 2016 18:44:29.373968000 UTC",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "frame",
+      "pos": "0",
+      "showname": "Frame 1: 722 bytes on wire (5776 bits), 722 bytes captured (5776 bits)",
+      "size": "722",
+      "hide": null,
+      "fields": [
+        {
+          "name": "frame.dlt",
+          "pos": "0",
+          "showname": "WTAP_ENCAP: 1",
+          "size": "0",
+          "value": null,
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time",
+          "pos": "0",
+          "showname": "Arrival Time: Mar 17, 2016 18:44:29.373968000 UTC",
+          "size": "0",
+          "value": null,
+          "show": "Mar 17, 2016 18:44:29.373968000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.offset_shift",
+          "pos": "0",
+          "showname": "Time shift for this packet: 0.000000000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "0.000000000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time_epoch",
+          "pos": "0",
+          "showname": "Epoch Time: 1458240269.373968000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "1458240269.373968000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time_delta",
+          "pos": "0",
+          "showname": "Time delta from previous captured frame: 0.000000000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "0.000000000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time_delta_displayed",
+          "pos": "0",
+          "showname": "Time delta from previous displayed frame: 0.000000000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "0.000000000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time_relative",
+          "pos": "0",
+          "showname": "Time since reference or first frame: 0.000000000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "0.000000000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.number",
+          "pos": "0",
+          "showname": "Frame Number: 1",
+          "size": "0",
+          "value": null,
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.len",
+          "pos": "0",
+          "showname": "Frame Length: 722 bytes (5776 bits)",
+          "size": "0",
+          "value": null,
+          "show": "722",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.cap_len",
+          "pos": "0",
+          "showname": "Capture Length: 722 bytes (5776 bits)",
+          "size": "0",
+          "value": null,
+          "show": "722",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.marked",
+          "pos": "0",
+          "showname": "Frame is marked: False",
+          "size": "0",
+          "value": null,
+          "show": "0",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.ignored",
+          "pos": "0",
+          "showname": "Frame is ignored: False",
+          "size": "0",
+          "value": null,
+          "show": "0",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.protocols",
+          "pos": "0",
+          "showname": "Protocols in frame: eth:ip:tcp:ssh",
+          "size": "0",
+          "value": null,
+          "show": "eth:ip:tcp:ssh",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "eth",
+      "pos": "0",
+      "showname": "Ethernet II, Src: CadmusCo_96:a4:7a (08:00:27:96:a4:7a), Dst: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
+      "size": "14",
+      "hide": null,
+      "fields": [
+        {
+          "name": "eth.dst",
+          "pos": "0",
+          "showname": "Destination: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
+          "size": "6",
+          "value": "0a0027000000",
+          "show": "0a:00:27:00:00:00",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "eth.addr",
+              "pos": "0",
+              "showname": "Address: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
+              "size": "6",
+              "value": "0a0027000000",
+              "show": "0a:00:27:00:00:00",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "eth.lg",
+              "pos": "0",
+              "showname": ".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)",
+              "size": "3",
+              "value": "1",
+              "show": "1",
+              "unmaskedvalue": "0a0027",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "eth.ig",
+              "pos": "0",
+              "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
+              "size": "3",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "0a0027",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "eth.src",
+          "pos": "6",
+          "showname": "Source: CadmusCo_96:a4:7a (08:00:27:96:a4:7a)",
+          "size": "6",
+          "value": "08002796a47a",
+          "show": "08:00:27:96:a4:7a",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "eth.addr",
+              "pos": "6",
+              "showname": "Address: CadmusCo_96:a4:7a (08:00:27:96:a4:7a)",
+              "size": "6",
+              "value": "08002796a47a",
+              "show": "08:00:27:96:a4:7a",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "eth.lg",
+              "pos": "6",
+              "showname": ".... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)",
+              "size": "3",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "080027",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "eth.ig",
+              "pos": "6",
+              "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
+              "size": "3",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "080027",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "eth.type",
+          "pos": "12",
+          "showname": "Type: IP (0x0800)",
+          "size": "2",
+          "value": "0800",
+          "show": "0x0800",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "ip",
+      "pos": "14",
+      "showname": "Internet Protocol Version 4, Src: 192.168.66.121 (192.168.66.121), Dst: 192.168.66.1 (192.168.66.1)",
+      "size": "20",
+      "hide": null,
+      "fields": [
+        {
+          "name": "ip.version",
+          "pos": "14",
+          "showname": "Version: 4",
+          "size": "1",
+          "value": "45",
+          "show": "4",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.hdr_len",
+          "pos": "14",
+          "showname": "Header length: 20 bytes",
+          "size": "1",
+          "value": "45",
+          "show": "20",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.dsfield",
+          "pos": "15",
+          "showname": "Differentiated Services Field: 0x10 (DSCP 0x04: Unknown DSCP; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))",
+          "size": "1",
+          "value": "10",
+          "show": "16",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "ip.dsfield.dscp",
+              "pos": "15",
+              "showname": "0001 00.. = Differentiated Services Codepoint: Unknown (0x04)",
+              "size": "1",
+              "value": "4",
+              "show": "0x04",
+              "unmaskedvalue": "10",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "ip.dsfield.ecn",
+              "pos": "15",
+              "showname": ".... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)",
+              "size": "1",
+              "value": "0",
+              "show": "0x00",
+              "unmaskedvalue": "10",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "ip.len",
+          "pos": "16",
+          "showname": "Total Length: 708",
+          "size": "2",
+          "value": "02c4",
+          "show": "708",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.id",
+          "pos": "18",
+          "showname": "Identification: 0x7cd9 (31961)",
+          "size": "2",
+          "value": "7cd9",
+          "show": "0x7cd9",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.flags",
+          "pos": "20",
+          "showname": "Flags: 0x02 (Don't Fragment)",
+          "size": "1",
+          "value": "40",
+          "show": "0x02",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "ip.flags.rb",
+              "pos": "20",
+              "showname": "0... .... = Reserved bit: Not set",
+              "size": "1",
+              "value": "40",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "ip.flags.df",
+              "pos": "20",
+              "showname": ".1.. .... = Don't fragment: Set",
+              "size": "1",
+              "value": "40",
+              "show": "1",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "ip.flags.mf",
+              "pos": "20",
+              "showname": "..0. .... = More fragments: Not set",
+              "size": "1",
+              "value": "40",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "ip.frag_offset",
+          "pos": "20",
+          "showname": "Fragment offset: 0",
+          "size": "2",
+          "value": "4000",
+          "show": "0",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.ttl",
+          "pos": "22",
+          "showname": "Time to live: 64",
+          "size": "1",
+          "value": "40",
+          "show": "64",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.proto",
+          "pos": "23",
+          "showname": "Protocol: TCP (6)",
+          "size": "1",
+          "value": "06",
+          "show": "6",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.checksum",
+          "pos": "24",
+          "showname": "Header checksum: 0xb57f [correct]",
+          "size": "2",
+          "value": "b57f",
+          "show": "0xb57f",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "ip.checksum_good",
+              "pos": "24",
+              "showname": "Good: True",
+              "size": "2",
+              "value": "b57f",
+              "show": "1",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "ip.checksum_bad",
+              "pos": "24",
+              "showname": "Bad: False",
+              "size": "2",
+              "value": "b57f",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "ip.src",
+          "pos": "26",
+          "showname": "Source: 192.168.66.121 (192.168.66.121)",
+          "size": "4",
+          "value": "c0a84279",
+          "show": "192.168.66.121",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.addr",
+          "pos": "26",
+          "showname": "Source or Destination Address: 192.168.66.121 (192.168.66.121)",
+          "size": "4",
+          "value": "c0a84279",
+          "show": "192.168.66.121",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.src_host",
+          "pos": "26",
+          "showname": "Source Host: 192.168.66.121",
+          "size": "4",
+          "value": "c0a84279",
+          "show": "192.168.66.121",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.host",
+          "pos": "26",
+          "showname": "Source or Destination Host: 192.168.66.121",
+          "size": "4",
+          "value": "c0a84279",
+          "show": "192.168.66.121",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.dst",
+          "pos": "30",
+          "showname": "Destination: 192.168.66.1 (192.168.66.1)",
+          "size": "4",
+          "value": "c0a84201",
+          "show": "192.168.66.1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.addr",
+          "pos": "30",
+          "showname": "Source or Destination Address: 192.168.66.1 (192.168.66.1)",
+          "size": "4",
+          "value": "c0a84201",
+          "show": "192.168.66.1",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.dst_host",
+          "pos": "30",
+          "showname": "Destination Host: 192.168.66.1",
+          "size": "4",
+          "value": "c0a84201",
+          "show": "192.168.66.1",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.host",
+          "pos": "30",
+          "showname": "Source or Destination Host: 192.168.66.1",
+          "size": "4",
+          "value": "c0a84201",
+          "show": "192.168.66.1",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "tcp",
+      "pos": "34",
+      "showname": "Transmission Control Protocol, Src Port: ssh (22), Dst Port: 55791 (55791), Seq: 1, Ack: 1, Len: 656",
+      "size": "32",
+      "hide": null,
+      "fields": [
+        {
+          "name": "tcp.srcport",
+          "pos": "34",
+          "showname": "Source port: ssh (22)",
+          "size": "2",
+          "value": "0016",
+          "show": "22",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.dstport",
+          "pos": "36",
+          "showname": "Destination port: 55791 (55791)",
+          "size": "2",
+          "value": "d9ef",
+          "show": "55791",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.port",
+          "pos": "34",
+          "showname": "Source or Destination Port: 22",
+          "size": "2",
+          "value": "0016",
+          "show": "22",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.port",
+          "pos": "36",
+          "showname": "Source or Destination Port: 55791",
+          "size": "2",
+          "value": "d9ef",
+          "show": "55791",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.stream",
+          "pos": "34",
+          "showname": "Stream index: 0",
+          "size": "0",
+          "value": null,
+          "show": "0",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.len",
+          "pos": "46",
+          "showname": "TCP Segment Len: 656",
+          "size": "1",
+          "value": "80",
+          "show": "656",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.seq",
+          "pos": "38",
+          "showname": "Sequence number: 1    (relative sequence number)",
+          "size": "4",
+          "value": "12903044",
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.nxtseq",
+          "pos": "34",
+          "showname": "Next sequence number: 657    (relative sequence number)",
+          "size": "0",
+          "value": null,
+          "show": "657",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.ack",
+          "pos": "42",
+          "showname": "Acknowledgment number: 1    (relative ack number)",
+          "size": "4",
+          "value": "8b92f3e7",
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.hdr_len",
+          "pos": "46",
+          "showname": "Header length: 32 bytes",
+          "size": "1",
+          "value": "80",
+          "show": "32",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.flags",
+          "pos": "46",
+          "showname": "Flags: 0x018 (PSH, ACK)",
+          "size": "2",
+          "value": "18",
+          "show": "0x0018",
+          "unmaskedvalue": "8018",
+          "hide": null,
+          "fields": [
+            {
+              "name": "tcp.flags.res",
+              "pos": "46",
+              "showname": "000. .... .... = Reserved: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "80",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.ns",
+              "pos": "46",
+              "showname": "...0 .... .... = Nonce: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "80",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.cwr",
+              "pos": "47",
+              "showname": ".... 0... .... = Congestion Window Reduced (CWR): Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.ecn",
+              "pos": "47",
+              "showname": ".... .0.. .... = ECN-Echo: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.urg",
+              "pos": "47",
+              "showname": ".... ..0. .... = Urgent: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.ack",
+              "pos": "47",
+              "showname": ".... ...1 .... = Acknowledgment: Set",
+              "size": "1",
+              "value": "1",
+              "show": "1",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.push",
+              "pos": "47",
+              "showname": ".... .... 1... = Push: Set",
+              "size": "1",
+              "value": "1",
+              "show": "1",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.reset",
+              "pos": "47",
+              "showname": ".... .... .0.. = Reset: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.syn",
+              "pos": "47",
+              "showname": ".... .... ..0. = Syn: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.fin",
+              "pos": "47",
+              "showname": ".... .... ...0 = Fin: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "tcp.window_size_value",
+          "pos": "48",
+          "showname": "Window size value: 501",
+          "size": "2",
+          "value": "01f5",
+          "show": "501",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.window_size",
+          "pos": "48",
+          "showname": "Calculated window size: 501",
+          "size": "2",
+          "value": "01f5",
+          "show": "501",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.window_size_scalefactor",
+          "pos": "48",
+          "showname": "Window size scaling factor: -1 (unknown)",
+          "size": "2",
+          "value": "01f5",
+          "show": "-1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.checksum",
+          "pos": "50",
+          "showname": "Checksum: 0x0882 [validation disabled]",
+          "size": "2",
+          "value": "0882",
+          "show": "0x0882",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "tcp.checksum_good",
+              "pos": "50",
+              "showname": "Good Checksum: False",
+              "size": "2",
+              "value": "0882",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.checksum_bad",
+              "pos": "50",
+              "showname": "Bad Checksum: False",
+              "size": "2",
+              "value": "0882",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "tcp.options",
+          "pos": "54",
+          "showname": "Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps",
+          "size": "12",
+          "value": "0101080a0014f4f811bdb98f",
+          "show": "01:01:08:0a:00:14:f4:f8:11:bd:b9:8f",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "",
+              "pos": "54",
+              "showname": null,
+              "size": "1",
+              "value": "01",
+              "show": "No-Operation (NOP)",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": [
+                {
+                  "name": "ip.opt.type",
+                  "pos": "54",
+                  "showname": "Type: 1",
+                  "size": "1",
+                  "value": "01",
+                  "show": "1",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": [
+                    {
+                      "name": "ip.opt.type.copy",
+                      "pos": "54",
+                      "showname": "0... .... = Copy on fragmentation: No",
+                      "size": "1",
+                      "value": "0",
+                      "show": "0",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    },
+                    {
+                      "name": "ip.opt.type.class",
+                      "pos": "54",
+                      "showname": ".00. .... = Class: Control (0)",
+                      "size": "1",
+                      "value": "0",
+                      "show": "0",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    },
+                    {
+                      "name": "ip.opt.type.number",
+                      "pos": "54",
+                      "showname": "...0 0001 = Number: No-Operation (NOP) (1)",
+                      "size": "1",
+                      "value": "1",
+                      "show": "1",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    }
+                  ],
+                  "protos": null
+                }
+              ],
+              "protos": null
+            },
+            {
+              "name": "",
+              "pos": "55",
+              "showname": null,
+              "size": "1",
+              "value": "01",
+              "show": "No-Operation (NOP)",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": [
+                {
+                  "name": "ip.opt.type",
+                  "pos": "55",
+                  "showname": "Type: 1",
+                  "size": "1",
+                  "value": "01",
+                  "show": "1",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": [
+                    {
+                      "name": "ip.opt.type.copy",
+                      "pos": "55",
+                      "showname": "0... .... = Copy on fragmentation: No",
+                      "size": "1",
+                      "value": "0",
+                      "show": "0",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    },
+                    {
+                      "name": "ip.opt.type.class",
+                      "pos": "55",
+                      "showname": ".00. .... = Class: Control (0)",
+                      "size": "1",
+                      "value": "0",
+                      "show": "0",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    },
+                    {
+                      "name": "ip.opt.type.number",
+                      "pos": "55",
+                      "showname": "...0 0001 = Number: No-Operation (NOP) (1)",
+                      "size": "1",
+                      "value": "1",
+                      "show": "1",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    }
+                  ],
+                  "protos": null
+                }
+              ],
+              "protos": null
+            },
+            {
+              "name": "",
+              "pos": "56",
+              "showname": null,
+              "size": "10",
+              "value": "080a0014f4f811bdb98f",
+              "show": "Timestamps: TSval 1373432, TSecr 297646479",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": [
+                {
+                  "name": "tcp.option_kind",
+                  "pos": "56",
+                  "showname": "Kind: Timestamp (8)",
+                  "size": "1",
+                  "value": "08",
+                  "show": "8",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": null,
+                  "protos": null
+                },
+                {
+                  "name": "tcp.option_len",
+                  "pos": "57",
+                  "showname": "Length: 10",
+                  "size": "1",
+                  "value": "0a",
+                  "show": "10",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": null,
+                  "protos": null
+                },
+                {
+                  "name": "tcp.options.timestamp.tsval",
+                  "pos": "58",
+                  "showname": "Timestamp value: 1373432",
+                  "size": "4",
+                  "value": "0014f4f8",
+                  "show": "1373432",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": null,
+                  "protos": null
+                },
+                {
+                  "name": "tcp.options.timestamp.tsecr",
+                  "pos": "62",
+                  "showname": "Timestamp echo reply: 297646479",
+                  "size": "4",
+                  "value": "11bdb98f",
+                  "show": "297646479",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": null,
+                  "protos": null
+                }
+              ],
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "tcp.analysis",
+          "pos": "34",
+          "showname": "SEQ/ACK analysis",
+          "size": "0",
+          "value": "",
+          "show": "",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "tcp.analysis.bytes_in_flight",
+              "pos": "34",
+              "showname": "Bytes in flight: 656",
+              "size": "0",
+              "value": null,
+              "show": "656",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "ssh",
+      "pos": "66",
+      "showname": "SSH Protocol",
+      "size": "656",
+      "hide": null,
+      "fields": [
+        {
+          "name": "ssh.encrypted_packet",
+          "pos": "66",
+          "showname": "Encrypted Packet: 5b2bfe1fa006867834412184af9f5b239737763adead7140...",
+          "size": "656",
+          "value": "5b2bfe1fa006867834412184af9f5b239737763adead71408fc01b88e548b2cc86f341a39771c6ed16f2b0bb3e6ab6109e73c7d68ca7545852f91930e4633c17fb9dc7aa794b0d820d0fa3ac65bf0f013e5449d5953d2943506657e2b76b548e67f5c9ce1a4c53db1b52465bde7208baf11f3fe01975418b4db186a38ad32947d1908b62e532da4b729353a932423d25f5f734484076aa4143c6a74937a4ea49448e261ae1ecb6b07bbdf5c98d0855940a19018c88263b8936f7b3e9a4b6cd98090fa10a10e37ad20fe5d833071ad6d5b2886ba85ec72affb83e316443dbe29dbf643e6aa05595c90765cf85f6da55cc1c09d8dccc7d05da022429ad602a559a044b7e2663b0c153a3011bf824ef8d1fa56cba957c5f5d2276a1c9e92de65782f406848c6e20f634c5d1fea843a8bf1a4058e85553f5838f7299958fbf54be84e46c5a3c3965f8bed7fe03a9a1168a892e0073adeb54deca171a318d11fc1a8179f91632310213da327965a40bc6fe18eae55e8da6b57d7ef9f3a05b42381bcb3db8f8efd6d0c638a2cdd46efb0b8f1274e98672f644b2275947e626b02e5166f86c2dd4a67b81e213f8c064927a396815db589f10e5e521ffedb13f8edbe2de01c6fc8bf0e12c82212e497794aa045e9b6fcca83b4cad0a3b5e6ca2d1feaf8887b4d64f22989396e
 cfa8f7f1835eed422580505109fed36797bdc10a9168d5148daef6a8710c3df1d6366c9763ab4ebd359d86a8ea14819252fb52ba423422d1f60b0179316b3729e479ba07e88cb886938c8daae65d470dde91e5336e0fc4221a72cc49057d878aa5924875d097483e94bc44a4ea93aee8780e56c50a405932841f50da156e1f90559a7c4f76999442fb433a26fc703dea656bbe03790ac3c9c5318ff5f81d87d483524bbfe7ff167",
+          "show": "5b:2b:fe:1f:a0:06:86:78:34:41:21:84:af:9f:5b:23:97:37:76:3a:de:ad:71:40:8f:c0:1b:88:e5:48:b2:cc:86:f3:41:a3:97:71:c6:ed:16:f2:b0:bb:3e:6a:b6:10:9e:73:c7:d6:8c:a7:54:58:52:f9:19:30:e4:63:3c:17:fb:9d:c7:aa:79:4b:0d:82:0d:0f:a3:ac:65:bf:0f:01:3e:54:49:d5:95:3d:29:43:50:66:57:e2:b7:6b:54:8e:67:f5:c9:ce:1a:4c:53:db:1b:52:46:5b:de:72:08:ba:f1:1f:3f:e0:19:75:41:8b:4d:b1:86:a3:8a:d3:29:47:d1:90:8b:62:e5:32:da:4b:72:93:53:a9:32:42:3d:25:f5:f7:34:48:40:76:aa:41:43:c6:a7:49:37:a4:ea:49:44:8e:26:1a:e1:ec:b6:b0:7b:bd:f5:c9:8d:08:55:94:0a:19:01:8c:88:26:3b:89:36:f7:b3:e9:a4:b6:cd:98:09:0f:a1:0a:10:e3:7a:d2:0f:e5:d8:33:07:1a:d6:d5:b2:88:6b:a8:5e:c7:2a:ff:b8:3e:31:64:43:db:e2:9d:bf:64:3e:6a:a0:55:95:c9:07:65:cf:85:f6:da:55:cc:1c:09:d8:dc:cc:7d:05:da:02:24:29:ad:60:2a:55:9a:04:4b:7e:26:63:b0:c1:53:a3:01:1b:f8:24:ef:8d:1f:a5:6c:ba:95:7c:5f:5d:22:76:a1:c9:e9:2d:e6:57:82:f4:06:84:8c:6e:20:f6:34:c5:d1:fe:a8:43:a8:bf:1a:40:58:e8:55:53:f5:83:8f:72:99:95:8f:bf:54:be:84:e4:6c:5a:3c:39:65:
 f8:be:d7:fe:03:a9:a1:16:8a:89:2e:00:73:ad:eb:54:de:ca:17:1a:31:8d:11:fc:1a:81:79:f9:16:32:31:02:13:da:32:79:65:a4:0b:c6:fe:18:ea:e5:5e:8d:a6:b5:7d:7e:f9:f3:a0:5b:42:38:1b:cb:3d:b8:f8:ef:d6:d0:c6:38:a2:cd:d4:6e:fb:0b:8f:12:74:e9:86:72:f6:44:b2:27:59:47:e6:26:b0:2e:51:66:f8:6c:2d:d4:a6:7b:81:e2:13:f8:c0:64:92:7a:39:68:15:db:58:9f:10:e5:e5:21:ff:ed:b1:3f:8e:db:e2:de:01:c6:fc:8b:f0:e1:2c:82:21:2e:49:77:94:aa:04:5e:9b:6f:cc:a8:3b:4c:ad:0a:3b:5e:6c:a2:d1:fe:af:88:87:b4:d6:4f:22:98:93:96:ec:fa:8f:7f:18:35:ee:d4:22:58:05:05:10:9f:ed:36:79:7b:dc:10:a9:16:8d:51:48:da:ef:6a:87:10:c3:df:1d:63:66:c9:76:3a:b4:eb:d3:59:d8:6a:8e:a1:48:19:25:2f:b5:2b:a4:23:42:2d:1f:60:b0:17:93:16:b3:72:9e:47:9b:a0:7e:88:cb:88:69:38:c8:da:ae:65:d4:70:dd:e9:1e:53:36:e0:fc:42:21:a7:2c:c4:90:57:d8:78:aa:59:24:87:5d:09:74:83:e9:4b:c4:4a:4e:a9:3a:ee:87:80:e5:6c:50:a4:05:93:28:41:f5:0d:a1:56:e1:f9:05:59:a7:c4:f7:69:99:44:2f:b4:33:a2:6f:c7:03:de:a6:56:bb:e0:37:90:ac:3c:9c:53:18:ff:5f:81:d8:7d:48:35:24:bb:fe:7f:f1:67",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        }
+      ]
+    }
+  ]
+};

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.ts
new file mode 100644
index 0000000..f1bbe2d
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.ts
@@ -0,0 +1,39 @@
+/**
+ * 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, OnInit, Input } from '@angular/core';
+
+import { PdmlPacket } from '../model/pdml'
+
+@Component({
+  selector: 'app-pcap-packet',
+  templateUrl: './pcap-packet.component.html',
+  styleUrls: ['./pcap-packet.component.scss']
+})
+export class PcapPacketComponent implements OnInit {
+  @Input() packet: PdmlPacket
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+  toggle() {
+    this.packet.expanded = !this.packet.expanded
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
new file mode 100644
index 0000000..b373914
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
@@ -0,0 +1,31 @@
+<!--
+  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="panel my-4">
+  <div class="panel-header">
+    <app-pcap-filters [queryRunning]="queryRunning" (search)="onSearch($event)"></app-pcap-filters>
+  </div>
+
+  <div *ngIf="queryRunning" class="progress pcap-progress-background">
+      <div class="progress-bar progress-bar-animated pcap-progress" role="progressbar" attr.aria-valuenow="{{progressWidth}}" aria-valuemin="0" aria-valuemax="100" [ngStyle]="{'max-width': progressWidth + '%', 'transition-duration':'1000ms'}">{{progressWidth}}%</div>
+  </div>
+  
+  <div *ngIf="errorMsg" class="alert alert-danger" role="alert">
+    {{ errorMsg }}
+  </div>
+
+  <div class="panel-body" *ngIf="pdml">
+    <app-pcap-list [packets]="pdml.packets"></app-pcap-list>
+  </div>
+
+</div>

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
new file mode 100644
index 0000000..a8cc2ce
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.scss
@@ -0,0 +1,52 @@
+/**
+ * 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 "../../../vendor.scss";
+@import "../../../styles.scss";
+@import "../../../variables.scss";
+
+.pcap-progress-background {
+  background-color: #2E2E2E;
+}
+
+.pcap-progress {
+  background-color: #0F6F9E;
+  transition-duration:2s;
+  animation-duration: 2s;
+}
+
+.progress-bar {
+  width: 0;
+  animation: progress 1.5s ease-in-out forwards;
+}
+
+@keyframes progress {
+  from {
+    width: 0;
+  }
+  to {
+    width: 100%;
+  }
+}
+@keyframes show  {
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
new file mode 100644
index 0000000..82b29f2
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
@@ -0,0 +1,68 @@
+/**
+ * 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, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PcapPanelComponent } from './pcap-panel.component';
+import { Component, Input } from '../../../../node_modules/@angular/core';
+import { PdmlPacket } from '../model/pdml';
+import { PcapService } from '../service/pcap.service';
+
+@Component({
+  selector: 'app-pcap-filters',
+  template: '',
+})
+class FakeFilterComponent {
+  @Input() queryRunning: boolean;
+}
+
+@Component({
+  selector: 'app-pcap-list',
+  template: '',
+})
+class FakePcapListComponent {
+  @Input() packets: PdmlPacket[];
+}
+
+describe('PcapPanelComponent', () => {
+  let component: PcapPanelComponent;
+  let fixture: ComponentFixture<PcapPanelComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        FakeFilterComponent,
+        FakePcapListComponent,
+        PcapPanelComponent,
+      ],
+      providers: [
+        { provide: PcapService, useValue: {} },
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PcapPanelComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
new file mode 100644
index 0000000..fd49ec7
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
@@ -0,0 +1,75 @@
+/**
+ * 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, OnInit, Input } from '@angular/core';
+
+import { PcapService, PcapStatusResponse } from '../service/pcap.service'
+import { PcapRequest } from '../model/pcap.request'
+import { Pdml } from '../model/pdml'
+import {Subscription} from "rxjs/Rx";
+
+@Component({
+  selector: 'app-pcap-panel',
+  templateUrl: './pcap-panel.component.html',
+  styleUrls: ['./pcap-panel.component.scss']
+})
+export class PcapPanelComponent implements OnInit {
+
+  @Input() pdml: Pdml = null;
+
+  @Input() pcapRequest: PcapRequest;
+
+  statusSubscription: Subscription;
+  queryRunning: boolean = false;
+  progressWidth: number = 0;
+  selectedPage: number = 1;
+  errorMsg: string;
+
+  constructor(private pcapService: PcapService ) { }
+
+  ngOnInit() {
+  }
+
+  onSearch(pcapRequest) {
+    console.log(pcapRequest);
+    this.pdml = null;
+    this.progressWidth = 0;
+    this.pcapService.submitRequest(pcapRequest).subscribe(id => {
+      this.queryRunning = true;
+      this.errorMsg = null;
+      this.statusSubscription = this.pcapService.pollStatus(id).subscribe((statusResponse: PcapStatusResponse) => {
+        if ('SUCCEEDED' === statusResponse.jobStatus) {
+          this.statusSubscription.unsubscribe();
+          this.queryRunning = false;
+          this.pcapService.getPackets(id, this.selectedPage).toPromise().then(pdml => {
+            this.pdml = pdml;
+          });
+        } else if ('FAILED' === statusResponse.jobStatus) {
+          this.statusSubscription.unsubscribe();
+          this.queryRunning = false;
+          this.errorMsg = `Query status: ${statusResponse.jobStatus}. Check your filter criteria and try again!`;
+        } else if (this.progressWidth < 100) {
+          this.progressWidth = Math.trunc(statusResponse.percentComplete);
+        }
+      }, (error: any) => {
+        this.statusSubscription.unsubscribe();
+        this.queryRunning = false;
+        this.errorMsg = `Response status: ${error.responseCode}. Something went wrong with your status request!`;
+      });
+    });
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
new file mode 100644
index 0000000..c66b965
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
@@ -0,0 +1,50 @@
+/**
+ * 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 { CommonModule } from '@angular/common';  
+import { FormsModule } from '@angular/forms';
+import { HttpModule } from '@angular/http';
+
+import { routing } from './pcap.routing';
+
+import { PcapListComponent } from './pcap-list/pcap-list.component';
+import { PcapPacketComponent } from './pcap-packet/pcap-packet.component';
+import { PcapFiltersComponent } from './pcap-filters/pcap-filters.component';
+import { PcapPanelComponent } from './pcap-panel/pcap-panel.component';
+import { PcapPacketLineComponent } from './pcap-packet-line/pcap-packet-line.component';
+
+import { PcapService } from './service/pcap.service'
+ 
+@NgModule({
+  imports: [
+    routing,
+    CommonModule,
+    FormsModule,
+    HttpModule    
+  ],
+  declarations: [
+    PcapListComponent,
+    PcapPacketComponent,
+    PcapFiltersComponent,
+    PcapPanelComponent,
+    PcapPacketLineComponent
+  ],
+  exports: [ PcapPanelComponent ],
+  providers: [ PcapService ]
+})
+export class PcapModule {}

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap.routing.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap.routing.ts b/metron-interface/metron-alerts/src/app/pcap/pcap.routing.ts
new file mode 100644
index 0000000..25ee0ed
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap.routing.ts
@@ -0,0 +1,27 @@
+/**
+ * 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 { Routes, RouterModule } from '@angular/router';
+import { ModuleWithProviders }  from '@angular/core';
+
+import {PcapPanelComponent} from './pcap-panel/pcap-panel.component';
+
+export const routes: Routes = [
+    {path: '', component: PcapPanelComponent},
+];
+
+export const routing: ModuleWithProviders = RouterModule.forChild(routes);


[10/50] [abbrv] metron git commit: METRON-1671 Create PCAP UI (tiborm via merrimanr) closes apache/metron#1103

Posted by ni...@apache.org.
http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/app-routing.module.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/app-routing.module.ts b/metron-interface/metron-alerts/src/app/app-routing.module.ts
index 8357d66..56ad41c 100644
--- a/metron-interface/metron-alerts/src/app/app-routing.module.ts
+++ b/metron-interface/metron-alerts/src/app/app-routing.module.ts
@@ -26,7 +26,8 @@ const routes: Routes = [
   { path: 'alerts-list', loadChildren: 'app/alerts/alerts-list/alerts-list.module#AlertsListModule', canActivate: [AuthGuard]},
   { path: 'save-search', loadChildren: 'app/alerts/save-search/save-search.module#SaveSearchModule', canActivate: [AuthGuard]},
   { path: 'saved-searches', loadChildren: 'app/alerts/saved-searches/saved-searches.module.ts#SavedSearchesModule',
-      canActivate: [AuthGuard]}
+      canActivate: [AuthGuard]},
+  { path: 'pcap', loadChildren: 'app/pcap/pcap.module#PcapModule', canActivate: [AuthGuard] }
 ];
 
 @NgModule({

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/app.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/app.component.html b/metron-interface/metron-alerts/src/app/app.component.html
index 8867a9b..1b992ca 100644
--- a/metron-interface/metron-alerts/src/app/app.component.html
+++ b/metron-interface/metron-alerts/src/app/app.component.html
@@ -16,6 +16,14 @@
         <a class="" href="#">
             <img alt="" src="../assets/images/logo.png" width="135" height="45">
         </a>
+        <ul class="nav ml-4">
+            <li class="nav-item" routerLinkActive="active">
+                <a class="nav-link " routerLink="/alerts-list" routerLinkActive="active">Alerts</a>
+            </li>
+            <li class="nav-item" routerLinkActive="active">
+                <a class="nav-link" routerLink="/pcap" routerLinkActive="active">PCAP</a>
+            </li>
+        </ul>
         <div class="logout ml-auto">Logged in as {{authService.currentUser}} - <span class="logout-link" (click)="authService.logout()">Logout</span></div>
     </nav>
     <div class="container-fluid">

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/app.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/app.component.scss b/metron-interface/metron-alerts/src/app/app.component.scss
index 8358e7f..8505ee8 100644
--- a/metron-interface/metron-alerts/src/app/app.component.scss
+++ b/metron-interface/metron-alerts/src/app/app.component.scss
@@ -27,6 +27,24 @@
   max-height: 50px;
 }
 
+.nav-link
+{
+  padding-bottom: 0;
+  padding-top: 0;
+  color: inherit;
+}
+
+.nav-item.active
+{
+  border-bottom: 3px solid #32abe2;
+  margin-bottom: 5px;
+}
+
+.nav-link.active
+{
+  color: #ffffff;
+}
+
 .logout {
   padding-left: 10px;
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/app.module.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/app.module.ts b/metron-interface/metron-alerts/src/app/app.module.ts
index 227eaab..82b4970 100644
--- a/metron-interface/metron-alerts/src/app/app.module.ts
+++ b/metron-interface/metron-alerts/src/app/app.module.ts
@@ -46,8 +46,7 @@ import {MetaAlertService} from './service/meta-alert.service';
 import {MetaAlertsModule} from './alerts/meta-alerts/meta-alerts.module';
 import {SearchService} from './service/search.service';
 import { GlobalConfigService } from './service/global-config.service';
-
-
+import {PcapModule} from './pcap/pcap.module';
 
 export function initConfig(config: ColumnNamesService) {
   return () => config.list();
@@ -70,7 +69,8 @@ export function initConfig(config: ColumnNamesService) {
     ConfigureRowsModule,
     SaveSearchModule,
     SavedSearchesModule,
-    SwitchModule
+    SwitchModule,
+    PcapModule
   ],
   providers: [{ provide: APP_INITIALIZER, useFactory: initConfig, deps: [ColumnNamesService], multi: true },
               { provide: DataSource, useClass: ElasticSearchLocalstorageImpl },

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
new file mode 100644
index 0000000..3a27714
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.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.
+ */
+
+export class PcapRequest {
+  startTimeMs: number = 0;
+  endTimeMs: number = 150000000000000000;
+  ipSrcAddr: string = '';
+  ipSrcPort: string = '';
+  ipDstAddr: string = '';
+  ipDstPort: string = '';
+  protocol: string = '';
+  packetFilter: string = '';
+  includeReverse: boolean = false;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts b/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
new file mode 100644
index 0000000..97fc347
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pdml.ts
@@ -0,0 +1,50 @@
+/**
+ * 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.
+ */
+ 
+ export class PdmlField {
+  //public $: any[]
+    public name: string;
+  public fields?: PdmlField[]
+  public show: string;
+  public value: string;
+  public showname: string;
+}
+
+export class PdmlProto {
+  //public $: any
+    public name: string;
+    public showname: string;
+  public fields: PdmlField[]
+  
+  public static findField(p: PdmlProto, name: string): PdmlField {
+    return p.fields.find(f => f['name'] == name)
+  }
+}
+
+
+export class PdmlPacket {
+  //public $: any
+    public name: string;
+  public protos: PdmlProto[]
+  public expanded: boolean = false
+}
+
+export class Pdml {
+  public name: string;
+  public packets: PdmlPacket[];
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
new file mode 100644
index 0000000..abc4cf0
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
@@ -0,0 +1,52 @@
+<!--
+  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.
+  -->
+<form (ngSubmit)="onSubmit()" #f="ngForm" class="form-inline pcap-search">
+  <div class="form-group">
+    <label for="ip_src_addr">IP Source Address</label>
+    <input name="ip_src_addr" #ip_src_addr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipSrcAddr">
+  </div>
+  
+  <div class="form-group">
+    <label for="ip_src_port">IP Source Port</label>
+    <input name="ip_src_port" #ip_src_port="ngModel" class="form-control" type="number" [(ngModel)]="model.ipSrcPort">
+  </div>
+
+  <div class="form-group"><label for="ip_dest_addr">IP Dest Address</label>
+    <input name="ip_dest_addr" #ip_dest_addr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipDstAddr">
+  </div>
+
+  <div class="form-group">
+    <label for="ip_dest_port">IP Dest Port</label>
+    <input id="ip_dest_port" name="ip_dest_port" #ip_dest_port="ngModel" class="form-control" type="number" [(ngModel)]="model.ipDstPort">
+  </div>
+
+    <div class="form-group">
+        <label for="protocol">Protocol</label>
+        <input id="protocol" name="protocol" #protocol="ngModel" class="form-control" [(ngModel)]="model.protocol">
+    </div>
+
+    <div class="form-group">
+        <label for="includeReverseTraffic">Include Reverse Traffic</label>
+        <input id="includeReverseTraffic" name="includeReverseTraffic" #includeReverseTraffic="ngModel" class="form-control" type="checkbox" [(ngModel)]="model.includeReverse">
+    </div>
+
+  <div class="form-group">
+    <label for="filter">Filter</label>
+    <input id="filter" name="filter" #filter="ngModel" class="form-control" [(ngModel)]="model.packetFilter">
+  </div>
+
+  <div class="form-group">
+    <button type="submit" [ngClass]="{'disabled':!f.form.valid || queryRunning}" class="btn btn-primary btn-search" [disabled]="!f.form.valid || queryRunning"></button>
+  </div>
+</form>

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
new file mode 100644
index 0000000..80bac08
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss
@@ -0,0 +1,69 @@
+/**
+ * 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 "../../../vendor.scss";
+@import "../../../styles.scss";
+@import "../../../variables.scss";
+
+.ng-valid[required], .ng-valid.required  {
+  
+}
+
+.ng-invalid:not(form)  {
+  border-left: 5px solid #a94442; /* red */
+}
+
+.form-group {
+  padding-right: 0.75em;
+  label {
+    margin-right: 1em;
+  }
+}
+
+.btn-search {
+
+  min-width: 42px;
+  padding-left: 0;
+  padding-right: 0;
+  border: 1px solid $blue-chill !important;
+  cursor: pointer;
+
+  &:focus {
+    box-shadow: none;
+  }
+
+  &::before {
+    font-family: "FontAwesome";
+    content: '\f002';
+    color: $piction-blue;
+  }
+}
+
+.pcap-search {
+
+  padding-bottom: 15px;
+
+  label {
+    font-size: 15px;
+  }
+
+  button {
+    padding-top: 6px;
+    padding-bottom: 6px;
+    background: $icon-button-background;
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
new file mode 100644
index 0000000..8134f1d
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
@@ -0,0 +1,46 @@
+/**
+ * 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, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PcapFiltersComponent } from './pcap-filters.component';
+import { FormsModule } from '../../../../node_modules/@angular/forms';
+
+describe('PcapFiltersComponent', () => {
+  let component: PcapFiltersComponent;
+  let fixture: ComponentFixture<PcapFiltersComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        FormsModule
+      ],
+      declarations: [ PcapFiltersComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PcapFiltersComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
new file mode 100644
index 0000000..e16d71b
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
@@ -0,0 +1,41 @@
+/**
+ * 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, OnInit, Input, Output, EventEmitter } from '@angular/core';
+import { PcapRequest } from '../model/pcap.request'
+
+@Component({
+  selector: 'app-pcap-filters',
+  templateUrl: './pcap-filters.component.html',
+  styleUrls: ['./pcap-filters.component.scss']
+})
+export class PcapFiltersComponent implements OnInit {
+
+  @Input() queryRunning: boolean = true;
+  @Output() search: EventEmitter<PcapRequest> = new EventEmitter<PcapRequest>();
+
+  model = new PcapRequest();
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+  onSubmit() {
+    this.search.emit(this.model)
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
new file mode 100644
index 0000000..92dfb1d
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
@@ -0,0 +1,37 @@
+<!--
+  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="table-wrapper">
+  <table class="table table-sm">
+    <thead>
+      <tr>
+        <th scope="col">Timestamp</th>
+        <th scope="col">Source Addr</th>
+        <th scope="col">Source Port</th>
+        <th scope="col">Dest Addr</th>
+        <th scope="col">Dest Port</th>
+        <th scope="col">Protocol</th>
+      </tr>
+    </thead>
+    <tbody>
+      <ng-container *ngFor="let packet of packets">
+        <tr (click)="toggle(packet)" app-pcap-packet-line [packet]="packet"></tr>
+        <tr *ngIf="packet.expanded">
+          <td colspan="6" class="pcap-packet">
+            <app-pcap-packet [packet]="packet"></app-pcap-packet>
+          </td>
+        </tr>
+      </ng-container>
+    </tbody>
+  </table>
+</div>

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.scss
new file mode 100644
index 0000000..b724782
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.scss
@@ -0,0 +1,21 @@
+/**
+ * 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.
+ */
+.pcap-packet {
+  padding-top: 0;
+  border-top: none;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
new file mode 100644
index 0000000..1ac8c70
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
@@ -0,0 +1,70 @@
+/**
+ * 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, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PcapListComponent } from './pcap-list.component';
+import { FormsModule } from '../../../../node_modules/@angular/forms';
+import { PdmlPacket } from '../model/pdml';
+import { Component, Input } from '@angular/core';
+import { PcapPacketLineComponent } from '../pcap-packet-line/pcap-packet-line.component';
+import { PcapPacketComponent } from '../pcap-packet/pcap-packet.component';
+
+@Component({
+  selector: '[app-pcap-packet-line]',
+  template: ``,
+})
+class FakePcapPacketLineComponent {
+  @Input() packet: PdmlPacket;
+}
+
+@Component({
+  selector: 'app-pcap-packet',
+  template: ``,
+})
+class FakePcapPacketComponent {
+  @Input() packet: PdmlPacket;
+}
+
+describe('PcapListComponent', () => {
+  let component: PcapListComponent;
+  let fixture: ComponentFixture<PcapListComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        FormsModule
+      ],
+      declarations: [
+        FakePcapPacketLineComponent,
+        FakePcapPacketComponent,
+        PcapListComponent,
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PcapListComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.ts
new file mode 100644
index 0000000..fbc1aec
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.ts
@@ -0,0 +1,39 @@
+/**
+ * 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, OnInit, Input } from '@angular/core';
+import { Pdml,PdmlPacket } from '../model/pdml'
+
+@Component({
+  selector: 'app-pcap-list',
+  templateUrl: './pcap-list.component.html',
+  styleUrls: ['./pcap-list.component.scss']
+})
+export class PcapListComponent implements OnInit {
+
+  @Input() packets: PdmlPacket[]
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+  toggle(packet) {
+    packet.expanded= !packet.expanded
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.html
new file mode 100644
index 0000000..25724ee
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.html
@@ -0,0 +1,19 @@
+<!--
+  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.
+  -->
+<td class="timestamp">{{ip.timestamp.value}} <span class="date">{{ip.timestamp.show}}</span></td>
+<td class="ip-addr">{{ip.ipSrcAddr.show}}</td>
+<td class="ip-port">{{ip.ipSrcPort.show}}</td>
+<td class="ip-addr">{{ip.ipDestAddr.show}}</td>
+<td class="ip-port">{{ip.ipDestPort.show}}</td>
+<td class="ip-protocol">{{ip.protocol.showname}}</td>

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.scss
new file mode 100644
index 0000000..a3f036c
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.scss
@@ -0,0 +1,17 @@
+/**
+ * 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.
+ */
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
new file mode 100644
index 0000000..e01f9c6
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
@@ -0,0 +1,1283 @@
+/**
+ * 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, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PcapPacketLineComponent } from './pcap-packet-line.component';
+import { PdmlPacket } from '../model/pdml';
+
+describe('PcapPacketLineComponent', () => {
+  let component: PcapPacketLineComponent;
+  let fixture: ComponentFixture<PcapPacketLineComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ PcapPacketLineComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PcapPacketLineComponent);
+    component = fixture.componentInstance;
+    component.packet = fakePacket as PdmlPacket;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
+
+const fakePacket = {
+  name: "something",
+  expanded: false,
+  protos: [
+    {
+      "name": "geninfo",
+      "pos": "0",
+      "showname": "General information",
+      "size": "722",
+      "hide": null,
+      "fields": [
+        {
+          "name": "num",
+          "pos": "0",
+          "showname": "Number",
+          "size": "722",
+          "value": "1",
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "len",
+          "pos": "0",
+          "showname": "Frame Length",
+          "size": "722",
+          "value": "2d2",
+          "show": "722",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "caplen",
+          "pos": "0",
+          "showname": "Captured Length",
+          "size": "722",
+          "value": "2d2",
+          "show": "722",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "timestamp",
+          "pos": "0",
+          "showname": "Captured Time",
+          "size": "722",
+          "value": "1458240269.373968000",
+          "show": "Mar 17, 2016 18:44:29.373968000 UTC",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "frame",
+      "pos": "0",
+      "showname": "Frame 1: 722 bytes on wire (5776 bits), 722 bytes captured (5776 bits)",
+      "size": "722",
+      "hide": null,
+      "fields": [
+        {
+          "name": "frame.dlt",
+          "pos": "0",
+          "showname": "WTAP_ENCAP: 1",
+          "size": "0",
+          "value": null,
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time",
+          "pos": "0",
+          "showname": "Arrival Time: Mar 17, 2016 18:44:29.373968000 UTC",
+          "size": "0",
+          "value": null,
+          "show": "Mar 17, 2016 18:44:29.373968000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.offset_shift",
+          "pos": "0",
+          "showname": "Time shift for this packet: 0.000000000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "0.000000000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time_epoch",
+          "pos": "0",
+          "showname": "Epoch Time: 1458240269.373968000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "1458240269.373968000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time_delta",
+          "pos": "0",
+          "showname": "Time delta from previous captured frame: 0.000000000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "0.000000000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time_delta_displayed",
+          "pos": "0",
+          "showname": "Time delta from previous displayed frame: 0.000000000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "0.000000000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.time_relative",
+          "pos": "0",
+          "showname": "Time since reference or first frame: 0.000000000 seconds",
+          "size": "0",
+          "value": null,
+          "show": "0.000000000",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.number",
+          "pos": "0",
+          "showname": "Frame Number: 1",
+          "size": "0",
+          "value": null,
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.len",
+          "pos": "0",
+          "showname": "Frame Length: 722 bytes (5776 bits)",
+          "size": "0",
+          "value": null,
+          "show": "722",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.cap_len",
+          "pos": "0",
+          "showname": "Capture Length: 722 bytes (5776 bits)",
+          "size": "0",
+          "value": null,
+          "show": "722",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.marked",
+          "pos": "0",
+          "showname": "Frame is marked: False",
+          "size": "0",
+          "value": null,
+          "show": "0",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.ignored",
+          "pos": "0",
+          "showname": "Frame is ignored: False",
+          "size": "0",
+          "value": null,
+          "show": "0",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "frame.protocols",
+          "pos": "0",
+          "showname": "Protocols in frame: eth:ip:tcp:ssh",
+          "size": "0",
+          "value": null,
+          "show": "eth:ip:tcp:ssh",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "eth",
+      "pos": "0",
+      "showname": "Ethernet II, Src: CadmusCo_96:a4:7a (08:00:27:96:a4:7a), Dst: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
+      "size": "14",
+      "hide": null,
+      "fields": [
+        {
+          "name": "eth.dst",
+          "pos": "0",
+          "showname": "Destination: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
+          "size": "6",
+          "value": "0a0027000000",
+          "show": "0a:00:27:00:00:00",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "eth.addr",
+              "pos": "0",
+              "showname": "Address: 0a:00:27:00:00:00 (0a:00:27:00:00:00)",
+              "size": "6",
+              "value": "0a0027000000",
+              "show": "0a:00:27:00:00:00",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "eth.lg",
+              "pos": "0",
+              "showname": ".... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)",
+              "size": "3",
+              "value": "1",
+              "show": "1",
+              "unmaskedvalue": "0a0027",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "eth.ig",
+              "pos": "0",
+              "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
+              "size": "3",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "0a0027",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "eth.src",
+          "pos": "6",
+          "showname": "Source: CadmusCo_96:a4:7a (08:00:27:96:a4:7a)",
+          "size": "6",
+          "value": "08002796a47a",
+          "show": "08:00:27:96:a4:7a",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "eth.addr",
+              "pos": "6",
+              "showname": "Address: CadmusCo_96:a4:7a (08:00:27:96:a4:7a)",
+              "size": "6",
+              "value": "08002796a47a",
+              "show": "08:00:27:96:a4:7a",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "eth.lg",
+              "pos": "6",
+              "showname": ".... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)",
+              "size": "3",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "080027",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "eth.ig",
+              "pos": "6",
+              "showname": ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
+              "size": "3",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "080027",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "eth.type",
+          "pos": "12",
+          "showname": "Type: IP (0x0800)",
+          "size": "2",
+          "value": "0800",
+          "show": "0x0800",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "ip",
+      "pos": "14",
+      "showname": "Internet Protocol Version 4, Src: 192.168.66.121 (192.168.66.121), Dst: 192.168.66.1 (192.168.66.1)",
+      "size": "20",
+      "hide": null,
+      "fields": [
+        {
+          "name": "ip.version",
+          "pos": "14",
+          "showname": "Version: 4",
+          "size": "1",
+          "value": "45",
+          "show": "4",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.hdr_len",
+          "pos": "14",
+          "showname": "Header length: 20 bytes",
+          "size": "1",
+          "value": "45",
+          "show": "20",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.dsfield",
+          "pos": "15",
+          "showname": "Differentiated Services Field: 0x10 (DSCP 0x04: Unknown DSCP; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))",
+          "size": "1",
+          "value": "10",
+          "show": "16",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "ip.dsfield.dscp",
+              "pos": "15",
+              "showname": "0001 00.. = Differentiated Services Codepoint: Unknown (0x04)",
+              "size": "1",
+              "value": "4",
+              "show": "0x04",
+              "unmaskedvalue": "10",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "ip.dsfield.ecn",
+              "pos": "15",
+              "showname": ".... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)",
+              "size": "1",
+              "value": "0",
+              "show": "0x00",
+              "unmaskedvalue": "10",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "ip.len",
+          "pos": "16",
+          "showname": "Total Length: 708",
+          "size": "2",
+          "value": "02c4",
+          "show": "708",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.id",
+          "pos": "18",
+          "showname": "Identification: 0x7cd9 (31961)",
+          "size": "2",
+          "value": "7cd9",
+          "show": "0x7cd9",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.flags",
+          "pos": "20",
+          "showname": "Flags: 0x02 (Don't Fragment)",
+          "size": "1",
+          "value": "40",
+          "show": "0x02",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "ip.flags.rb",
+              "pos": "20",
+              "showname": "0... .... = Reserved bit: Not set",
+              "size": "1",
+              "value": "40",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "ip.flags.df",
+              "pos": "20",
+              "showname": ".1.. .... = Don't fragment: Set",
+              "size": "1",
+              "value": "40",
+              "show": "1",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "ip.flags.mf",
+              "pos": "20",
+              "showname": "..0. .... = More fragments: Not set",
+              "size": "1",
+              "value": "40",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "ip.frag_offset",
+          "pos": "20",
+          "showname": "Fragment offset: 0",
+          "size": "2",
+          "value": "4000",
+          "show": "0",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.ttl",
+          "pos": "22",
+          "showname": "Time to live: 64",
+          "size": "1",
+          "value": "40",
+          "show": "64",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.proto",
+          "pos": "23",
+          "showname": "Protocol: TCP (6)",
+          "size": "1",
+          "value": "06",
+          "show": "6",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.checksum",
+          "pos": "24",
+          "showname": "Header checksum: 0xb57f [correct]",
+          "size": "2",
+          "value": "b57f",
+          "show": "0xb57f",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "ip.checksum_good",
+              "pos": "24",
+              "showname": "Good: True",
+              "size": "2",
+              "value": "b57f",
+              "show": "1",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "ip.checksum_bad",
+              "pos": "24",
+              "showname": "Bad: False",
+              "size": "2",
+              "value": "b57f",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "ip.src",
+          "pos": "26",
+          "showname": "Source: 192.168.66.121 (192.168.66.121)",
+          "size": "4",
+          "value": "c0a84279",
+          "show": "192.168.66.121",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.addr",
+          "pos": "26",
+          "showname": "Source or Destination Address: 192.168.66.121 (192.168.66.121)",
+          "size": "4",
+          "value": "c0a84279",
+          "show": "192.168.66.121",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.src_host",
+          "pos": "26",
+          "showname": "Source Host: 192.168.66.121",
+          "size": "4",
+          "value": "c0a84279",
+          "show": "192.168.66.121",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.host",
+          "pos": "26",
+          "showname": "Source or Destination Host: 192.168.66.121",
+          "size": "4",
+          "value": "c0a84279",
+          "show": "192.168.66.121",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.dst",
+          "pos": "30",
+          "showname": "Destination: 192.168.66.1 (192.168.66.1)",
+          "size": "4",
+          "value": "c0a84201",
+          "show": "192.168.66.1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.addr",
+          "pos": "30",
+          "showname": "Source or Destination Address: 192.168.66.1 (192.168.66.1)",
+          "size": "4",
+          "value": "c0a84201",
+          "show": "192.168.66.1",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.dst_host",
+          "pos": "30",
+          "showname": "Destination Host: 192.168.66.1",
+          "size": "4",
+          "value": "c0a84201",
+          "show": "192.168.66.1",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "ip.host",
+          "pos": "30",
+          "showname": "Source or Destination Host: 192.168.66.1",
+          "size": "4",
+          "value": "c0a84201",
+          "show": "192.168.66.1",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "tcp",
+      "pos": "34",
+      "showname": "Transmission Control Protocol, Src Port: ssh (22), Dst Port: 55791 (55791), Seq: 1, Ack: 1, Len: 656",
+      "size": "32",
+      "hide": null,
+      "fields": [
+        {
+          "name": "tcp.srcport",
+          "pos": "34",
+          "showname": "Source port: ssh (22)",
+          "size": "2",
+          "value": "0016",
+          "show": "22",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.dstport",
+          "pos": "36",
+          "showname": "Destination port: 55791 (55791)",
+          "size": "2",
+          "value": "d9ef",
+          "show": "55791",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.port",
+          "pos": "34",
+          "showname": "Source or Destination Port: 22",
+          "size": "2",
+          "value": "0016",
+          "show": "22",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.port",
+          "pos": "36",
+          "showname": "Source or Destination Port: 55791",
+          "size": "2",
+          "value": "d9ef",
+          "show": "55791",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.stream",
+          "pos": "34",
+          "showname": "Stream index: 0",
+          "size": "0",
+          "value": null,
+          "show": "0",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.len",
+          "pos": "46",
+          "showname": "TCP Segment Len: 656",
+          "size": "1",
+          "value": "80",
+          "show": "656",
+          "unmaskedvalue": null,
+          "hide": "yes",
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.seq",
+          "pos": "38",
+          "showname": "Sequence number: 1    (relative sequence number)",
+          "size": "4",
+          "value": "12903044",
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.nxtseq",
+          "pos": "34",
+          "showname": "Next sequence number: 657    (relative sequence number)",
+          "size": "0",
+          "value": null,
+          "show": "657",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.ack",
+          "pos": "42",
+          "showname": "Acknowledgment number: 1    (relative ack number)",
+          "size": "4",
+          "value": "8b92f3e7",
+          "show": "1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.hdr_len",
+          "pos": "46",
+          "showname": "Header length: 32 bytes",
+          "size": "1",
+          "value": "80",
+          "show": "32",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.flags",
+          "pos": "46",
+          "showname": "Flags: 0x018 (PSH, ACK)",
+          "size": "2",
+          "value": "18",
+          "show": "0x0018",
+          "unmaskedvalue": "8018",
+          "hide": null,
+          "fields": [
+            {
+              "name": "tcp.flags.res",
+              "pos": "46",
+              "showname": "000. .... .... = Reserved: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "80",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.ns",
+              "pos": "46",
+              "showname": "...0 .... .... = Nonce: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "80",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.cwr",
+              "pos": "47",
+              "showname": ".... 0... .... = Congestion Window Reduced (CWR): Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.ecn",
+              "pos": "47",
+              "showname": ".... .0.. .... = ECN-Echo: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.urg",
+              "pos": "47",
+              "showname": ".... ..0. .... = Urgent: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.ack",
+              "pos": "47",
+              "showname": ".... ...1 .... = Acknowledgment: Set",
+              "size": "1",
+              "value": "1",
+              "show": "1",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.push",
+              "pos": "47",
+              "showname": ".... .... 1... = Push: Set",
+              "size": "1",
+              "value": "1",
+              "show": "1",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.reset",
+              "pos": "47",
+              "showname": ".... .... .0.. = Reset: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.syn",
+              "pos": "47",
+              "showname": ".... .... ..0. = Syn: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.flags.fin",
+              "pos": "47",
+              "showname": ".... .... ...0 = Fin: Not set",
+              "size": "1",
+              "value": "0",
+              "show": "0",
+              "unmaskedvalue": "18",
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "tcp.window_size_value",
+          "pos": "48",
+          "showname": "Window size value: 501",
+          "size": "2",
+          "value": "01f5",
+          "show": "501",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.window_size",
+          "pos": "48",
+          "showname": "Calculated window size: 501",
+          "size": "2",
+          "value": "01f5",
+          "show": "501",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.window_size_scalefactor",
+          "pos": "48",
+          "showname": "Window size scaling factor: -1 (unknown)",
+          "size": "2",
+          "value": "01f5",
+          "show": "-1",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        },
+        {
+          "name": "tcp.checksum",
+          "pos": "50",
+          "showname": "Checksum: 0x0882 [validation disabled]",
+          "size": "2",
+          "value": "0882",
+          "show": "0x0882",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "tcp.checksum_good",
+              "pos": "50",
+              "showname": "Good Checksum: False",
+              "size": "2",
+              "value": "0882",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            },
+            {
+              "name": "tcp.checksum_bad",
+              "pos": "50",
+              "showname": "Bad Checksum: False",
+              "size": "2",
+              "value": "0882",
+              "show": "0",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "tcp.options",
+          "pos": "54",
+          "showname": "Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps",
+          "size": "12",
+          "value": "0101080a0014f4f811bdb98f",
+          "show": "01:01:08:0a:00:14:f4:f8:11:bd:b9:8f",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "",
+              "pos": "54",
+              "showname": null,
+              "size": "1",
+              "value": "01",
+              "show": "No-Operation (NOP)",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": [
+                {
+                  "name": "ip.opt.type",
+                  "pos": "54",
+                  "showname": "Type: 1",
+                  "size": "1",
+                  "value": "01",
+                  "show": "1",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": [
+                    {
+                      "name": "ip.opt.type.copy",
+                      "pos": "54",
+                      "showname": "0... .... = Copy on fragmentation: No",
+                      "size": "1",
+                      "value": "0",
+                      "show": "0",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    },
+                    {
+                      "name": "ip.opt.type.class",
+                      "pos": "54",
+                      "showname": ".00. .... = Class: Control (0)",
+                      "size": "1",
+                      "value": "0",
+                      "show": "0",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    },
+                    {
+                      "name": "ip.opt.type.number",
+                      "pos": "54",
+                      "showname": "...0 0001 = Number: No-Operation (NOP) (1)",
+                      "size": "1",
+                      "value": "1",
+                      "show": "1",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    }
+                  ],
+                  "protos": null
+                }
+              ],
+              "protos": null
+            },
+            {
+              "name": "",
+              "pos": "55",
+              "showname": null,
+              "size": "1",
+              "value": "01",
+              "show": "No-Operation (NOP)",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": [
+                {
+                  "name": "ip.opt.type",
+                  "pos": "55",
+                  "showname": "Type: 1",
+                  "size": "1",
+                  "value": "01",
+                  "show": "1",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": [
+                    {
+                      "name": "ip.opt.type.copy",
+                      "pos": "55",
+                      "showname": "0... .... = Copy on fragmentation: No",
+                      "size": "1",
+                      "value": "0",
+                      "show": "0",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    },
+                    {
+                      "name": "ip.opt.type.class",
+                      "pos": "55",
+                      "showname": ".00. .... = Class: Control (0)",
+                      "size": "1",
+                      "value": "0",
+                      "show": "0",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    },
+                    {
+                      "name": "ip.opt.type.number",
+                      "pos": "55",
+                      "showname": "...0 0001 = Number: No-Operation (NOP) (1)",
+                      "size": "1",
+                      "value": "1",
+                      "show": "1",
+                      "unmaskedvalue": "01",
+                      "hide": null,
+                      "fields": null,
+                      "protos": null
+                    }
+                  ],
+                  "protos": null
+                }
+              ],
+              "protos": null
+            },
+            {
+              "name": "",
+              "pos": "56",
+              "showname": null,
+              "size": "10",
+              "value": "080a0014f4f811bdb98f",
+              "show": "Timestamps: TSval 1373432, TSecr 297646479",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": [
+                {
+                  "name": "tcp.option_kind",
+                  "pos": "56",
+                  "showname": "Kind: Timestamp (8)",
+                  "size": "1",
+                  "value": "08",
+                  "show": "8",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": null,
+                  "protos": null
+                },
+                {
+                  "name": "tcp.option_len",
+                  "pos": "57",
+                  "showname": "Length: 10",
+                  "size": "1",
+                  "value": "0a",
+                  "show": "10",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": null,
+                  "protos": null
+                },
+                {
+                  "name": "tcp.options.timestamp.tsval",
+                  "pos": "58",
+                  "showname": "Timestamp value: 1373432",
+                  "size": "4",
+                  "value": "0014f4f8",
+                  "show": "1373432",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": null,
+                  "protos": null
+                },
+                {
+                  "name": "tcp.options.timestamp.tsecr",
+                  "pos": "62",
+                  "showname": "Timestamp echo reply: 297646479",
+                  "size": "4",
+                  "value": "11bdb98f",
+                  "show": "297646479",
+                  "unmaskedvalue": null,
+                  "hide": null,
+                  "fields": null,
+                  "protos": null
+                }
+              ],
+              "protos": null
+            }
+          ],
+          "protos": null
+        },
+        {
+          "name": "tcp.analysis",
+          "pos": "34",
+          "showname": "SEQ/ACK analysis",
+          "size": "0",
+          "value": "",
+          "show": "",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": [
+            {
+              "name": "tcp.analysis.bytes_in_flight",
+              "pos": "34",
+              "showname": "Bytes in flight: 656",
+              "size": "0",
+              "value": null,
+              "show": "656",
+              "unmaskedvalue": null,
+              "hide": null,
+              "fields": null,
+              "protos": null
+            }
+          ],
+          "protos": null
+        }
+      ]
+    },
+    {
+      "name": "ssh",
+      "pos": "66",
+      "showname": "SSH Protocol",
+      "size": "656",
+      "hide": null,
+      "fields": [
+        {
+          "name": "ssh.encrypted_packet",
+          "pos": "66",
+          "showname": "Encrypted Packet: 5b2bfe1fa006867834412184af9f5b239737763adead7140...",
+          "size": "656",
+          "value": "5b2bfe1fa006867834412184af9f5b239737763adead71408fc01b88e548b2cc86f341a39771c6ed16f2b0bb3e6ab6109e73c7d68ca7545852f91930e4633c17fb9dc7aa794b0d820d0fa3ac65bf0f013e5449d5953d2943506657e2b76b548e67f5c9ce1a4c53db1b52465bde7208baf11f3fe01975418b4db186a38ad32947d1908b62e532da4b729353a932423d25f5f734484076aa4143c6a74937a4ea49448e261ae1ecb6b07bbdf5c98d0855940a19018c88263b8936f7b3e9a4b6cd98090fa10a10e37ad20fe5d833071ad6d5b2886ba85ec72affb83e316443dbe29dbf643e6aa05595c90765cf85f6da55cc1c09d8dccc7d05da022429ad602a559a044b7e2663b0c153a3011bf824ef8d1fa56cba957c5f5d2276a1c9e92de65782f406848c6e20f634c5d1fea843a8bf1a4058e85553f5838f7299958fbf54be84e46c5a3c3965f8bed7fe03a9a1168a892e0073adeb54deca171a318d11fc1a8179f91632310213da327965a40bc6fe18eae55e8da6b57d7ef9f3a05b42381bcb3db8f8efd6d0c638a2cdd46efb0b8f1274e98672f644b2275947e626b02e5166f86c2dd4a67b81e213f8c064927a396815db589f10e5e521ffedb13f8edbe2de01c6fc8bf0e12c82212e497794aa045e9b6fcca83b4cad0a3b5e6ca2d1feaf8887b4d64f22989396e
 cfa8f7f1835eed422580505109fed36797bdc10a9168d5148daef6a8710c3df1d6366c9763ab4ebd359d86a8ea14819252fb52ba423422d1f60b0179316b3729e479ba07e88cb886938c8daae65d470dde91e5336e0fc4221a72cc49057d878aa5924875d097483e94bc44a4ea93aee8780e56c50a405932841f50da156e1f90559a7c4f76999442fb433a26fc703dea656bbe03790ac3c9c5318ff5f81d87d483524bbfe7ff167",
+          "show": "5b:2b:fe:1f:a0:06:86:78:34:41:21:84:af:9f:5b:23:97:37:76:3a:de:ad:71:40:8f:c0:1b:88:e5:48:b2:cc:86:f3:41:a3:97:71:c6:ed:16:f2:b0:bb:3e:6a:b6:10:9e:73:c7:d6:8c:a7:54:58:52:f9:19:30:e4:63:3c:17:fb:9d:c7:aa:79:4b:0d:82:0d:0f:a3:ac:65:bf:0f:01:3e:54:49:d5:95:3d:29:43:50:66:57:e2:b7:6b:54:8e:67:f5:c9:ce:1a:4c:53:db:1b:52:46:5b:de:72:08:ba:f1:1f:3f:e0:19:75:41:8b:4d:b1:86:a3:8a:d3:29:47:d1:90:8b:62:e5:32:da:4b:72:93:53:a9:32:42:3d:25:f5:f7:34:48:40:76:aa:41:43:c6:a7:49:37:a4:ea:49:44:8e:26:1a:e1:ec:b6:b0:7b:bd:f5:c9:8d:08:55:94:0a:19:01:8c:88:26:3b:89:36:f7:b3:e9:a4:b6:cd:98:09:0f:a1:0a:10:e3:7a:d2:0f:e5:d8:33:07:1a:d6:d5:b2:88:6b:a8:5e:c7:2a:ff:b8:3e:31:64:43:db:e2:9d:bf:64:3e:6a:a0:55:95:c9:07:65:cf:85:f6:da:55:cc:1c:09:d8:dc:cc:7d:05:da:02:24:29:ad:60:2a:55:9a:04:4b:7e:26:63:b0:c1:53:a3:01:1b:f8:24:ef:8d:1f:a5:6c:ba:95:7c:5f:5d:22:76:a1:c9:e9:2d:e6:57:82:f4:06:84:8c:6e:20:f6:34:c5:d1:fe:a8:43:a8:bf:1a:40:58:e8:55:53:f5:83:8f:72:99:95:8f:bf:54:be:84:e4:6c:5a:3c:39:65:
 f8:be:d7:fe:03:a9:a1:16:8a:89:2e:00:73:ad:eb:54:de:ca:17:1a:31:8d:11:fc:1a:81:79:f9:16:32:31:02:13:da:32:79:65:a4:0b:c6:fe:18:ea:e5:5e:8d:a6:b5:7d:7e:f9:f3:a0:5b:42:38:1b:cb:3d:b8:f8:ef:d6:d0:c6:38:a2:cd:d4:6e:fb:0b:8f:12:74:e9:86:72:f6:44:b2:27:59:47:e6:26:b0:2e:51:66:f8:6c:2d:d4:a6:7b:81:e2:13:f8:c0:64:92:7a:39:68:15:db:58:9f:10:e5:e5:21:ff:ed:b1:3f:8e:db:e2:de:01:c6:fc:8b:f0:e1:2c:82:21:2e:49:77:94:aa:04:5e:9b:6f:cc:a8:3b:4c:ad:0a:3b:5e:6c:a2:d1:fe:af:88:87:b4:d6:4f:22:98:93:96:ec:fa:8f:7f:18:35:ee:d4:22:58:05:05:10:9f:ed:36:79:7b:dc:10:a9:16:8d:51:48:da:ef:6a:87:10:c3:df:1d:63:66:c9:76:3a:b4:eb:d3:59:d8:6a:8e:a1:48:19:25:2f:b5:2b:a4:23:42:2d:1f:60:b0:17:93:16:b3:72:9e:47:9b:a0:7e:88:cb:88:69:38:c8:da:ae:65:d4:70:dd:e9:1e:53:36:e0:fc:42:21:a7:2c:c4:90:57:d8:78:aa:59:24:87:5d:09:74:83:e9:4b:c4:4a:4e:a9:3a:ee:87:80:e5:6c:50:a4:05:93:28:41:f5:0d:a1:56:e1:f9:05:59:a7:c4:f7:69:99:44:2f:b4:33:a2:6f:c7:03:de:a6:56:bb:e0:37:90:ac:3c:9c:53:18:ff:5f:81:d8:7d:48:35:24:bb:fe:7f:f1:67",
+          "unmaskedvalue": null,
+          "hide": null,
+          "fields": null,
+          "protos": null
+        }
+      ]
+    }
+  ]
+};

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
new file mode 100644
index 0000000..bb160a0
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
@@ -0,0 +1,55 @@
+/**
+ * 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, OnInit, Input } from '@angular/core';
+import { PdmlPacket, PdmlProto, PdmlField } from '../model/pdml'
+
+@Component({
+  selector: '[app-pcap-packet-line]',
+  templateUrl: './pcap-packet-line.component.html',
+  styleUrls: ['./pcap-packet-line.component.scss']
+})
+export class PcapPacketLineComponent implements OnInit {
+
+  @Input() packet: PdmlPacket
+
+  ip: {
+    timestamp: PdmlField,
+    ipSrcAddr: PdmlField, ipSrcPort: PdmlField,
+    ipDestAddr: PdmlField, ipDestPort: PdmlField,
+    protocol: PdmlField
+  }
+
+  constructor() { }
+
+  ngOnInit() {
+    const genProto: PdmlProto = this.packet.protos.filter(p => p.name == "geninfo")[0]
+    const ipProto: PdmlProto = this.packet.protos.filter(p => p.name == "ip")[0]
+    const tcpProto: PdmlProto = this.packet.protos.filter(p => p.name == "tcp")[0]
+
+    this.ip = {
+      timestamp: PdmlProto.findField(genProto,'timestamp'),
+      ipSrcAddr: PdmlProto.findField(ipProto,'ip.src'),
+      ipSrcPort: PdmlProto.findField(tcpProto,'tcp.srcport'),
+      ipDestAddr: PdmlProto.findField(ipProto,'ip.dst'),
+      ipDestPort: PdmlProto.findField(tcpProto,'tcp.dstport'),
+      protocol: PdmlProto.findField(ipProto,'ip.proto')
+    };
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
new file mode 100644
index 0000000..750c315
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.html
@@ -0,0 +1,21 @@
+<!--
+  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="proto" *ngFor="let proto of packet.protos" (click)="proto.expanded = !proto.expanded">
+  <div class="proto-header">{{ proto.showname }}</div>
+  <div class="proto-fields" *ngIf="proto.expanded">
+    <div *ngFor="let field of proto.fields">
+      <span class="field-name">{{ field.name}}</span> {{ field.showname}}
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/metron/blob/d5eb56a9/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.component.scss
new file mode 100644
index 0000000..e9d4662
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet/pcap-packet.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.
+ */
+@import "../../../_variables.scss";
+
+.proto-header {
+  font-weight: bold;
+  border-top: 1px solid $table-border-color;
+}
+
+.proto {
+  margin-left: 1em;
+}
+
+.proto-fields {
+  margin-left: 1em;
+}
+
+
+.field-name {
+  display: none
+}


[41/50] [abbrv] metron git commit: METRON-1739 UDP packets are not handled (merrimanr) closes apache/metron#1168

Posted by ni...@apache.org.
METRON-1739 UDP packets are not handled (merrimanr) closes apache/metron#1168


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/14e80b3c
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/14e80b3c
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/14e80b3c

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 14e80b3c229ea2e5a8d615c87d822d4b44d8ec98
Parents: 076a6a1
Author: merrimanr <me...@gmail.com>
Authored: Fri Aug 17 08:47:32 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Fri Aug 17 08:47:32 2018 -0500

----------------------------------------------------------------------
 .../src/app/pcap/model/pdml.mock.ts             | 31 ++++++++
 .../pcap-packet-line.component.spec.ts          | 53 ++++++++++++--
 .../pcap-packet-line.component.ts               | 15 ++--
 .../java/org/apache/metron/pcap/PcapHelper.java | 18 ++++-
 .../org/apache/metron/pcap/PcapHelperTest.java  | 76 ++++++++++++++++++++
 5 files changed, 180 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/14e80b3c/metron-interface/metron-alerts/src/app/pcap/model/pdml.mock.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pdml.mock.ts b/metron-interface/metron-alerts/src/app/pcap/model/pdml.mock.ts
index 6316b92..2520da1 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pdml.mock.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pdml.mock.ts
@@ -60,3 +60,34 @@ export const fakePacket = {
     } as PdmlProto
   ]
 } as PdmlPacket;
+
+export const fakeUdpPacket = {
+  "name": '',
+  "expanded": false,
+  "protos": [
+    {
+      "name": "geninfo",
+      "showname": "",
+      "fields": [
+        { "name": "timestamp", "pos": "0", "showname": "Captured Time", "size": "342", "value": "1534414874.932794000", "show": "Aug 16, 2018 10:21:14.932794000 UTC", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField
+      ]
+    } as PdmlProto,
+    {
+      "name": "ip",
+      "showname": "",
+      "fields": [
+        { "name": "ip.proto", "pos": "23", "showname": "Protocol: UDP (17)", "size": "1", "value": "11", "show": "17", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField,
+        { "name": "ip.src", "pos": "26", "showname": "Source: 0.0.0.0 (0.0.0.0)", "size": "4", "value": "00000000", "show": "0.0.0.0", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField,
+        { "name": "ip.dst", "pos": "30", "showname": "Destination: 255.255.255.255 (255.255.255.255)", "size": "4", "value": "ffffffff", "show": "255.255.255.255", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField
+      ]
+    } as PdmlProto,
+    {
+      "name": "udp",
+      "showname": "User Datagram Protocol, Src Port: bootpc (68), Dst Port: bootps (67)",
+      "fields": [
+        { "name": "udp.srcport", "pos": "34", "showname": "Source port: bootpc (68)", "size": "2", "value": "0044", "show": "68", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField,
+        { "name": "udp.dstport", "pos": "36", "showname": "Destination port: bootps (67)", "size": "2", "value": "0043", "show": "67", "unmaskedvalue": null, "hide": null, "fields": null, "protos": null } as PdmlField
+      ],
+    } as PdmlProto
+  ]
+} as PdmlPacket;

http://git-wip-us.apache.org/repos/asf/metron/blob/14e80b3c/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
index d01c0cb..00f081f 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.spec.ts
@@ -17,6 +17,7 @@
  */
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 import { fakePacket } from '../model/pdml.mock';
+import { fakeUdpPacket } from '../model/pdml.mock';
 
 import { PcapPacketLineComponent } from './pcap-packet-line.component';
 
@@ -34,8 +35,6 @@ describe('PcapPacketLineComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(PcapPacketLineComponent);
     component = fixture.componentInstance;
-    component.packet = fakePacket;
-    fixture.detectChanges();
   });
 
   it('should be created', () => {
@@ -43,6 +42,8 @@ describe('PcapPacketLineComponent', () => {
   });
 
   it('should extract timestamp fields', () => {
+    component.packet = fakePacket;
+    fixture.detectChanges();
     expect(component.ip.timestamp).toEqual({ 
       "name": "timestamp", 
       "pos": "0", 
@@ -59,6 +60,8 @@ describe('PcapPacketLineComponent', () => {
   });
 
   it('should extract ipSrcAddr fields', () => {
+    component.packet = fakePacket;
+    fixture.detectChanges();
     expect(component.ip.ipSrcAddr).toEqual({ 
       "name": "ip.src", 
       "pos": "26", 
@@ -74,6 +77,8 @@ describe('PcapPacketLineComponent', () => {
   });
 
   it('should extract ipSrcPort fields', () => {
+    component.packet = fakePacket;
+    fixture.detectChanges();
     expect(component.ip.ipSrcPort).toEqual({ 
       "name": "tcp.srcport", 
       "pos": "34", 
@@ -88,7 +93,9 @@ describe('PcapPacketLineComponent', () => {
     });
   });
 
-  it('should extract ipDestAddr fields', () => {
+  it('should extract TCP ipDestAddr fields', () => {
+    component.packet = fakePacket;
+    fixture.detectChanges();
     expect(component.ip.ipDestAddr).toEqual({ 
       "name": "ip.dst", 
       "pos": "30", 
@@ -103,7 +110,9 @@ describe('PcapPacketLineComponent', () => {
     });
   });
   
-  it('should extract ipDestPort fields', () => {
+  it('should extract TCP ipDestPort fields', () => {
+    component.packet = fakePacket;
+    fixture.detectChanges();
     expect(component.ip.ipDestPort).toEqual({ 
       "name": "tcp.dstport", 
       "pos": "36", 
@@ -119,6 +128,8 @@ describe('PcapPacketLineComponent', () => {
   });
   
   it('should extract protocol fields', () => {
+    component.packet = fakePacket;
+    fixture.detectChanges();
     expect(component.ip.protocol).toEqual({ 
       "name": "ip.proto", 
       "pos": "23", 
@@ -132,4 +143,38 @@ describe('PcapPacketLineComponent', () => {
       "protos": null 
     });
   });
+
+  it('should extract UDP ipSrcPort fields', () => {
+    component.packet = fakeUdpPacket;
+    fixture.detectChanges();
+    expect(component.ip.ipSrcPort).toEqual({
+      "name": "udp.srcport",
+      "pos": "34",
+      "showname": "Source port: bootpc (68)",
+      "size": "2",
+      "value": "0044",
+      "show": "68",
+      "unmaskedvalue": null,
+      "hide": null,
+      "fields": null,
+      "protos": null
+    });
+  });
+
+  it('should extract UDP ipDestPort fields', () => {
+    component.packet = fakeUdpPacket;
+    fixture.detectChanges();
+    expect(component.ip.ipDestPort).toEqual({
+      "name": "udp.dstport",
+      "pos": "36",
+      "showname": "Destination port: bootps (67)",
+      "size": "2",
+      "value": "0043",
+      "show": "67",
+      "unmaskedvalue": null,
+      "hide": null,
+      "fields": null,
+      "protos": null
+    });
+  });
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/14e80b3c/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
index bb160a0..b1546af 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-packet-line/pcap-packet-line.component.ts
@@ -25,28 +25,29 @@ import { PdmlPacket, PdmlProto, PdmlField } from '../model/pdml'
 })
 export class PcapPacketLineComponent implements OnInit {
 
-  @Input() packet: PdmlPacket
+  @Input() packet: PdmlPacket;
 
   ip: {
     timestamp: PdmlField,
     ipSrcAddr: PdmlField, ipSrcPort: PdmlField,
     ipDestAddr: PdmlField, ipDestPort: PdmlField,
     protocol: PdmlField
-  }
+  };
 
   constructor() { }
 
   ngOnInit() {
-    const genProto: PdmlProto = this.packet.protos.filter(p => p.name == "geninfo")[0]
-    const ipProto: PdmlProto = this.packet.protos.filter(p => p.name == "ip")[0]
-    const tcpProto: PdmlProto = this.packet.protos.filter(p => p.name == "tcp")[0]
+    const genProto: PdmlProto = this.packet.protos.filter(p => p.name == "geninfo")[0];
+    const ipProto: PdmlProto = this.packet.protos.filter(p => p.name == "ip")[0];
+    const tcpProto: PdmlProto = this.packet.protos.filter(p => p.name == "tcp")[0];
+    const udpProto: PdmlProto = this.packet.protos.filter(p => p.name == "udp")[0];
 
     this.ip = {
       timestamp: PdmlProto.findField(genProto,'timestamp'),
       ipSrcAddr: PdmlProto.findField(ipProto,'ip.src'),
-      ipSrcPort: PdmlProto.findField(tcpProto,'tcp.srcport'),
+      ipSrcPort: tcpProto ? PdmlProto.findField(tcpProto,'tcp.srcport') : PdmlProto.findField(udpProto,'udp.srcport'),
       ipDestAddr: PdmlProto.findField(ipProto,'ip.dst'),
-      ipDestPort: PdmlProto.findField(tcpProto,'tcp.dstport'),
+      ipDestPort: tcpProto ? PdmlProto.findField(tcpProto,'tcp.dstport') : PdmlProto.findField(udpProto,'udp.dstport'),
       protocol: PdmlProto.findField(ipProto,'ip.proto')
     };
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/14e80b3c/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapHelper.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapHelper.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapHelper.java
index 1ebdf7c..aa3eafe 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapHelper.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/PcapHelper.java
@@ -257,9 +257,23 @@ public class PcapHelper {
       if(pi.getTcpPacket().getDestination() != null ) {
         ret.put(org.apache.metron.common.Constants.Fields.DST_PORT.getName(), pi.getTcpPacket().getDestination().getPort());
       }
-      if(pi.getIpv4Packet() != null) {
-        ret.put(org.apache.metron.common.Constants.Fields.PROTOCOL.getName(), pi.getIpv4Packet().getProtocol());
+    }
+    if(pi.getUdpPacket() != null) {
+      if (pi.getUdpPacket().getSource() != null) {
+        if(pi.getUdpPacket().getSource().getAddress() != null) {
+          ret.put(org.apache.metron.common.Constants.Fields.SRC_ADDR.getName(), pi.getUdpPacket().getSource().getAddress().getHostAddress());
+        }
+        ret.put(org.apache.metron.common.Constants.Fields.SRC_PORT.getName(), pi.getUdpPacket().getSource().getPort());
       }
+      if (pi.getUdpPacket().getDestination() != null) {
+        if(pi.getUdpPacket().getDestination().getAddress() != null ) {
+          ret.put(org.apache.metron.common.Constants.Fields.DST_ADDR.getName(), pi.getUdpPacket().getDestination().getAddress().getHostAddress());
+        }
+        ret.put(org.apache.metron.common.Constants.Fields.DST_PORT.getName(), pi.getUdpPacket().getDestination().getPort());
+      }
+    }
+    if(pi.getIpv4Packet() != null) {
+      ret.put(org.apache.metron.common.Constants.Fields.PROTOCOL.getName(), pi.getIpv4Packet().getProtocol());
     }
     return ret;
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/14e80b3c/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/PcapHelperTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/PcapHelperTest.java b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/PcapHelperTest.java
index 5d2bee0..73f359d 100644
--- a/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/PcapHelperTest.java
+++ b/metron-platform/metron-pcap/src/test/java/org/apache/metron/pcap/PcapHelperTest.java
@@ -27,11 +27,22 @@ import org.apache.hadoop.io.SequenceFile;
 import org.apache.metron.spout.pcap.Endianness;
 import org.junit.Assert;
 import org.junit.Test;
+import org.krakenapps.pcap.decoder.ip.Ipv4Packet;
+import org.krakenapps.pcap.decoder.tcp.TcpPacket;
+import org.krakenapps.pcap.decoder.udp.UdpPacket;
 
 import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+
+import static org.apache.metron.common.Constants.Fields;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class PcapHelperTest {
   public static List<byte[]> readSamplePackets(String pcapLoc) throws IOException {
@@ -75,4 +86,69 @@ public class PcapHelperTest {
       }
     }
   }
+
+  @Test
+  public void packetToFieldsShouldProperlyParserTcpPackets() throws Exception {
+    PacketInfo packetInfo = mock(PacketInfo.class);
+    when(packetInfo.getPacketBytes()).thenReturn("packet bytes".getBytes(StandardCharsets.UTF_8));
+    TcpPacket tcpPacket = mock(TcpPacket.class);
+    // Tcp source address and port
+    InetAddress tcpSourceInetAddress = mock(InetAddress.class);
+    when(tcpSourceInetAddress.getHostAddress()).thenReturn("tcp source address");
+    when(tcpPacket.getSourceAddress()).thenReturn(tcpSourceInetAddress);
+    InetSocketAddress tcpSourceInetSocketAddress = new InetSocketAddress(22);
+    when(tcpPacket.getSource()).thenReturn(tcpSourceInetSocketAddress);
+    // Tcp destination address and port
+    InetAddress tcpDestinationInetAddress = mock(InetAddress.class);
+    when(tcpDestinationInetAddress.getHostAddress()).thenReturn("tcp destination address");
+    when(tcpPacket.getDestinationAddress()).thenReturn(tcpDestinationInetAddress);
+    InetSocketAddress tcpDestinationInetSocketAddress = new InetSocketAddress(55791);
+    when(tcpPacket.getDestination()).thenReturn(tcpDestinationInetSocketAddress);
+    when(packetInfo.getTcpPacket()).thenReturn(tcpPacket);
+
+    Ipv4Packet ipv4Packet = mock(Ipv4Packet.class);
+    when(ipv4Packet.getProtocol()).thenReturn(6);
+    when(packetInfo.getIpv4Packet()).thenReturn(ipv4Packet);
+
+    Map<String, Object> actualFields = PcapHelper.packetToFields(packetInfo);
+    Assert.assertArrayEquals("packet bytes".getBytes(StandardCharsets.UTF_8),
+            (byte[]) actualFields.get(PcapHelper.PacketFields.PACKET_DATA.getName()));
+    Assert.assertEquals("tcp source address", actualFields.get(Fields.SRC_ADDR.getName()));
+    Assert.assertEquals(22, actualFields.get(Fields.SRC_PORT.getName()));
+    Assert.assertEquals("tcp destination address", actualFields.get(Fields.DST_ADDR.getName()));
+    Assert.assertEquals(55791, actualFields.get(Fields.DST_PORT.getName()));
+    Assert.assertEquals(6, actualFields.get(Fields.PROTOCOL.getName()));
+  }
+
+  @Test
+  public void packetToFieldsShouldProperlyParserUdpPackets() throws Exception {
+    PacketInfo packetInfo = mock(PacketInfo.class);
+    when(packetInfo.getPacketBytes()).thenReturn("packet bytes".getBytes(StandardCharsets.UTF_8));
+
+    UdpPacket udpPacket = mock(UdpPacket.class);
+    // Udp source address and port
+    InetAddress udpSourceInetAddress = mock(InetAddress.class);
+    when(udpSourceInetAddress.getHostAddress()).thenReturn("udp source address");
+    InetSocketAddress udpSourceInetSocketAddress = new InetSocketAddress(udpSourceInetAddress, 68);
+    when(udpPacket.getSource()).thenReturn(udpSourceInetSocketAddress);
+    // Udp destination address and port
+    InetAddress udpDestinationInetAddress = mock(InetAddress.class);
+    when(udpDestinationInetAddress.getHostAddress()).thenReturn("udp destination address");
+    InetSocketAddress udpDestinationInetSocketAddress = new InetSocketAddress(udpDestinationInetAddress, 67);
+    when(udpPacket.getDestination()).thenReturn(udpDestinationInetSocketAddress);
+    when(packetInfo.getUdpPacket()).thenReturn(udpPacket);
+
+    Ipv4Packet ipv4Packet = mock(Ipv4Packet.class);
+    when(ipv4Packet.getProtocol()).thenReturn(17);
+    when(packetInfo.getIpv4Packet()).thenReturn(ipv4Packet);
+
+    Map<String, Object> actualFields = PcapHelper.packetToFields(packetInfo);
+    Assert.assertArrayEquals("packet bytes".getBytes(StandardCharsets.UTF_8),
+            (byte[]) actualFields.get(PcapHelper.PacketFields.PACKET_DATA.getName()));
+    Assert.assertEquals("udp source address", actualFields.get(Fields.SRC_ADDR.getName()));
+    Assert.assertEquals(68, actualFields.get(Fields.SRC_PORT.getName()));
+    Assert.assertEquals("udp destination address", actualFields.get(Fields.DST_ADDR.getName()));
+    Assert.assertEquals(67, actualFields.get(Fields.DST_PORT.getName()));
+    Assert.assertEquals(17, actualFields.get(Fields.PROTOCOL.getName()));
+  }
 }


[29/50] [abbrv] metron git commit: Merge branch 'master' into feature/METRON-1554-pcap-query-panel

Posted by ni...@apache.org.
Merge branch 'master' into feature/METRON-1554-pcap-query-panel


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/d8a5922b
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/d8a5922b
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/d8a5922b

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: d8a5922b4cc5b20be7cb08f333a861241c365fd5
Parents: 9064cca 2b0060b
Author: Michael Miklavcic <mi...@gmail.com>
Authored: Thu Aug 9 09:31:01 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Thu Aug 9 09:31:01 2018 -0600

----------------------------------------------------------------------
 metron-sensors/pycapa/README.md        | 92 ++++++++++++++++++++++++++---
 metron-sensors/pycapa/requirements.txt |  6 +-
 2 files changed, 88 insertions(+), 10 deletions(-)
----------------------------------------------------------------------



[48/50] [abbrv] metron git commit: METRON-1757 Storm Profiler Serialization Exception (nickwallen) closes apache/metron#1178

Posted by ni...@apache.org.
METRON-1757 Storm Profiler Serialization Exception (nickwallen) closes apache/metron#1178


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/661e23e2
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/661e23e2
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/661e23e2

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 661e23e274d2bf88370d8feb5e5dbf1b82f15124
Parents: 29cbdcc
Author: nickwallen <ni...@nickallen.org>
Authored: Wed Aug 29 12:51:05 2018 -0400
Committer: nickallen <ni...@apache.org>
Committed: Wed Aug 29 12:51:05 2018 -0400

----------------------------------------------------------------------
 metron-analytics/metron-profiler-common/pom.xml | 26 +++------------
 .../metron/profiler/ProfileMeasurement.java     | 35 ++++++++++++++++++++
 .../apache/metron/profiler/ProfilePeriod.java   |  3 ++
 .../metron/profiler/ProfileMeasurementTest.java | 28 +++++++++++++---
 .../metron/profiler/ProfilePeriodTest.java      | 23 ++++++++++---
 .../profiler/hbase/SaltyRowKeyBuilderTest.java  | 11 ------
 metron-analytics/metron-profiler/README.md      | 18 ++++++----
 .../integration/ProfilerIntegrationTest.java    | 30 +++++++++++++----
 .../configuration/profiler/ProfileResult.java   |  4 +++
 .../profiler/ProfileResultExpressions.java      |  4 +++
 .../profiler/ProfilerConfigTest.java            | 35 +++++++++++++++-----
 11 files changed, 154 insertions(+), 63 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-analytics/metron-profiler-common/pom.xml
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler-common/pom.xml b/metron-analytics/metron-profiler-common/pom.xml
index 928515f..74253cc 100644
--- a/metron-analytics/metron-profiler-common/pom.xml
+++ b/metron-analytics/metron-profiler-common/pom.xml
@@ -55,6 +55,10 @@
                     <groupId>org.slf4j</groupId>
                     <artifactId>slf4j-log4j12</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>asm</groupId>
+                    <artifactId>asm</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
         <dependency>
@@ -109,30 +113,10 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.storm</groupId>
-            <artifactId>storm-core</artifactId>
-            <version>${global_storm_version}</version>
-            <scope>provided</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.apache.logging.log4j</groupId>
-                    <artifactId>log4j-api</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.apache.logging.log4j</groupId>
-                    <artifactId>log4j-core</artifactId>
-                </exclusion>
-                <exclusion>
-                    <artifactId>log4j-slf4j-impl</artifactId>
-                    <groupId>org.apache.logging.log4j</groupId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-all</artifactId>
             <version>${global_mockito_version}</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfileMeasurement.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfileMeasurement.java b/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfileMeasurement.java
index 4737c3d..2212297 100644
--- a/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfileMeasurement.java
+++ b/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfileMeasurement.java
@@ -20,6 +20,8 @@
 
 package org.apache.metron.profiler;
 
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.metron.common.configuration.profiler.ProfileConfig;
 
 import java.io.Serializable;
@@ -174,4 +176,37 @@ public class ProfileMeasurement implements Serializable {
   public void setTriageValues(Map<String, Object> triageValues) {
     this.triageValues = triageValues;
   }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    ProfileMeasurement that = (ProfileMeasurement) o;
+    return new EqualsBuilder()
+            .append(profileName, that.profileName)
+            .append(entity, that.entity)
+            .append(groups, that.groups)
+            .append(period, that.period)
+            .append(definition, that.definition)
+            .append(profileValue, that.profileValue)
+            .append(triageValues, that.triageValues)
+            .isEquals();
+  }
+
+  @Override
+  public int hashCode() {
+    return new HashCodeBuilder(17, 37)
+            .append(profileName)
+            .append(entity)
+            .append(groups)
+            .append(period)
+            .append(definition)
+            .append(profileValue)
+            .append(triageValues)
+            .toHashCode();
+  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfilePeriod.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfilePeriod.java b/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfilePeriod.java
index cbb8275..6c971a7 100644
--- a/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfilePeriod.java
+++ b/metron-analytics/metron-profiler-common/src/main/java/org/apache/metron/profiler/ProfilePeriod.java
@@ -47,6 +47,9 @@ public class ProfilePeriod implements Serializable {
    */
   private long durationMillis;
 
+  public ProfilePeriod() {
+    // no-arg constructor required for kryo serialization in storm
+  }
 
   /**
    * @param epochMillis A timestamp contained somewhere within the profile period.

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfileMeasurementTest.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfileMeasurementTest.java b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfileMeasurementTest.java
index 3a8d650..b25e694 100644
--- a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfileMeasurementTest.java
+++ b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfileMeasurementTest.java
@@ -20,9 +20,13 @@
 
 package org.apache.metron.profiler;
 
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
 import org.adrianwalker.multilinestring.Multiline;
 import org.apache.metron.common.configuration.profiler.ProfileConfig;
 import org.apache.metron.common.utils.SerDeUtils;
+import org.junit.Before;
 import org.junit.Test;
 
 import java.io.ByteArrayInputStream;
@@ -33,6 +37,7 @@ import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 public class ProfileMeasurementTest {
@@ -56,9 +61,9 @@ public class ProfileMeasurementTest {
   private ProfileConfig definition;
   private ProfileMeasurement measurement;
 
+  @Before
   public void setup() throws Exception {
     definition = ProfileConfig.fromJSON(profile);
-
     measurement = new ProfileMeasurement()
             .withProfileName("profile")
             .withEntity("entity")
@@ -74,10 +79,24 @@ public class ProfileMeasurementTest {
    */
   @Test
   public void testKryoSerialization() throws Exception {
+    assertNotNull(measurement);
+    Kryo kryo = new Kryo();
+
+    // serialize
+    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+    Output output = new Output(byteStream);
+    kryo.writeObject(output, measurement);
+
+    // validate serialization
+    byte[] bits = output.toBytes();
+    assertNotNull(bits);
+
+    // deserialize
+    Input input = new Input(new ByteArrayInputStream(bits));
+    ProfileMeasurement actual = kryo.readObject(input, ProfileMeasurement.class);
 
-    // round-trip serialization
-    byte[] raw = SerDeUtils.toBytes(measurement);
-    Object actual = SerDeUtils.fromBytes(raw, Object.class);
+    // validate deserialization
+    assertNotNull(actual);
     assertEquals(measurement, actual);
   }
 
@@ -88,6 +107,7 @@ public class ProfileMeasurementTest {
    */
   @Test
   public void testJavaSerialization() throws Exception {
+    assertNotNull(measurement);
 
     // serialize using java
     ByteArrayOutputStream bytes = new ByteArrayOutputStream();

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfilePeriodTest.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfilePeriodTest.java b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfilePeriodTest.java
index f52bd09..17bb8fa 100644
--- a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfilePeriodTest.java
+++ b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/ProfilePeriodTest.java
@@ -20,6 +20,9 @@
 
 package org.apache.metron.profiler;
 
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
 import org.apache.metron.common.utils.SerDeUtils;
 import org.junit.Test;
 
@@ -31,6 +34,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.stream.IntStream;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -137,13 +141,24 @@ public class ProfilePeriodTest {
    */
   @Test
   public void testKryoSerialization() throws Exception {
-
     ProfilePeriod expected = new ProfilePeriod(AUG2016, 1, TimeUnit.HOURS);
+    Kryo kryo = new Kryo();
+
+    // serialize
+    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+    Output output = new Output(byteStream);
+    kryo.writeObject(output, expected);
+
+    // validate serialization
+    byte[] bits = output.toBytes();
+    assertNotNull(bits);
 
-    // round-trip java serialization
-    byte[] raw = SerDeUtils.toBytes(expected);
-    Object actual = SerDeUtils.fromBytes(raw, Object.class);
+    // deserialize
+    Input input = new Input(new ByteArrayInputStream(bits));
+    ProfilePeriod actual = kryo.readObject(input, ProfilePeriod.class);
 
+    // validate deserialization
+    assertNotNull(actual);
     assertEquals(expected, actual);
   }
 

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/hbase/SaltyRowKeyBuilderTest.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/hbase/SaltyRowKeyBuilderTest.java b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/hbase/SaltyRowKeyBuilderTest.java
index 57edea0..e92a447 100644
--- a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/hbase/SaltyRowKeyBuilderTest.java
+++ b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/hbase/SaltyRowKeyBuilderTest.java
@@ -20,11 +20,8 @@
 
 package org.apache.metron.profiler.hbase;
 
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.storm.tuple.Tuple;
 import org.apache.metron.profiler.ProfileMeasurement;
 import org.apache.metron.profiler.ProfilePeriod;
-import org.apache.metron.profiler.hbase.SaltyRowKeyBuilder;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -39,9 +36,6 @@ import java.util.concurrent.TimeUnit;
 
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 /**
  * Tests the SaltyRowKeyBuilder.
@@ -54,7 +48,6 @@ public class SaltyRowKeyBuilderTest {
 
   private SaltyRowKeyBuilder rowKeyBuilder;
   private ProfileMeasurement measurement;
-  private Tuple tuple;
 
   /**
    * Thu, Aug 25 2016 13:27:10 GMT
@@ -70,10 +63,6 @@ public class SaltyRowKeyBuilderTest {
             .withEntity("entity")
             .withPeriod(AUG2016, periodDuration, periodUnits);
 
-    // the tuple will contain the original message
-    tuple = mock(Tuple.class);
-    when(tuple.getValueByField(eq("measurement"))).thenReturn(measurement);
-
     rowKeyBuilder = new SaltyRowKeyBuilder(saltDivisor, periodDuration, periodUnits);
   }
 

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-analytics/metron-profiler/README.md
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler/README.md b/metron-analytics/metron-profiler/README.md
index 1a17e10..345f38a 100644
--- a/metron-analytics/metron-profiler/README.md
+++ b/metron-analytics/metron-profiler/README.md
@@ -221,9 +221,10 @@ This section will describe the steps required to get your first "Hello, World!""
 
 Continuing the previous running example, at this point, you have seen how your profile behaves against real, live telemetry in a controlled execution environment.  The next step is to deploy your profile to the live, actively running Profiler topology.
 
-1.  Start the Stellar Shell with the `-z ZK:2181` command line argument.  This is required when deploying a new profile to the active Profiler topology.  Replace `ZK:2181` with a URL that is appropriate to your environment.
+1.  Start the Stellar Shell with the `-z` command line argument so that a connection to Zookeeper is established.  This is required when  deploying a new profile definition as shown in the steps below.
 	```
-	[root@node1 ~]# $METRON_HOME/bin/stellar -z ZK:2181
+  [root@node1 ~]# source /etc/default/metron
+	[root@node1 ~]# $METRON_HOME/bin/stellar -z $ZOOKEEPER
 	Stellar, Go!
 	[Stellar]>>>
 	[Stellar]>>> %functions CONFIG CONFIG_GET, CONFIG_PUT
@@ -280,16 +281,17 @@ Continuing the previous running example, at this point, you have seen how your p
     }
     ```
 
-1. Upload the profile definition to Zookeeper.  Change `node1:2181` to refer the actual Zookeeper host in your environment.
+1. Upload the profile definition to Zookeeper.
 
     ```
+    $ source /etc/default/metron
     $ cd $METRON_HOME
-    $ bin/zk_load_configs.sh -m PUSH -i config/zookeeper/ -z node1:2181
+    $ bin/zk_load_configs.sh -m PUSH -i config/zookeeper/ -z $ZOOKEEPER
     ```
 
     You can validate this by reading back the Metron configuration from Zookeeper using the same script. The result should look-like the following.
     ```
-    $ bin/zk_load_configs.sh -m DUMP -z node1:2181
+    $ bin/zk_load_configs.sh -m DUMP -z $ZOOKEEPER
     ...
     PROFILER Config: profiler
     {
@@ -317,7 +319,8 @@ Continuing the previous running example, at this point, you have seen how your p
 1. Use the [Profiler Client](../metron-profiler-client) to read the profile data.  The following `PROFILE_GET` command will read the data written by the `hello-world` profile. This assumes that `10.0.0.1` is one of the values for `ip_src_addr` contained within the telemetry consumed by the Profiler.
 
     ```
-    $ bin/stellar -z node1:2181
+    $ source /etc/default/metron
+    $ bin/stellar -z $ZOOKEEPER
     [Stellar]>>> PROFILE_GET( "hello-world", "10.0.0.1", PROFILE_FIXED(30, "MINUTES"))
     [451, 448]
     ```
@@ -827,7 +830,8 @@ It is assumed that the PROFILE_GET client is configured as described [here](../m
 
 Retrieve the last 30 minutes of profile measurements for a specific host.
 ```
-$ bin/stellar -z node1:2181
+$ source /etc/default/metron
+$ bin/stellar -z $ZOOKEEPER
 
 [Stellar]>>> stats := PROFILE_GET( "example4", "10.0.0.1", PROFILE_FIXED(30, "MINUTES"))
 [Stellar]>>> stats

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/integration/ProfilerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/integration/ProfilerIntegrationTest.java b/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/integration/ProfilerIntegrationTest.java
index c02c469..268ce26 100644
--- a/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/integration/ProfilerIntegrationTest.java
+++ b/metron-analytics/metron-profiler/src/test/java/org/apache/metron/profiler/integration/ProfilerIntegrationTest.java
@@ -20,7 +20,12 @@
 
 package org.apache.metron.profiler.integration;
 
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.esotericsoftware.kryo.serializers.FieldSerializer;
 import org.adrianwalker.multilinestring.Multiline;
+import org.apache.commons.io.output.ByteArrayOutputStream;
 import org.apache.hadoop.hbase.Cell;
 import org.apache.hadoop.hbase.client.Put;
 import org.apache.hadoop.hbase.util.Bytes;
@@ -39,16 +44,27 @@ import org.apache.metron.profiler.hbase.ColumnBuilder;
 import org.apache.metron.profiler.hbase.RowKeyBuilder;
 import org.apache.metron.profiler.hbase.SaltyRowKeyBuilder;
 import org.apache.metron.profiler.hbase.ValueOnlyColumnBuilder;
+import org.apache.storm.Config;
+import org.apache.storm.serialization.KryoTupleDeserializer;
+import org.apache.storm.serialization.KryoTupleSerializer;
+import org.apache.storm.serialization.KryoValuesDeserializer;
+import org.apache.storm.serialization.KryoValuesSerializer;
+import org.apache.storm.task.TopologyContext;
+import org.apache.storm.tuple.Tuple;
+import org.apache.storm.tuple.TupleImpl;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.mockito.Mockito;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
@@ -58,6 +74,7 @@ import static com.google.code.tempusfugit.temporal.Timeout.timeout;
 import static com.google.code.tempusfugit.temporal.WaitFor.waitOrTimeout;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -313,16 +330,15 @@ public class ProfilerIntegrationTest extends BaseIntegrationTest {
       // storm settings
       setProperty("profiler.workers", "1");
       setProperty("profiler.executors", "0");
-      setProperty("storm.auto.credentials", "[]");
-      setProperty("topology.auto-credentials", "[]");
-      setProperty("topology.message.timeout.secs", "60");
-      setProperty("topology.max.spout.pending", "100000");
+      setProperty(Config.TOPOLOGY_AUTO_CREDENTIALS, "[]");
+      setProperty(Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS, "60");
+      setProperty(Config.TOPOLOGY_MAX_SPOUT_PENDING, "100000");
 
       // ensure tuples are serialized during the test, otherwise serialization problems
       // will not be found until the topology is run on a cluster with multiple workers
-      setProperty("topology.testing.always.try.serialize", "true");
-      setProperty("topology.fall.back.on.java.serialization", "false");
-      setProperty("topology.kryo.register", kryoSerializers);
+      setProperty(Config.TOPOLOGY_TESTING_ALWAYS_TRY_SERIALIZE, "true");
+      setProperty(Config.TOPOLOGY_FALL_BACK_ON_JAVA_SERIALIZATION, "false");
+      setProperty(Config.TOPOLOGY_KRYO_REGISTER, kryoSerializers);
 
       // kafka settings
       setProperty("profiler.input.topic", inputTopic);

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResult.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResult.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResult.java
index e2aa54d..b66efe8 100644
--- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResult.java
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResult.java
@@ -42,6 +42,10 @@ public class ProfileResult implements Serializable {
   @JsonProperty("triage")
   private ProfileTriageExpressions triageExpressions;
 
+  public ProfileResult() {
+    // no-arg constructor required for kryo serialization in storm
+  }
+
   @JsonCreator
   public ProfileResult(
           @JsonProperty(value = "profile", required = true) ProfileResultExpressions profileExpressions,

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java
index 2cada01..2af83b5 100644
--- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java
@@ -30,6 +30,10 @@ public class ProfileResultExpressions implements Serializable {
 
   private String expression;
 
+  public ProfileResultExpressions() {
+    // no-arg constructor required for kryo serialization in storm
+  }
+
   @JsonCreator
   public ProfileResultExpressions(String expression) {
     this.expression = expression;

http://git-wip-us.apache.org/repos/asf/metron/blob/661e23e2/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java
index 4c70a8c..f3b7c58 100644
--- a/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java
@@ -19,18 +19,22 @@
  */
 package org.apache.metron.common.configuration.profiler;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import org.adrianwalker.multilinestring.Multiline;
+import org.junit.Test;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
-import org.adrianwalker.multilinestring.Multiline;
-import org.apache.metron.common.utils.SerDeUtils;
-import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 /**
  * Tests the {@link ProfilerConfig} class.
@@ -252,11 +256,24 @@ public class ProfilerConfigTest {
 
     // setup a profiler config to serialize
     ProfilerConfig expected = ProfilerConfig.fromJSON(profilesToSerialize);
+    assertNotNull(expected);
+    Kryo kryo = new Kryo();
+
+    // serialize
+    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+    Output output = new Output(byteStream);
+    kryo.writeObject(output, expected);
+
+    // validate serialization
+    byte[] bits = output.toBytes();
+    assertNotNull(bits);
 
-    // round-trip java serialization
-    byte[] raw = SerDeUtils.toBytes(expected);
-    Object actual = SerDeUtils.fromBytes(raw, Object.class);
+    // deserialize
+    Input input = new Input(new ByteArrayInputStream(bits));
+    ProfilerConfig actual = kryo.readObject(input, ProfilerConfig.class);
 
+    // validate deserialization
+    assertNotNull(actual);
     assertEquals(expected, actual);
   }
 


[18/50] [abbrv] metron git commit: METRON-1675 PCAP UI - Introduce the paging capability (sardell via merrimanr) closes apache/metron#1121

Posted by ni...@apache.org.
METRON-1675 PCAP UI - Introduce the paging capability (sardell via merrimanr) closes apache/metron#1121


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/037b50b9
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/037b50b9
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/037b50b9

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 037b50b9a829d7292db1148fc628d9231da499e1
Parents: 756ceed
Author: sardell <sa...@hortonworks.com>
Authored: Tue Aug 7 09:04:33 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Tue Aug 7 09:04:33 2018 -0500

----------------------------------------------------------------------
 .../src/app/pcap/model/pcap-pagination.ts       | 21 ++++++
 .../src/app/pcap/model/pcap-status-response.ts  | 23 ++++++
 .../src/app/pcap/model/pcap.request.ts          |  2 +-
 .../app/pcap/pcap-list/pcap-list.component.html |  1 +
 .../pcap/pcap-list/pcap-list.component.spec.ts  | 11 +++
 .../app/pcap/pcap-list/pcap-list.component.ts   | 18 +++--
 .../pcap-pagination.component.html              | 19 +++++
 .../pcap-pagination.component.scss              | 40 ++++++++++
 .../pcap-pagination.component.spec.ts           | 78 ++++++++++++++++++++
 .../pcap-pagination.component.ts                | 41 ++++++++++
 .../pcap/pcap-panel/pcap-panel.component.html   |  7 +-
 .../pcap-panel/pcap-panel.component.spec.ts     |  2 +
 .../app/pcap/pcap-panel/pcap-panel.component.ts | 27 +++++--
 .../metron-alerts/src/app/pcap/pcap.module.ts   | 10 +--
 .../metron-alerts/src/app/pcap/pcap.routing.ts  |  2 +-
 .../src/app/pcap/service/pcap.service.spec.ts   |  2 +-
 .../src/app/pcap/service/pcap.service.ts        | 10 +--
 17 files changed, 279 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/model/pcap-pagination.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap-pagination.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap-pagination.ts
new file mode 100644
index 0000000..ae7619b
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap-pagination.ts
@@ -0,0 +1,21 @@
+/**
+ * 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.
+ */
+export class PcapPagination {
+  selectedPage = 1;
+  total: number;
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/model/pcap-status-response.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap-status-response.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap-status-response.ts
new file mode 100644
index 0000000..d4d9a5e
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap-status-response.ts
@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+export class PcapStatusResponse {
+  jobId: string;
+  jobStatus: string;
+  percentComplete: number;
+  pageTotal: number;
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
index 3a27714..91c2287 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
@@ -26,4 +26,4 @@ export class PcapRequest {
   protocol: string = '';
   packetFilter: string = '';
   includeReverse: boolean = false;
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
index 92dfb1d..4d74541 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.html
@@ -34,4 +34,5 @@
       </ng-container>
     </tbody>
   </table>
+  <app-pcap-pagination [(pagination)]="pagination" (pageChange)="onPageChange()"></app-pcap-pagination>
 </div>

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
index 1ac8c70..1d0b996 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.spec.ts
@@ -18,6 +18,8 @@
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { PcapListComponent } from './pcap-list.component';
+import { PcapPagination } from '../model/pcap-pagination';
+import { PcapPaginationComponent } from '../pcap-pagination/pcap-pagination.component';
 import { FormsModule } from '../../../../node_modules/@angular/forms';
 import { PdmlPacket } from '../model/pdml';
 import { Component, Input } from '@angular/core';
@@ -53,6 +55,7 @@ describe('PcapListComponent', () => {
         FakePcapPacketLineComponent,
         FakePcapPacketComponent,
         PcapListComponent,
+        PcapPaginationComponent
       ]
     })
     .compileComponents();
@@ -61,10 +64,18 @@ describe('PcapListComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(PcapListComponent);
     component = fixture.componentInstance;
+    component.pagination = new PcapPagination();
+    component.pagination.total = 10;
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should emit an event with onPageChange', () => {
+    const incrementSpy = spyOn(component.pageUpdate, 'emit');
+    component.onPageChange();
+    expect(incrementSpy).toHaveBeenCalled();
+  });
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.ts
index fbc1aec..e340a0b 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-list/pcap-list.component.ts
@@ -15,25 +15,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, OnInit, Input } from '@angular/core';
-import { Pdml,PdmlPacket } from '../model/pdml'
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { PdmlPacket } from '../model/pdml';
+import { PcapPagination } from '../model/pcap-pagination';
 
 @Component({
   selector: 'app-pcap-list',
   templateUrl: './pcap-list.component.html',
   styleUrls: ['./pcap-list.component.scss']
 })
-export class PcapListComponent implements OnInit {
+export class PcapListComponent  {
 
-  @Input() packets: PdmlPacket[]
+  @Input() pagination: PcapPagination = new PcapPagination();
+  @Input() packets: PdmlPacket[];
+  @Output() pageUpdate: EventEmitter<number> = new EventEmitter();
 
   constructor() { }
 
-  ngOnInit() {
+  toggle(packet) {
+    packet.expanded = !packet.expanded;
   }
 
-  toggle(packet) {
-    packet.expanded= !packet.expanded
+  onPageChange() {
+    this.pageUpdate.emit(this.pagination.selectedPage);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.html
new file mode 100644
index 0000000..c819aee
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.html
@@ -0,0 +1,19 @@
+<!--
+  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="pcap-pagination" *ngIf="pagination.total > 0">
+    <button class="pcap-pagination-button fa fa-chevron-left" data-qe-id="pcap-pagination-back" (click)="onPrevious()" [disabled]="pagination.selectedPage <= 1"></button>
+    <span>Page {{ pagination.selectedPage }} of {{ pagination.total }}</span>
+    <button class="pcap-pagination-button fa fa-chevron-right" data-qe-id="pcap-pagination-next" (click)="onNext()" [disabled]="pagination.total === pagination.selectedPage"></button>
+</div>

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.scss
new file mode 100644
index 0000000..bca567c
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.scss
@@ -0,0 +1,40 @@
+/**
+ * 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.
+ */
+
+.disabled {
+  cursor: default;
+  opacity: 0.3;
+}
+
+.pcap-pagination {
+  font-size: 14px;
+  text-align: center;
+ }
+
+.pcap-pagination-button {
+  background: transparent;
+  border: 0;
+  color: #999;
+  cursor: pointer;
+  font-size: 14px;
+
+  &[disabled] {
+    cursor: default;
+    opacity: 0.3;
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.spec.ts
new file mode 100644
index 0000000..d7e0113
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.spec.ts
@@ -0,0 +1,78 @@
+/**
+ * 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, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+
+import { PcapPagination } from '../model/pcap-pagination';
+import { PcapPaginationComponent } from './pcap-pagination.component';
+
+describe('PcapPaginationComponent', () => {
+  let component: PcapPaginationComponent;
+  let fixture: ComponentFixture<PcapPaginationComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ PcapPaginationComponent ]
+    })
+    .compileComponents();
+    fixture = TestBed.createComponent(PcapPaginationComponent);
+    component = fixture.componentInstance;
+    component.pagination = new PcapPagination();
+    component.pagination.total = 10;
+    fixture.detectChanges();
+  }));
+
+  beforeEach(() => {
+  });
+
+  it('should disable the back button if on the first page result', () => {
+    const nextButton = fixture.debugElement.query(By.css('[data-qe-id="pcap-pagination-back"]')).nativeElement;
+    expect(nextButton.disabled).toBe(true);
+  });
+
+  it('should disable the next button if on the last page result', () => {
+    component.pagination.selectedPage = 10;
+    fixture.detectChanges();
+    const nextButton = fixture.debugElement.query(By.css('[data-qe-id="pcap-pagination-next"]')).nativeElement;
+    expect(nextButton.disabled).toBe(true);
+  });
+
+  it('should increment the current page by 1 with onNext()', () => {
+    component.onNext();
+    expect(component.pagination.selectedPage).toBe(2);
+  });
+
+  it('should emit an event with onNext()', () => {
+    const incrementSpy = spyOn(component.pageChange, 'emit');
+    component.onNext();
+    expect(incrementSpy).toHaveBeenCalled();
+  });
+
+  it('should decrement the current page by 1 with OnPrevious()', () => {
+    component.pagination.selectedPage += 1;
+    component.onPrevious();
+    expect(component.pagination.selectedPage).toBe(1);
+  });
+
+  it('should emit an event with OnPrevious()', () => {
+    const incrementSpy = spyOn(component.pageChange, 'emit');
+    component.onPrevious();
+    expect(incrementSpy).toHaveBeenCalled();
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.ts
new file mode 100644
index 0000000..8a0a45c
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-pagination/pcap-pagination.component.ts
@@ -0,0 +1,41 @@
+/**
+ * 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, Output, EventEmitter } from '@angular/core';
+import { PcapPagination } from '../model/pcap-pagination';
+
+@Component({
+  selector: 'app-pcap-pagination',
+  templateUrl: './pcap-pagination.component.html',
+  styleUrls: ['./pcap-pagination.component.scss']
+})
+export class PcapPaginationComponent {
+
+  @Input() pagination = new PcapPagination();
+  @Output() pageChange = new EventEmitter();
+
+  onPrevious() {
+    this.pagination.selectedPage -= 1;
+    this.pageChange.emit();
+  }
+
+  onNext() {
+    this.pagination.selectedPage  += 1;
+    this.pageChange.emit();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
index 1fd250f..12d8df0 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.html
@@ -15,17 +15,14 @@
   <div class="panel-header">
     <app-pcap-filters [queryRunning]="queryRunning" (search)="onSearch($event)"></app-pcap-filters>
   </div>
-
   <div *ngIf="queryRunning" class="progress pcap-progress-background">
-      <div class="progress-bar progress-bar-animated pcap-progress" role="progressbar" attr.aria-valuenow="{{progressWidth}}" aria-valuemin="0" aria-valuemax="100" [ngStyle]="{'max-width': progressWidth + '%', 'transition-duration':'1000ms'}">{{progressWidth}}%</div>
+    <div class="progress-bar progress-bar-animated pcap-progress" role="progressbar" attr.aria-valuenow="{{progressWidth}}" aria-valuemin="0" aria-valuemax="100" [ngStyle]="{'max-width': progressWidth + '%', 'transition-duration':'1000ms'}">{{progressWidth}}%</div>
   </div>
-  
   <div *ngIf="errorMsg" class="alert alert-danger" role="alert">
     {{ errorMsg }}
   </div>
-
   <div class="panel-body" *ngIf="pdml">
-    <app-pcap-list [packets]="pdml.packets"></app-pcap-list>
+    <app-pcap-list [packets]="pdml.packets" [pagination]="pagination" (pageUpdate)="changePage($event)"></app-pcap-list>
   </div>
   <a class="btn btn-primary" [attr.href]="getDownloadUrl()" *ngIf="pdml">Download PCAP</a>
 </div>

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
index 82b29f2..6eb375e 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.spec.ts
@@ -21,6 +21,7 @@ import { PcapPanelComponent } from './pcap-panel.component';
 import { Component, Input } from '../../../../node_modules/@angular/core';
 import { PdmlPacket } from '../model/pdml';
 import { PcapService } from '../service/pcap.service';
+import { PcapPagination } from '../model/pcap-pagination';
 
 @Component({
   selector: 'app-pcap-filters',
@@ -36,6 +37,7 @@ class FakeFilterComponent {
 })
 class FakePcapListComponent {
   @Input() packets: PdmlPacket[];
+  @Input() pagination: PcapPagination;
 }
 
 describe('PcapPanelComponent', () => {

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
index 4114e24..8e7f2f1 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-panel/pcap-panel.component.ts
@@ -17,10 +17,12 @@
  */
 import { Component, Input } from '@angular/core';
 
-import { PcapService, PcapStatusResponse } from '../service/pcap.service';
+import { PcapService } from '../service/pcap.service';
+import { PcapStatusResponse } from '../model/pcap-status-response';
 import { PcapRequest } from '../model/pcap.request';
 import { Pdml } from '../model/pdml';
 import { Subscription } from 'rxjs/Rx';
+import { PcapPagination } from '../model/pcap-pagination';
 
 @Component({
   selector: 'app-pcap-panel',
@@ -31,18 +33,28 @@ export class PcapPanelComponent {
 
   @Input() pdml: Pdml = null;
   @Input() pcapRequest: PcapRequest;
+  @Input() resetPaginationForSearch: boolean;
 
   statusSubscription: Subscription;
   queryRunning: boolean = false;
   queryId: string;
   progressWidth: number = 0;
-  selectedPage: number = 1;
+  pagination: PcapPagination = new PcapPagination();
+  savedPcapRequest: {};
   errorMsg: string;
 
-  constructor(private pcapService: PcapService) {}
+  constructor(private pcapService: PcapService) { }
+
+  changePage(page) {
+    this.pagination.selectedPage = page;
+    this.pcapService.getPackets(this.queryId, this.pagination.selectedPage).toPromise().then(pdml => {
+      this.pdml = pdml;
+    });
+  }
 
   onSearch(pcapRequest) {
-    console.log(pcapRequest);
+    this.savedPcapRequest = pcapRequest;
+    this.pagination.selectedPage = 1;
     this.pdml = null;
     this.progressWidth = 0;
     this.pcapService.submitRequest(pcapRequest).subscribe((submitResponse: PcapStatusResponse) => {
@@ -51,9 +63,10 @@ export class PcapPanelComponent {
       this.errorMsg = null;
       this.statusSubscription = this.pcapService.pollStatus(submitResponse.jobId).subscribe((statusResponse: PcapStatusResponse) => {
         if ('SUCCEEDED' === statusResponse.jobStatus) {
+          this.pagination.total = statusResponse.pageTotal;
           this.statusSubscription.unsubscribe();
           this.queryRunning = false;
-          this.pcapService.getPackets(submitResponse.jobId, this.selectedPage).toPromise().then(pdml => {
+          this.pcapService.getPackets(submitResponse.jobId, this.pagination.selectedPage).toPromise().then(pdml => {
             this.pdml = pdml;
           });
         } else if ('FAILED' === statusResponse.jobStatus) {
@@ -72,6 +85,6 @@ export class PcapPanelComponent {
   }
 
   getDownloadUrl() {
-    return this.pcapService.getDownloadUrl(this.queryId, this.selectedPage);
+    return this.pcapService.getDownloadUrl(this.queryId, this.pagination.selectedPage);
   }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
index ef5c6c0..8c0db02 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 import {NgModule} from '@angular/core';
-import { CommonModule } from '@angular/common';  
+import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { HttpModule } from '@angular/http';
 
@@ -29,10 +29,9 @@ import { PcapPacketComponent } from './pcap-packet/pcap-packet.component';
 import { PcapFiltersComponent } from './pcap-filters/pcap-filters.component';
 import { PcapPanelComponent } from './pcap-panel/pcap-panel.component';
 import { PcapPacketLineComponent } from './pcap-packet-line/pcap-packet-line.component';
+import { PcapPaginationComponent } from './pcap-pagination/pcap-pagination.component'
+import { PcapService } from './service/pcap.service';
 
-import { PcapService } from './service/pcap.service'
-
- 
 @NgModule({
   imports: [
     routing,
@@ -46,7 +45,8 @@ import { PcapService } from './service/pcap.service'
     PcapPacketComponent,
     PcapFiltersComponent,
     PcapPanelComponent,
-    PcapPacketLineComponent
+    PcapPacketLineComponent,
+    PcapPaginationComponent
   ],
   exports: [ PcapPanelComponent ],
   providers: [ PcapService ]

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/pcap.routing.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap.routing.ts b/metron-interface/metron-alerts/src/app/pcap/pcap.routing.ts
index 25ee0ed..165a4e4 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap.routing.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap.routing.ts
@@ -1,4 +1,4 @@
-/**
+/*
  * 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

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
index 244a3ea..fdaa569 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.spec.ts
@@ -1749,4 +1749,4 @@ function pdml() {
 
 
   </pdml>`
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/037b50b9/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
index 6fd3e62..0bbcef2 100644
--- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts
@@ -24,13 +24,7 @@ import 'rxjs/add/operator/map';
 
 import {PcapRequest} from '../model/pcap.request';
 import {Pdml} from '../model/pdml';
-
-export class PcapStatusResponse {
-  jobId: string;
-  jobStatus: string;
-  percentComplete: number;
-  totalPages: number;
-}
+import { PcapStatusResponse } from '../model/pcap-status-response';
 
 @Injectable()
 export class PcapService {
@@ -70,4 +64,4 @@ export class PcapService {
     public getDownloadUrl(id: string, pageId: number) {
       return `/api/v1/pcap/${id}/raw?page=${pageId}`;
     }
-}
+}
\ No newline at end of file


[06/50] [abbrv] metron git commit: Merge branch 'master' into feature/METRON-1554-pcap-query-panel

Posted by ni...@apache.org.
Merge branch 'master' into feature/METRON-1554-pcap-query-panel


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/7dff4def
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/7dff4def
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/7dff4def

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 7dff4def23a7c839ea599a120ab6d4cf5b9bbbef
Parents: 6c90724 ccdbeff
Author: cstella <ce...@gmail.com>
Authored: Thu Jul 26 12:29:04 2018 -0400
Committer: cstella <ce...@gmail.com>
Committed: Thu Jul 26 12:29:04 2018 -0400

----------------------------------------------------------------------
 .../docker/rpm-docker/SPECS/metron.spec         |   1 +
 .../e2e/utils/clean_metron_update_table.ts      |  18 ++
 .../metron-alerts/e2e/utils/e2e_util.ts         |  20 +-
 .../alert-filters.component.spec.ts             |  18 ++
 .../alert-filters/alert-filters.component.ts    |  20 +-
 .../table-view/table-view.component.spec.ts     |  18 ++
 .../tree-view/tree-view.component.spec.ts       |  18 ++
 .../alerts/meta-alerts/meta-alerts.module.ts    |  18 ++
 .../date-picker/date-picker.component.spec.ts   |  18 ++
 .../shared/date-picker/date-picker.component.ts |  18 ++
 .../shared/date-picker/date-picker.module.ts    |  18 ++
 .../alert-severity-hexagon.directive.spec.ts    |  18 ++
 .../app/shared/group-by/group-by.component.ts   |  18 ++
 .../metron-alerts/src/app/shared/index.ts       |  18 ++
 .../src/app/shared/pipes/map-keys.pipe.spec.ts  |  18 ++
 .../src/app/shared/pipes/map-keys.pipe.ts       |  18 ++
 .../src/app/shared/pipes/time-lapse.pipe.ts     |  18 ++
 .../time-range/time-range.component.spec.ts     |  18 ++
 .../app/shared/time-range/time-range.module.ts  |  18 ++
 metron-interface/metron-alerts/src/polyfills.ts |  18 ++
 .../metron-config/e2e/utils/e2e_util.ts         |  18 ++
 .../src/app/app.config.interface.ts             |  18 ++
 .../metron-config/src/app/app.config.ts         |  18 ++
 .../sensor-grok/sensor-grok.component.ts        |  18 ++
 .../sensor-parser-config-readonly/index.ts      |  18 ++
 .../app/sensors/sensor-parser-config/index.ts   |  18 ++
 .../shared/ace-editor/ace-editor.component.ts   |  18 ++
 .../app/shared/ace-editor/ace-editor.module.ts  |  18 ++
 .../advanced-config-form.module.ts              |  18 ++
 .../src/app/shared/metron-modal/index.ts        |  18 ++
 .../shared/metron-table/metron-sorter/index.ts  |  18 ++
 .../metron-sorter.component.spec.ts             |  18 ++
 .../metron-sorter/metron-sorter.component.ts    |  18 ++
 .../shared/metron-table/metron-table.module.ts  |  18 ++
 .../src/app/shared/multiple-input/index.ts      |  18 ++
 .../multiple-input/multiple-input.module.ts     |  18 ++
 .../src/app/shared/number-spinner/index.ts      |  18 ++
 .../number-spinner/number-spinner.module.ts     |  18 ++
 .../shared/sample-data/sample-data.module.ts    |  18 ++
 .../src/environments/environment.prod.ts        |  18 ++
 .../src/environments/environment.ts             |  18 ++
 metron-interface/metron-rest/README.md          |  12 +-
 .../parsed/jsonMapExampleParsed                 |   6 +
 .../raw/jsonMapExampleOutput                    |   3 +
 metron-platform/metron-parsers/README.md        |   5 +
 .../zookeeper/parsers/jsonMapWrappedQuery.json  |   5 +
 .../metron/parsers/json/JSONMapParser.java      |  40 +++-
 .../JSONMapWrappedQueryIntegrationTest.java     |  37 ++++
 .../integration/ParserIntegrationTest.java      |  22 +-
 .../json/JSONMapParserWrappedQueryTest.java     | 199 +++++++++++++++++++
 pom.xml                                         |  19 +-
 51 files changed, 1044 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/7dff4def/metron-deployment/packaging/docker/rpm-docker/SPECS/metron.spec
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/metron/blob/7dff4def/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --cc metron-interface/metron-rest/README.md
index 2d9a535,caa9c78..68ec559
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@@ -253,15 -242,8 +253,15 @@@ Request and Response objects are JSON f
  | [ `GET /api/v1/metaalert/add/alert`](#get-apiv1metaalertaddalert)|
  | [ `GET /api/v1/metaalert/remove/alert`](#get-apiv1metaalertremovealert)|
  | [ `GET /api/v1/metaalert/update/status/{guid}/{status}`](#get-apiv1metaalertupdatestatusguidstatus)|
 +| [ `POST /api/v1/pcap/fixed`](#post-apiv1pcapfixed)|
 +| [ `POST /api/v1/pcap/query`](#post-apiv1pcapquery)|
 +| [ `GET /api/v1/pcap`](#get-apiv1pcap)|
 +| [ `GET /api/v1/pcap/{jobId}`](#get-apiv1pcapjobid)|
 +| [ `GET /api/v1/pcap/{jobId}/pdml`](#get-apiv1pcapjobidpdml)|
 +| [ `GET /api/v1/pcap/{jobId}/raw`](#get-apiv1pcapjobidraw)|
 +| [ `GET /api/v1/search/search`](#get-apiv1searchsearch)|
- | [ `POST /api/v1/search/search`](#get-apiv1searchsearch)|
- | [ `POST /api/v1/search/group`](#get-apiv1searchgroup)|
+ | [ `POST /api/v1/search/search`](#post-apiv1searchsearch)|
+ | [ `POST /api/v1/search/group`](#post-apiv1searchgroup)|
  | [ `GET /api/v1/search/findOne`](#get-apiv1searchfindone)|
  | [ `GET /api/v1/search/column/metadata`](#get-apiv1searchcolumnmetadata)|
  | [ `GET /api/v1/sensor/enrichment/config`](#get-apiv1sensorenrichmentconfig)|


[28/50] [abbrv] metron git commit: METRON-1728: Handle null values in config in Pcap backend more gracefully (mmiklavc via mmiklavc) closes apache/metron#1151

Posted by ni...@apache.org.
METRON-1728: Handle null values in config in Pcap backend more gracefully (mmiklavc via mmiklavc) closes apache/metron#1151


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/9064cca0
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/9064cca0
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/9064cca0

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 9064cca0317881176471c51abd16e99bf2ad7b10
Parents: 14dcb2d
Author: mmiklavc <mi...@gmail.com>
Authored: Thu Aug 9 09:25:29 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Thu Aug 9 09:25:29 2018 -0600

----------------------------------------------------------------------
 .../common/configuration/ConfigOption.java      |  32 ++++--
 .../common/configuration/ConfigOptionTest.java  | 112 +++++++++++++++++++
 .../org/apache/metron/pcap/query/CliParser.java |  25 +++--
 .../org/apache/metron/pcap/PcapJobTest.java     |  23 ++++
 .../apache/metron/pcap/query/PcapCliTest.java   |  10 +-
 .../metron/pcap/config/PcapGlobalDefaults.java  |  28 +++++
 .../metron/pcap/finalizer/PcapFinalizer.java    |   8 +-
 .../pcap/finalizer/PcapRestFinalizer.java       |  11 +-
 .../java/org/apache/metron/pcap/mr/PcapJob.java |  25 +++--
 9 files changed, 237 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/9064cca0/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/ConfigOption.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/ConfigOption.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/ConfigOption.java
index 8e4211b..6308f0a 100644
--- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/ConfigOption.java
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/ConfigOption.java
@@ -18,36 +18,54 @@
 
 package org.apache.metron.common.configuration;
 
-import org.apache.metron.stellar.common.utils.ConversionUtils;
-
 import java.util.Map;
 import java.util.function.BiFunction;
+import org.apache.metron.stellar.common.utils.ConversionUtils;
 
 public interface ConfigOption {
+
   String getKey();
+
   default BiFunction<String, Object, Object> transform() {
-    return (s,o) -> o;
+    return (s, o) -> o;
   }
 
   default void put(Map<String, Object> map, Object value) {
     map.put(getKey(), value);
   }
 
+  default <T> T getOrDefault(Map<String, Object> map, Class<T> clazz, T defaultValue) {
+    T val;
+    return ((val = get(map, clazz)) == null ? defaultValue : val);
+  }
+
   default <T> T get(Map<String, Object> map, Class<T> clazz) {
     Object obj = map.get(getKey());
-    if(clazz.isInstance(obj)) {
+    if (clazz.isInstance(obj)) {
       return clazz.cast(obj);
-    }
-    else {
+    } else {
       return ConversionUtils.convert(obj, clazz);
     }
   }
 
-  default <T> T get(Map<String, Object> map, BiFunction<String, Object, T> transform, Class<T> clazz) {
+  default <T> T getOrDefault(Map<String, Object> map, BiFunction<String, Object, T> transform,
+      Class<T> clazz, T defaultValue) {
+    T val;
+    return ((val = get(map, transform, clazz)) == null ? defaultValue : val);
+  }
+
+  default <T> T get(Map<String, Object> map, BiFunction<String, Object, T> transform,
+      Class<T> clazz) {
     return clazz.cast(transform.apply(getKey(), map.get(getKey())));
   }
 
+  default <T> T getTransformedOrDefault(Map<String, Object> map, Class<T> clazz, T defaultValue) {
+    T val;
+    return ((val = getTransformed(map, clazz)) == null ? defaultValue : val);
+  }
+
   default <T> T getTransformed(Map<String, Object> map, Class<T> clazz) {
     return clazz.cast(transform().apply(getKey(), map.get(getKey())));
   }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/9064cca0/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/ConfigOptionTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/ConfigOptionTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/ConfigOptionTest.java
new file mode 100644
index 0000000..95db080
--- /dev/null
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/ConfigOptionTest.java
@@ -0,0 +1,112 @@
+/**
+ * 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.
+ */
+
+package org.apache.metron.common.configuration;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test the default interface methods
+ */
+public class ConfigOptionTest {
+
+  @Before
+  public void setup() {
+  }
+
+  @Test
+  public void gets_value_of_specified_type() {
+    ConfigOption option = newOption("foo");
+    Map<String, Object> config = new HashMap<>();
+    option.put(config, 25L);
+    assertThat(option.get(config, Long.class), equalTo(25L));
+    assertThat(option.get(mapWith("foo", 25L), Long.class), equalTo(25L));
+  }
+
+  @Test
+  public void gets_value_of_specified_type_with_transform() {
+    ConfigOption option = newOption("foo");
+    Map<String, Object> config = new HashMap<>();
+    option.put(config, "25");
+    BiFunction<String, Object, Long> transform = (s, o) -> o == null ? null
+        : new Long(o.toString());
+    assertThat(option.get(config, transform, Long.class), equalTo(25L));
+    assertThat(option.get(mapWith("foo", "25"), transform, Long.class), equalTo(25L));
+  }
+
+  @Test
+  public void gets_default_value_of_specified_type_with_transform() {
+    ConfigOption option = newOption("foo");
+    Map<String, Object> config = new HashMap<>();
+    option.put(config, null);
+    BiFunction<String, Object, Long> transform = (s, o) -> o == null ? null
+        : new Long(o.toString());
+    assertThat(option.getOrDefault(config, transform, Long.class, 25L), equalTo(25L));
+    assertThat(option.getOrDefault(mapWith("foo", null), transform, Long.class, 25L), equalTo(25L));
+  }
+
+  @Test
+  public void gets_default_when_null_value() {
+    ConfigOption option = newOption("foo");
+    Map<String, Object> config = new HashMap<>();
+    option.put(config, null);
+    assertThat(option.getOrDefault(config, Long.class, 0L), equalTo(0L));
+    assertThat(option.getOrDefault(mapWith("foo", null), Long.class, 0L), equalTo(0L));
+  }
+
+  @Test
+  public void gets_object_transformed_by_class_cast() {
+    ConfigOption option = newOption("foo");
+    Map<String, Object> config = new HashMap<>();
+    option.put(config, (Object) 25L);
+    assertThat(option.getTransformed(config, Long.class), equalTo(25L));
+    assertThat(option.getTransformed(mapWith("foo", (Object) 25L), Long.class), equalTo(25L));
+  }
+
+  @Test
+  public void gets_default_null_with_cast_when_null() {
+    ConfigOption option = newOption("foo");
+    Map<String, Object> config = new HashMap<>();
+    option.put(config, null);
+    assertThat(option.getTransformedOrDefault(config, Long.class, 25L), equalTo(25L));
+    assertThat(option.getTransformedOrDefault(mapWith("foo", null), Long.class, 25L), equalTo(25L));
+  }
+
+  private <K, V> Map<K, V> mapWith(K key, V val) {
+    Map<K, V> map = new HashMap<>();
+    map.put(key, val);
+    return map;
+  }
+
+  private ConfigOption newOption(final String key) {
+    return new ConfigOption() {
+      @Override
+      public String getKey() {
+        return key;
+      }
+    };
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/9064cca0/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
index 4ad6ffa..2d15e8b 100644
--- a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
+++ b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/CliParser.java
@@ -18,17 +18,23 @@
 
 package org.apache.metron.pcap.query;
 
-import org.apache.commons.cli.*;
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INTERIM_RESULT_PATH_DEFAULT;
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INPUT_PATH_DEFAULT;
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.NUM_RECORDS_PER_FILE_DEFAULT;
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.NUM_REDUCERS_DEFAULT;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.PosixParser;
 import org.apache.metron.pcap.config.PcapConfig;
 
 /**
  * Provides commmon required fields for the PCAP filter jobs
  */
 public class CliParser {
-  public static final String BASE_PATH_DEFAULT = "/apps/metron/pcap/input";
-  public static final String BASE_INTERIM_OUTPUT_PATH_DEFAULT = "/tmp";
-  public static final int NUM_REDUCERS_DEFAULT = 10;
-  public static final int NUM_RECORDS_PER_FILE_DEFAULT = 10000;
   private CommandLineParser parser;
   protected PcapConfig.PrefixStrategy prefixStrategy;
 
@@ -40,9 +46,10 @@ public class CliParser {
   public Options buildOptions() {
     Options options = new Options();
     options.addOption(newOption("h", "help", false, "Display help"));
-    options.addOption(newOption("bp", "base_path", true, String.format("Base PCAP data path. Default is '%s'", BASE_PATH_DEFAULT)));
+    options.addOption(newOption("bp", "base_path", true, String.format("Base PCAP data path. Default is '%s'",
+        BASE_INPUT_PATH_DEFAULT)));
     options.addOption(newOption("bop", "base_output_path", true, String.format("Query result output path. Default is '%s'",
-        BASE_INTERIM_OUTPUT_PATH_DEFAULT)));
+        BASE_INTERIM_RESULT_PATH_DEFAULT)));
     options.addOption(newOption("st", "start_time", true, "(required) Packet start time range.", true));
     options.addOption(newOption("nr", "num_reducers", true, String.format("Number of reducers to use (defaults to %s)", NUM_REDUCERS_DEFAULT)));
     options.addOption(newOption("rpf", "records_per_file", true, String.format("Number of records to include in each output pcap file (defaults to %s)", NUM_RECORDS_PER_FILE_DEFAULT)));
@@ -71,12 +78,12 @@ public class CliParser {
     if (commandLine.hasOption("base_path")) {
       config.setBasePath(commandLine.getOptionValue("base_path"));
     } else {
-      config.setBasePath(BASE_PATH_DEFAULT);
+      config.setBasePath(BASE_INPUT_PATH_DEFAULT);
     }
     if (commandLine.hasOption("base_output_path")) {
       config.setBaseInterimResultPath(commandLine.getOptionValue("base_output_path"));
     } else {
-      config.setBaseInterimResultPath(BASE_INTERIM_OUTPUT_PATH_DEFAULT);
+      config.setBaseInterimResultPath(BASE_INTERIM_RESULT_PATH_DEFAULT);
     }
     if (commandLine.hasOption("start_time")) {
       try {

http://git-wip-us.apache.org/repos/asf/metron/blob/9064cca0/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
index 14963fd..796c8a5 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/PcapJobTest.java
@@ -264,4 +264,27 @@ public class PcapJobTest {
     Assert.assertThat(status.getState(), equalTo(State.KILLED));
   }
 
+  @Test
+  public void handles_null_values_with_defaults() throws Exception {
+    PcapOptions.START_TIME_NS.put(config, null);
+    PcapOptions.END_TIME_NS.put(config, null);
+    PcapOptions.NUM_REDUCERS.put(config, null);
+    PcapOptions.NUM_RECORDS_PER_FILE.put(config, null);
+
+    pageableResult = new PcapPages(
+        Arrays.asList(new Path("1.txt"), new Path("2.txt"), new Path("3.txt")));
+    when(finalizer.finalizeJob(any())).thenReturn(pageableResult);
+    when(mrJob.isComplete()).thenReturn(true);
+    when(mrStatus.getState()).thenReturn(org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
+    when(mrJob.getStatus()).thenReturn(mrStatus);
+    Statusable<Path> statusable = testJob.submit(finalizer, config);
+    timer.updateJobStatus();
+    Pageable<Path> results = statusable.get();
+    Assert.assertThat(results.getSize(), equalTo(3));
+    JobStatus status = statusable.getStatus();
+    Assert.assertThat(status.getState(), equalTo(State.SUCCEEDED));
+    Assert.assertThat(status.getPercentComplete(), equalTo(100.0));
+    Assert.assertThat(status.getJobId(), equalTo(jobIdVal));
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/9064cca0/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
index c7d6fdf..96ca354 100644
--- a/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
+++ b/metron-platform/metron-pcap-backend/src/test/java/org/apache/metron/pcap/query/PcapCliTest.java
@@ -17,6 +17,8 @@
  */
 package org.apache.metron.pcap.query;
 
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INTERIM_RESULT_PATH_DEFAULT;
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.BASE_INPUT_PATH_DEFAULT;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Matchers.argThat;
@@ -91,8 +93,8 @@ public class PcapCliTest {
       put(PcapHelper.PacketFields.PACKET_FILTER.getName(), "`casey`");
     }};
     FixedPcapConfig config = new FixedPcapConfig(prefixStrategy);
-    PcapOptions.BASE_PATH.put(config, CliParser.BASE_PATH_DEFAULT);
-    PcapOptions.BASE_INTERIM_RESULT_PATH.put(config, CliParser.BASE_INTERIM_OUTPUT_PATH_DEFAULT);
+    PcapOptions.BASE_PATH.put(config, BASE_INPUT_PATH_DEFAULT);
+    PcapOptions.BASE_INTERIM_RESULT_PATH.put(config, BASE_INTERIM_RESULT_PATH_DEFAULT);
     PcapOptions.FIELDS.put(config, query);
     PcapOptions.NUM_REDUCERS.put(config, 10);
     PcapOptions.START_TIME_MS.put(config, 500L);
@@ -237,8 +239,8 @@ public class PcapCliTest {
 
     String query = "some query string";
     FixedPcapConfig config = new FixedPcapConfig(prefixStrategy);
-    PcapOptions.BASE_PATH.put(config, CliParser.BASE_PATH_DEFAULT);
-    PcapOptions.BASE_INTERIM_RESULT_PATH.put(config, CliParser.BASE_INTERIM_OUTPUT_PATH_DEFAULT);
+    PcapOptions.BASE_PATH.put(config, BASE_INPUT_PATH_DEFAULT);
+    PcapOptions.BASE_INTERIM_RESULT_PATH.put(config, BASE_INTERIM_RESULT_PATH_DEFAULT);
     PcapOptions.FIELDS.put(config, query);
     PcapOptions.NUM_REDUCERS.put(config, 10);
     PcapOptions.START_TIME_MS.put(config, 500L);

http://git-wip-us.apache.org/repos/asf/metron/blob/9064cca0/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapGlobalDefaults.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapGlobalDefaults.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapGlobalDefaults.java
new file mode 100644
index 0000000..b8c674c
--- /dev/null
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/config/PcapGlobalDefaults.java
@@ -0,0 +1,28 @@
+/**
+ * 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.
+ */
+
+package org.apache.metron.pcap.config;
+
+public class PcapGlobalDefaults {
+  public static final String BASE_PCAP_PATH_DEFAULT = "/apps/metron/pcap";
+  public static final String BASE_INPUT_PATH_DEFAULT = BASE_PCAP_PATH_DEFAULT + "/input";
+  public static final String BASE_INTERIM_RESULT_PATH_DEFAULT = BASE_PCAP_PATH_DEFAULT + "/interim";
+  public static final String FINAL_OUTPUT_PATH_DEFAULT = BASE_PCAP_PATH_DEFAULT + "/output";
+  public static final int NUM_REDUCERS_DEFAULT = 10;
+  public static final int NUM_RECORDS_PER_FILE_DEFAULT = 10000;
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/9064cca0/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
index 8dcc401..5a61f9b 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
@@ -18,6 +18,8 @@
 
 package org.apache.metron.pcap.finalizer;
 
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.NUM_RECORDS_PER_FILE_DEFAULT;
+
 import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
@@ -62,9 +64,9 @@ public abstract class PcapFinalizer implements Finalizer<Path> {
   @Override
   public Pageable<Path> finalizeJob(Map<String, Object> config) throws JobException {
     Configuration hadoopConfig = PcapOptions.HADOOP_CONF.get(config, Configuration.class);
-    int recPerFile = PcapOptions.NUM_RECORDS_PER_FILE.get(config, Integer.class);
-    Path interimResultPath = PcapOptions.INTERIM_RESULT_PATH
-        .get(config, PcapOptions.STRING_TO_PATH, Path.class);
+    int recPerFile = PcapOptions.NUM_RECORDS_PER_FILE
+        .getOrDefault(config, Integer.class, NUM_RECORDS_PER_FILE_DEFAULT);
+    Path interimResultPath = PcapOptions.INTERIM_RESULT_PATH.get(config, PcapOptions.STRING_TO_PATH, Path.class);
     FileSystem fs = PcapOptions.FILESYSTEM.get(config, FileSystem.class);
 
     SequenceFileIterable interimResults = null;

http://git-wip-us.apache.org/repos/asf/metron/blob/9064cca0/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java
index 93a3222..13fa795 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java
@@ -18,14 +18,15 @@
 
 package org.apache.metron.pcap.finalizer;
 
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.FINAL_OUTPUT_PATH_DEFAULT;
+
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
 import org.apache.metron.job.Statusable;
 import org.apache.metron.pcap.config.PcapOptions;
-
-import java.util.Map;
 import org.apache.metron.pcap.writer.PcapResultsWriter;
 
 /**
@@ -45,10 +46,12 @@ public class PcapRestFinalizer extends PcapFinalizer {
 
   @Override
   protected Path getOutputPath(Map<String, Object> config, int partition) {
-    String finalOutputPath = PcapOptions.FINAL_OUTPUT_PATH.get(config, String.class);
+    String finalOutputPath = PcapOptions.FINAL_OUTPUT_PATH
+        .getOrDefault(config, String.class, FINAL_OUTPUT_PATH_DEFAULT);
     String user = PcapOptions.USERNAME.get(config, String.class);
     String jobId = PcapOptions.JOB_ID.get(config, String.class);
-    return new Path(String.format(PCAP_REST_FILEPATH_FORMAT, finalOutputPath, user, jobType, jobId, partition));
+    return new Path(
+        String.format(PCAP_REST_FILEPATH_FORMAT, finalOutputPath, user, jobType, jobId, partition));
   }
 
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/9064cca0/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
index ea2aa29..23bd510 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/mr/PcapJob.java
@@ -20,6 +20,7 @@ package org.apache.metron.pcap.mr;
 
 import static org.apache.metron.pcap.PcapHelper.greaterThanOrEqualTo;
 import static org.apache.metron.pcap.PcapHelper.lessThanOrEqualTo;
+import static org.apache.metron.pcap.config.PcapGlobalDefaults.NUM_REDUCERS_DEFAULT;
 
 import com.google.common.base.Joiner;
 import java.io.IOException;
@@ -51,6 +52,7 @@ import org.apache.hadoop.mapreduce.Partitioner;
 import org.apache.hadoop.mapreduce.Reducer;
 import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat;
 import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
+import org.apache.metron.common.utils.timestamp.TimestampConverters;
 import org.apache.metron.job.Finalizer;
 import org.apache.metron.job.JobException;
 import org.apache.metron.job.JobStatus;
@@ -60,6 +62,7 @@ import org.apache.metron.job.Statusable;
 import org.apache.metron.pcap.PacketInfo;
 import org.apache.metron.pcap.PcapHelper;
 import org.apache.metron.pcap.PcapPages;
+import org.apache.metron.pcap.config.PcapGlobalDefaults;
 import org.apache.metron.pcap.config.PcapOptions;
 import org.apache.metron.pcap.filter.PcapFilter;
 import org.apache.metron.pcap.filter.PcapFilterConfigurator;
@@ -216,20 +219,22 @@ public class PcapJob<T> implements Statusable<Path> {
     Configuration hadoopConf = PcapOptions.HADOOP_CONF.get(configuration, Configuration.class);
     FileSystem fileSystem = PcapOptions.FILESYSTEM.get(configuration, FileSystem.class);
     Path basePath = PcapOptions.BASE_PATH.getTransformed(configuration, Path.class);
-    Path baseInterimResultPath = PcapOptions.BASE_INTERIM_RESULT_PATH.getTransformed(configuration, Path.class);
-    long startTime;
+    Path baseInterimResultPath = PcapOptions.BASE_INTERIM_RESULT_PATH
+        .getTransformedOrDefault(configuration, Path.class,
+            new Path(PcapGlobalDefaults.BASE_INTERIM_RESULT_PATH_DEFAULT));
+    long startTimeNs;
     if (configuration.containsKey(PcapOptions.START_TIME_NS.getKey())) {
-      startTime = PcapOptions.START_TIME_NS.get(configuration, Long.class);
+      startTimeNs = PcapOptions.START_TIME_NS.getOrDefault(configuration, Long.class, 0L);
     } else {
-      startTime = PcapOptions.START_TIME_MS.get(configuration, Long.class) * 1000000;
+      startTimeNs = TimestampConverters.MILLISECONDS.toNanoseconds(PcapOptions.START_TIME_MS.getOrDefault(configuration, Long.class, 0L));
     }
-    long endTime;
+    long endTimeNs;
     if (configuration.containsKey(PcapOptions.END_TIME_NS.getKey())) {
-      endTime = PcapOptions.END_TIME_NS.get(configuration, Long.class);
+      endTimeNs = PcapOptions.END_TIME_NS.getOrDefault(configuration, Long.class, TimestampConverters.MILLISECONDS.toNanoseconds(System.currentTimeMillis()));
     } else {
-      endTime = PcapOptions.END_TIME_MS.get(configuration, Long.class) * 1000000;
+      endTimeNs = TimestampConverters.MILLISECONDS.toNanoseconds(PcapOptions.END_TIME_MS.getOrDefault(configuration, Long.class, System.currentTimeMillis()));
     }
-    int numReducers = PcapOptions.NUM_REDUCERS.get(configuration, Integer.class);
+    int numReducers = PcapOptions.NUM_REDUCERS.getOrDefault(configuration, Integer.class, NUM_REDUCERS_DEFAULT);
     T fields = (T) PcapOptions.FIELDS.get(configuration, Object.class);
     PcapFilterConfigurator<T> filterImpl = PcapOptions.FILTER_IMPL.get(configuration, PcapFilterConfigurator.class);
 
@@ -237,8 +242,8 @@ public class PcapJob<T> implements Statusable<Path> {
       Statusable<Path> statusable = query(jobName,
           basePath,
           baseInterimResultPath,
-          startTime,
-          endTime,
+          startTimeNs,
+          endTimeNs,
           numReducers,
           fields,
           // create a new copy for each job, bad things happen when hadoop config is reused


[39/50] [abbrv] metron git commit: METRON-1738: Pcap directories should have correct permissions (merrimanr via mmiklavc) closes apache/metron#1166

Posted by ni...@apache.org.
METRON-1738: Pcap directories should have correct permissions (merrimanr via mmiklavc) closes apache/metron#1166


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/076a6a19
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/076a6a19
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/076a6a19

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 076a6a197df629d3b04e0757320e7681ea2fb3d9
Parents: 6b70571
Author: merrimanr <me...@gmail.com>
Authored: Wed Aug 15 16:02:47 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Wed Aug 15 16:02:47 2018 -0600

----------------------------------------------------------------------
 .../package/scripts/params/params_linux.py      |  1 +
 .../package/scripts/params/status_params.py     |  1 +
 .../CURRENT/package/scripts/rest_commands.py    | 23 +++++++++++++++-----
 .../CURRENT/package/scripts/rest_master.py      |  5 +++++
 4 files changed, 24 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/076a6a19/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
index 115a54c..9be09f1 100755
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/params_linux.py
@@ -393,6 +393,7 @@ pcap_page_size = config['configurations']['metron-rest-env']['pcap_page_size']
 pcap_yarn_queue = config['configurations']['metron-rest-env']['pcap_yarn_queue']
 pcap_finalizer_threadpool_size= config['configurations']['metron-rest-env']['pcap_finalizer_threadpool_size']
 pcap_configured_flag_file = status_params.pcap_configured_flag_file
+pcap_perm_configured_flag_file = status_params.pcap_perm_configured_flag_file
 
 # MapReduce
 metron_user_hdfs_dir = '/user/' + metron_user

http://git-wip-us.apache.org/repos/asf/metron/blob/076a6a19/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/status_params.py
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/status_params.py b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/status_params.py
index 0a9fdd0..99f5ec0 100644
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/status_params.py
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/params/status_params.py
@@ -120,6 +120,7 @@ metron_keytab_path = config['configurations']['metron-env']['metron_service_keyt
 
 # Pcap
 pcap_configured_flag_file = metron_zookeeper_config_path + '/../metron_pcap_configured'
+pcap_perm_configured_flag_file = metron_zookeeper_config_path + '/../metron_pcap_perm_configured'
 
 # MapReduce
 metron_user_hdfs_dir_configured_flag_file = metron_zookeeper_config_path + '/../metron_user_hdfs_dir_configured'
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/076a6a19/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py
index 463dca1..d44f478 100755
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_commands.py
@@ -38,6 +38,7 @@ class RestCommands:
     __hbase_configured = False
     __hbase_acl_configured = False
     __pcap_configured = False
+    __pcap_perm_configured = False
     __metron_user_hdfs_dir_configured = False
 
     def __init__(self, params):
@@ -49,6 +50,7 @@ class RestCommands:
         self.__hbase_configured = os.path.isfile(self.__params.rest_hbase_configured_flag_file)
         self.__hbase_acl_configured = os.path.isfile(self.__params.rest_hbase_acl_configured_flag_file)
         self.__pcap_configured = os.path.isfile(self.__params.pcap_configured_flag_file)
+        self.__pcap_perm_configured = os.path.isfile(self.__params.pcap_perm_configured_flag_file)
         self.__metron_user_hdfs_dir_configured = os.path.isfile(self.__params.metron_user_hdfs_dir_configured_flag_file)
         Directory(params.metron_rest_pid_dir,
                   mode=0755,
@@ -81,6 +83,9 @@ class RestCommands:
     def is_pcap_configured(self):
         return self.__pcap_configured
 
+    def is_pcap_perm_configured(self):
+        return self.__pcap_perm_configured
+
     def is_metron_user_hdfs_dir_configured(self):
         return self.__metron_user_hdfs_dir_configured
 
@@ -99,6 +104,9 @@ class RestCommands:
     def set_pcap_configured(self):
         metron_service.set_configured(self.__params.metron_user, self.__params.pcap_configured_flag_file, "Setting Pcap configured to True")
 
+    def set_pcap_perm_configured(self):
+        metron_service.set_configured(self.__params.metron_user, self.__params.pcap_perm_configured_flag_file, "Setting Pcap perm configured to True")
+
     def set_metron_user_hdfs_dir_configured(self):
         metron_service.set_configured(self.__params.metron_user, self.__params.metron_user_hdfs_dir_configured_flag_file, "Setting Metron user HDFS directory configured to True")
 
@@ -118,26 +126,29 @@ class RestCommands:
 
     def init_pcap(self):
         Logger.info("Creating HDFS locations for Pcap")
+        # Non Kerberized Metron runs under 'storm', requiring write under the 'hadoop' group.
+        # Kerberized Metron runs under it's own user.
+        ownership = 0755 if self.__params.security_enabled else 0775
         self.__params.HdfsResource(self.__params.pcap_base_path,
                                    type="directory",
                                    action="create_on_execute",
                                    owner=self.__params.metron_user,
-                                   group=self.__params.metron_group,
-                                   mode=0755,
+                                   group=self.__params.hadoop_group,
+                                   mode=ownership,
                                    )
         self.__params.HdfsResource(self.__params.pcap_base_interim_result_path,
                                    type="directory",
                                    action="create_on_execute",
                                    owner=self.__params.metron_user,
-                                   group=self.__params.metron_group,
-                                   mode=0755,
+                                   group=self.__params.hadoop_group,
+                                   mode=ownership,
                                    )
         self.__params.HdfsResource(self.__params.pcap_final_output_path,
                                    type="directory",
                                    action="create_on_execute",
                                    owner=self.__params.metron_user,
-                                   group=self.__params.metron_group,
-                                   mode=0755,
+                                   group=self.__params.hadoop_group,
+                                   mode=ownership,
                                    )
 
     def create_metron_user_hdfs_dir(self):

http://git-wip-us.apache.org/repos/asf/metron/blob/076a6a19/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_master.py
----------------------------------------------------------------------
diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_master.py b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_master.py
index c842214..791ca77 100755
--- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_master.py
+++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/scripts/rest_master.py
@@ -60,6 +60,11 @@ class RestMaster(Script):
         if params.security_enabled and not commands.is_kafka_acl_configured():
             commands.init_kafka_acls()
             commands.set_kafka_acl_configured()
+        if params.security_enabled and not commands.is_pcap_perm_configured():
+            # If we Kerberize the cluster, we need to call this again, to remove write perms from hadoop group
+            # If we start off Kerberized, it just does the same thing twice.
+            commands.init_pcap()
+            commands.set_pcap_perm_configured()
 
     def start(self, env, upgrade_type=None):
         from params import params


[04/50] [abbrv] metron git commit: METRON-1693: Fix Pcap CLI local FS finalizer (mmiklavc via mmiklavc) closes apache/metron#1130

Posted by ni...@apache.org.
METRON-1693: Fix Pcap CLI local FS finalizer (mmiklavc via mmiklavc) closes apache/metron#1130


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/f1f5dda4
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/f1f5dda4
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/f1f5dda4

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: f1f5dda4c643394c12ab9f4501c5b806adb21129
Parents: 9d4842f
Author: mmiklavc <mi...@gmail.com>
Authored: Wed Jul 25 08:26:45 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Wed Jul 25 08:26:45 2018 -0600

----------------------------------------------------------------------
 .../java/org/apache/metron/pcap/query/PcapCli.java  |  1 -
 .../metron/pcap/finalizer/PcapCliFinalizer.java     | 15 ++++++++++++---
 .../apache/metron/pcap/finalizer/PcapFinalizer.java |  4 +++-
 .../metron/pcap/finalizer/PcapRestFinalizer.java    | 10 ++++++++++
 .../metron/pcap/writer/PcapResultsWriter.java       | 16 ++++++++++++++++
 5 files changed, 41 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/f1f5dda4/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
index 1a23740..0b06b0c 100644
--- a/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
+++ b/metron-platform/metron-pcap-backend/src/main/java/org/apache/metron/pcap/query/PcapCli.java
@@ -87,7 +87,6 @@ public class PcapCli {
       try {
         config = fixedParser.parse(otherArgs);
         commonConfig = config;
-        PcapOptions.FINAL_OUTPUT_PATH.put(commonConfig, new Path("file:///" + execDir));
       } catch (ParseException | java.text.ParseException e) {
         System.err.println(e.getMessage());
         System.err.flush();

http://git-wip-us.apache.org/repos/asf/metron/blob/f1f5dda4/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java
index c379515..c912e58 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapCliFinalizer.java
@@ -18,9 +18,13 @@
 
 package org.apache.metron.pcap.finalizer;
 
+import java.io.IOException;
+import java.util.List;
 import java.util.Map;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
 import org.apache.metron.pcap.config.PcapOptions;
+import org.apache.metron.pcap.writer.PcapResultsWriter;
 
 /**
  * Write to local FS.
@@ -33,13 +37,18 @@ public class PcapCliFinalizer extends PcapFinalizer {
    * as a formatted timestamp + uuid. A final sample format will look as follows:
    * /base/output/path/pcap-data-201807181911-09855b4ae3204dee8b63760d65198da3+0001.pcap
    */
-  private static final String PCAP_CLI_FILENAME_FORMAT = "%s/pcap-data-%s+%04d.pcap";
+  private static final String PCAP_CLI_FILENAME_FORMAT = "pcap-data-%s+%04d.pcap";
+
+  @Override
+  protected void write(PcapResultsWriter resultsWriter, Configuration hadoopConfig,
+      List<byte[]> data, Path outputPath) throws IOException {
+    resultsWriter.writeLocal(data, outputPath.toString());
+  }
 
   @Override
   protected Path getOutputPath(Map<String, Object> config, int partition) {
-    Path finalOutputPath = PcapOptions.FINAL_OUTPUT_PATH.get(config, PcapOptions.STRING_TO_PATH, Path.class);
     String prefix = PcapOptions.FINAL_FILENAME_PREFIX.get(config, String.class);
-    return new Path(String.format(PCAP_CLI_FILENAME_FORMAT, finalOutputPath, prefix, partition));
+    return new Path(String.format(PCAP_CLI_FILENAME_FORMAT, prefix, partition));
   }
 
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/f1f5dda4/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
index 2c55e15..8dcc401 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapFinalizer.java
@@ -81,7 +81,7 @@ public abstract class PcapFinalizer implements Finalizer<Path> {
         for (List<byte[]> data : partitions) {
           Path outputPath = getOutputPath(config, part++);
           if (data.size() > 0) {
-            getResultsWriter().write(hadoopConfig, data, outputPath.toUri().getPath());
+            write(resultsWriter, hadoopConfig, data, outputPath);
             outFiles.add(outputPath);
           }
         }
@@ -100,6 +100,8 @@ public abstract class PcapFinalizer implements Finalizer<Path> {
     return new PcapPages(outFiles);
   }
 
+  protected abstract void write(PcapResultsWriter resultsWriter, Configuration hadoopConfig, List<byte[]> data, Path outputPath) throws IOException;
+
   protected abstract Path getOutputPath(Map<String, Object> config, int partition);
 
   /**

http://git-wip-us.apache.org/repos/asf/metron/blob/f1f5dda4/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java
index 95907df..93a3222 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/finalizer/PcapRestFinalizer.java
@@ -18,11 +18,15 @@
 
 package org.apache.metron.pcap.finalizer;
 
+import java.io.IOException;
+import java.util.List;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
 import org.apache.metron.job.Statusable;
 import org.apache.metron.pcap.config.PcapOptions;
 
 import java.util.Map;
+import org.apache.metron.pcap.writer.PcapResultsWriter;
 
 /**
  * Write to HDFS.
@@ -34,6 +38,12 @@ public class PcapRestFinalizer extends PcapFinalizer {
   private String jobType = Statusable.JobType.MAP_REDUCE.name();
 
   @Override
+  protected void write(PcapResultsWriter resultsWriter, Configuration hadoopConfig,
+      List<byte[]> data, Path outputPath) throws IOException {
+    resultsWriter.write(hadoopConfig, data, outputPath.toString());
+  }
+
+  @Override
   protected Path getOutputPath(Map<String, Object> config, int partition) {
     String finalOutputPath = PcapOptions.FINAL_OUTPUT_PATH.get(config, String.class);
     String user = PcapOptions.USERNAME.get(config, String.class);

http://git-wip-us.apache.org/repos/asf/metron/blob/f1f5dda4/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/writer/PcapResultsWriter.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/writer/PcapResultsWriter.java b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/writer/PcapResultsWriter.java
index 62ac27c..05d9991 100644
--- a/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/writer/PcapResultsWriter.java
+++ b/metron-platform/metron-pcap/src/main/java/org/apache/metron/pcap/writer/PcapResultsWriter.java
@@ -18,6 +18,8 @@
 package org.apache.metron.pcap.writer;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.List;
 import org.apache.hadoop.conf.Configuration;
@@ -39,6 +41,20 @@ public class PcapResultsWriter {
   }
 
   /**
+   * Write out pcaps to the local FS
+   *
+   * @param pcaps pcap data to write. Pre-merged format as a list of pcaps as byte arrays.
+   * @param outPath where to write the pcap data to.
+   * @throws IOException I/O issue encountered.
+   */
+  public void writeLocal(List<byte[]> pcaps, String outPath) throws IOException {
+    File out = new File(outPath);
+    try (FileOutputStream fos = new FileOutputStream(out)) {
+      fos.write(mergePcaps(pcaps));
+    }
+  }
+
+  /**
    * Creates a pcap file with proper global header from individual pcaps.
    *
    * @param pcaps pcap records to merge into a pcap file with header.


[27/50] [abbrv] metron git commit: METRON-1730: Update steps to run pycapa on Centos 6 (mmiklavc via mmiklavc) closes apache/metron#1152

Posted by ni...@apache.org.
METRON-1730: Update steps to run pycapa on Centos 6 (mmiklavc via mmiklavc) closes apache/metron#1152


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/2b0060b0
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/2b0060b0
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/2b0060b0

Branch: refs/remotes/apache/feature/METRON-1699-create-batch-profiler
Commit: 2b0060b0663ba3fa34047afdfe2fa856fe9007e8
Parents: 1b2bdff
Author: mmiklavc <mi...@gmail.com>
Authored: Thu Aug 9 09:21:33 2018 -0600
Committer: Michael Miklavcic <mi...@gmail.com>
Committed: Thu Aug 9 09:21:33 2018 -0600

----------------------------------------------------------------------
 metron-sensors/pycapa/README.md        | 92 ++++++++++++++++++++++++++---
 metron-sensors/pycapa/requirements.txt |  6 +-
 2 files changed, 88 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/2b0060b0/metron-sensors/pycapa/README.md
----------------------------------------------------------------------
diff --git a/metron-sensors/pycapa/README.md b/metron-sensors/pycapa/README.md
index 9f01d33..38baf74 100644
--- a/metron-sensors/pycapa/README.md
+++ b/metron-sensors/pycapa/README.md
@@ -20,6 +20,8 @@ Pycapa
 
 * [Overview](#overview)
 * [Installation](#installation)
+  * [Centos 7](#centos-7)
+  * [Centos 6](#centos-6)
 * [Usage](#usage)
   * [Parameters](#parameters)
   * [Examples](#examples)
@@ -27,22 +29,23 @@ Pycapa
 * [FAQs](#faqs)
 
 Overview
-========
+--------
 
 Pycapa performs network packet capture, both off-the-wire and from a Kafka topic, which is useful for the testing and development of [Apache Metron](https://github.com/apache/metron).  It is not intended for production use. The tool will capture packets from a specified interface and push them into a Kafka Topic.  The tool can also do the reverse.  It can consume packets from Kafka and reconstruct each network packet.  This can then be used to create a [libpcap-compliant file](https://wiki.wireshark.org/Development/LibpcapFileFormat) or even to feed directly into a tool like Wireshark to monitor ongoing activity.
 
 Installation
-============
+------------
 
 General notes on the installation of Pycapa. 
 * Python 2.7 is required.
 * The following package dependencies are required and can be installed automatically with `pip`. The requirements are installed as part of step 4
   * [confluent-kafka-python](https://github.com/confluentinc/confluent-kafka-python)
   * [pcapy](https://github.com/CoreSecurity/pcapy)
-* These instructions can be used directly on CentOS 7+.  
-* Other Linux distributions that come with Python 2.7 can use these instructions with some minor modifications.  
-* Older distributions, like CentOS 6, that come with Python 2.6 installed, should install Python 2.7 within a virtual environment and then run Pycapa from within the virtual environment.
 
+### Centos 7
+
+* These instructions can be used directly on CentOS 7+.
+* Other Linux distributions that come with Python 2.7 can use these instructions with some minor modifications.  
 
 1. Install system dependencies including the core development tools, Python libraries and header files, and Libpcap libraries and header files.  On CentOS 7+, you can install these requirements with the following command.
 
@@ -76,8 +79,83 @@ General notes on the installation of Pycapa.
     python setup.py install
     ```
 
+### Centos 6
+
+* These instructions can be used directly on CentOS 6 - useful for developers using the Full Dev Vagrant test box.
+* Older distributions, like CentOS 6, that come with Python 2.6 installed, should install Python 2.7 within a virtual environment and then run Pycapa from within the virtual environment.
+
+1. Set up a couple environment variables.
+
+    ```
+    PYCAPA_HOME=/opt/pycapa
+    PYTHON27_HOME=/opt/rh/python27/root
+    ```
+
+1. Install required packages.
+
+    ```
+    for item in epel-release centos-release-scl "@Development tools" python27 python27-scldevel python27-python-virtualenv libpcap-devel libselinux-python; do yum install -y $item; done
+    ```
+
+1. Setup Pycapa directory.
+
+    ```
+    mkdir $PYCAPA_HOME && chmod 755 $PYCAPA_HOME
+    ```
+
+1. Create the virtualenv.
+
+    ```
+    export LD_LIBRARY_PATH="/opt/rh/python27/root/usr/lib64"
+    cd $PYCAPA_HOME
+    ${PYTHON27_HOME}/usr/bin/virtualenv pycapa-venv
+    ```
+
+1. Install Librdkafka at your chosen $PREFIX.
+
+    ```
+    export PREFIX=/usr
+    wget https://github.com/edenhill/librdkafka/archive/v0.11.5.tar.gz   -O - | tar -xz
+    cd librdkafka-0.11.5/
+    ./configure --prefix=$PREFIX
+    make
+    make install
+    ```
+
+1. Add Librdkafka to the dynamic library load path.
+
+    ```
+    echo "$PREFIX/lib" >> /etc/ld.so.conf.d/pycapa.conf
+    ldconfig -v
+    ```
+
+1. Copy the Pycapa source files from the Metron project to your chosen $PYCAPA_HOME (e.g. `/opt/pycapa`). You should have pycapa source files in `/opt/pycapa/pycapa`.
+
+    ```
+    scp -r metron-sensors/pycapa root@node1:$PYCAPA_HOME
+    ```
+
+1. Install Pycapa using the `pycapa-venv` virtualenv you created earlier.
+
+    ```
+    cd ${PYCAPA_HOME}/pycapa
+    # activate the virtualenv
+    source ${PYCAPA_HOME}/pycapa-venv/bin/activate
+    pip install -r requirements.txt
+    python setup.py install
+    ```
+
+1. Special notes on running pycapa on Centos 6. You should run it using the virtualenv.
+
+    ```
+    cd ${PYCAPA_HOME}/pycapa-venv/bin
+    pycapa --producer --kafka-topic pcap --interface eth1 --kafka-broker $BROKERLIST
+    ```
+
+**Note:** To deactivate your virtualenv, simply type "deactivate" and hit enter.
+
 Usage
-=====
+-----
 
 Pycapa has two primary runtime modes.
 
@@ -306,7 +384,7 @@ The probe can be used in a Kerberized environment. The Python client README (htt
     ```
     
 FAQs
-====
+----
 
 ### How do I get more logs?
 

http://git-wip-us.apache.org/repos/asf/metron/blob/2b0060b0/metron-sensors/pycapa/requirements.txt
----------------------------------------------------------------------
diff --git a/metron-sensors/pycapa/requirements.txt b/metron-sensors/pycapa/requirements.txt
index 0119daa..6b92028 100644
--- a/metron-sensors/pycapa/requirements.txt
+++ b/metron-sensors/pycapa/requirements.txt
@@ -14,6 +14,6 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 #
-confluent_kafka
-pcapy
-argparse
+confluent-kafka==0.11.5
+pcapy==0.11.4
+argparse==1.4.0