turcsanyip commented on a change in pull request #5028: URL: https://github.com/apache/nifi/pull/5028#discussion_r621259816
########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPResponse.java ########## @@ -0,0 +1,64 @@ +/* + * 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.dto; + +import org.snmp4j.PDU; +import org.snmp4j.smi.VariableBinding; + +import java.util.Map; +import java.util.Vector; + +public class SNMPResponse { + + private final Map<String, String> attributes; + private final Vector<? extends VariableBinding> variableBindings; + private final int errorStatus; + private final String errorStatusText; + private final String targetAddress; + + public SNMPResponse(final Map<String, String> attributes, + final Vector<? extends VariableBinding> variableBindings, Review comment: I see `org.snmp4j.PDU#getVariableBindings()` returns `Vector` but I would rather use the `List` interface in our DTO class. ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/CompositeSNMPFactory.java ########## @@ -0,0 +1,71 @@ +/* + * 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.factory; + +import org.apache.nifi.snmp.configuration.SNMPConfiguration; +import org.apache.nifi.snmp.exception.InvalidSnmpVersionException; +import org.snmp4j.Snmp; +import org.snmp4j.Target; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class CompositeSNMPFactory implements SNMPFactory { + + private static final String INVALID_SNMP_VERSION = "SNMP version is not supported."; + private static final List<SNMPFactory> FACTORIES; + + static { + List<SNMPFactory> factories = new ArrayList<>(Arrays.asList(new V1SNMPFactory(), new V2cSNMPFactory(), new V3SNMPFactory())); Review comment: `Arrays.asList()` already returns a `List`. Is it needed to create an `ArrayList` here? ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/SNMPFactory.java ########## @@ -0,0 +1,30 @@ +/* + * 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.factory; + +import org.apache.nifi.snmp.configuration.SNMPConfiguration; +import org.snmp4j.Snmp; +import org.snmp4j.Target; + +public interface SNMPFactory { + + boolean supports(int version); + + Snmp getSnmpManagerInstance(SNMPConfiguration configuration); + + Target getTargetInstance(SNMPConfiguration configuration); Review comment: These methods create new instances so I would rather call them `create***Instance()` instead of `get***Instance`. ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/StandardSNMPRequestHandler.java ########## @@ -0,0 +1,124 @@ +/* + * 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.operations; + +import org.apache.nifi.flowfile.FlowFile; +import org.apache.nifi.snmp.dto.SNMPResponse; +import org.apache.nifi.snmp.dto.SNMPWalkResponse; +import org.apache.nifi.snmp.exception.CloseSNMPClientException; +import org.apache.nifi.snmp.exception.InvalidFlowFileException; +import org.apache.nifi.snmp.exception.RequestTimeoutException; +import org.apache.nifi.snmp.exception.SNMPWalkException; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.snmp4j.PDU; +import org.snmp4j.Snmp; +import org.snmp4j.Target; +import org.snmp4j.event.ResponseEvent; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.VariableBinding; +import org.snmp4j.util.DefaultPDUFactory; +import org.snmp4j.util.PDUFactory; +import org.snmp4j.util.TreeEvent; +import org.snmp4j.util.TreeUtils; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +final class StandardSNMPRequestHandler implements SNMPRequestHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(StandardSNMPRequestHandler.class); + private static final PDUFactory GET_PDU_FACTORY = new DefaultPDUFactory(PDU.GET); + private static final PDUFactory SET_PDU_FACTORY = new DefaultPDUFactory(PDU.SET); + private final Snmp snmpClient; + private final Target target; + + StandardSNMPRequestHandler(Snmp snmpClient, Target target) { + this.snmpClient = snmpClient; + this.target = target; + } + + /** + * Construct the PDU to perform the SNMP Get request and returns + * the result in order to create the flow file. + * + * @return {@link ResponseEvent} + */ + public SNMPResponse get(String oid) throws IOException { + final PDU pdu = GET_PDU_FACTORY.createPDU(target); + pdu.add(new VariableBinding(new OID(oid))); + final ResponseEvent response = snmpClient.get(pdu, target); + final PDU responsePdu = response.getResponse(); + if (responsePdu == null) { + throw new RequestTimeoutException("Get request timed out."); + } + final Map<String, String> attributes = SNMPUtils.getPduAttributeMap(responsePdu); + return new SNMPResponse(attributes, responsePdu.getVariableBindings(), responsePdu.getErrorStatus(), responsePdu.getErrorStatusText(), + target.getAddress().toString()); + } + + /** + * Perform a SNMP walk and returns the list of {@link TreeEvent} + * + * @return the list of {@link TreeEvent} + */ + public SNMPWalkResponse walk(String oid) { + TreeUtils treeUtils = new TreeUtils(snmpClient, GET_PDU_FACTORY); Review comment: `treeUtils` can be final. ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessor.java ########## @@ -16,413 +16,248 @@ */ package org.apache.nifi.snmp.processors; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - +import org.apache.nifi.annotation.lifecycle.OnScheduled; import org.apache.nifi.annotation.lifecycle.OnStopped; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.ValidationContext; -import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.AbstractProcessor; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Processor; -import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.util.StandardValidators; -import org.snmp4j.AbstractTarget; -import org.snmp4j.CommunityTarget; -import org.snmp4j.Snmp; -import org.snmp4j.TransportMapping; -import org.snmp4j.UserTarget; -import org.snmp4j.mp.MPv3; -import org.snmp4j.mp.SnmpConstants; -import org.snmp4j.security.SecurityModels; -import org.snmp4j.security.SecurityProtocols; -import org.snmp4j.security.USM; -import org.snmp4j.security.UsmUser; -import org.snmp4j.smi.OctetString; -import org.snmp4j.smi.UdpAddress; -import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.apache.nifi.reporting.InitializationException; +import org.apache.nifi.snmp.configuration.SNMPConfiguration; +import org.apache.nifi.snmp.configuration.SNMPConfigurationBuilder; +import org.apache.nifi.snmp.logging.SLF4JLogFactory; +import org.apache.nifi.snmp.operations.SNMPRequestHandler; +import org.apache.nifi.snmp.operations.SNMPRequestHandlerFactory; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.snmp4j.log.LogFactory; + +import java.util.HashMap; +import java.util.Map; /** - * Base processor that uses SNMP4J client API + * Base processor that uses SNMP4J client API. * (http://www.snmp4j.org/) - * - * @param <T> the type of {@link SNMPWorker}. Please see {@link SNMPSetter} - * and {@link SNMPGetter} */ -abstract class AbstractSNMPProcessor<T extends SNMPWorker> extends AbstractProcessor { +abstract class AbstractSNMPProcessor extends AbstractProcessor { + + static { + LogFactory.setLogFactory(new SLF4JLogFactory()); + } - /** property to define host of the SNMP agent */ - public static final PropertyDescriptor HOST = new PropertyDescriptor.Builder() + // SNMP versions + public static final AllowableValue SNMP_V1 = new AllowableValue("SNMPv1", "v1", "SNMP version 1"); + public static final AllowableValue SNMP_V2C = new AllowableValue("SNMPv2c", "v2c", "SNMP version 2c"); + public static final AllowableValue SNMP_V3 = new AllowableValue("SNMPv3", "v3", "SNMP version 3 with improved security"); + + // SNMPv3 privacy protocols + public static final AllowableValue NO_AUTH_NO_PRIV = new AllowableValue("noAuthNoPriv", "No authentication or encryption", + "No authentication or encryption."); + public static final AllowableValue AUTH_NO_PRIV = new AllowableValue("authNoPriv", "Authentication without encryption", + "Authentication without encryption."); + public static final AllowableValue AUTH_PRIV = new AllowableValue("authPriv", "Authentication and encryption", + "Authentication and encryption."); + + // SNMPv3 authentication protocols + public static final AllowableValue MD5 = new AllowableValue("MD5", "MD5 based authentication", + "Provides authentication based on the HMAC-MD5 algorithm."); + public static final AllowableValue SHA = new AllowableValue("SHA", "SHA based authentication", + "Provides authentication based on the HMAC-SHA algorithm."); + public static final AllowableValue NO_AUTHENTICATION = new AllowableValue("", "None", + "Sends SNMP requests without authentication."); + + // SNMPv3 encryption + public static final AllowableValue DES = new AllowableValue("DES", "DES", + "Symmetric-key algorithm for the encryption of digital data."); + public static final AllowableValue DES3 = new AllowableValue("3DES", "3DES", + "Symmetric-key block cipher, which applies the DES cipher algorithm three times to each data block."); + + private static final String AES_DESCRIPTION = "AES is a symmetric algorithm which uses the same 128, 192, or 256 bit" + + " key for both encryption and decryption (the security of an AES system increases exponentially with key length)."; + + public static final AllowableValue AES128 = new AllowableValue("AES128", "AES128", AES_DESCRIPTION); + public static final AllowableValue AES192 = new AllowableValue("AES192", "AES192", AES_DESCRIPTION); + public static final AllowableValue AES256 = new AllowableValue("AES256", "AES256", AES_DESCRIPTION); + public static final AllowableValue NO_ENCRYPTION = new AllowableValue("", "None", + "Sends SNMP requests without encryption."); + + + public static final PropertyDescriptor AGENT_HOST = new PropertyDescriptor.Builder() .name("snmp-hostname") - .displayName("Host Name") - .description("Network address of SNMP Agent (e.g., localhost)") + .displayName("SNMP Agent Hostname") + .description("Network address of the SNMP Agent.") .required(true) .defaultValue("localhost") - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .addValidator(StandardValidators.NETWORK_ADDRESS_VALIDATOR) .build(); - /** property to define port of the SNMP agent */ - public static final PropertyDescriptor PORT = new PropertyDescriptor.Builder() + public static final PropertyDescriptor AGENT_PORT = new PropertyDescriptor.Builder() .name("snmp-port") - .displayName("Port") - .description("Numeric value identifying Port of SNMP Agent (e.g., 161)") + .displayName("SNMP Agent Port") + .description("Numeric value identifying the port of SNMP Agent.") .required(true) .defaultValue("161") .addValidator(StandardValidators.PORT_VALIDATOR) .build(); - /** property to define SNMP version to use */ public static final PropertyDescriptor SNMP_VERSION = new PropertyDescriptor.Builder() .name("snmp-version") .displayName("SNMP Version") - .description("SNMP Version to use") + .description("Three significant versions of SNMP have been developed and deployed. " + + "SNMPv1 is the original version of the protocol. More recent versions, " + + "SNMPv2c and SNMPv3, feature improvements in performance, flexibility and security.") .required(true) - .allowableValues("SNMPv1", "SNMPv2c", "SNMPv3") - .defaultValue("SNMPv1") + .allowableValues(SNMP_V1, SNMP_V2C, SNMP_V3) + .defaultValue(SNMP_V1.getValue()) .build(); - /** property to define SNMP community to use */ public static final PropertyDescriptor SNMP_COMMUNITY = new PropertyDescriptor.Builder() .name("snmp-community") .displayName("SNMP Community (v1 & v2c)") Review comment: "SNMP Community" property is only displayed when v1 or v2c SNMP Versions selected. It is not necessary to add "(v1 & v2c)" anymore. ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessor.java ########## @@ -16,413 +16,248 @@ */ package org.apache.nifi.snmp.processors; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - +import org.apache.nifi.annotation.lifecycle.OnScheduled; import org.apache.nifi.annotation.lifecycle.OnStopped; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.ValidationContext; -import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.AbstractProcessor; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Processor; -import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.util.StandardValidators; -import org.snmp4j.AbstractTarget; -import org.snmp4j.CommunityTarget; -import org.snmp4j.Snmp; -import org.snmp4j.TransportMapping; -import org.snmp4j.UserTarget; -import org.snmp4j.mp.MPv3; -import org.snmp4j.mp.SnmpConstants; -import org.snmp4j.security.SecurityModels; -import org.snmp4j.security.SecurityProtocols; -import org.snmp4j.security.USM; -import org.snmp4j.security.UsmUser; -import org.snmp4j.smi.OctetString; -import org.snmp4j.smi.UdpAddress; -import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.apache.nifi.reporting.InitializationException; +import org.apache.nifi.snmp.configuration.SNMPConfiguration; +import org.apache.nifi.snmp.configuration.SNMPConfigurationBuilder; +import org.apache.nifi.snmp.logging.SLF4JLogFactory; +import org.apache.nifi.snmp.operations.SNMPRequestHandler; +import org.apache.nifi.snmp.operations.SNMPRequestHandlerFactory; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.snmp4j.log.LogFactory; + +import java.util.HashMap; +import java.util.Map; /** - * Base processor that uses SNMP4J client API + * Base processor that uses SNMP4J client API. * (http://www.snmp4j.org/) - * - * @param <T> the type of {@link SNMPWorker}. Please see {@link SNMPSetter} - * and {@link SNMPGetter} */ -abstract class AbstractSNMPProcessor<T extends SNMPWorker> extends AbstractProcessor { +abstract class AbstractSNMPProcessor extends AbstractProcessor { + + static { + LogFactory.setLogFactory(new SLF4JLogFactory()); + } - /** property to define host of the SNMP agent */ - public static final PropertyDescriptor HOST = new PropertyDescriptor.Builder() + // SNMP versions + public static final AllowableValue SNMP_V1 = new AllowableValue("SNMPv1", "v1", "SNMP version 1"); + public static final AllowableValue SNMP_V2C = new AllowableValue("SNMPv2c", "v2c", "SNMP version 2c"); + public static final AllowableValue SNMP_V3 = new AllowableValue("SNMPv3", "v3", "SNMP version 3 with improved security"); + + // SNMPv3 privacy protocols + public static final AllowableValue NO_AUTH_NO_PRIV = new AllowableValue("noAuthNoPriv", "No authentication or encryption", + "No authentication or encryption."); + public static final AllowableValue AUTH_NO_PRIV = new AllowableValue("authNoPriv", "Authentication without encryption", + "Authentication without encryption."); + public static final AllowableValue AUTH_PRIV = new AllowableValue("authPriv", "Authentication and encryption", + "Authentication and encryption."); + + // SNMPv3 authentication protocols + public static final AllowableValue MD5 = new AllowableValue("MD5", "MD5 based authentication", + "Provides authentication based on the HMAC-MD5 algorithm."); + public static final AllowableValue SHA = new AllowableValue("SHA", "SHA based authentication", + "Provides authentication based on the HMAC-SHA algorithm."); + public static final AllowableValue NO_AUTHENTICATION = new AllowableValue("", "None", + "Sends SNMP requests without authentication."); + + // SNMPv3 encryption + public static final AllowableValue DES = new AllowableValue("DES", "DES", + "Symmetric-key algorithm for the encryption of digital data."); + public static final AllowableValue DES3 = new AllowableValue("3DES", "3DES", + "Symmetric-key block cipher, which applies the DES cipher algorithm three times to each data block."); + + private static final String AES_DESCRIPTION = "AES is a symmetric algorithm which uses the same 128, 192, or 256 bit" + + " key for both encryption and decryption (the security of an AES system increases exponentially with key length)."; + + public static final AllowableValue AES128 = new AllowableValue("AES128", "AES128", AES_DESCRIPTION); + public static final AllowableValue AES192 = new AllowableValue("AES192", "AES192", AES_DESCRIPTION); + public static final AllowableValue AES256 = new AllowableValue("AES256", "AES256", AES_DESCRIPTION); + public static final AllowableValue NO_ENCRYPTION = new AllowableValue("", "None", + "Sends SNMP requests without encryption."); + + + public static final PropertyDescriptor AGENT_HOST = new PropertyDescriptor.Builder() .name("snmp-hostname") - .displayName("Host Name") - .description("Network address of SNMP Agent (e.g., localhost)") + .displayName("SNMP Agent Hostname") + .description("Network address of the SNMP Agent.") .required(true) .defaultValue("localhost") - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .addValidator(StandardValidators.NETWORK_ADDRESS_VALIDATOR) .build(); - /** property to define port of the SNMP agent */ - public static final PropertyDescriptor PORT = new PropertyDescriptor.Builder() + public static final PropertyDescriptor AGENT_PORT = new PropertyDescriptor.Builder() .name("snmp-port") - .displayName("Port") - .description("Numeric value identifying Port of SNMP Agent (e.g., 161)") + .displayName("SNMP Agent Port") + .description("Numeric value identifying the port of SNMP Agent.") .required(true) .defaultValue("161") .addValidator(StandardValidators.PORT_VALIDATOR) .build(); - /** property to define SNMP version to use */ public static final PropertyDescriptor SNMP_VERSION = new PropertyDescriptor.Builder() .name("snmp-version") .displayName("SNMP Version") - .description("SNMP Version to use") + .description("Three significant versions of SNMP have been developed and deployed. " + + "SNMPv1 is the original version of the protocol. More recent versions, " + + "SNMPv2c and SNMPv3, feature improvements in performance, flexibility and security.") .required(true) - .allowableValues("SNMPv1", "SNMPv2c", "SNMPv3") - .defaultValue("SNMPv1") + .allowableValues(SNMP_V1, SNMP_V2C, SNMP_V3) + .defaultValue(SNMP_V1.getValue()) .build(); - /** property to define SNMP community to use */ public static final PropertyDescriptor SNMP_COMMUNITY = new PropertyDescriptor.Builder() .name("snmp-community") .displayName("SNMP Community (v1 & v2c)") - .description("SNMP Community to use (e.g., public)") - .required(false) + .description("SNMPv1 and SNMPv2 use communities to establish trust between managers and agents." + + " Most agents support three community names, one each for read-only, read-write and trap." + + " These three community strings control different types of activities. The read-only community" + + " applies to get requests. The read-write community string applies to set requests. The trap" + + " community string applies to receipt of traps.") + .required(true) .defaultValue("public") .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .dependsOn(SNMP_VERSION, SNMP_V1, SNMP_V2C) .build(); - /** property to define SNMP security level to use */ public static final PropertyDescriptor SNMP_SECURITY_LEVEL = new PropertyDescriptor.Builder() .name("snmp-security-level") - .displayName("SNMP Security Level") - .description("SNMP Security Level to use") + .displayName("SNMP Security Level (v3 only)") Review comment: "SNMP Security Level" property is only displayed when v3 SNMP Version selected. It is not necessary to add "(v3 only)". ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/GetSNMP.java ########## @@ -16,184 +16,188 @@ */ package org.apache.nifi.snmp.processors; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import org.apache.nifi.annotation.behavior.InputRequirement; import org.apache.nifi.annotation.behavior.InputRequirement.Requirement; import org.apache.nifi.annotation.behavior.WritesAttribute; import org.apache.nifi.annotation.behavior.WritesAttributes; import org.apache.nifi.annotation.documentation.CapabilityDescription; import org.apache.nifi.annotation.documentation.Tags; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Processor; import org.apache.nifi.processor.Relationship; import org.apache.nifi.processor.util.StandardValidators; -import org.apache.nifi.processor.exception.ProcessException; -import org.snmp4j.PDU; -import org.snmp4j.event.ResponseEvent; -import org.snmp4j.smi.OID; -import org.snmp4j.util.TreeEvent; +import org.apache.nifi.snmp.dto.SNMPResponse; +import org.apache.nifi.snmp.dto.SNMPWalkResponse; +import org.apache.nifi.snmp.exception.SNMPException; +import org.apache.nifi.snmp.exception.SNMPWalkException; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.apache.nifi.snmp.validators.OIDValidator; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Retrieving data from configured SNMP agent which, upon each invocation of * {@link #onTrigger(ProcessContext, ProcessSession)} method, will construct a * {@link FlowFile} containing in its properties the information retrieved. * The output {@link FlowFile} won't have any content. */ -@Tags({ "snmp", "get", "oid", "walk" }) +@Tags({"snmp", "get", "oid", "walk"}) @InputRequirement(Requirement.INPUT_FORBIDDEN) @CapabilityDescription("Retrieves information from SNMP Agent and outputs a FlowFile with information in attributes and without any content") @WritesAttributes({ - @WritesAttribute(attribute=SNMPUtils.SNMP_PROP_PREFIX + "*", description="Attributes retrieved from the SNMP response. It may include:" - + " snmp$errorIndex, snmp$errorStatus, snmp$errorStatusText, snmp$nonRepeaters, snmp$requestID, snmp$type, snmp$variableBindings"), - @WritesAttribute(attribute=SNMPUtils.SNMP_PROP_PREFIX + "textualOid", description="This attribute will exist if and only if the strategy" - + " is GET and will be equal to the value given in Textual Oid property.") + @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "*", description = "Attributes retrieved from the SNMP response. It may include:" + + " snmp$errorIndex, snmp$errorStatus, snmp$errorStatusText, snmp$nonRepeaters, snmp$requestID, snmp$type, snmp$variableBindings"), + @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "textualOid", description = "This attribute will exist if and only if the strategy" + + " is GET and will be equal to the value given in Textual Oid property.") }) -public class GetSNMP extends AbstractSNMPProcessor<SNMPGetter> { +public class GetSNMP extends AbstractSNMPProcessor { + + // SNMP strategies + public static final AllowableValue GET = new AllowableValue("GET", "GET", + "A manager-to-agent request to retrieve the value of a variable. A response with the current value returned."); + public static final AllowableValue WALK = new AllowableValue("WALK", "WALK", + "A manager-to-agent request to retrieve the value of multiple variables. Snmpwalk also traverses all subnodes" + + "under the specified OID."); Review comment: Missing space: "subnodesunder" ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessor.java ########## @@ -16,413 +16,248 @@ */ package org.apache.nifi.snmp.processors; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - +import org.apache.nifi.annotation.lifecycle.OnScheduled; import org.apache.nifi.annotation.lifecycle.OnStopped; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.ValidationContext; -import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.AbstractProcessor; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Processor; -import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.util.StandardValidators; -import org.snmp4j.AbstractTarget; -import org.snmp4j.CommunityTarget; -import org.snmp4j.Snmp; -import org.snmp4j.TransportMapping; -import org.snmp4j.UserTarget; -import org.snmp4j.mp.MPv3; -import org.snmp4j.mp.SnmpConstants; -import org.snmp4j.security.SecurityModels; -import org.snmp4j.security.SecurityProtocols; -import org.snmp4j.security.USM; -import org.snmp4j.security.UsmUser; -import org.snmp4j.smi.OctetString; -import org.snmp4j.smi.UdpAddress; -import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.apache.nifi.reporting.InitializationException; +import org.apache.nifi.snmp.configuration.SNMPConfiguration; +import org.apache.nifi.snmp.configuration.SNMPConfigurationBuilder; +import org.apache.nifi.snmp.logging.SLF4JLogFactory; +import org.apache.nifi.snmp.operations.SNMPRequestHandler; +import org.apache.nifi.snmp.operations.SNMPRequestHandlerFactory; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.snmp4j.log.LogFactory; + +import java.util.HashMap; +import java.util.Map; /** - * Base processor that uses SNMP4J client API + * Base processor that uses SNMP4J client API. * (http://www.snmp4j.org/) - * - * @param <T> the type of {@link SNMPWorker}. Please see {@link SNMPSetter} - * and {@link SNMPGetter} */ -abstract class AbstractSNMPProcessor<T extends SNMPWorker> extends AbstractProcessor { +abstract class AbstractSNMPProcessor extends AbstractProcessor { + + static { + LogFactory.setLogFactory(new SLF4JLogFactory()); + } - /** property to define host of the SNMP agent */ - public static final PropertyDescriptor HOST = new PropertyDescriptor.Builder() + // SNMP versions + public static final AllowableValue SNMP_V1 = new AllowableValue("SNMPv1", "v1", "SNMP version 1"); + public static final AllowableValue SNMP_V2C = new AllowableValue("SNMPv2c", "v2c", "SNMP version 2c"); + public static final AllowableValue SNMP_V3 = new AllowableValue("SNMPv3", "v3", "SNMP version 3 with improved security"); + + // SNMPv3 privacy protocols + public static final AllowableValue NO_AUTH_NO_PRIV = new AllowableValue("noAuthNoPriv", "No authentication or encryption", + "No authentication or encryption."); + public static final AllowableValue AUTH_NO_PRIV = new AllowableValue("authNoPriv", "Authentication without encryption", + "Authentication without encryption."); + public static final AllowableValue AUTH_PRIV = new AllowableValue("authPriv", "Authentication and encryption", + "Authentication and encryption."); + + // SNMPv3 authentication protocols + public static final AllowableValue MD5 = new AllowableValue("MD5", "MD5 based authentication", + "Provides authentication based on the HMAC-MD5 algorithm."); + public static final AllowableValue SHA = new AllowableValue("SHA", "SHA based authentication", + "Provides authentication based on the HMAC-SHA algorithm."); + public static final AllowableValue NO_AUTHENTICATION = new AllowableValue("", "None", + "Sends SNMP requests without authentication."); + + // SNMPv3 encryption + public static final AllowableValue DES = new AllowableValue("DES", "DES", + "Symmetric-key algorithm for the encryption of digital data."); + public static final AllowableValue DES3 = new AllowableValue("3DES", "3DES", + "Symmetric-key block cipher, which applies the DES cipher algorithm three times to each data block."); + + private static final String AES_DESCRIPTION = "AES is a symmetric algorithm which uses the same 128, 192, or 256 bit" + + " key for both encryption and decryption (the security of an AES system increases exponentially with key length)."; + + public static final AllowableValue AES128 = new AllowableValue("AES128", "AES128", AES_DESCRIPTION); + public static final AllowableValue AES192 = new AllowableValue("AES192", "AES192", AES_DESCRIPTION); + public static final AllowableValue AES256 = new AllowableValue("AES256", "AES256", AES_DESCRIPTION); + public static final AllowableValue NO_ENCRYPTION = new AllowableValue("", "None", + "Sends SNMP requests without encryption."); + + + public static final PropertyDescriptor AGENT_HOST = new PropertyDescriptor.Builder() .name("snmp-hostname") - .displayName("Host Name") - .description("Network address of SNMP Agent (e.g., localhost)") + .displayName("SNMP Agent Hostname") + .description("Network address of the SNMP Agent.") .required(true) .defaultValue("localhost") - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .addValidator(StandardValidators.NETWORK_ADDRESS_VALIDATOR) .build(); - /** property to define port of the SNMP agent */ - public static final PropertyDescriptor PORT = new PropertyDescriptor.Builder() + public static final PropertyDescriptor AGENT_PORT = new PropertyDescriptor.Builder() .name("snmp-port") - .displayName("Port") - .description("Numeric value identifying Port of SNMP Agent (e.g., 161)") + .displayName("SNMP Agent Port") + .description("Numeric value identifying the port of SNMP Agent.") .required(true) .defaultValue("161") .addValidator(StandardValidators.PORT_VALIDATOR) .build(); - /** property to define SNMP version to use */ public static final PropertyDescriptor SNMP_VERSION = new PropertyDescriptor.Builder() .name("snmp-version") .displayName("SNMP Version") - .description("SNMP Version to use") + .description("Three significant versions of SNMP have been developed and deployed. " + + "SNMPv1 is the original version of the protocol. More recent versions, " + + "SNMPv2c and SNMPv3, feature improvements in performance, flexibility and security.") .required(true) - .allowableValues("SNMPv1", "SNMPv2c", "SNMPv3") - .defaultValue("SNMPv1") + .allowableValues(SNMP_V1, SNMP_V2C, SNMP_V3) + .defaultValue(SNMP_V1.getValue()) .build(); - /** property to define SNMP community to use */ public static final PropertyDescriptor SNMP_COMMUNITY = new PropertyDescriptor.Builder() .name("snmp-community") .displayName("SNMP Community (v1 & v2c)") - .description("SNMP Community to use (e.g., public)") - .required(false) + .description("SNMPv1 and SNMPv2 use communities to establish trust between managers and agents." + + " Most agents support three community names, one each for read-only, read-write and trap." + + " These three community strings control different types of activities. The read-only community" + + " applies to get requests. The read-write community string applies to set requests. The trap" + + " community string applies to receipt of traps.") + .required(true) .defaultValue("public") .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .dependsOn(SNMP_VERSION, SNMP_V1, SNMP_V2C) .build(); - /** property to define SNMP security level to use */ public static final PropertyDescriptor SNMP_SECURITY_LEVEL = new PropertyDescriptor.Builder() .name("snmp-security-level") - .displayName("SNMP Security Level") - .description("SNMP Security Level to use") + .displayName("SNMP Security Level (v3 only)") + .description("SNMP version 3 provides extra security with User Based Security Model (USM). The three levels of security is \n" + + "- Communication without authentication and encryption (NoAuthNoPriv).\n" + + "- Communication with authentication and without encryption (AuthNoPriv).\n" + + "- Communication with authentication and encryption (AuthPriv).") .required(true) - .allowableValues("noAuthNoPriv", "authNoPriv", "authPriv") - .defaultValue("authPriv") + .allowableValues(NO_AUTH_NO_PRIV, AUTH_NO_PRIV, AUTH_PRIV) + .defaultValue(NO_AUTH_NO_PRIV.getValue()) + .dependsOn(SNMP_VERSION, SNMP_V3) .build(); - /** property to define SNMP security name to use */ public static final PropertyDescriptor SNMP_SECURITY_NAME = new PropertyDescriptor.Builder() .name("snmp-security-name") - .displayName("SNMP Security name / user name") - .description("Security name used for SNMP exchanges") - .required(false) + .displayName("SNMP Security Name / Username for SNMP v3 Authentication") + .description("Security name / User name used for SNMP exchanges.") + .required(true) .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .dependsOn(SNMP_VERSION, SNMP_V3) .build(); - /** property to define SNMP authentication protocol to use */ - public static final PropertyDescriptor SNMP_AUTH_PROTOCOL = new PropertyDescriptor.Builder() - .name("snmp-authentication-protocol") - .displayName("SNMP Authentication Protocol") - .description("SNMP Authentication Protocol to use") + public static final PropertyDescriptor SNMP_PRIVACY_PROTOCOL = new PropertyDescriptor.Builder() + .name("snmp-private-protocol") + .displayName("SNMP Privacy Protocol") + .description("Privacy allows for encryption of SNMP v3 messages to ensure confidentiality of data.") .required(true) - .allowableValues("MD5", "SHA", "") + .allowableValues(DES, DES3, AES128, AES192, AES256, NO_ENCRYPTION) .defaultValue("") + .dependsOn(SNMP_SECURITY_LEVEL, AUTH_PRIV) .build(); - /** property to define SNMP authentication password to use */ - public static final PropertyDescriptor SNMP_AUTH_PASSWORD = new PropertyDescriptor.Builder() - .name("snmp-authentication-passphrase") - .displayName("SNMP Authentication pass phrase") - .description("Pass phrase used for SNMP authentication protocol") - .required(false) + public static final PropertyDescriptor SNMP_PRIVACY_PASSWORD = new PropertyDescriptor.Builder() + .name("snmp-private-protocol-passphrase") + .displayName("SNMP Privacy Protocol Passphrase") + .description("Pass phrase used for SNMP privacy protocol") + .required(true) .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) .sensitive(true) + .dependsOn(SNMP_SECURITY_LEVEL, AUTH_PRIV) .build(); - /** property to define SNMP private protocol to use */ - public static final PropertyDescriptor SNMP_PRIV_PROTOCOL = new PropertyDescriptor.Builder() - .name("snmp-private-protocol") - .displayName("SNMP Private Protocol") - .description("SNMP Private Protocol to use") + public static final PropertyDescriptor SNMP_AUTH_PROTOCOL = new PropertyDescriptor.Builder() + .name("snmp-authentication-protocol") + .displayName("SNMP Authentication Protocol") + .description("Hash based auhentication protocols for secure authentication.") .required(true) - .allowableValues("DES", "3DES", "AES128", "AES192", "AES256", "") + .allowableValues(MD5, SHA, NO_AUTHENTICATION) .defaultValue("") + .dependsOn(SNMP_SECURITY_LEVEL, AUTH_NO_PRIV, AUTH_PRIV) .build(); - /** property to define SNMP private password to use */ - public static final PropertyDescriptor SNMP_PRIV_PASSWORD = new PropertyDescriptor.Builder() - .name("snmp-private-protocol-passphrase") - .displayName("SNMP Private protocol pass phrase") - .description("Pass phrase used for SNMP private protocol") - .required(false) + public static final PropertyDescriptor SNMP_AUTH_PASSWORD = new PropertyDescriptor.Builder() + .name("snmp-authentication-passphrase") + .displayName("SNMP Authentication Passphrase") + .description("Passphrase used for SNMP authentication protocol") + .required(true) .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) .sensitive(true) + .dependsOn(SNMP_SECURITY_LEVEL, AUTH_NO_PRIV, AUTH_PRIV) .build(); - /** property to define the number of SNMP retries when requesting the SNMP Agent */ public static final PropertyDescriptor SNMP_RETRIES = new PropertyDescriptor.Builder() .name("snmp-retries") - .displayName("Number of retries") - .description("Set the number of retries when requesting the SNMP Agent") - .required(true) + .displayName("Number of Retries") + .description("Set the number of retries when requesting the SNMP Agent.") + .required(false) .defaultValue("0") - .addValidator(StandardValidators.INTEGER_VALIDATOR) + .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR) .build(); - /** property to define the timeout when requesting the SNMP Agent */ public static final PropertyDescriptor SNMP_TIMEOUT = new PropertyDescriptor.Builder() .name("snmp-timeout") .displayName("Timeout (ms)") - .description("Set the timeout (in milliseconds) when requesting the SNMP Agent") - .required(true) + .description("Set the timeout (in milliseconds) when requesting the SNMP Agent.") + .required(false) .defaultValue("5000") - .addValidator(StandardValidators.INTEGER_VALIDATOR) + .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR) .build(); - /** list of property descriptors */ - static List<PropertyDescriptor> descriptors = new ArrayList<>(); - /* - * Will ensure that list of PropertyDescriptors is build only once, since - * all other life cycle methods are invoked multiple times. - */ - static { - descriptors.add(HOST); - descriptors.add(PORT); - descriptors.add(SNMP_VERSION); - descriptors.add(SNMP_COMMUNITY); - descriptors.add(SNMP_SECURITY_LEVEL); - descriptors.add(SNMP_SECURITY_NAME); - descriptors.add(SNMP_AUTH_PROTOCOL); - descriptors.add(SNMP_AUTH_PASSWORD); - descriptors.add(SNMP_PRIV_PROTOCOL); - descriptors.add(SNMP_PRIV_PASSWORD); - descriptors.add(SNMP_RETRIES); - descriptors.add(SNMP_TIMEOUT); - } - - /** SNMP target */ - protected volatile AbstractTarget snmpTarget; - - /** transport mapping */ - protected volatile TransportMapping transportMapping; + protected volatile SNMPRequestHandler snmpRequestHandler; - /** SNMP */ - protected volatile Snmp snmp; - - /** target resource */ - protected volatile T targetResource; - - /** - * Will builds target resource upon first invocation and will delegate to the - * implementation of {@link #onTriggerSnmp(ProcessContext, ProcessSession)} method for - * further processing. - */ - @Override - public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException { - synchronized (this) { - this.buildTargetResource(context); + @OnScheduled + public void initSnmpClient(ProcessContext context) throws InitializationException { + final int version = SNMPUtils.getVersion(context.getProperty(SNMP_VERSION).getValue()); + SNMPConfiguration configuration; Review comment: `configuration` could be final. ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/GetSNMP.java ########## @@ -16,184 +16,188 @@ */ package org.apache.nifi.snmp.processors; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import org.apache.nifi.annotation.behavior.InputRequirement; import org.apache.nifi.annotation.behavior.InputRequirement.Requirement; import org.apache.nifi.annotation.behavior.WritesAttribute; import org.apache.nifi.annotation.behavior.WritesAttributes; import org.apache.nifi.annotation.documentation.CapabilityDescription; import org.apache.nifi.annotation.documentation.Tags; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Processor; import org.apache.nifi.processor.Relationship; import org.apache.nifi.processor.util.StandardValidators; -import org.apache.nifi.processor.exception.ProcessException; -import org.snmp4j.PDU; -import org.snmp4j.event.ResponseEvent; -import org.snmp4j.smi.OID; -import org.snmp4j.util.TreeEvent; +import org.apache.nifi.snmp.dto.SNMPResponse; +import org.apache.nifi.snmp.dto.SNMPWalkResponse; +import org.apache.nifi.snmp.exception.SNMPException; +import org.apache.nifi.snmp.exception.SNMPWalkException; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.apache.nifi.snmp.validators.OIDValidator; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Retrieving data from configured SNMP agent which, upon each invocation of * {@link #onTrigger(ProcessContext, ProcessSession)} method, will construct a * {@link FlowFile} containing in its properties the information retrieved. * The output {@link FlowFile} won't have any content. */ -@Tags({ "snmp", "get", "oid", "walk" }) +@Tags({"snmp", "get", "oid", "walk"}) @InputRequirement(Requirement.INPUT_FORBIDDEN) @CapabilityDescription("Retrieves information from SNMP Agent and outputs a FlowFile with information in attributes and without any content") @WritesAttributes({ - @WritesAttribute(attribute=SNMPUtils.SNMP_PROP_PREFIX + "*", description="Attributes retrieved from the SNMP response. It may include:" - + " snmp$errorIndex, snmp$errorStatus, snmp$errorStatusText, snmp$nonRepeaters, snmp$requestID, snmp$type, snmp$variableBindings"), - @WritesAttribute(attribute=SNMPUtils.SNMP_PROP_PREFIX + "textualOid", description="This attribute will exist if and only if the strategy" - + " is GET and will be equal to the value given in Textual Oid property.") + @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "*", description = "Attributes retrieved from the SNMP response. It may include:" + + " snmp$errorIndex, snmp$errorStatus, snmp$errorStatusText, snmp$nonRepeaters, snmp$requestID, snmp$type, snmp$variableBindings"), + @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "textualOid", description = "This attribute will exist if and only if the strategy" + + " is GET and will be equal to the value given in Textual Oid property.") }) -public class GetSNMP extends AbstractSNMPProcessor<SNMPGetter> { +public class GetSNMP extends AbstractSNMPProcessor { + + // SNMP strategies + public static final AllowableValue GET = new AllowableValue("GET", "GET", + "A manager-to-agent request to retrieve the value of a variable. A response with the current value returned."); + public static final AllowableValue WALK = new AllowableValue("WALK", "WALK", + "A manager-to-agent request to retrieve the value of multiple variables. Snmpwalk also traverses all subnodes" + + "under the specified OID."); - /** OID to request (if walk, it is the root ID of the request) */ + // OID to request (if walk, it is the root ID of the request). public static final PropertyDescriptor OID = new PropertyDescriptor.Builder() .name("snmp-oid") .displayName("OID") - .description("The OID to request") + .description("Each OID (object identifier) identifies a variable that can be read or set via SNMP.") .required(true) - .addValidator(SNMPUtils.SNMP_OID_VALIDATOR) + .addValidator(new OIDValidator()) + .build(); + + public static final PropertyDescriptor SNMP_STRATEGY = new PropertyDescriptor.Builder() + .name("snmp-strategy") + .displayName("SNMP Strategy (GET / WALK)") + .description("SNMP strategy to use (SNMP Get or SNMP Walk)") + .required(true) + .allowableValues(GET, WALK) + .defaultValue(GET.getValue()) .build(); - /** Textual OID to request */ public static final PropertyDescriptor TEXTUAL_OID = new PropertyDescriptor.Builder() .name("snmp-textual-oid") .displayName("Textual OID") - .description("The textual OID to request") + .description("The textual OID to request.") .required(false) .addValidator(StandardValidators.NON_BLANK_VALIDATOR) .defaultValue(null) .build(); - /** SNMP strategy for SNMP Get processor : simple get or walk */ - public static final PropertyDescriptor SNMP_STRATEGY = new PropertyDescriptor.Builder() - .name("snmp-strategy") - .displayName("SNMP strategy (GET/WALK)") - .description("SNMP strategy to use (SNMP Get or SNMP Walk)") - .required(true) - .allowableValues("GET", "WALK") - .defaultValue("GET") - .build(); - - /** relationship for success */ public static final Relationship REL_SUCCESS = new Relationship.Builder() .name("success") - .description("All FlowFiles that are received from the SNMP agent are routed to this relationship") + .description("All FlowFiles that are received from the SNMP agent are routed to this relationship.") .build(); - /** relationship for failure */ public static final Relationship REL_FAILURE = new Relationship.Builder() .name("failure") - .description("All FlowFiles that cannot received from the SNMP agent are routed to this relationship") + .description("All FlowFiles that cannot received from the SNMP agent are routed to this relationship.") .build(); - /** list of property descriptors */ - private final static List<PropertyDescriptor> propertyDescriptors; - - /** list of relationships */ - private final static Set<Relationship> relationships; - - /* - * Will ensure that the list of property descriptors is build only once. - * Will also create a Set of relationships - */ - static { - List<PropertyDescriptor> _propertyDescriptors = new ArrayList<>(); - _propertyDescriptors.add(OID); - _propertyDescriptors.add(TEXTUAL_OID); - _propertyDescriptors.add(SNMP_STRATEGY); - _propertyDescriptors.addAll(descriptors); - propertyDescriptors = Collections.unmodifiableList(_propertyDescriptors); - - Set<Relationship> _relationships = new HashSet<>(); - _relationships.add(REL_SUCCESS); - _relationships.add(REL_FAILURE); - relationships = Collections.unmodifiableSet(_relationships); - } + protected static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = Collections.unmodifiableList(Arrays.asList( + AGENT_HOST, + AGENT_PORT, + SNMP_VERSION, + SNMP_COMMUNITY, + SNMP_SECURITY_LEVEL, + SNMP_SECURITY_NAME, + SNMP_AUTH_PROTOCOL, + SNMP_AUTH_PASSWORD, + SNMP_PRIVACY_PROTOCOL, + SNMP_PRIVACY_PASSWORD, + SNMP_RETRIES, + SNMP_TIMEOUT, + OID, + TEXTUAL_OID, + SNMP_STRATEGY + )); + + private static final Set<Relationship> RELATIONSHIPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + REL_SUCCESS, + REL_FAILURE + ))); - /** - * Delegate method to supplement - * {@link #onTrigger(ProcessContext, ProcessSession)}. It is implemented by - * sub-classes to perform {@link Processor} specific functionality. - * - * @param context - * instance of {@link ProcessContext} - * @param processSession - * instance of {@link ProcessSession} - * @throws ProcessException Process exception - */ @Override - protected void onTriggerSnmp(ProcessContext context, ProcessSession processSession) throws ProcessException { - if("GET".equals(context.getProperty(SNMP_STRATEGY).getValue())) { - final ResponseEvent response = this.targetResource.get(); - if (response.getResponse() != null){ - FlowFile flowFile = processSession.create(); - PDU pdu = response.getResponse(); - flowFile = SNMPUtils.updateFlowFileAttributesWithPduProperties(pdu, flowFile, processSession); - flowFile = SNMPUtils.addAttribute(SNMPUtils.SNMP_PROP_PREFIX + "textualOid", - context.getProperty(TEXTUAL_OID).getValue(), flowFile, processSession); - processSession.getProvenanceReporter().receive(flowFile, - this.snmpTarget.getAddress().toString() + "/" + context.getProperty(OID).getValue()); - if(pdu.getErrorStatus() == PDU.noError) { - processSession.transfer(flowFile, REL_SUCCESS); - } else { - processSession.transfer(flowFile, REL_FAILURE); - } - } else { - this.getLogger().error("Get request timed out or parameters are incorrect."); - context.yield(); - } - } else if("WALK".equals(context.getProperty(SNMP_STRATEGY).getValue())) { - final List<TreeEvent> events = this.targetResource.walk(); - if((events != null) && !events.isEmpty() && (events.get(0).getVariableBindings() != null)) { - FlowFile flowFile = processSession.create(); - for (TreeEvent treeEvent : events) { - flowFile = SNMPUtils.updateFlowFileAttributesWithTreeEventProperties(treeEvent, flowFile, processSession); - } - processSession.getProvenanceReporter().receive(flowFile, - this.snmpTarget.getAddress().toString() + "/" + context.getProperty(OID).getValue()); + public void onTrigger(final ProcessContext context, final ProcessSession processSession) { + final SNMPStrategy snmpStrategy = SNMPStrategy.valueOf(context.getProperty(SNMP_STRATEGY).getValue()); + final String oid = context.getProperty(OID).getValue(); + + if (SNMPStrategy.GET == snmpStrategy) { + performSnmpGet(context, processSession, oid); + } else if (SNMPStrategy.WALK == snmpStrategy) { + performSnmpWalk(context, processSession, oid); + } + } + + private void performSnmpWalk(final ProcessContext context, final ProcessSession processSession, final String oid) { + try { + final SNMPWalkResponse response = snmpRequestHandler.walk(oid); + FlowFile flowFile = createFlowFileWithTreeEventProperties(response, processSession); + processSession.getProvenanceReporter().receive(flowFile, response.getTargetUri() + "/" + oid); + processSession.transfer(flowFile, REL_SUCCESS); + } catch (SNMPWalkException e) { + getLogger().error("Could not perform SNMP Walk: Check if SNMP agent is accessible and the specified OID exists."); + context.yield(); + } + } + + private void performSnmpGet(final ProcessContext context, final ProcessSession processSession, final String oid) { + final SNMPResponse response; + FlowFile flowFile = processSession.create(); Review comment: `flowFile` could be final. ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessor.java ########## @@ -16,413 +16,248 @@ */ package org.apache.nifi.snmp.processors; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - +import org.apache.nifi.annotation.lifecycle.OnScheduled; import org.apache.nifi.annotation.lifecycle.OnStopped; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.ValidationContext; -import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.AbstractProcessor; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Processor; -import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.util.StandardValidators; -import org.snmp4j.AbstractTarget; -import org.snmp4j.CommunityTarget; -import org.snmp4j.Snmp; -import org.snmp4j.TransportMapping; -import org.snmp4j.UserTarget; -import org.snmp4j.mp.MPv3; -import org.snmp4j.mp.SnmpConstants; -import org.snmp4j.security.SecurityModels; -import org.snmp4j.security.SecurityProtocols; -import org.snmp4j.security.USM; -import org.snmp4j.security.UsmUser; -import org.snmp4j.smi.OctetString; -import org.snmp4j.smi.UdpAddress; -import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.apache.nifi.reporting.InitializationException; +import org.apache.nifi.snmp.configuration.SNMPConfiguration; +import org.apache.nifi.snmp.configuration.SNMPConfigurationBuilder; +import org.apache.nifi.snmp.logging.SLF4JLogFactory; +import org.apache.nifi.snmp.operations.SNMPRequestHandler; +import org.apache.nifi.snmp.operations.SNMPRequestHandlerFactory; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.snmp4j.log.LogFactory; + +import java.util.HashMap; +import java.util.Map; /** - * Base processor that uses SNMP4J client API + * Base processor that uses SNMP4J client API. * (http://www.snmp4j.org/) - * - * @param <T> the type of {@link SNMPWorker}. Please see {@link SNMPSetter} - * and {@link SNMPGetter} */ -abstract class AbstractSNMPProcessor<T extends SNMPWorker> extends AbstractProcessor { +abstract class AbstractSNMPProcessor extends AbstractProcessor { + + static { + LogFactory.setLogFactory(new SLF4JLogFactory()); + } - /** property to define host of the SNMP agent */ - public static final PropertyDescriptor HOST = new PropertyDescriptor.Builder() + // SNMP versions + public static final AllowableValue SNMP_V1 = new AllowableValue("SNMPv1", "v1", "SNMP version 1"); + public static final AllowableValue SNMP_V2C = new AllowableValue("SNMPv2c", "v2c", "SNMP version 2c"); + public static final AllowableValue SNMP_V3 = new AllowableValue("SNMPv3", "v3", "SNMP version 3 with improved security"); + + // SNMPv3 privacy protocols + public static final AllowableValue NO_AUTH_NO_PRIV = new AllowableValue("noAuthNoPriv", "No authentication or encryption", + "No authentication or encryption."); + public static final AllowableValue AUTH_NO_PRIV = new AllowableValue("authNoPriv", "Authentication without encryption", + "Authentication without encryption."); + public static final AllowableValue AUTH_PRIV = new AllowableValue("authPriv", "Authentication and encryption", + "Authentication and encryption."); + + // SNMPv3 authentication protocols + public static final AllowableValue MD5 = new AllowableValue("MD5", "MD5 based authentication", + "Provides authentication based on the HMAC-MD5 algorithm."); + public static final AllowableValue SHA = new AllowableValue("SHA", "SHA based authentication", + "Provides authentication based on the HMAC-SHA algorithm."); + public static final AllowableValue NO_AUTHENTICATION = new AllowableValue("", "None", + "Sends SNMP requests without authentication."); + + // SNMPv3 encryption + public static final AllowableValue DES = new AllowableValue("DES", "DES", + "Symmetric-key algorithm for the encryption of digital data."); + public static final AllowableValue DES3 = new AllowableValue("3DES", "3DES", + "Symmetric-key block cipher, which applies the DES cipher algorithm three times to each data block."); + + private static final String AES_DESCRIPTION = "AES is a symmetric algorithm which uses the same 128, 192, or 256 bit" + + " key for both encryption and decryption (the security of an AES system increases exponentially with key length)."; + + public static final AllowableValue AES128 = new AllowableValue("AES128", "AES128", AES_DESCRIPTION); + public static final AllowableValue AES192 = new AllowableValue("AES192", "AES192", AES_DESCRIPTION); + public static final AllowableValue AES256 = new AllowableValue("AES256", "AES256", AES_DESCRIPTION); + public static final AllowableValue NO_ENCRYPTION = new AllowableValue("", "None", + "Sends SNMP requests without encryption."); + + + public static final PropertyDescriptor AGENT_HOST = new PropertyDescriptor.Builder() .name("snmp-hostname") - .displayName("Host Name") - .description("Network address of SNMP Agent (e.g., localhost)") + .displayName("SNMP Agent Hostname") + .description("Network address of the SNMP Agent.") .required(true) .defaultValue("localhost") - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .addValidator(StandardValidators.NETWORK_ADDRESS_VALIDATOR) .build(); - /** property to define port of the SNMP agent */ - public static final PropertyDescriptor PORT = new PropertyDescriptor.Builder() + public static final PropertyDescriptor AGENT_PORT = new PropertyDescriptor.Builder() .name("snmp-port") - .displayName("Port") - .description("Numeric value identifying Port of SNMP Agent (e.g., 161)") + .displayName("SNMP Agent Port") + .description("Numeric value identifying the port of SNMP Agent.") .required(true) .defaultValue("161") .addValidator(StandardValidators.PORT_VALIDATOR) .build(); - /** property to define SNMP version to use */ public static final PropertyDescriptor SNMP_VERSION = new PropertyDescriptor.Builder() .name("snmp-version") .displayName("SNMP Version") - .description("SNMP Version to use") + .description("Three significant versions of SNMP have been developed and deployed. " + + "SNMPv1 is the original version of the protocol. More recent versions, " + + "SNMPv2c and SNMPv3, feature improvements in performance, flexibility and security.") .required(true) - .allowableValues("SNMPv1", "SNMPv2c", "SNMPv3") - .defaultValue("SNMPv1") + .allowableValues(SNMP_V1, SNMP_V2C, SNMP_V3) + .defaultValue(SNMP_V1.getValue()) .build(); - /** property to define SNMP community to use */ public static final PropertyDescriptor SNMP_COMMUNITY = new PropertyDescriptor.Builder() .name("snmp-community") .displayName("SNMP Community (v1 & v2c)") - .description("SNMP Community to use (e.g., public)") - .required(false) + .description("SNMPv1 and SNMPv2 use communities to establish trust between managers and agents." + + " Most agents support three community names, one each for read-only, read-write and trap." + + " These three community strings control different types of activities. The read-only community" + + " applies to get requests. The read-write community string applies to set requests. The trap" + + " community string applies to receipt of traps.") + .required(true) .defaultValue("public") .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .dependsOn(SNMP_VERSION, SNMP_V1, SNMP_V2C) .build(); - /** property to define SNMP security level to use */ public static final PropertyDescriptor SNMP_SECURITY_LEVEL = new PropertyDescriptor.Builder() .name("snmp-security-level") - .displayName("SNMP Security Level") - .description("SNMP Security Level to use") + .displayName("SNMP Security Level (v3 only)") + .description("SNMP version 3 provides extra security with User Based Security Model (USM). The three levels of security is \n" + Review comment: I'm afraid using `\n` is useless. It will not result in new lines on the UI ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/GetSNMP.java ########## @@ -16,184 +16,188 @@ */ package org.apache.nifi.snmp.processors; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import org.apache.nifi.annotation.behavior.InputRequirement; import org.apache.nifi.annotation.behavior.InputRequirement.Requirement; import org.apache.nifi.annotation.behavior.WritesAttribute; import org.apache.nifi.annotation.behavior.WritesAttributes; import org.apache.nifi.annotation.documentation.CapabilityDescription; import org.apache.nifi.annotation.documentation.Tags; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Processor; import org.apache.nifi.processor.Relationship; import org.apache.nifi.processor.util.StandardValidators; -import org.apache.nifi.processor.exception.ProcessException; -import org.snmp4j.PDU; -import org.snmp4j.event.ResponseEvent; -import org.snmp4j.smi.OID; -import org.snmp4j.util.TreeEvent; +import org.apache.nifi.snmp.dto.SNMPResponse; +import org.apache.nifi.snmp.dto.SNMPWalkResponse; +import org.apache.nifi.snmp.exception.SNMPException; +import org.apache.nifi.snmp.exception.SNMPWalkException; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.apache.nifi.snmp.validators.OIDValidator; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Retrieving data from configured SNMP agent which, upon each invocation of * {@link #onTrigger(ProcessContext, ProcessSession)} method, will construct a * {@link FlowFile} containing in its properties the information retrieved. * The output {@link FlowFile} won't have any content. */ -@Tags({ "snmp", "get", "oid", "walk" }) +@Tags({"snmp", "get", "oid", "walk"}) @InputRequirement(Requirement.INPUT_FORBIDDEN) @CapabilityDescription("Retrieves information from SNMP Agent and outputs a FlowFile with information in attributes and without any content") @WritesAttributes({ - @WritesAttribute(attribute=SNMPUtils.SNMP_PROP_PREFIX + "*", description="Attributes retrieved from the SNMP response. It may include:" - + " snmp$errorIndex, snmp$errorStatus, snmp$errorStatusText, snmp$nonRepeaters, snmp$requestID, snmp$type, snmp$variableBindings"), - @WritesAttribute(attribute=SNMPUtils.SNMP_PROP_PREFIX + "textualOid", description="This attribute will exist if and only if the strategy" - + " is GET and will be equal to the value given in Textual Oid property.") + @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "*", description = "Attributes retrieved from the SNMP response. It may include:" + + " snmp$errorIndex, snmp$errorStatus, snmp$errorStatusText, snmp$nonRepeaters, snmp$requestID, snmp$type, snmp$variableBindings"), + @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "textualOid", description = "This attribute will exist if and only if the strategy" + + " is GET and will be equal to the value given in Textual Oid property.") }) -public class GetSNMP extends AbstractSNMPProcessor<SNMPGetter> { +public class GetSNMP extends AbstractSNMPProcessor { + + // SNMP strategies + public static final AllowableValue GET = new AllowableValue("GET", "GET", + "A manager-to-agent request to retrieve the value of a variable. A response with the current value returned."); + public static final AllowableValue WALK = new AllowableValue("WALK", "WALK", + "A manager-to-agent request to retrieve the value of multiple variables. Snmpwalk also traverses all subnodes" + + "under the specified OID."); - /** OID to request (if walk, it is the root ID of the request) */ + // OID to request (if walk, it is the root ID of the request). public static final PropertyDescriptor OID = new PropertyDescriptor.Builder() .name("snmp-oid") .displayName("OID") - .description("The OID to request") + .description("Each OID (object identifier) identifies a variable that can be read or set via SNMP.") .required(true) - .addValidator(SNMPUtils.SNMP_OID_VALIDATOR) + .addValidator(new OIDValidator()) + .build(); + + public static final PropertyDescriptor SNMP_STRATEGY = new PropertyDescriptor.Builder() + .name("snmp-strategy") + .displayName("SNMP Strategy (GET / WALK)") + .description("SNMP strategy to use (SNMP Get or SNMP Walk)") + .required(true) + .allowableValues(GET, WALK) + .defaultValue(GET.getValue()) .build(); - /** Textual OID to request */ public static final PropertyDescriptor TEXTUAL_OID = new PropertyDescriptor.Builder() .name("snmp-textual-oid") .displayName("Textual OID") - .description("The textual OID to request") + .description("The textual OID to request.") .required(false) .addValidator(StandardValidators.NON_BLANK_VALIDATOR) .defaultValue(null) .build(); - /** SNMP strategy for SNMP Get processor : simple get or walk */ - public static final PropertyDescriptor SNMP_STRATEGY = new PropertyDescriptor.Builder() - .name("snmp-strategy") - .displayName("SNMP strategy (GET/WALK)") - .description("SNMP strategy to use (SNMP Get or SNMP Walk)") - .required(true) - .allowableValues("GET", "WALK") - .defaultValue("GET") - .build(); - - /** relationship for success */ public static final Relationship REL_SUCCESS = new Relationship.Builder() .name("success") - .description("All FlowFiles that are received from the SNMP agent are routed to this relationship") + .description("All FlowFiles that are received from the SNMP agent are routed to this relationship.") .build(); - /** relationship for failure */ public static final Relationship REL_FAILURE = new Relationship.Builder() .name("failure") - .description("All FlowFiles that cannot received from the SNMP agent are routed to this relationship") + .description("All FlowFiles that cannot received from the SNMP agent are routed to this relationship.") .build(); - /** list of property descriptors */ - private final static List<PropertyDescriptor> propertyDescriptors; - - /** list of relationships */ - private final static Set<Relationship> relationships; - - /* - * Will ensure that the list of property descriptors is build only once. - * Will also create a Set of relationships - */ - static { - List<PropertyDescriptor> _propertyDescriptors = new ArrayList<>(); - _propertyDescriptors.add(OID); - _propertyDescriptors.add(TEXTUAL_OID); - _propertyDescriptors.add(SNMP_STRATEGY); - _propertyDescriptors.addAll(descriptors); - propertyDescriptors = Collections.unmodifiableList(_propertyDescriptors); - - Set<Relationship> _relationships = new HashSet<>(); - _relationships.add(REL_SUCCESS); - _relationships.add(REL_FAILURE); - relationships = Collections.unmodifiableSet(_relationships); - } + protected static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = Collections.unmodifiableList(Arrays.asList( + AGENT_HOST, + AGENT_PORT, + SNMP_VERSION, + SNMP_COMMUNITY, + SNMP_SECURITY_LEVEL, + SNMP_SECURITY_NAME, + SNMP_AUTH_PROTOCOL, + SNMP_AUTH_PASSWORD, + SNMP_PRIVACY_PROTOCOL, + SNMP_PRIVACY_PASSWORD, + SNMP_RETRIES, + SNMP_TIMEOUT, + OID, + TEXTUAL_OID, + SNMP_STRATEGY + )); + + private static final Set<Relationship> RELATIONSHIPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + REL_SUCCESS, + REL_FAILURE + ))); - /** - * Delegate method to supplement - * {@link #onTrigger(ProcessContext, ProcessSession)}. It is implemented by - * sub-classes to perform {@link Processor} specific functionality. - * - * @param context - * instance of {@link ProcessContext} - * @param processSession - * instance of {@link ProcessSession} - * @throws ProcessException Process exception - */ @Override - protected void onTriggerSnmp(ProcessContext context, ProcessSession processSession) throws ProcessException { - if("GET".equals(context.getProperty(SNMP_STRATEGY).getValue())) { - final ResponseEvent response = this.targetResource.get(); - if (response.getResponse() != null){ - FlowFile flowFile = processSession.create(); - PDU pdu = response.getResponse(); - flowFile = SNMPUtils.updateFlowFileAttributesWithPduProperties(pdu, flowFile, processSession); - flowFile = SNMPUtils.addAttribute(SNMPUtils.SNMP_PROP_PREFIX + "textualOid", - context.getProperty(TEXTUAL_OID).getValue(), flowFile, processSession); - processSession.getProvenanceReporter().receive(flowFile, - this.snmpTarget.getAddress().toString() + "/" + context.getProperty(OID).getValue()); - if(pdu.getErrorStatus() == PDU.noError) { - processSession.transfer(flowFile, REL_SUCCESS); - } else { - processSession.transfer(flowFile, REL_FAILURE); - } - } else { - this.getLogger().error("Get request timed out or parameters are incorrect."); - context.yield(); - } - } else if("WALK".equals(context.getProperty(SNMP_STRATEGY).getValue())) { - final List<TreeEvent> events = this.targetResource.walk(); - if((events != null) && !events.isEmpty() && (events.get(0).getVariableBindings() != null)) { - FlowFile flowFile = processSession.create(); - for (TreeEvent treeEvent : events) { - flowFile = SNMPUtils.updateFlowFileAttributesWithTreeEventProperties(treeEvent, flowFile, processSession); - } - processSession.getProvenanceReporter().receive(flowFile, - this.snmpTarget.getAddress().toString() + "/" + context.getProperty(OID).getValue()); + public void onTrigger(final ProcessContext context, final ProcessSession processSession) { + final SNMPStrategy snmpStrategy = SNMPStrategy.valueOf(context.getProperty(SNMP_STRATEGY).getValue()); + final String oid = context.getProperty(OID).getValue(); + + if (SNMPStrategy.GET == snmpStrategy) { + performSnmpGet(context, processSession, oid); + } else if (SNMPStrategy.WALK == snmpStrategy) { + performSnmpWalk(context, processSession, oid); + } + } + + private void performSnmpWalk(final ProcessContext context, final ProcessSession processSession, final String oid) { + try { + final SNMPWalkResponse response = snmpRequestHandler.walk(oid); + FlowFile flowFile = createFlowFileWithTreeEventProperties(response, processSession); Review comment: `flowFile` could be final. ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/GetSNMP.java ########## @@ -16,184 +16,188 @@ */ package org.apache.nifi.snmp.processors; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import org.apache.nifi.annotation.behavior.InputRequirement; import org.apache.nifi.annotation.behavior.InputRequirement.Requirement; import org.apache.nifi.annotation.behavior.WritesAttribute; import org.apache.nifi.annotation.behavior.WritesAttributes; import org.apache.nifi.annotation.documentation.CapabilityDescription; import org.apache.nifi.annotation.documentation.Tags; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Processor; import org.apache.nifi.processor.Relationship; import org.apache.nifi.processor.util.StandardValidators; -import org.apache.nifi.processor.exception.ProcessException; -import org.snmp4j.PDU; -import org.snmp4j.event.ResponseEvent; -import org.snmp4j.smi.OID; -import org.snmp4j.util.TreeEvent; +import org.apache.nifi.snmp.dto.SNMPResponse; +import org.apache.nifi.snmp.dto.SNMPWalkResponse; +import org.apache.nifi.snmp.exception.SNMPException; +import org.apache.nifi.snmp.exception.SNMPWalkException; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.apache.nifi.snmp.validators.OIDValidator; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Retrieving data from configured SNMP agent which, upon each invocation of * {@link #onTrigger(ProcessContext, ProcessSession)} method, will construct a * {@link FlowFile} containing in its properties the information retrieved. * The output {@link FlowFile} won't have any content. */ -@Tags({ "snmp", "get", "oid", "walk" }) +@Tags({"snmp", "get", "oid", "walk"}) @InputRequirement(Requirement.INPUT_FORBIDDEN) @CapabilityDescription("Retrieves information from SNMP Agent and outputs a FlowFile with information in attributes and without any content") @WritesAttributes({ - @WritesAttribute(attribute=SNMPUtils.SNMP_PROP_PREFIX + "*", description="Attributes retrieved from the SNMP response. It may include:" - + " snmp$errorIndex, snmp$errorStatus, snmp$errorStatusText, snmp$nonRepeaters, snmp$requestID, snmp$type, snmp$variableBindings"), - @WritesAttribute(attribute=SNMPUtils.SNMP_PROP_PREFIX + "textualOid", description="This attribute will exist if and only if the strategy" - + " is GET and will be equal to the value given in Textual Oid property.") + @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "*", description = "Attributes retrieved from the SNMP response. It may include:" + + " snmp$errorIndex, snmp$errorStatus, snmp$errorStatusText, snmp$nonRepeaters, snmp$requestID, snmp$type, snmp$variableBindings"), + @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "textualOid", description = "This attribute will exist if and only if the strategy" + + " is GET and will be equal to the value given in Textual Oid property.") }) -public class GetSNMP extends AbstractSNMPProcessor<SNMPGetter> { +public class GetSNMP extends AbstractSNMPProcessor { + + // SNMP strategies + public static final AllowableValue GET = new AllowableValue("GET", "GET", + "A manager-to-agent request to retrieve the value of a variable. A response with the current value returned."); + public static final AllowableValue WALK = new AllowableValue("WALK", "WALK", + "A manager-to-agent request to retrieve the value of multiple variables. Snmpwalk also traverses all subnodes" + + "under the specified OID."); - /** OID to request (if walk, it is the root ID of the request) */ + // OID to request (if walk, it is the root ID of the request). public static final PropertyDescriptor OID = new PropertyDescriptor.Builder() .name("snmp-oid") .displayName("OID") - .description("The OID to request") + .description("Each OID (object identifier) identifies a variable that can be read or set via SNMP.") .required(true) - .addValidator(SNMPUtils.SNMP_OID_VALIDATOR) + .addValidator(new OIDValidator()) + .build(); + + public static final PropertyDescriptor SNMP_STRATEGY = new PropertyDescriptor.Builder() + .name("snmp-strategy") + .displayName("SNMP Strategy (GET / WALK)") Review comment: The options in the list show the possible values (GET and WALK). I think it can be removed from the displayName. ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessor.java ########## @@ -16,413 +16,248 @@ */ package org.apache.nifi.snmp.processors; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - +import org.apache.nifi.annotation.lifecycle.OnScheduled; import org.apache.nifi.annotation.lifecycle.OnStopped; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.ValidationContext; -import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.processor.AbstractProcessor; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Processor; -import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.util.StandardValidators; -import org.snmp4j.AbstractTarget; -import org.snmp4j.CommunityTarget; -import org.snmp4j.Snmp; -import org.snmp4j.TransportMapping; -import org.snmp4j.UserTarget; -import org.snmp4j.mp.MPv3; -import org.snmp4j.mp.SnmpConstants; -import org.snmp4j.security.SecurityModels; -import org.snmp4j.security.SecurityProtocols; -import org.snmp4j.security.USM; -import org.snmp4j.security.UsmUser; -import org.snmp4j.smi.OctetString; -import org.snmp4j.smi.UdpAddress; -import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.apache.nifi.reporting.InitializationException; +import org.apache.nifi.snmp.configuration.SNMPConfiguration; +import org.apache.nifi.snmp.configuration.SNMPConfigurationBuilder; +import org.apache.nifi.snmp.logging.SLF4JLogFactory; +import org.apache.nifi.snmp.operations.SNMPRequestHandler; +import org.apache.nifi.snmp.operations.SNMPRequestHandlerFactory; +import org.apache.nifi.snmp.utils.SNMPUtils; +import org.snmp4j.log.LogFactory; + +import java.util.HashMap; +import java.util.Map; /** - * Base processor that uses SNMP4J client API + * Base processor that uses SNMP4J client API. * (http://www.snmp4j.org/) - * - * @param <T> the type of {@link SNMPWorker}. Please see {@link SNMPSetter} - * and {@link SNMPGetter} */ -abstract class AbstractSNMPProcessor<T extends SNMPWorker> extends AbstractProcessor { +abstract class AbstractSNMPProcessor extends AbstractProcessor { + + static { + LogFactory.setLogFactory(new SLF4JLogFactory()); + } - /** property to define host of the SNMP agent */ - public static final PropertyDescriptor HOST = new PropertyDescriptor.Builder() + // SNMP versions + public static final AllowableValue SNMP_V1 = new AllowableValue("SNMPv1", "v1", "SNMP version 1"); + public static final AllowableValue SNMP_V2C = new AllowableValue("SNMPv2c", "v2c", "SNMP version 2c"); + public static final AllowableValue SNMP_V3 = new AllowableValue("SNMPv3", "v3", "SNMP version 3 with improved security"); + + // SNMPv3 privacy protocols + public static final AllowableValue NO_AUTH_NO_PRIV = new AllowableValue("noAuthNoPriv", "No authentication or encryption", + "No authentication or encryption."); + public static final AllowableValue AUTH_NO_PRIV = new AllowableValue("authNoPriv", "Authentication without encryption", + "Authentication without encryption."); + public static final AllowableValue AUTH_PRIV = new AllowableValue("authPriv", "Authentication and encryption", + "Authentication and encryption."); + + // SNMPv3 authentication protocols + public static final AllowableValue MD5 = new AllowableValue("MD5", "MD5 based authentication", + "Provides authentication based on the HMAC-MD5 algorithm."); + public static final AllowableValue SHA = new AllowableValue("SHA", "SHA based authentication", + "Provides authentication based on the HMAC-SHA algorithm."); + public static final AllowableValue NO_AUTHENTICATION = new AllowableValue("", "None", + "Sends SNMP requests without authentication."); + + // SNMPv3 encryption + public static final AllowableValue DES = new AllowableValue("DES", "DES", + "Symmetric-key algorithm for the encryption of digital data."); + public static final AllowableValue DES3 = new AllowableValue("3DES", "3DES", + "Symmetric-key block cipher, which applies the DES cipher algorithm three times to each data block."); + + private static final String AES_DESCRIPTION = "AES is a symmetric algorithm which uses the same 128, 192, or 256 bit" + + " key for both encryption and decryption (the security of an AES system increases exponentially with key length)."; + + public static final AllowableValue AES128 = new AllowableValue("AES128", "AES128", AES_DESCRIPTION); + public static final AllowableValue AES192 = new AllowableValue("AES192", "AES192", AES_DESCRIPTION); + public static final AllowableValue AES256 = new AllowableValue("AES256", "AES256", AES_DESCRIPTION); + public static final AllowableValue NO_ENCRYPTION = new AllowableValue("", "None", + "Sends SNMP requests without encryption."); + + + public static final PropertyDescriptor AGENT_HOST = new PropertyDescriptor.Builder() .name("snmp-hostname") - .displayName("Host Name") - .description("Network address of SNMP Agent (e.g., localhost)") + .displayName("SNMP Agent Hostname") Review comment: The property name says "Hostname" but only IP is allowed (due to the validator). I believe we should support hostnames as well. (or the property name needs to be adjusted) ########## File path: nifi-commons/nifi-utils/src/main/java/org/apache/nifi/processor/util/StandardValidators.java ########## @@ -151,7 +152,24 @@ public ValidationResult validate(final String subject, final String value, final } }; - public static final Validator PORT_VALIDATOR = createLongValidator(1, 65535, true); + public static final Validator NETWORK_ADDRESS_VALIDATOR = new Validator() { + private final Pattern ipv4Pattern = Pattern.compile("^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!$)|$)){4}$"); Review comment: `ipv4Pattern` could be static. ########## File path: nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.GetSNMP/additionalDetails.html ########## @@ -54,16 +54,18 @@ <h2>Configuration Details</h2> <li><b>Host</b> - [REQUIRED] the name of the host where the SNMP agent is running. (default is localhost)</li> <li><b>Port</b> - [REQUIRED] the port number on which the SNMP agent is listening. (default is 161)</li> <li><b>OID</b> - [REQUIRED] the root OID to request.</li> + <li><b>SNMP Strategy</b> - [REQUIRED] SNMP Get or SNMP Walk request strategy.</li> <li><b>Version</b> - [REQUIRED] the SNMP version to use (SNMPv1 [default], or SNMPv2c, or SNMPv3)</li> <li><b>Community</b> - [REQUIRED if SNMPv1 or SNMPv2c] the SNMP community to use. (default is public)</li> <li><b>Security Level</b> - [REQUIRED if SNMPv3] the security level to use. (default is authPriv)</li> <li><b>Security Name</b> - [REQUIRED if SNMPv3] the security name (user name) to use.</li> <li><b>Authentication Protocol</b> - [REQUIRED if SNMPv3 and authNoPriv or authPriv] the authentication protocol to use.</li> <li><b>Authentication Password</b> - [REQUIRED if SNMPv3 and authNoPriv or authPriv] the authentication password to use.</li> - <li><b>Private Protocol</b> - [REQUIRED if SNMPv3 and authPriv] the private protocol to use.</li> - <li><b>Private Password</b> - [REQUIRED if SNMPv3 and authPriv] the private password to use.</li> - <li><b>Retries</b> - [REQUIRED] Sets the number of retries to be performed before a request is timed out. (default is 0)</li> - <li><b>Timeout</b> - [REQUIRED] Sets timeout in milliseconds before a confirmed request is resent or timed out. (default is 5000)</li> + <li><b>Privacy Protocol</b> - [REQUIRED if SNMPv3 and authPriv] the privacy protocol to use.</li> + <li><b>Privacy Password</b> - [REQUIRED if SNMPv3 and authPriv] the privacy password to use.</li> + <li><b>Retries</b> - [NOT REQUIRED] Sets the number of retries to be performed before a request is timed out. (default is 0)</li> + <li><b>Timeout</b> - [NOT REQUIRED] Sets timeout in milliseconds before a confirmed request is resent or timed out. (default is 1000)</li> + <li><b>Textual OID</b> - [NOT REQUIRED] Optionally added to the flow file</li> Review comment: Are these the same properties as the PropertyDescriptors in the processor? If yes, the same names should be used as on the UI (eg. Password vs Passphrase). I feel a bit overkill to maintain this list (unless it contains additional info, but I don't really see it). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected]
