You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by ce...@apache.org on 2016/09/26 19:15:20 UTC
[2/3] incubator-metron git commit: METRON-452: Add rudimentary
configuration management functions to Stellar closes
apache/incubator-metron#269
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/c85c7426/metron-platform/metron-management/src/main/java/org/apache/metron/management/ConfigurationFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/main/java/org/apache/metron/management/ConfigurationFunctions.java b/metron-platform/metron-management/src/main/java/org/apache/metron/management/ConfigurationFunctions.java
new file mode 100644
index 0000000..ea9f93a
--- /dev/null
+++ b/metron-platform/metron-management/src/main/java/org/apache/metron/management/ConfigurationFunctions.java
@@ -0,0 +1,301 @@
+/**
+ * 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.management;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import org.apache.commons.configuration.ConfigurationUtils;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.cache.ChildData;
+import org.apache.curator.framework.recipes.cache.TreeCache;
+import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
+import org.apache.curator.framework.recipes.cache.TreeCacheListener;
+import org.apache.log4j.Logger;
+import org.apache.metron.common.Constants;
+import org.apache.metron.common.configuration.ConfigurationType;
+import org.apache.metron.common.configuration.ConfigurationsUtils;
+import org.apache.metron.common.configuration.SensorParserConfig;
+import org.apache.metron.common.configuration.enrichment.SensorEnrichmentConfig;
+import org.apache.metron.common.dsl.Context;
+import org.apache.metron.common.dsl.ParseException;
+import org.apache.metron.common.dsl.Stellar;
+import org.apache.metron.common.dsl.StellarFunction;
+import org.apache.metron.common.utils.ConversionUtils;
+import org.apache.metron.common.utils.JSONUtils;
+
+import java.util.*;
+
+public class ConfigurationFunctions {
+ private static final Logger LOG = Logger.getLogger(ConfigurationFunctions.class);
+ private static EnumMap<ConfigurationType, Object> configMap = new EnumMap<ConfigurationType, Object>(ConfigurationType.class) {{
+ for(ConfigurationType ct : ConfigurationType.values()) {
+ put(ct, Collections.synchronizedMap(new HashMap<String, String>()));
+ }
+ put(ConfigurationType.GLOBAL, "");
+ put(ConfigurationType.PROFILER, "");
+ }};
+ private static synchronized void setupTreeCache(Context context) throws Exception {
+ try {
+ Optional<Object> treeCacheOpt = context.getCapability("treeCache");
+ if (treeCacheOpt.isPresent()) {
+ return;
+ }
+ }
+ catch(IllegalStateException ex) {
+
+ }
+ Optional<Object> clientOpt = context.getCapability(Context.Capabilities.ZOOKEEPER_CLIENT);
+ if(!clientOpt.isPresent()) {
+ throw new IllegalStateException("I expected a zookeeper client to exist and it did not. Please connect to zookeeper.");
+ }
+ CuratorFramework client = (CuratorFramework) clientOpt.get();
+ TreeCache cache = new TreeCache(client, Constants.ZOOKEEPER_TOPOLOGY_ROOT);
+ TreeCacheListener listener = new TreeCacheListener() {
+ @Override
+ public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
+ if (event.getType().equals(TreeCacheEvent.Type.NODE_ADDED) || event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) {
+ String path = event.getData().getPath();
+ byte[] data = event.getData().getData();
+ String sensor = Iterables.getLast(Splitter.on("/").split(path), null);
+ if (path.startsWith(ConfigurationType.PARSER.getZookeeperRoot())) {
+ Map<String, String> sensorMap = (Map<String, String>)configMap.get(ConfigurationType.PARSER);
+ sensorMap.put(sensor, new String(data));
+ } else if (ConfigurationType.GLOBAL.getZookeeperRoot().equals(path)) {
+ configMap.put(ConfigurationType.GLOBAL, new String(data));
+ } else if (ConfigurationType.PROFILER.getZookeeperRoot().equals(path)) {
+ configMap.put(ConfigurationType.PROFILER, new String(data));
+ } else if (path.startsWith(ConfigurationType.ENRICHMENT.getZookeeperRoot())) {
+ Map<String, String> sensorMap = (Map<String, String>)configMap.get(ConfigurationType.ENRICHMENT);
+ sensorMap.put(sensor, new String(data));
+ }
+ }
+ else if(event.getType().equals(TreeCacheEvent.Type.NODE_REMOVED)) {
+ String path = event.getData().getPath();
+ String sensor = Iterables.getLast(Splitter.on("/").split(path), null);
+ if (path.startsWith(ConfigurationType.PARSER.getZookeeperRoot())) {
+ Map<String, String> sensorMap = (Map<String, String>)configMap.get(ConfigurationType.PARSER);
+ sensorMap.remove(sensor);
+ }
+ else if (path.startsWith(ConfigurationType.ENRICHMENT.getZookeeperRoot())) {
+ Map<String, String> sensorMap = (Map<String, String>)configMap.get(ConfigurationType.ENRICHMENT);
+ sensorMap.remove(sensor);
+ }
+ else if (ConfigurationType.PROFILER.getZookeeperRoot().equals(path)) {
+ configMap.put(ConfigurationType.PROFILER, null);
+ }
+ else if (ConfigurationType.GLOBAL.getZookeeperRoot().equals(path)) {
+ configMap.put(ConfigurationType.GLOBAL, null);
+ }
+ }
+ }
+ };
+ cache.getListenable().addListener(listener);
+ cache.start();
+ for(ConfigurationType ct : ConfigurationType.values()) {
+ switch(ct) {
+ case GLOBAL:
+ case PROFILER:
+ {
+ String data = "";
+ try {
+ byte[] bytes = ConfigurationsUtils.readFromZookeeper(ct.getZookeeperRoot(), client);
+ data = new String(bytes);
+ }
+ catch(Exception ex) {
+
+ }
+ configMap.put(ct, data);
+ }
+ break;
+ case ENRICHMENT:
+ case PARSER:
+ {
+ List<String> sensorTypes = client.getChildren().forPath(ct.getZookeeperRoot());
+ Map<String, String> sensorMap = (Map<String, String>)configMap.get(ct);
+ for(String sensorType : sensorTypes) {
+ sensorMap.put(sensorType, new String(ConfigurationsUtils.readFromZookeeper(ct.getZookeeperRoot() + "/" + sensorType, client)));
+ }
+ }
+ break;
+ }
+ }
+ context.addCapability("treeCache", () -> cache);
+ }
+
+ @Stellar(
+ namespace = "CONFIG"
+ ,name = "GET"
+ ,description = "Retrieve a Metron configuration from zookeeper."
+ ,params = {"type - One of ENRICHMENT, PARSER, GLOBAL, PROFILER"
+ , "sensor - Sensor to retrieve (required for enrichment and parser, not used for profiler and global)"
+ , "emptyIfNotPresent - If true, then return an empty, minimally viable config"
+ }
+ ,returns = "The String representation of the config in zookeeper"
+ )
+ public static class ConfigGet implements StellarFunction {
+ boolean initialized = false;
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ ConfigurationType type = ConfigurationType.valueOf((String)args.get(0));
+ boolean emptyIfNotPresent = true;
+
+ switch(type) {
+ case GLOBAL:
+ case PROFILER:
+ return configMap.get(type);
+ case PARSER: {
+ String sensor = (String) args.get(1);
+ if(args.size() > 2) {
+ emptyIfNotPresent = ConversionUtils.convert(args.get(2), Boolean.class);
+ }
+ Map<String, String> sensorMap = (Map<String, String>) configMap.get(type);
+ String ret = sensorMap.get(sensor);
+ if (ret == null && emptyIfNotPresent ) {
+ SensorParserConfig config = new SensorParserConfig();
+ config.setSensorTopic(sensor);
+ try {
+ ret = JSONUtils.INSTANCE.toJSON(config, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to serialize default object: " + e.getMessage(), e);
+ throw new ParseException("Unable to serialize default object: " + e.getMessage(), e);
+ }
+ }
+ return ret;
+ }
+ case ENRICHMENT: {
+ String sensor = (String) args.get(1);
+ if(args.size() > 2) {
+ emptyIfNotPresent = ConversionUtils.convert(args.get(2), Boolean.class);
+ }
+ Map<String, String> sensorMap = (Map<String, String>) configMap.get(type);
+ String ret = sensorMap.get(sensor);
+ if (ret == null && emptyIfNotPresent ) {
+ SensorEnrichmentConfig config = new SensorEnrichmentConfig();
+ config.setIndex(sensor);
+ try {
+ ret = JSONUtils.INSTANCE.toJSON(config, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to serialize default object: " + e.getMessage(), e);
+ throw new ParseException("Unable to serialize default object: " + e.getMessage(), e);
+ }
+ }
+ return ret;
+ }
+ default:
+ throw new UnsupportedOperationException("Unable to support type " + type);
+ }
+ }
+
+ @Override
+ public void initialize(Context context) {
+ try {
+ setupTreeCache(context);
+ } catch (Exception e) {
+ LOG.error("Unable to initialize: " + e.getMessage(), e);
+ }
+ finally {
+ initialized = true;
+ }
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return initialized;
+ }
+ }
+ @Stellar(
+ namespace = "CONFIG"
+ ,name = "PUT"
+ ,description = "Updates a Metron config to Zookeeper."
+ ,params = {"type - One of ENRICHMENT, PARSER, GLOBAL, PROFILER"
+ ,"config - The config (a string in JSON form) to update"
+ , "sensor - Sensor to retrieve (required for enrichment and parser, not used for profiler and global)"
+ }
+ ,returns = "The String representation of the config in zookeeper"
+ )
+ public static class ConfigPut implements StellarFunction {
+ private CuratorFramework client;
+ private boolean initialized = false;
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ ConfigurationType type = ConfigurationType.valueOf((String)args.get(0));
+ String config = (String)args.get(1);
+ if(config == null) {
+ return null;
+ }
+ try {
+ switch (type) {
+ case GLOBAL:
+ ConfigurationsUtils.writeGlobalConfigToZookeeper(config.getBytes(), client);
+ break;
+ case PROFILER:
+ ConfigurationsUtils.writeProfilerConfigToZookeeper(config.getBytes(), client);
+ break;
+ case ENRICHMENT:
+ {
+ String sensor = (String) args.get(2);
+ if(sensor == null) {
+ return null;
+ }
+ ConfigurationsUtils.writeSensorEnrichmentConfigToZookeeper(sensor, config.getBytes(), client);
+ }
+ break;
+ case PARSER:
+ {
+ String sensor = (String) args.get(2);
+ if(sensor == null) {
+ return null;
+ }
+ ConfigurationsUtils.writeSensorParserConfigToZookeeper(sensor, config.getBytes(), client);
+ }
+ break;
+ }
+ }
+ catch(Exception ex) {
+ LOG.error("Unable to put config: " + ex.getMessage(), ex);
+ throw new ParseException("Unable to put config: " + ex.getMessage(), ex);
+ }
+ return null;
+ }
+
+ @Override
+ public void initialize(Context context) {
+ Optional<Object> clientOpt = context.getCapability(Context.Capabilities.ZOOKEEPER_CLIENT);
+ if(!clientOpt.isPresent()) {
+ throw new IllegalStateException("I expected a zookeeper client to exist and it did not. Please connect to zookeeper.");
+ }
+ client = (CuratorFramework) clientOpt.get();
+ try {
+ setupTreeCache(context);
+ } catch (Exception e) {
+ LOG.error("Unable to initialize: " + e.getMessage(), e);
+ }
+ finally {
+ initialized = true;
+ }
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return initialized;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/c85c7426/metron-platform/metron-management/src/main/java/org/apache/metron/management/EnrichmentConfigFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/main/java/org/apache/metron/management/EnrichmentConfigFunctions.java b/metron-platform/metron-management/src/main/java/org/apache/metron/management/EnrichmentConfigFunctions.java
new file mode 100644
index 0000000..b1566ca
--- /dev/null
+++ b/metron-platform/metron-management/src/main/java/org/apache/metron/management/EnrichmentConfigFunctions.java
@@ -0,0 +1,345 @@
+/**
+ * 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.management;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.jakewharton.fliptables.FlipTable;
+import org.apache.log4j.Logger;
+import org.apache.metron.common.configuration.FieldTransformer;
+import org.apache.metron.common.configuration.enrichment.EnrichmentConfig;
+import org.apache.metron.common.configuration.enrichment.SensorEnrichmentConfig;
+import org.apache.metron.common.dsl.Context;
+import org.apache.metron.common.dsl.ParseException;
+import org.apache.metron.common.dsl.Stellar;
+import org.apache.metron.common.dsl.StellarFunction;
+import org.apache.metron.common.utils.ConversionUtils;
+import org.apache.metron.common.utils.JSONUtils;
+
+import java.util.*;
+
+import static org.apache.metron.common.configuration.ConfigurationType.ENRICHMENT;
+
+public class EnrichmentConfigFunctions {
+
+ private static final Logger LOG = Logger.getLogger(ConfigurationFunctions.class);
+ public enum Type {
+ ENRICHMENT, THREAT_INTEL, THREATINTEL;
+ }
+ public static Map<String, Object> getStellarHandler(EnrichmentConfig enrichmentConfig) {
+ Map<String, Object> fieldMap = enrichmentConfig.getFieldMap();
+ Map<String, Object> stellarHandler = (Map<String, Object>) fieldMap.getOrDefault("stellar", new HashMap<>());
+ fieldMap.put("stellar", stellarHandler);
+ stellarHandler.putIfAbsent("config", new LinkedHashMap<String, Object>());
+ return stellarHandler;
+ }
+
+ public static EnrichmentConfig getConfig(SensorEnrichmentConfig sensorConfig, Type type) {
+ EnrichmentConfig enrichmentConfig = null;
+ switch(type) {
+ case ENRICHMENT:
+ enrichmentConfig = sensorConfig.getEnrichment();
+ break;
+ case THREAT_INTEL:
+ case THREATINTEL:
+ enrichmentConfig = sensorConfig.getThreatIntel();
+ }
+ return enrichmentConfig;
+ }
+
+ @Stellar(
+ namespace = "ENRICHMENT_STELLAR_TRANSFORM"
+ ,name = "PRINT"
+ ,description = "Retrieve stellar enrichment transformations."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"type - ENRICHMENT or THREAT_INTEL"
+ }
+ ,returns = "The String representation of the transformations"
+ )
+ public static class GetStellarTransformation implements StellarFunction {
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ String config = (String) args.get(0);
+ SensorEnrichmentConfig configObj;
+ String[] headers = new String[] { "Group", "Field", "Transformation"};
+ if(config == null || config.isEmpty()) {
+ return FlipTable.of(headers, new String[0][3]);
+ }
+ else {
+ configObj = (SensorEnrichmentConfig) ENRICHMENT.deserialize(config);
+ }
+ Type type = Type.valueOf((String) args.get(1));
+ EnrichmentConfig enrichmentConfig = getConfig(configObj, type);
+
+ Map<String, Object> stellarHandler = getStellarHandler(enrichmentConfig);
+ Map<String, Object> transforms = (Map<String, Object>) stellarHandler.get("config");
+ List<String[]> objs = new ArrayList<>();
+ for(Map.Entry<String, Object> kv : transforms.entrySet()) {
+ if(kv.getValue() instanceof Map) {
+ Map<String, String> groupMap = (Map<String, String>) kv.getValue();
+ for(Map.Entry<String, String> groupKv : groupMap.entrySet()) {
+ objs.add(new String[]{kv.getKey(), groupKv.getKey(), groupKv.getValue().toString()});
+ }
+ }
+ else {
+ objs.add(new String[]{"(default)", kv.getKey(), kv.getValue().toString()});
+ }
+ }
+ String[][] data = new String[objs.size()][3];
+ for(int i = 0;i < objs.size();++i) {
+ data[i] = objs.get(i);
+ }
+ return FlipTable.of(headers, data);
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "ENRICHMENT_STELLAR_TRANSFORM"
+ ,name = "ADD"
+ ,description = "Add stellar field transformation."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"type - ENRICHMENT or THREAT_INTEL"
+ ,"stellarTransforms - A Map associating fields to stellar expressions"
+ ,"group - Group to add to (optional)"
+ }
+ ,returns = "The String representation of the config in zookeeper"
+ )
+ public static class AddStellarTransformation implements StellarFunction{
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ int i = 0;
+ String config = (String) args.get(i++);
+ SensorEnrichmentConfig configObj;
+ if(config == null || config.isEmpty()) {
+ throw new IllegalStateException("Invalid config: " + config);
+ }
+ else {
+ configObj = (SensorEnrichmentConfig) ENRICHMENT.deserialize(config);
+ }
+ Type type = Type.valueOf((String) args.get(i++));
+ EnrichmentConfig enrichmentConfig = getConfig(configObj, type);
+
+ Map<String, Object> stellarHandler = getStellarHandler(enrichmentConfig);
+
+ Map<String, String> transformsToAdd = (Map<String, String>) args.get(i++);
+ String group = null;
+ if(i < args.size() ) {
+ group = (String) args.get(i++);
+ }
+ Map<String, Object> baseTransforms = (Map<String, Object>) stellarHandler.get("config");
+ Map<String, Object> groupMap = baseTransforms;
+ if(group != null) {
+ groupMap = (Map<String, Object>) baseTransforms.getOrDefault(group, new LinkedHashMap<>());
+ baseTransforms.put(group, groupMap);
+ }
+ for(Map.Entry<String, String> kv : transformsToAdd.entrySet()) {
+ groupMap.put(kv.getKey(), kv.getValue());
+ }
+ if(group != null && groupMap.isEmpty()) {
+ baseTransforms.remove(group);
+ }
+ try {
+ return JSONUtils.INSTANCE.toJSON(configObj, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to convert object to JSON: " + configObj, e);
+ return config;
+ }
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "ENRICHMENT_STELLAR_TRANSFORM"
+ ,name = "REMOVE"
+ ,description = "Remove one or more stellar field transformations."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"type - ENRICHMENT or THREAT_INTEL"
+ ,"stellarTransforms - A list of removals"
+ ,"group - Group to remove from (optional)"
+ }
+ ,returns = "The String representation of the config in zookeeper"
+ )
+ public static class RemoveStellarTransformation implements StellarFunction{
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ int i = 0;
+ String config = (String) args.get(i++);
+ SensorEnrichmentConfig configObj;
+ if(config == null || config.isEmpty()) {
+ throw new IllegalStateException("Invalid config: " + config);
+ }
+ else {
+ configObj = (SensorEnrichmentConfig) ENRICHMENT.deserialize(config);
+ }
+ Type type = Type.valueOf((String) args.get(i++));
+ EnrichmentConfig enrichmentConfig = getConfig(configObj, type);
+
+ Map<String, Object> stellarHandler = getStellarHandler(enrichmentConfig);
+
+ List<String> removals = (List<String>) args.get(i++);
+ String group = null;
+ if(i < args.size() ) {
+ group = (String) args.get(i++);
+ }
+ Map<String, Object> baseTransforms = (Map<String, Object>) stellarHandler.get("config");
+ Map<String, Object> groupMap = baseTransforms;
+ if(group != null) {
+ groupMap = (Map<String, Object>) baseTransforms.getOrDefault(group, new LinkedHashMap<>());
+ baseTransforms.put(group, groupMap);
+ }
+ for(String remove : removals) {
+ groupMap.remove(remove);
+ }
+ if(group != null && groupMap.isEmpty()) {
+ baseTransforms.remove(group);
+ }
+ if(baseTransforms.isEmpty()) {
+ enrichmentConfig.getFieldMap().remove("stellar");
+ }
+ try {
+ return JSONUtils.INSTANCE.toJSON(configObj, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to convert object to JSON: " + configObj, e);
+ return config;
+ }
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "ENRICHMENT"
+ ,name = "SET_BATCH"
+ ,description = "Set batch size"
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"size - batch size (integer)"
+ }
+ ,returns = "The String representation of the config in zookeeper"
+ )
+ public static class SetBatchSize implements StellarFunction{
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ int i = 0;
+ String config = (String) args.get(i++);
+ SensorEnrichmentConfig configObj;
+ if(config == null || config.isEmpty()) {
+ throw new IllegalStateException("Invalid config: " + config);
+ }
+ else {
+ configObj = (SensorEnrichmentConfig) ENRICHMENT.deserialize(config);
+ }
+ int batchSize = 5;
+ if(args.size() > 1) {
+ batchSize = ConversionUtils.convert(args.get(i++), Integer.class);
+ }
+ configObj.setBatchSize(batchSize);
+ try {
+ return JSONUtils.INSTANCE.toJSON(configObj, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to convert object to JSON: " + configObj, e);
+ return config;
+ }
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "ENRICHMENT"
+ ,name = "SET_INDEX"
+ ,description = "Set the index for the enrichment"
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"sensor - sensor name"
+ }
+ ,returns = "The String representation of the config in zookeeper"
+ )
+ public static class SetIndex implements StellarFunction{
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ int i = 0;
+ String config = (String) args.get(i++);
+ SensorEnrichmentConfig configObj;
+ if(config == null || config.isEmpty()) {
+ throw new IllegalStateException("Invalid config: " + config);
+ }
+ else {
+ configObj = (SensorEnrichmentConfig) ENRICHMENT.deserialize(config);
+ }
+ String sensorName = ConversionUtils.convert(args.get(i++), String.class);
+ if(sensorName == null) {
+ throw new IllegalStateException("Invalid sensor name: " + config);
+ }
+ configObj.setIndex(sensorName);
+ try {
+ return JSONUtils.INSTANCE.toJSON(configObj, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to convert object to JSON: " + configObj, e);
+ return config;
+ }
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/c85c7426/metron-platform/metron-management/src/main/java/org/apache/metron/management/ParserConfigFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/main/java/org/apache/metron/management/ParserConfigFunctions.java b/metron-platform/metron-management/src/main/java/org/apache/metron/management/ParserConfigFunctions.java
new file mode 100644
index 0000000..7ceb7af
--- /dev/null
+++ b/metron-platform/metron-management/src/main/java/org/apache/metron/management/ParserConfigFunctions.java
@@ -0,0 +1,211 @@
+/**
+ * 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.management;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.jakewharton.fliptables.FlipTable;
+import org.apache.log4j.Logger;
+import org.apache.metron.common.configuration.ConfigurationType;
+import org.apache.metron.common.configuration.FieldTransformer;
+import org.apache.metron.common.configuration.SensorParserConfig;
+import org.apache.metron.common.dsl.*;
+import org.apache.metron.common.field.transformation.FieldTransformation;
+import org.apache.metron.common.field.transformation.FieldTransformations;
+import org.apache.metron.common.stellar.shell.StellarExecutor;
+import org.apache.metron.common.utils.JSONUtils;
+import org.jboss.aesh.console.Console;
+
+import java.util.*;
+
+import static org.apache.metron.common.configuration.ConfigurationType.PARSER;
+
+public class ParserConfigFunctions {
+ private static final Logger LOG = Logger.getLogger(ConfigurationFunctions.class);
+
+ private static void pruneEmptyStellarTransformers(SensorParserConfig config) {
+ List<FieldTransformer> toRemove = new ArrayList<>();
+ List<FieldTransformer> fieldTransformations = config.getFieldTransformations();
+ for(FieldTransformer transformer : fieldTransformations) {
+ if(transformer.getFieldTransformation().getClass().getName()
+ .equals(FieldTransformations.STELLAR.getMappingClass().getName())
+ && transformer.getConfig().isEmpty()
+ ) {
+ toRemove.add(transformer);
+ }
+ }
+ for(FieldTransformer t : toRemove) {
+ fieldTransformations.remove(t);
+ }
+ }
+ private static FieldTransformer getStellarTransformer(SensorParserConfig config) {
+ List<FieldTransformer> fieldTransformations = config.getFieldTransformations();
+ FieldTransformer stellarTransformer = null;
+ for(FieldTransformer transformer : fieldTransformations) {
+ if(transformer.getFieldTransformation().getClass().getName()
+ .equals(FieldTransformations.STELLAR.getMappingClass().getName())) {
+ stellarTransformer = transformer;
+ }
+ }
+ if(stellarTransformer == null) {
+ stellarTransformer = new FieldTransformer();
+ stellarTransformer.setConfig(new LinkedHashMap<>());
+ stellarTransformer.setTransformation(FieldTransformations.STELLAR.toString());
+ fieldTransformations.add(stellarTransformer);
+ }
+ return stellarTransformer;
+ }
+
+ @Stellar(
+ namespace = "PARSER_STELLAR_TRANSFORM"
+ ,name = "PRINT"
+ ,description = "Retrieve stellar field transformations."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ }
+ ,returns = "The String representation of the transformations"
+ )
+ public static class PrintStellarTransformation implements StellarFunction {
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ String config = (String) args.get(0);
+ if(config == null) {
+ return null;
+ }
+ SensorParserConfig configObj = (SensorParserConfig) PARSER.deserialize(config);
+ FieldTransformer stellarTransformer = getStellarTransformer(configObj);
+ String[] headers = new String[] { "Field", "Transformation"};
+ String[][] data = new String[stellarTransformer.getConfig().size()][2];
+ int i = 0;
+ for(Map.Entry<String, Object> kv : stellarTransformer.getConfig().entrySet()) {
+ data[i++] = new String[] {kv.getKey(), kv.getValue().toString()};
+ }
+ return FlipTable.of(headers, data);
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "PARSER_STELLAR_TRANSFORM"
+ ,name = "REMOVE"
+ ,description = "Remove stellar field transformation."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"stellarTransforms - A list of stellar transforms to remove"
+ }
+ ,returns = "The String representation of the config in zookeeper"
+ )
+ public static class RemoveStellarTransformation implements StellarFunction{
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ String config = (String) args.get(0);
+ if(config == null) {
+ return null;
+ }
+ SensorParserConfig configObj = (SensorParserConfig) PARSER.deserialize(config);
+ FieldTransformer stellarTransformer = getStellarTransformer(configObj);
+ List<String> removals = (List<String>)args.get(1);
+ if(removals == null || removals.isEmpty()) {
+ return config;
+ }
+ for(String removal : removals) {
+ stellarTransformer.getConfig().remove(removal);
+ }
+ List<String> output = new ArrayList<>();
+ output.addAll(stellarTransformer.getConfig().keySet());
+ stellarTransformer.setOutput(output);
+ pruneEmptyStellarTransformers(configObj);
+ try {
+ return JSONUtils.INSTANCE.toJSON(configObj, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to convert object to JSON: " + configObj, e);
+ return config;
+ }
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "PARSER_STELLAR_TRANSFORM"
+ ,name = "ADD"
+ ,description = "Add stellar field transformation."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"stellarTransforms - A Map associating fields to stellar expressions"
+ }
+ ,returns = "The String representation of the config in zookeeper"
+ )
+ public static class AddStellarTransformation implements StellarFunction{
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ String config = (String) args.get(0);
+ if(config == null) {
+ return null;
+ }
+ SensorParserConfig configObj = (SensorParserConfig) PARSER.deserialize(config);
+ FieldTransformer stellarTransformer = getStellarTransformer(configObj);
+ Map<String, String> additionalTransforms = (Map<String, String>) args.get(1);
+ if(additionalTransforms == null || additionalTransforms.isEmpty()) {
+ return config;
+ }
+ for(Map.Entry<String, String> kv : additionalTransforms.entrySet()) {
+ stellarTransformer.getConfig().put(kv.getKey(), kv.getValue());
+
+ }
+ List<String> output = new ArrayList<>();
+ output.addAll(stellarTransformer.getConfig().keySet());
+ stellarTransformer.setOutput(output);
+
+ try {
+ return JSONUtils.INSTANCE.toJSON(configObj, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to convert object to JSON: " + configObj, e);
+ return config;
+ }
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/c85c7426/metron-platform/metron-management/src/main/java/org/apache/metron/management/ShellFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/main/java/org/apache/metron/management/ShellFunctions.java b/metron-platform/metron-management/src/main/java/org/apache/metron/management/ShellFunctions.java
new file mode 100644
index 0000000..300805c
--- /dev/null
+++ b/metron-platform/metron-management/src/main/java/org/apache/metron/management/ShellFunctions.java
@@ -0,0 +1,188 @@
+/**
+ * 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.management;
+
+import com.jakewharton.fliptables.FlipTable;
+import org.apache.commons.lang3.text.WordUtils;
+import org.apache.metron.common.dsl.*;
+import org.apache.metron.common.stellar.shell.StellarExecutor;
+import org.apache.metron.common.utils.ConversionUtils;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ShellFunctions {
+
+ @Stellar(
+ namespace = "SHELL"
+ ,name = "MAP2TABLE"
+ ,description = "Take a map and return a table"
+ ,params = {"map - Map"
+ }
+ ,returns = "The map in table form"
+ )
+ public static class Map2Table extends BaseStellarFunction {
+
+ @Override
+ public Object apply(List<Object> args) {
+ if(args.size() < 1) {
+ return null;
+ }
+ Map<Object, Object> map = (Map<Object, Object>) args.get(0);
+ if(map == null) {
+ map = new HashMap<>();
+ }
+ String[] headers = {"KEY", "VALUE"};
+ String[][] data = new String[map.size()][2];
+ int i = 0;
+ for(Map.Entry<Object, Object> kv : map.entrySet()) {
+ data[i++] = new String[] {kv.getKey().toString(), kv.getValue().toString()};
+ }
+ return FlipTable.of(headers, data);
+ }
+ }
+
+ @Stellar(
+ namespace = "SHELL"
+ ,name = "LIST_VARS"
+ ,description = "Return the variables in a tabular form"
+ ,params = {
+ "wrap : Length of string to wrap the columns"
+ }
+ ,returns = "A tabular representation of the variables."
+ )
+ public static class ListVars implements StellarFunction {
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+
+ Map<String, StellarExecutor.VariableResult> variables = (Map<String, StellarExecutor.VariableResult>) context.getCapability(StellarExecutor.SHELL_VARIABLES).get();
+ String[] headers = {"VARIABLE", "VALUE", "EXPRESSION"};
+ String[][] data = new String[variables.size()][3];
+ int wordWrap = -1;
+ if(args.size() > 0) {
+ wordWrap = ConversionUtils.convert(args.get(0), Integer.class);
+ }
+ int i = 0;
+ for(Map.Entry<String, StellarExecutor.VariableResult> kv : variables.entrySet()) {
+ StellarExecutor.VariableResult result = kv.getValue();
+ data[i++] = new String[] { toWrappedString(kv.getKey().toString(), wordWrap)
+ , toWrappedString(result.getResult(), wordWrap)
+ , toWrappedString(result.getExpression(), wordWrap)
+ };
+ }
+ return FlipTable.of(headers, data);
+ }
+
+ private static String toWrappedString(Object o, int wrap) {
+ String s = "" + o;
+ if(wrap <= 0) {
+ return s;
+ }
+ return WordUtils.wrap(s, wrap);
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "SHELL"
+ ,name = "VARS2MAP"
+ ,description = "Take a set of variables and return a map"
+ ,params = {"variables* - variable names to use to create map "
+ }
+ ,returns = "A map associating the variable name with the stellar expression."
+ )
+ public static class Var2Map implements StellarFunction {
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ Map<String, StellarExecutor.VariableResult> variables = (Map<String, StellarExecutor.VariableResult>) context.getCapability(StellarExecutor.SHELL_VARIABLES).get();
+ LinkedHashMap<String, String> ret = new LinkedHashMap<>();
+ for(Object arg : args) {
+ if(arg == null) {
+ continue;
+ }
+ String variable = (String)arg;
+ StellarExecutor.VariableResult result = variables.get(variable);
+ if(result != null && result.getExpression() != null) {
+ ret.put(variable, result.getExpression());
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "SHELL"
+ ,name = "GET_EXPRESSION"
+ ,description = "Get a stellar expression from a variable"
+ ,params = {"variable - variable name"
+ }
+ ,returns = "The stellar expression associated with the variable."
+ )
+ public static class GetExpression implements StellarFunction {
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ Map<String, StellarExecutor.VariableResult> variables = (Map<String, StellarExecutor.VariableResult>) context.getCapability(StellarExecutor.SHELL_VARIABLES).get();
+ if(args.size() == 0) {
+ return null;
+ }
+ String variable = (String) args.get(0);
+ if(variable == null) {
+ return null;
+ }
+ StellarExecutor.VariableResult result = variables.get(variable);
+ if(result != null && result.getExpression() != null) {
+ return result.getExpression();
+ }
+ return null;
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/c85c7426/metron-platform/metron-management/src/main/java/org/apache/metron/management/ThreatTriageFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/main/java/org/apache/metron/management/ThreatTriageFunctions.java b/metron-platform/metron-management/src/main/java/org/apache/metron/management/ThreatTriageFunctions.java
new file mode 100644
index 0000000..966f281
--- /dev/null
+++ b/metron-platform/metron-management/src/main/java/org/apache/metron/management/ThreatTriageFunctions.java
@@ -0,0 +1,293 @@
+/**
+ * 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.management;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.jakewharton.fliptables.FlipTable;
+import org.apache.log4j.Logger;
+import org.apache.metron.common.configuration.enrichment.SensorEnrichmentConfig;
+import org.apache.metron.common.configuration.enrichment.threatintel.ThreatIntelConfig;
+import org.apache.metron.common.configuration.enrichment.threatintel.ThreatTriageConfig;
+import org.apache.metron.common.dsl.Context;
+import org.apache.metron.common.dsl.ParseException;
+import org.apache.metron.common.dsl.Stellar;
+import org.apache.metron.common.dsl.StellarFunction;
+import org.apache.metron.common.utils.ConversionUtils;
+import org.apache.metron.common.utils.JSONUtils;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.metron.common.configuration.ConfigurationType.ENRICHMENT;
+import static org.apache.metron.management.EnrichmentConfigFunctions.getConfig;
+
+public class ThreatTriageFunctions {
+ private static final Logger LOG = Logger.getLogger(ConfigurationFunctions.class);
+
+ @Stellar(
+ namespace = "THREAT_TRIAGE"
+ ,name = "PRINT"
+ ,description = "Retrieve stellar enrichment transformations."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ }
+ ,returns = "The String representation of the threat triage rules"
+ )
+ public static class GetStellarTransformation implements StellarFunction {
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ String config = (String) args.get(0);
+ SensorEnrichmentConfig configObj;
+ if(config == null || config.isEmpty()) {
+ configObj = new SensorEnrichmentConfig();
+ }
+ else {
+ configObj = (SensorEnrichmentConfig) ENRICHMENT.deserialize(config);
+ }
+ ThreatIntelConfig tiConfig = (ThreatIntelConfig) getConfig(configObj, EnrichmentConfigFunctions.Type.THREAT_INTEL);
+ if(tiConfig == null) {
+ return "";
+ }
+ ThreatTriageConfig triageConfig = tiConfig.getTriageConfig();
+ if(triageConfig == null) {
+ return "";
+ }
+ Map<String, Number> triageRules = triageConfig.getRiskLevelRules();
+ if(triageRules == null) {
+ triageRules = new LinkedHashMap<>();
+ }
+ String[] headers = new String[] {"Triage Rule", "Score"};
+ String[][] data = new String[triageRules.size()][2];
+ int i = 0;
+ for(Map.Entry<String, Number> kv : triageRules.entrySet()) {
+ double d = kv.getValue().doubleValue();
+ String val = d == (long)d ? String.format("%d", (long)d) : String.format("%s", d);
+ data[i++] = new String[] {kv.getKey(), val};
+ }
+ String ret = FlipTable.of(headers, data);
+ if(!triageRules.isEmpty()) {
+ ret += "\n\n";
+
+ ret += "Aggregation: " + triageConfig.getAggregator().name();
+ }
+ return ret;
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "THREAT_TRIAGE"
+ ,name = "ADD"
+ ,description = "Add a threat triage rule."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"stellarTransforms - A Map associating stellar rules to scores"
+ }
+ ,returns = "The String representation of the threat triage rules"
+ )
+ public static class AddStellarTransformation implements StellarFunction {
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ String config = (String) args.get(0);
+ SensorEnrichmentConfig configObj;
+ if(config == null || config.isEmpty()) {
+ configObj = new SensorEnrichmentConfig();
+ }
+ else {
+ configObj = (SensorEnrichmentConfig) ENRICHMENT.deserialize(config);
+ }
+ ThreatIntelConfig tiConfig = (ThreatIntelConfig) getConfig(configObj, EnrichmentConfigFunctions.Type.THREAT_INTEL);
+ if(tiConfig == null) {
+ tiConfig = new ThreatIntelConfig();
+ configObj.setThreatIntel(tiConfig);
+ }
+ ThreatTriageConfig triageConfig = tiConfig.getTriageConfig();
+ if(triageConfig == null) {
+ triageConfig = new ThreatTriageConfig();
+ tiConfig.setTriageConfig(triageConfig);
+ }
+ Map<String, Number> triageRules = triageConfig.getRiskLevelRules();
+ if(triageRules == null) {
+ triageRules = new LinkedHashMap<>();
+ triageConfig.setRiskLevelRules(triageRules);
+ }
+ Map<String, Object> newRules = (Map<String, Object>) args.get(1);
+ for(Map.Entry<String, Object> kv : newRules.entrySet()) {
+ if(kv.getKey() == null || kv.getKey().equals("null")) {
+ continue;
+ }
+ Double ret = ConversionUtils.convert(kv.getValue(), Double.class);
+ triageConfig.getRiskLevelRules().put(kv.getKey(), ret);
+ }
+ try {
+ return JSONUtils.INSTANCE.toJSON(configObj, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to convert object to JSON: " + configObj, e);
+ return config;
+ }
+
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "THREAT_TRIAGE"
+ ,name = "REMOVE"
+ ,description = "Remove stellar threat triage rule(s)."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"stellarTransforms - A list of stellar rules to remove"
+ }
+ ,returns = "The String representation of the enrichment config"
+ )
+ public static class RemoveStellarTransformation implements StellarFunction {
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ String config = (String) args.get(0);
+ SensorEnrichmentConfig configObj;
+ if(config == null || config.isEmpty()) {
+ configObj = new SensorEnrichmentConfig();
+ }
+ else {
+ configObj = (SensorEnrichmentConfig) ENRICHMENT.deserialize(config);
+ }
+ ThreatIntelConfig tiConfig = (ThreatIntelConfig) getConfig(configObj, EnrichmentConfigFunctions.Type.THREAT_INTEL);
+ if(tiConfig == null) {
+ tiConfig = new ThreatIntelConfig();
+ configObj.setThreatIntel(tiConfig);
+ }
+ ThreatTriageConfig triageConfig = tiConfig.getTriageConfig();
+ if(triageConfig == null) {
+ triageConfig = new ThreatTriageConfig();
+ tiConfig.setTriageConfig(triageConfig);
+ }
+ Map<String, Number> triageRules = triageConfig.getRiskLevelRules();
+ if(triageRules == null) {
+ triageRules = new LinkedHashMap<>();
+ triageConfig.setRiskLevelRules(triageRules);
+ }
+ List<String> rulesToRemove = (List<String>) args.get(1);
+ for(String rule : rulesToRemove) {
+ triageConfig.getRiskLevelRules().remove(rule);
+ }
+ try {
+ return JSONUtils.INSTANCE.toJSON(configObj, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to convert object to JSON: " + configObj, e);
+ return config;
+ }
+
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+
+ @Stellar(
+ namespace = "THREAT_TRIAGE"
+ ,name = "SET_AGGREGATOR"
+ ,description = "Set the threat triage aggregator."
+ ,params = {"sensorConfig - Sensor config to add transformation to."
+ ,"aggregator - Aggregator to use. One of MIN, MAX, MEAN, SUM, POSITIVE_MEAN"
+ ,"aggregatorConfig - Optional config for aggregator"
+ }
+ ,returns = "The String representation of the enrichment config"
+ )
+ public static class SetAggregator implements StellarFunction {
+
+ @Override
+ public Object apply(List<Object> args, Context context) throws ParseException {
+ String config = (String) args.get(0);
+
+ SensorEnrichmentConfig configObj;
+ if(config == null || config.isEmpty()) {
+ configObj = new SensorEnrichmentConfig();
+ }
+ else {
+ configObj = (SensorEnrichmentConfig) ENRICHMENT.deserialize(config);
+ }
+
+ ThreatIntelConfig tiConfig = (ThreatIntelConfig) getConfig(configObj, EnrichmentConfigFunctions.Type.THREAT_INTEL);
+ if(tiConfig == null) {
+ tiConfig = new ThreatIntelConfig();
+ configObj.setThreatIntel(tiConfig);
+ }
+ ThreatTriageConfig triageConfig = tiConfig.getTriageConfig();
+ if(triageConfig == null) {
+ triageConfig = new ThreatTriageConfig();
+ tiConfig.setTriageConfig(triageConfig);
+ }
+ Map<String, Number> triageRules = triageConfig.getRiskLevelRules();
+ if(triageRules == null) {
+ triageRules = new LinkedHashMap<>();
+ triageConfig.setRiskLevelRules(triageRules);
+ }
+ String aggregator = (String) args.get(1);
+ triageConfig.setAggregator(aggregator);
+ if(args.size() > 2) {
+ Map<String, Object> aggConfig = (Map<String, Object>) args.get(2);
+ triageConfig.setAggregationConfig(aggConfig);
+ }
+ try {
+ return JSONUtils.INSTANCE.toJSON(configObj, true);
+ } catch (JsonProcessingException e) {
+ LOG.error("Unable to convert object to JSON: " + configObj, e);
+ return config;
+ }
+
+ }
+
+ @Override
+ public void initialize(Context context) {
+
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/c85c7426/metron-platform/metron-management/src/test/java/org/apache/metron/management/ConfigurationFunctionsTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/test/java/org/apache/metron/management/ConfigurationFunctionsTest.java b/metron-platform/metron-management/src/test/java/org/apache/metron/management/ConfigurationFunctionsTest.java
new file mode 100644
index 0000000..0b7ea7d
--- /dev/null
+++ b/metron-platform/metron-management/src/test/java/org/apache/metron/management/ConfigurationFunctionsTest.java
@@ -0,0 +1,240 @@
+/**
+ * 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.management;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+import org.adrianwalker.multilinestring.Multiline;
+import org.apache.commons.cli.PosixParser;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.test.TestingServer;
+import org.apache.metron.TestConstants;
+import org.apache.metron.common.cli.ConfigurationManager;
+import org.apache.metron.common.configuration.ConfigurationsUtils;
+import org.apache.metron.common.dsl.Context;
+import org.apache.metron.common.dsl.ParseException;
+import org.apache.metron.common.stellar.StellarTest;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+
+import static org.apache.metron.TestConstants.PARSER_CONFIGS_PATH;
+import static org.apache.metron.TestConstants.SAMPLE_CONFIG_PATH;
+import static org.apache.metron.management.utils.FileUtils.slurp;
+
+public class ConfigurationFunctionsTest {
+ private TestingServer testZkServer;
+ private CuratorFramework client;
+ private String zookeeperUrl;
+ private Context context = new Context.Builder()
+ .with(Context.Capabilities.ZOOKEEPER_CLIENT, () -> client)
+ .build();
+ @Before
+ public void setup() throws Exception {
+ testZkServer = new TestingServer(true);
+ zookeeperUrl = testZkServer.getConnectString();
+ client = ConfigurationsUtils.getClient(zookeeperUrl);
+ client.start();
+
+ pushConfigs(SAMPLE_CONFIG_PATH);
+ pushConfigs(PARSER_CONFIGS_PATH);
+
+
+ }
+
+ private void pushConfigs(String inputPath) throws Exception {
+ String[] args = new String[]{
+ "-z", zookeeperUrl
+ , "--mode", "PUSH"
+ , "--input_dir", inputPath
+ };
+ ConfigurationManager manager = new ConfigurationManager();
+ manager.run(ConfigurationManager.ConfigurationOptions.parse(new PosixParser(), args));
+ }
+
+
+ static String goodBroParserConfig = slurp(PARSER_CONFIGS_PATH + "/parsers/bro.json");
+
+ /**
+{
+ "sensorTopic" : "brop",
+ "parserConfig" : { },
+ "fieldTransformations" : [ ]
+}*/
+ @Multiline
+ static String defaultBropParserConfig;
+
+
+ @Test
+ public void testParserGetHappyPath() {
+
+ Object out = StellarTest.run("CONFIG_GET('PARSER', 'bro')", new HashMap<>(), context);
+ Assert.assertEquals(goodBroParserConfig, out);
+ }
+
+ @Test
+ public void testParserGetMissWithoutDefault() {
+
+ {
+ Object out = StellarTest.run("CONFIG_GET('PARSER', 'brop', false)", new HashMap<>(), context);
+ Assert.assertNull(out);
+ }
+ }
+
+ @Test
+ public void testParserGetMissWithDefault() {
+
+ {
+ Object out = StellarTest.run("CONFIG_GET('PARSER', 'brop')", new HashMap<>(), context);
+ Assert.assertEquals(defaultBropParserConfig, out);
+ }
+ {
+ Object out = StellarTest.run("CONFIG_GET('PARSER', 'brop', true)", new HashMap<>(), context);
+ Assert.assertEquals(defaultBropParserConfig, out);
+ }
+ }
+
+ static String goodTestEnrichmentConfig = slurp( SAMPLE_CONFIG_PATH + "/enrichments/test.json");
+
+ /**
+{
+ "index" : "brop",
+ "batchSize" : 0,
+ "enrichment" : {
+ "fieldMap" : { },
+ "fieldToTypeMap" : { },
+ "config" : { }
+ },
+ "threatIntel" : {
+ "fieldMap" : { },
+ "fieldToTypeMap" : { },
+ "config" : { },
+ "triageConfig" : {
+ "riskLevelRules" : { },
+ "aggregator" : "MAX",
+ "aggregationConfig" : { }
+ }
+ },
+ "configuration" : { }
+}*/
+ @Multiline
+ static String defaultBropEnrichmentConfig;
+
+
+ @Test
+ public void testEnrichmentGetHappyPath() {
+
+ Object out = StellarTest.run("CONFIG_GET('ENRICHMENT', 'test')", new HashMap<>(), context);
+ Assert.assertEquals(goodTestEnrichmentConfig, out.toString().trim());
+ }
+
+ @Test
+ public void testEnrichmentGetMissWithoutDefault() {
+
+ {
+ Object out = StellarTest.run("CONFIG_GET('ENRICHMENT', 'brop', false)", new HashMap<>(), context);
+ Assert.assertNull(out);
+ }
+ }
+
+ @Test
+ public void testEnrichmentGetMissWithDefault() {
+
+ {
+ Object out = StellarTest.run("CONFIG_GET('ENRICHMENT', 'brop')", new HashMap<>(), context);
+ Assert.assertEquals(defaultBropEnrichmentConfig, out.toString().trim());
+ }
+ {
+ Object out = StellarTest.run("CONFIG_GET('ENRICHMENT', 'brop', true)", new HashMap<>(), context);
+ Assert.assertEquals(defaultBropEnrichmentConfig, out.toString().trim());
+ }
+ }
+
+ static String goodGlobalConfig = slurp( SAMPLE_CONFIG_PATH+ "/global.json");
+
+ @Test
+ public void testGlobalGet() {
+
+ Object out = StellarTest.run("CONFIG_GET('GLOBAL')", new HashMap<>(), context);
+ Assert.assertEquals(goodGlobalConfig, out.toString().trim());
+ }
+
+ @Test
+ public void testGlobalPut() {
+
+ Object out = StellarTest.run("CONFIG_GET('GLOBAL')", new HashMap<>(), context);
+ Assert.assertEquals(goodGlobalConfig, out.toString().trim());
+ }
+
+ @Test(expected=ParseException.class)
+ public void testGlobalPutBad() {
+ StellarTest.run("CONFIG_PUT('GLOBAL', 'foo bar')", new HashMap<>(), context);
+ }
+
+ @Test
+ public void testEnrichmentPut() throws InterruptedException {
+ String brop= (String) StellarTest.run("CONFIG_GET('ENRICHMENT', 'testEnrichmentPut')", new HashMap<>(), context);
+ StellarTest.run("CONFIG_PUT('ENRICHMENT', config, 'testEnrichmentPut')", ImmutableMap.of("config", brop), context);
+ boolean foundMatch = false;
+ for(int i = 0;i < 10 && !foundMatch;++i) {
+ String bropNew = (String) StellarTest.run("CONFIG_GET('ENRICHMENT', 'testEnrichmentPut', false)", new HashMap<>(), context);
+ foundMatch = brop.equals(bropNew);
+ if(foundMatch) {
+ break;
+ }
+ Thread.sleep(2000);
+ }
+ Assert.assertTrue(foundMatch);
+ }
+
+ @Test(expected= ParseException.class)
+ public void testEnrichmentPutBad() throws InterruptedException {
+ {
+ StellarTest.run("CONFIG_PUT('ENRICHMENT', config, 'brop')", ImmutableMap.of("config", "foo bar"), context);
+ }
+ }
+
+ @Test
+ public void testParserPut() throws InterruptedException {
+ String brop= (String) StellarTest.run("CONFIG_GET('PARSER', 'testParserPut')", new HashMap<>(), context);
+ StellarTest.run("CONFIG_PUT('PARSER', config, 'testParserPut')", ImmutableMap.of("config", brop), context);
+ boolean foundMatch = false;
+ for(int i = 0;i < 10 && !foundMatch;++i) {
+ String bropNew = (String) StellarTest.run("CONFIG_GET('PARSER', 'testParserPut', false)", new HashMap<>(), context);
+ foundMatch = brop.equals(bropNew);
+ if(foundMatch) {
+ break;
+ }
+ Thread.sleep(2000);
+ }
+ Assert.assertTrue(foundMatch);
+ }
+
+ @Test(expected= ParseException.class)
+ public void testParserPutBad() throws InterruptedException {
+ {
+ StellarTest.run("CONFIG_PUT('PARSER', config, 'brop')", ImmutableMap.of("config", "foo bar"), context);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/c85c7426/metron-platform/metron-management/src/test/java/org/apache/metron/management/EnrichmentConfigFunctionsTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/test/java/org/apache/metron/management/EnrichmentConfigFunctionsTest.java b/metron-platform/metron-management/src/test/java/org/apache/metron/management/EnrichmentConfigFunctionsTest.java
new file mode 100644
index 0000000..850991a
--- /dev/null
+++ b/metron-platform/metron-management/src/test/java/org/apache/metron/management/EnrichmentConfigFunctionsTest.java
@@ -0,0 +1,385 @@
+/**
+ * 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.management;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.collect.ImmutableMap;
+import org.adrianwalker.multilinestring.Multiline;
+import org.apache.metron.common.configuration.enrichment.EnrichmentConfig;
+import org.apache.metron.common.configuration.enrichment.SensorEnrichmentConfig;
+import org.apache.metron.common.dsl.Context;
+import org.apache.metron.common.dsl.ParseException;
+import org.apache.metron.common.dsl.StellarFunctions;
+import org.apache.metron.common.stellar.StellarProcessor;
+import org.apache.metron.common.stellar.StellarTest;
+import org.apache.metron.common.stellar.shell.StellarExecutor;
+import org.apache.metron.common.utils.JSONUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.metron.common.configuration.ConfigurationType.ENRICHMENT;
+
+@RunWith(Parameterized.class)
+public class EnrichmentConfigFunctionsTest {
+
+ String configStr = emptyTransformationsConfig();
+ Map<String, StellarExecutor.VariableResult> variables;
+ Context context = null;
+ String enrichmentType = null;
+ String group = null;
+ public EnrichmentConfigFunctionsTest(String enrichmentType, String group) {
+ this.enrichmentType = enrichmentType;
+ this.group = group;
+ }
+
+ public static String emptyTransformationsConfig() {
+ SensorEnrichmentConfig config = new SensorEnrichmentConfig();
+ config.setIndex("dummy");
+ config.setBatchSize(5);
+ try {
+ return JSONUtils.INSTANCE.toJSON(config, true);
+ } catch (JsonProcessingException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> types() {
+ // each test will be run against these values for windowSize
+ return Arrays.asList(new Object[][]{
+ {"ENRICHMENT", "group"}
+ , {"ENRICHMENT", null}
+ , {"THREAT_INTEL", "group"}
+ , {"THREAT_INTEL", null}
+ });
+ }
+
+
+
+ @Before
+ public void setup() {
+ variables = ImmutableMap.of(
+ "upper", new StellarExecutor.VariableResult("TO_UPPER('foo')", "FOO"),
+ "lower", new StellarExecutor.VariableResult("TO_LOWER('FOO')", "foo")
+ );
+
+ context = new Context.Builder()
+ .with(StellarExecutor.SHELL_VARIABLES, () -> variables)
+ .build();
+ }
+
+ static Map<String, Object> toMap(String... k) {
+ Map<String, Object> ret = new HashMap<>();
+ for(int i = 0;i < k.length;i+=2) {
+ ret.put(k[i], k[i+1]);
+ }
+ return ret;
+ }
+ private int size(Map<String, Object> stellarFunctions) {
+ if(group == null) {
+ return stellarFunctions.size();
+ }
+ else {
+ return ((Map<String, Object>)stellarFunctions.getOrDefault(group, new HashMap<>())).size();
+ }
+ }
+ private Object get(Map<String, Object> stellarFunctions, String key) {
+ if(group == null) {
+ return stellarFunctions.get(key);
+ }
+ else {
+ return ((Map<String, Object>)stellarFunctions.get(group)).get(key);
+ }
+ }
+
+ private EnrichmentConfig getEnrichmentConfig(String configStr) {
+ SensorEnrichmentConfig sensorConfig = (SensorEnrichmentConfig) ENRICHMENT.deserialize(configStr);
+ switch (enrichmentType) {
+ case "ENRICHMENT":
+ return sensorConfig.getEnrichment();
+ case "THREAT_INTEL":
+ return sensorConfig.getThreatIntel();
+ }
+ return null;
+ }
+
+ private static Map<String, Object> getStellarMappings(EnrichmentConfig config) {
+ Map<String, Object> fieldMap = config.getFieldMap();
+ if (fieldMap == null) {
+ return new HashMap<>();
+ }
+ Map<String, Object> stellarMap = (Map<String, Object>) fieldMap.get("stellar");
+ if (stellarMap == null) {
+ return new HashMap<>();
+ }
+ return (Map<String, Object>) stellarMap.get("config");
+ }
+
+ private Object run(String rule, Map<String, Object> variables) {
+ StellarProcessor processor = new StellarProcessor();
+ return processor.parse(rule, x -> variables.get(x), StellarFunctions.FUNCTION_RESOLVER(), context);
+ }
+
+ @Test
+ public void testAddEmpty() {
+
+ String newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('upper'), group)"
+ , toMap("config", configStr
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ Map<String, Object> stellarFunctions = getStellarMappings(getEnrichmentConfig(newConfig));
+ Assert.assertEquals(1, size(stellarFunctions));
+ Assert.assertEquals(variables.get("upper").getExpression(), get(stellarFunctions,"upper"));
+ }
+
+
+
+ @Test
+ public void testAddHasExisting() {
+ String newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('upper'), group)"
+ ,toMap( "config", configStr
+ , "type", enrichmentType
+ , "group", group
+ )
+
+ );
+ newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('lower'), group)"
+ , toMap("config",newConfig
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ Map<String, Object> stellarFunctions = getStellarMappings(getEnrichmentConfig(newConfig));
+ Assert.assertEquals(2, size(stellarFunctions));
+ Assert.assertEquals(variables.get("upper").getExpression(), get(stellarFunctions,"upper"));
+ Assert.assertEquals(variables.get("lower").getExpression(), get(stellarFunctions,"lower"));
+ }
+
+ @Test
+ public void testAddMalformed() {
+ String newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('foo'), group)"
+ , toMap("config", configStr
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ Map<String, Object> stellarFunctions = getStellarMappings(getEnrichmentConfig(newConfig));
+ Assert.assertEquals(0, size(stellarFunctions));
+ }
+
+ @Test
+ public void testAddDuplicate() {
+ String newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('upper'), group)"
+ , toMap("config", configStr
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('upper'), group)"
+ , toMap("config",newConfig
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ Map<String, Object> stellarFunctions = getStellarMappings(getEnrichmentConfig(newConfig));
+ Assert.assertEquals(1, size(stellarFunctions));
+ Assert.assertEquals(variables.get("upper").getExpression(), get(stellarFunctions,"upper"));
+ }
+
+ @Test
+ public void testRemove() {
+ String newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('upper', 'lower'), group)"
+ , toMap("config", configStr
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_REMOVE(config, type, ['upper'], group)"
+ , toMap("config",newConfig
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ Map<String, Object> stellarFunctions = getStellarMappings(getEnrichmentConfig(newConfig));
+ Assert.assertEquals(1, size(stellarFunctions));
+ Assert.assertEquals(variables.get("lower").getExpression(), get(stellarFunctions,"lower"));
+ }
+
+ @Test
+ public void testRemoveMultiple() {
+ String newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('upper', 'lower'), group)"
+ , toMap("config", configStr
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_REMOVE(config, type, ['upper', 'lower'], group)"
+ , toMap("config",newConfig
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ Map<String, Object> stellarFunctions = getStellarMappings(getEnrichmentConfig(newConfig));
+ Assert.assertEquals(0, size(stellarFunctions));
+ }
+
+ @Test
+ public void testRemoveMissing() {
+ String newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('lower'), group)"
+ , toMap("config", configStr
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_REMOVE(config, type, ['upper'], group)"
+ , toMap("config",newConfig
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ Map<String, Object> stellarFunctions = getStellarMappings(getEnrichmentConfig(newConfig));
+ Assert.assertEquals(1, size(stellarFunctions));
+ Assert.assertEquals(variables.get("lower").getExpression(), get(stellarFunctions,"lower"));
+ }
+
+ /**
+\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
+\u2551 Group \u2502 Field \u2502 Transformation \u2551
+\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
+\u2551 group \u2502 upper \u2502 TO_UPPER('foo') \u2551
+\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d
+ */
+ @Multiline
+ static String testPrintExpectedWithGroup;
+ /**
+\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
+\u2551 Group \u2502 Field \u2502 Transformation \u2551
+\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
+\u2551 (default) \u2502 upper \u2502 TO_UPPER('foo') \u2551
+\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d
+ */
+ @Multiline
+ static String testPrintExpectedWithoutGroup;
+ @Test
+ public void testPrint() {
+ String newConfig = (String) run(
+ "ENRICHMENT_STELLAR_TRANSFORM_ADD(config, type, SHELL_VARS2MAP('upper'), group)"
+ , toMap("config", configStr
+ , "type", enrichmentType
+ , "group", group
+ )
+ );
+ String out = (String) run("ENRICHMENT_STELLAR_TRANSFORM_PRINT(config, type)"
+ , toMap("config", newConfig
+ ,"type", enrichmentType
+ )
+ );
+ if(group == null) {
+ Assert.assertEquals(testPrintExpectedWithoutGroup, out);
+ }
+ else {
+ Assert.assertEquals(testPrintExpectedWithGroup, out);
+ }
+ }
+
+ /**
+\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
+\u2551 Group \u2502 Field \u2502 Transformation \u2551
+\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
+\u2551 (empty) \u2551
+\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d
+ */
+ @Multiline
+ static String testPrintEmptyExpected;
+
+ @Test
+ public void testPrintEmpty() {
+ String out = (String) run("ENRICHMENT_STELLAR_TRANSFORM_PRINT(config, type)"
+ , toMap("config", configStr
+ ,"type", enrichmentType
+ )
+ );
+ Assert.assertEquals(testPrintEmptyExpected, out);
+ }
+
+ @Test
+ public void testPrintNull() {
+
+ String out = (String) run("ENRICHMENT_STELLAR_TRANSFORM_PRINT(config, type)"
+ , toMap("config", configStr ,"type", enrichmentType)
+ );
+ Assert.assertEquals(testPrintEmptyExpected, out);
+ }
+
+ @Test
+ public void testSetBatch() {
+ String out = (String) run("ENRICHMENT_SET_BATCH(config, 10)"
+ , toMap("config", configStr)
+ );
+ SensorEnrichmentConfig config = (SensorEnrichmentConfig)ENRICHMENT.deserialize(out);
+ Assert.assertEquals(config.getBatchSize(), 10);
+ }
+
+ @Test(expected=ParseException.class)
+ public void testSetBatchBad() {
+ String out = (String) run("ENRICHMENT_SET_BATCH(config, 10)"
+ , new HashMap<>()
+ );
+ SensorEnrichmentConfig config = (SensorEnrichmentConfig)ENRICHMENT.deserialize(out);
+ Assert.assertEquals(config.getBatchSize(), 10);
+ }
+
+ @Test
+ public void testSetIndex() {
+ String out = (String) run("ENRICHMENT_SET_INDEX(config, 'foo')"
+ , toMap("config", configStr)
+ );
+ SensorEnrichmentConfig config = (SensorEnrichmentConfig)ENRICHMENT.deserialize(out);
+ Assert.assertEquals("foo", config.getIndex());
+ }
+
+ @Test(expected= ParseException.class)
+ public void testSetIndexBad() {
+ String out = (String) run("ENRICHMENT_SET_INDEX(config, NULL)"
+ , new HashMap<>()
+ );
+ SensorEnrichmentConfig config = (SensorEnrichmentConfig)ENRICHMENT.deserialize(out);
+ Assert.assertNotNull(config.getIndex());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/c85c7426/metron-platform/metron-management/src/test/java/org/apache/metron/management/ParserConfigFunctionsTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-management/src/test/java/org/apache/metron/management/ParserConfigFunctionsTest.java b/metron-platform/metron-management/src/test/java/org/apache/metron/management/ParserConfigFunctionsTest.java
new file mode 100644
index 0000000..c8e5591
--- /dev/null
+++ b/metron-platform/metron-management/src/test/java/org/apache/metron/management/ParserConfigFunctionsTest.java
@@ -0,0 +1,180 @@
+/**
+ * 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.management;
+
+import com.google.common.collect.ImmutableMap;
+import org.adrianwalker.multilinestring.Multiline;
+import org.apache.metron.common.configuration.FieldTransformer;
+import org.apache.metron.common.configuration.SensorParserConfig;
+import org.apache.metron.common.dsl.Context;
+import org.apache.metron.common.stellar.StellarTest;
+import org.apache.metron.common.stellar.shell.StellarExecutor;
+import org.json.simple.JSONObject;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.metron.TestConstants.PARSER_CONFIGS_PATH;
+import static org.apache.metron.management.utils.FileUtils.slurp;
+import static org.apache.metron.common.configuration.ConfigurationType.PARSER;
+
+public class ParserConfigFunctionsTest {
+
+ String emptyTransformationsConfig = slurp(PARSER_CONFIGS_PATH + "/parsers/bro.json");
+ String existingTransformationsConfig = slurp(PARSER_CONFIGS_PATH + "/parsers/squid.json");
+ Map<String, StellarExecutor.VariableResult> variables ;
+ Context context = null;
+ @Before
+ public void setup() {
+ variables = ImmutableMap.of(
+ "upper" , new StellarExecutor.VariableResult("TO_UPPER('foo')", "FOO"),
+ "lower" , new StellarExecutor.VariableResult("TO_LOWER('FOO')", "foo")
+ );
+
+ context = new Context.Builder()
+ .with(StellarExecutor.SHELL_VARIABLES , () -> variables)
+ .build();
+ }
+
+ public Map<String, Object> transform(String parserConfig){
+ return transform(parserConfig, new HashMap<>());
+ }
+
+ public Map<String, Object> transform(String parserConfig, Map<String, Object> variables){
+ JSONObject ret = new JSONObject(variables);
+ SensorParserConfig sensorParserConfig = (SensorParserConfig) PARSER.deserialize(parserConfig);
+ sensorParserConfig.init();
+ for (FieldTransformer handler : sensorParserConfig.getFieldTransformations()) {
+ if (handler != null) {
+ handler.transformAndUpdate(ret, sensorParserConfig.getParserConfig(), context);
+ }
+ }
+ return ret;
+ }
+
+ @Test
+ public void testAddEmpty() {
+ String newConfig = (String)StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('upper'))", ImmutableMap.of("config", emptyTransformationsConfig), context);
+ Map<String, Object> transformations = transform(newConfig);
+ Assert.assertEquals(1, transformations.size());
+ Assert.assertEquals("FOO", transformations.get("upper") );
+ }
+
+ @Test
+ public void testAddHasExisting() {
+ String newConfig = (String)StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('upper'))"
+ , ImmutableMap.of("config", existingTransformationsConfig )
+ , context
+ );
+ Map<String, Object> transformations = transform(newConfig, ImmutableMap.of("url", "http://www.google.com"));
+ //squid already has 2 transformations, we just added url, which makes 3
+ Assert.assertEquals(4, transformations.size());
+ Assert.assertEquals("FOO", transformations.get("upper") );
+ }
+
+ @Test
+ public void testAddMalformed() {
+ String newConfig = (String)StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('blah'))", ImmutableMap.of("config", emptyTransformationsConfig), context);
+ Map<String, Object> transformations = transform(newConfig);
+ Assert.assertEquals(0, transformations.size());
+ }
+
+ @Test
+ public void testAddDuplicate() {
+ String newConfig = (String)StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('upper'))", ImmutableMap.of("config", emptyTransformationsConfig), context);
+ newConfig = (String)StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('upper'))", ImmutableMap.of("config", newConfig), context);
+ Map<String, Object> transformations = transform(newConfig);
+ Assert.assertEquals(1, transformations.size());
+ Assert.assertEquals("FOO", transformations.get("upper") );
+ }
+
+ @Test
+ public void testRemove() {
+ String newConfig = (String)StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('upper'))", ImmutableMap.of("config", emptyTransformationsConfig), context);
+ newConfig = (String)StellarTest.run("PARSER_STELLAR_TRANSFORM_REMOVE(config, ['upper'])", ImmutableMap.of("config", newConfig), context);
+ Map<String, Object> transformations = transform(newConfig);
+ Assert.assertEquals(0, transformations.size());
+ }
+
+ @Test
+ public void testRemoveMultiple() {
+ String newConfig = (String)StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('upper', 'lower'))", ImmutableMap.of("config", emptyTransformationsConfig), context);
+ newConfig = (String)StellarTest.run("PARSER_STELLAR_TRANSFORM_REMOVE(config, ['upper', 'lower'])", ImmutableMap.of("config", newConfig), context);
+ Map<String, Object> transformations = transform(newConfig);
+ Assert.assertEquals(0, transformations.size());
+ }
+
+ @Test
+ public void testRemoveMissing() {
+ {
+ String newConfig = (String) StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('upper'))", ImmutableMap.of("config", emptyTransformationsConfig), context);
+ newConfig = (String) StellarTest.run("PARSER_STELLAR_TRANSFORM_REMOVE(config, ['lower'])", ImmutableMap.of("config", newConfig), context);
+ Map<String, Object> transformations = transform(newConfig);
+ Assert.assertEquals(1, transformations.size());
+ Assert.assertEquals("FOO", transformations.get("upper"));
+ }
+ {
+ String newConfig = (String) StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('upper'))", ImmutableMap.of("config", emptyTransformationsConfig), context);
+ newConfig = (String) StellarTest.run("PARSER_STELLAR_TRANSFORM_REMOVE(config, [''])", ImmutableMap.of("config", newConfig), context);
+ Map<String, Object> transformations = transform(newConfig);
+ Assert.assertEquals(1, transformations.size());
+ Assert.assertEquals("FOO", transformations.get("upper"));
+ }
+ }
+
+ /**
+\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
+\u2551 Field \u2502 Transformation \u2551
+\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
+\u2551 upper \u2502 TO_UPPER('foo') \u2551
+\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d
+ */
+ @Multiline
+ static String testPrintExpected;
+ @Test
+ public void testPrint() {
+ String newConfig = (String) StellarTest.run("PARSER_STELLAR_TRANSFORM_ADD(config, SHELL_VARS2MAP('upper'))", ImmutableMap.of("config", emptyTransformationsConfig), context);
+ String out = (String) StellarTest.run("PARSER_STELLAR_TRANSFORM_PRINT(config )", ImmutableMap.of("config", newConfig), context);
+ Assert.assertEquals(testPrintExpected, out);
+ }
+ /**
+\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2564\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
+\u2551 Field \u2502 Transformation \u2551
+\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2567\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
+\u2551 (empty) \u2551
+\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d
+ */
+ @Multiline
+ static String testPrintEmptyExpected;
+
+ @Test
+ public void testPrintEmpty() {
+ String out = (String) StellarTest.run("PARSER_STELLAR_TRANSFORM_PRINT(config )", ImmutableMap.of("config", emptyTransformationsConfig), context);
+ Assert.assertEquals(testPrintEmptyExpected, out);
+ }
+
+ @Test
+ public void testPrintNull() {
+
+ String out = (String) StellarTest.run("PARSER_STELLAR_TRANSFORM_PRINT(config )", new HashMap<>(), context);
+ Assert.assertNull( out);
+ }
+}