/*
* 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.nifi.snmp.processors;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.regex.Pattern;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.Validator;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.processor.ProcessSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.PDU;
import org.snmp4j.security.AuthMD5;
import org.snmp4j.security.AuthSHA;
import org.snmp4j.security.Priv3DES;
import org.snmp4j.security.PrivAES128;
import org.snmp4j.security.PrivAES192;
import org.snmp4j.security.PrivAES256;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.util.TreeEvent;
/**
* Utility helper class that simplifies interactions with target SNMP API and NIFI API.
*/
abstract class SNMPUtils {
/** logger */
private final static Logger logger = LoggerFactory.getLogger(SNMPUtils.class);
/** OID Pattern */
public final static Pattern OID_PATTERN = Pattern.compile("[[0-9]+\\.]*");
/** delimiter for properties name */
public final static String SNMP_PROP_DELIMITER = "$";
/** prefix for SNMP properties in flow file */
public final static String SNMP_PROP_PREFIX = "snmp" + SNMP_PROP_DELIMITER;
/** list of properties name when performing simple get */
private final static List<String> propertyNames = Arrays.asList("snmp$errorIndex", "snmp$errorStatus", "snmp$errorStatusText",
"snmp$nonRepeaters", "snmp$requestID", "snmp$type", "snmp$variableBindings");
/** used to validate OID syntax */
public static final Validator SNMP_OID_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
final ValidationResult.Builder builder = new ValidationResult.Builder();
builder.subject(subject).input(input);
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return builder.valid(true).explanation("Contains Expression Language").build();
}
try {
if (OID_PATTERN.matcher(input).matches()) {
builder.valid(true);
} else {
builder.valid(false).explanation(input + "is not a valid OID");
}
} catch (final IllegalArgumentException e) {
builder.valid(false).explanation(e.getMessage());
}
return builder.build();
}
};
/**
* Updates {@link FlowFile} with attributes representing PDU properties
* @param response PDU retried from SNMP Agent
* @param flowFile instance of target {@link FlowFile}
* @param processSession instance of {@link ProcessSession}
* @return updated {@link FlowFile}
*/
public static FlowFile updateFlowFileAttributesWithPduProperties(PDU response, FlowFile flowFile, ProcessSession processSession) {
if (response != null) {
try {
Method[] methods = PDU.class.getDeclaredMethods();
Map<String, String> attributes = new HashMap<String, String>();
for (Method method : methods) {
if (Modifier.isPublic(method.getModifiers()) && (method.getParameterTypes().length == 0) && method.getName().startsWith("get")) {
String propertyName = extractPropertyNameFromMethod(method);
if (isValidSnmpPropertyName(propertyName)) {
Object amqpPropertyValue = method.invoke(response);
if (amqpPropertyValue != null) {
if (propertyName.equals(SNMP_PROP_PREFIX + "variableBindings") && (amqpPropertyValue instanceof Vector)) {
addGetOidValues(attributes, amqpPropertyValue);
} else {
attributes.put(propertyName, amqpPropertyValue.toString());
}
}
}
}
}
flowFile = processSession.putAllAttributes(flowFile, attributes);
} catch (Exception e) {
logger.warn("Failed to update FlowFile with AMQP attributes", e);
}
}
return flowFile;
}
/**
* Method to add attribute in flow file
* @param key attribute key
* @param value attribute value
* @param flowFile flow file to update
* @param processSession session
* @return updated flow file
*/
public static FlowFile addAttribute(String key, String value, FlowFile flowFile, ProcessSession processSession) {
Map<String, String> attributes = new HashMap<String, String>();
attributes.put(key, value);
flowFile = processSession.putAllAttributes(flowFile, attributes);
return flowFile;
}
/**
* Method to construct {@link FlowFile} attributes from a {@link TreeEvent}
* @param treeEvent a {@link TreeEvent}
* @param flowFile instance of the {@link FlowFile} to update
* @param processSession instance of {@link ProcessSession}
* @return updated {@link FlowFile}
*/
public static FlowFile updateFlowFileAttributesWithTreeEventProperties(TreeEvent treeEvent, FlowFile flowFile, ProcessSession processSession) {
Map<String, String> attributes = new HashMap<String, String>();
addWalkOidValues(attributes, treeEvent.getVariableBindings());
flowFile = processSession.putAllAttributes(flowFile, attributes);
return flowFile;
}
/**
* Method to construct {@link FlowFile} attributes from a vector of {@link VariableBinding}
* @param attributes attributes
* @param vector vector of {@link VariableBinding}
*/
private static void addWalkOidValues(Map<String, String> attributes, Object vector) {
if (vector instanceof VariableBinding[]) {
VariableBinding[] variables = (VariableBinding[]) vector;
for (VariableBinding variableBinding : variables) {
addAttributeFromVariable(variableBinding, attributes);
}
}
}
/**
* Method to construct {@link FlowFile} attributes from a vector of {@link VariableBinding}
* @param attributes attributes
* @param vector vector of {@link VariableBinding}
*/
private static void addGetOidValues(Map<String, String> attributes, Object vector) {
if (vector instanceof Vector) {
@SuppressWarnings("unchecked")
Vector<VariableBinding> variables = (Vector<VariableBinding>) vector;
for (VariableBinding variableBinding : variables) {
addAttributeFromVariable(variableBinding, attributes);
}
}
}
/**
* Method to add {@link FlowFile} attributes from a {@link VariableBinding}
* @param variableBinding {@link VariableBinding}
* @param attributes {@link FlowFile} attributes to update
*/
private static void addAttributeFromVariable(VariableBinding variableBinding, Map<String, String> attributes) {
attributes.put(SNMP_PROP_PREFIX + variableBinding.getOid() + SNMP_PROP_DELIMITER + variableBinding.getVariable().getSyntax(), variableBinding.getVariable().toString());
}
/**
* Will validate if provided name corresponds to valid SNMP property.
* @param name the name of the property
* @return 'true' if valid otherwise 'false'
*/
public static boolean isValidSnmpPropertyName(String name) {
return propertyNames.contains(name);
}
/**
* Method to extract property name from given {@link Method}
* @param method method
* @return property name
*/
private static String extractPropertyNameFromMethod(Method method) {
char c[] = method.getName().substring(3).toCharArray();
c[0] = Character.toLowerCase(c[0]);
return SNMP_PROP_PREFIX + new String(c);
}
/**
* Method to return the private protocol given the property
* @param privProtocol property
* @return protocol
*/
public static OID getPriv(String privProtocol) {
switch (privProtocol) {
case "DES":
return PrivDES.ID;
case "3DES":
return Priv3DES.ID;
case "AES128":
return PrivAES128.ID;
case "AES192":
return PrivAES192.ID;
case "AES256":
return PrivAES256.ID;
default:
return null;
}
}
/**
* Method to return the authentication protocol given the property
* @param authProtocol property
* @return protocol
*/
public static OID getAuth(String authProtocol) {
switch (authProtocol) {
case "SHA":
return AuthSHA.ID;
case "MD5":
return AuthMD5.ID;
default:
return null;
}
}
/**
* Method to get security level from string representation of level
* @param level level
* @return security level as integer
*/
public static int getSecLevel(String level) {
switch (level) {
case "noAuthNoPriv":
return SecurityLevel.NOAUTH_NOPRIV;
case "authNoPriv":
return SecurityLevel.AUTH_NOPRIV;
case "authPriv":
default:
return SecurityLevel.AUTH_PRIV;
}
}
}