SNMPv3 protocol support for onos.

Implement RFC-rfc3414 standard.

Change-Id: Ibe8d10aaaf569274b922a7500ed237a9813c0428
diff --git a/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpDevice.java b/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpDevice.java
index 587e2b1..14f954a 100644
--- a/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpDevice.java
+++ b/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpDevice.java
@@ -107,4 +107,11 @@
      * @return DeviceId
      */
     DeviceId deviceId();
+
+    /**
+     * Return the SNMP protocol version.
+     *
+     * @return SNMP protocol version
+     */
+    int getVersion();
 }
diff --git a/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpDeviceConfig.java b/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpDeviceConfig.java
index 47446ea..8776b3d 100644
--- a/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpDeviceConfig.java
+++ b/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpDeviceConfig.java
@@ -21,6 +21,7 @@
 import org.onlab.packet.IpAddress;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.config.Config;
+import org.snmp4j.mp.SnmpConstants;
 
 /**
  * Configuration to push devices to the SNMP provider.
@@ -36,6 +37,15 @@
     private static final String NOTIFICATION_PORT = "notificationPort";
     private static final String USERNAME = "username";
     private static final String PASSWORD = "password";
+    public static final String VERSION = "version";
+    public static final String SECURITY_NAME = "securityName";
+    public static final String SECURITY_LEVEL = "securityLevel";
+    public static final String AUTH_PROTOCOL = "authProtocol";
+    public static final String AUTH_PASSWORD = "authPassword";
+    public static final String PRIVACY_PROTOCOL = "privacyProtocol";
+    public static final String PRIVACY_PASSWORD = "privacyPassword";
+    public static final String CONTEXT_NAME = "contextName";
+
 
     @Override
     public boolean isValid() {
@@ -107,6 +117,77 @@
         return get(PASSWORD, "");
     }
 
+    /**
+     * Gets the version of the SNMP device.
+     *
+     * @return snmp version
+     */
+    public int version() {
+        return get(VERSION, SnmpConstants.version2c);
+    }
+
+    /**
+     * Gets the securityName of the SNMPv3 device.
+     *
+     * @return securityName
+     */
+    public String securityName() {
+        return get(SECURITY_NAME, "");
+    }
+
+    /**
+     * Gets the securityLevel of the SNMPv3 device.
+     *
+     * @return securityLevel
+     */
+    public String securityLevel() {
+        return get(SECURITY_LEVEL, "");
+    }
+
+    /**
+     * Gets the authProtocol of the SNMPv3 device.
+     *
+     * @return authProtocol
+     */
+    public String authProtocol() {
+        return get(AUTH_PROTOCOL, "");
+    }
+
+    /**
+     * Gets the authPassword of the SNMPv3 device.
+     *
+     * @return authPassword
+     */
+    public String authPassword() {
+        return get(AUTH_PASSWORD, "");
+    }
+
+    /**
+     * Gets the privacyProtocol of the SNMPv3 device.
+     *
+     * @return privacyProtocol
+     */
+    public String privacyProtocol() {
+        return get(PRIVACY_PROTOCOL, "");
+    }
+
+    /**
+     * Gets the privacyPassword of the SNMPv3 device.
+     *
+     * @return privacyPassword
+     */
+    public String privacyPassword() {
+        return get(PRIVACY_PASSWORD, "");
+    }
+
+    /**
+     * Gets the context name of the SNMPv3 device.
+     *
+     * @return context name
+     */
+    public String contextName() {
+        return get(CONTEXT_NAME, "");
+    }
 
     private Pair<String, Integer> extractIpPort() {
         String info = subject.uri().getSchemeSpecificPart();
diff --git a/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpException.java b/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpException.java
new file mode 100644
index 0000000..ad22916
--- /dev/null
+++ b/protocols/snmp/api/src/main/java/org/onosproject/snmp/SnmpException.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.snmp;
+
+/**
+ * Custom Exception for SNMP.
+ */
+public class SnmpException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Default constructor to create a new snmp exception.
+     */
+    public SnmpException() {
+        super();
+    }
+
+    /**
+     * Constructor to create exception from message and cause.
+     *
+     * @param text      the detail of exception in string
+     * @param variables underlying cause of exception variables
+     */
+    public SnmpException(String text, String... variables) {
+        super(format(text, variables));
+    }
+
+    private static String format(String text, String... variables) {
+        return String.format(text, (Object[]) variables);
+    }
+
+    /**
+     * Constructor to create exception from message and cause.
+     *
+     * @param cause     underlying cause of the error
+     * @param text      the detail of exception in string
+     * @param variables underlying cause of exception variables
+     */
+    public SnmpException(Throwable cause, String text, String... variables) {
+        super(format(text, variables), cause);
+
+    }
+}
\ No newline at end of file
diff --git a/protocols/snmp/api/src/main/java/org/onosproject/snmp/Snmpv3Configuration.java b/protocols/snmp/api/src/main/java/org/onosproject/snmp/Snmpv3Configuration.java
new file mode 100644
index 0000000..4fdafa0
--- /dev/null
+++ b/protocols/snmp/api/src/main/java/org/onosproject/snmp/Snmpv3Configuration.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.snmp;
+
+import com.btisystems.pronx.ems.core.snmp.ISnmpConfiguration;
+import org.onlab.packet.IpAddress;
+import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.smi.OID;
+import org.snmp4j.util.PDUFactory;
+
+import java.io.Serializable;
+
+/**
+ * Abstraction of SNMPv3 configuration.
+ */
+public interface Snmpv3Configuration extends Serializable, ISnmpConfiguration {
+
+    /**
+     * Returns the ip address.
+     *
+     * @return ip address
+     */
+    IpAddress getAddress();
+
+    /**
+     * Returns the snmpv3 security name of the device.
+     * the SNMPv3 username.
+     *
+     * @return security name
+     */
+    String getSecurityName();
+
+    /**
+     * Returns the snmpv3 security level of the device.
+     * the security level is noAuthNoPriv or authNoPriv or authPriv
+     *
+     * @return security level
+     */
+    SecurityLevel getSecurityLevel();
+
+    /**
+     * Returns the snmpv3 authentication protocol of the device.
+     * the authentication method (either MD5 or SHA)
+     *
+     * @return authentication protocol
+     */
+    OID getAuthenticationProtocol();
+
+    /**
+     * Returns the snmpv3 authentication password of the device.
+     * the authentication password must be at least eight characters long
+     *
+     * @return authentication password
+     */
+    String getAuthenticationPassword();
+
+    /**
+     * Returns the snmpv3 privacy protocol of the device.
+     * the privacy method (either AES or DES)
+     *
+     * @return privacy protocol
+     */
+    OID getPrivacyProtocol();
+
+    /**
+     * Returns the snmpv3 privacy password of the device.
+     * the privacy password must be at least eight characters long
+     *
+     * @return privacy password
+     */
+    String getPrivacyPassword();
+
+    /**
+     * Returns the snmpv3 authoritative engine id of the device.
+     *
+     * @return authoritative engine id
+     */
+    byte[] getAuthoritativeEngineId();
+
+    /**
+     * Returns the snmpv3 context name of the device.
+     * An SNMP context name or "context" in short,
+     * is a collection of management information accessible by an SNMP entity.
+     * An item of management information may exist in more than one context.
+     * An SNMP entity potentially has access to many contexts. In other words,
+     * if a management information has been defined under certain context by an SNMPv3 entity,
+     * then any management application can access that information by giving that context name.
+     * The "context name" is an octet string, which has at least one management information
+     *
+     * @return snmpv3 context name
+     */
+    String getContextName();
+
+    /**
+     * Create snmp session PDU factory.
+     *
+     * @return session PDU factory
+     */
+    PDUFactory createPduFactory();
+
+    /**
+     * Remove Snmp user security model when close connection to device.
+     */
+    void removeUsm();
+
+}
diff --git a/protocols/snmp/api/src/main/java/org/onosproject/snmp/Snmpv3Device.java b/protocols/snmp/api/src/main/java/org/onosproject/snmp/Snmpv3Device.java
new file mode 100644
index 0000000..7e48c51
--- /dev/null
+++ b/protocols/snmp/api/src/main/java/org/onosproject/snmp/Snmpv3Device.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.snmp;
+
+
+/**
+ * Abstraction a Snmpv3 Device.
+ */
+public interface Snmpv3Device extends SnmpDevice {
+
+    /**
+     * Retrieves the security name of SNMPv3 device.
+     *
+     * @return security name
+     */
+    String getSecurityName();
+
+    /**
+     * Retrieves the security level of SNMPv3 device.
+     *
+     * @return security level
+     */
+    String getSecurityLevel();
+
+    /**
+     * Retrieves the authentication protocol of SNMPv3 device.
+     *
+     * @return authentication protocol
+     */
+    String getAuthProtocol();
+
+    /**
+     * Retrieves the authentication password of SNMPv3 device.
+     *
+     * @return authentication password
+     */
+    String getAuthPassword();
+
+    /**
+     * Retrieves the privacy protocol of SNMPv3 device.
+     *
+     * @return privacy protocol
+     */
+    String getPrivProtocol();
+
+    /**
+     * Retrieves the privacy password of SNMPv3 device.
+     *
+     * @return privacy password
+     */
+    String getPrivPassword();
+
+    /**
+     * Retrieves the context name of SNMPv3 device.
+     *
+     * @return context name
+     */
+    String getContextName();
+
+}
diff --git a/protocols/snmp/ctl/BUILD b/protocols/snmp/ctl/BUILD
index 2e5c6ef..6b4550e 100644
--- a/protocols/snmp/ctl/BUILD
+++ b/protocols/snmp/ctl/BUILD
@@ -1,4 +1,4 @@
-COMPILE_DEPS = CORE_DEPS + [
+COMPILE_DEPS = CORE_DEPS + JACKSON + [
     "@org_apache_servicemix_bundles_snmp4j//jar",
     "@snmp_core//jar",
     "@mibs_net_snmp//jar",
diff --git a/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpController.java b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpController.java
index d5664e6..d84ef09 100644
--- a/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpController.java
+++ b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpController.java
@@ -23,24 +23,28 @@
 import com.btisystems.pronx.ems.core.snmp.SnmpSessionFactory;
 import com.btisystems.pronx.ems.core.snmp.V2cSnmpConfiguration;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
 import org.onosproject.alarm.Alarm;
 import org.onosproject.alarm.AlarmId;
 import org.onosproject.alarm.DefaultAlarm;
 import org.onosproject.net.DeviceId;
 import org.onosproject.snmp.SnmpController;
 import org.onosproject.snmp.SnmpDevice;
+import org.onosproject.snmp.SnmpException;
 import org.osgi.service.component.ComponentContext;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.snmp4j.mp.SnmpConstants;
 
 import java.io.IOException;
 import java.net.URI;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -52,15 +56,18 @@
     private final Logger log = LoggerFactory
             .getLogger(getClass());
 
-    protected ISnmpSessionFactory sessionFactory;
+    protected Map<Integer, ISnmpSessionFactory> factoryMap;
 
     protected final Map<DeviceId, ISnmpSession> sessionMap = new HashMap<>();
     protected final Map<DeviceId, SnmpDevice> snmpDeviceMap = new ConcurrentHashMap<>();
 
     @Activate
     public void activate(ComponentContext context) {
-        sessionFactory = new SnmpSessionFactory(
-                new DefaultSnmpConfigurationFactory(new V2cSnmpConfiguration()));
+        factoryMap = Maps.newHashMap();
+        factoryMap.put(SnmpConstants.version2c, new SnmpSessionFactory(
+                new DefaultSnmpConfigurationFactory(new V2cSnmpConfiguration())));
+        factoryMap.put(SnmpConstants.version3, new SnmpSessionFactory(
+                new DefaultSnmpConfigurationFactory(new V3SnmpConfiguration())));
         log.info("Started");
     }
 
@@ -76,6 +83,11 @@
     public ISnmpSession getSession(DeviceId deviceId) throws IOException {
         if (!sessionMap.containsKey(deviceId)) {
             SnmpDevice device = snmpDeviceMap.get(deviceId);
+            ISnmpSessionFactory sessionFactory = factoryMap.get(device.getVersion());
+            if (Objects.isNull(sessionFactory)) {
+                log.error("Invalid session factory", deviceId);
+                throw new SnmpException("Invalid session factory");
+            }
             String ipAddress = null;
             int port = -1;
             if (device != null) {
@@ -93,8 +105,26 @@
             }
             Preconditions.checkNotNull(ipAddress, "ip address is empty, cannot start session");
             Preconditions.checkArgument(port != -1, "port is incorrect, cannot start session");
-
-            ISnmpConfiguration config = new V2cSnmpConfiguration();
+            ISnmpConfiguration config;
+            if (device.getVersion() == SnmpConstants.version2c) {
+                config = new V2cSnmpConfiguration();
+                config.setCommunity(device.getCommunity());
+            } else if (device.getVersion() == SnmpConstants.version3) {
+                DefaultSnmpv3Device v3Device = (DefaultSnmpv3Device) device;
+                config = V3SnmpConfiguration.builder()
+                        .setAddress(ipAddress)
+                        .setSecurityName(v3Device.getSecurityName())
+                        .setSecurityLevel(v3Device.getSecurityLevel())
+                        .setAuthenticationProtocol(v3Device.getAuthProtocol())
+                        .setAuthenticationPassword(v3Device.getAuthPassword())
+                        .setPrivacyProtocol(v3Device.getPrivProtocol())
+                        .setPrivacyPassword(v3Device.getPrivPassword())
+                        .setContextName(v3Device.getContextName())
+                        .build();
+            } else {
+                throw new SnmpException(
+                        String.format("Invalid snmp version %d", device.getVersion()));
+            }
             config.setPort(port);
             sessionMap.put(deviceId, sessionFactory.createSession(config, ipAddress));
         }
diff --git a/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpDevice.java b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpDevice.java
index 5104cb8..30972a0 100644
--- a/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpDevice.java
+++ b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpDevice.java
@@ -21,6 +21,7 @@
 import org.slf4j.Logger;
 import org.snmp4j.Snmp;
 import org.snmp4j.TransportMapping;
+import org.snmp4j.mp.SnmpConstants;
 import org.snmp4j.smi.GenericAddress;
 import org.snmp4j.transport.DefaultTcpTransportMapping;
 import org.snmp4j.transport.DefaultUdpTransportMapping;
@@ -52,6 +53,7 @@
     private boolean reachable = false;
     private final String protocol;
     private final String notificationProtocol;
+    private final int version;
 
     private Snmp session;
 
@@ -65,6 +67,7 @@
         this.username = username;
         this.community = community;
         this.deviceId = createDeviceId();
+        this.version = SnmpConstants.version2c;
         initializeSession();
     }
 
@@ -78,6 +81,7 @@
         this.username = snmpDeviceConfig.username();
         this.community = snmpDeviceConfig.password();
         this.deviceId = createDeviceId();
+        this.version = snmpDeviceConfig.version();
         initializeSession();
     }
 
@@ -146,6 +150,11 @@
     }
 
     @Override
+    public int getVersion() {
+        return version;
+    }
+
+    @Override
     public DeviceId deviceId() {
         return deviceId;
     }
diff --git a/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpv3Device.java b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpv3Device.java
new file mode 100644
index 0000000..81cc8d1
--- /dev/null
+++ b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpv3Device.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.snmp.ctl;
+
+import com.google.common.base.MoreObjects;
+import org.apache.commons.lang.StringUtils;
+import org.onosproject.snmp.Snmpv3Device;
+import org.onosproject.snmp.SnmpDeviceConfig;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * This is a logical representation of actual SNMPv3 device.
+ * carrying all the necessary information to connect and execute
+ * SNMPv3 operations.
+ */
+public class DefaultSnmpv3Device extends DefaultSnmpDevice implements Snmpv3Device {
+
+    private final String securityLevel;
+
+    private final String securityName;
+
+    private final String authProtocol;
+
+    private final String authPassword;
+
+    private final String privProtocol;
+
+    private final String privPassword;
+
+    private final String contextName;
+
+    /**
+     * Create a new DefaultSnmpv3Device.
+     *
+     * @param config snmp device config
+     */
+    public DefaultSnmpv3Device(SnmpDeviceConfig config) {
+        super(config);
+        checkState(!StringUtils.isEmpty(config.securityLevel()),
+                "SNMPv3 security level cannot be null or empty");
+        checkState(!StringUtils.isEmpty(config.securityName()),
+                "SNMPv3 security name cannot be null or empty");
+        this.securityLevel = config.securityLevel();
+        this.securityName = config.securityName();
+        this.authProtocol = config.authProtocol();
+        this.authPassword = config.authPassword();
+        this.privProtocol = config.privacyProtocol();
+        this.privPassword = config.privacyPassword();
+        this.contextName = config.contextName();
+    }
+
+    /**
+     * Retrieves the security name of SNMPv3 device.
+     *
+     * @return security name
+     */
+    @Override
+    public String getSecurityName() {
+        return securityName;
+    }
+
+    /**
+     * Retrieves the security level of SNMPv3 device.
+     *
+     * @return security level
+     */
+    @Override
+    public String getSecurityLevel() {
+        return securityLevel;
+    }
+
+    /**
+     * Retrieves the authentication protocol of SNMPv3 device.
+     *
+     * @return authentication protocol
+     */
+    @Override
+    public String getAuthProtocol() {
+        return authProtocol;
+    }
+
+    /**
+     * Retrieves the authentication password of SNMPv3 device.
+     *
+     * @return authentication password
+     */
+    @Override
+    public String getAuthPassword() {
+        return authPassword;
+    }
+
+    /**
+     * Retrieves the privacy protocol of SNMPv3 device.
+     *
+     * @return privacy protocol
+     */
+    @Override
+    public String getPrivProtocol() {
+        return privProtocol;
+    }
+
+    /**
+     * Retrieves the privacy password of SNMPv3 device.
+     *
+     * @return privacy password
+     */
+    @Override
+    public String getPrivPassword() {
+        return privPassword;
+    }
+
+    /**
+     * Retrieves the context name of SNMPv3 device.
+     *
+     * @return context name
+     */
+    @Override
+    public String getContextName() {
+        return contextName;
+    }
+
+    /**
+     * Convert the Snmpv3 device to string.
+     */
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("hostName", getSnmpHost())
+                .add("securityLevel", securityLevel)
+                .add("securityName", securityName)
+                .add("authProtocol", authProtocol)
+                .add("authPassword", authPassword)
+                .add("privProtocol", privProtocol)
+                .add("privPassword", privPassword)
+                .add("contextName", contextName)
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        DefaultSnmpv3Device otherv3Device = (DefaultSnmpv3Device) obj;
+        return Objects.equals(securityLevel, otherv3Device.securityLevel) &&
+                Objects.equals(securityName, otherv3Device.securityName) &&
+                Objects.equals(getSnmpHost(), otherv3Device.getSnmpHost());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(securityLevel, securityName, getSnmpHost());
+    }
+}
diff --git a/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/V3SnmpConfiguration.java b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/V3SnmpConfiguration.java
new file mode 100644
index 0000000..640668b
--- /dev/null
+++ b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/V3SnmpConfiguration.java
@@ -0,0 +1,658 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.snmp.ctl;
+
+import com.google.common.base.MoreObjects;
+import org.apache.commons.lang.StringUtils;
+import com.btisystems.pronx.ems.core.snmp.SnmpConfiguration;
+import org.onosproject.snmp.SnmpException;
+import org.onosproject.snmp.Snmpv3Configuration;
+import org.slf4j.Logger;
+import org.onlab.packet.IpAddress;
+
+import org.snmp4j.MessageDispatcher;
+import org.snmp4j.MessageDispatcherImpl;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.TransportMapping;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.Address;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.util.MultiThreadedMessageDispatcher;
+import org.snmp4j.util.ThreadPool;
+import org.snmp4j.UserTarget;
+import org.snmp4j.ScopedPDU;
+import org.snmp4j.mp.MPv3;
+
+import org.snmp4j.security.PrivDES;
+import org.snmp4j.security.AuthMD5;
+import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.security.SecurityModels;
+import org.snmp4j.security.SecurityProtocols;
+import org.snmp4j.security.USM;
+import org.snmp4j.security.UsmUser;
+import org.snmp4j.security.PrivAES128;
+import org.snmp4j.security.PrivAES256;
+import org.snmp4j.security.Priv3DES;
+import org.snmp4j.security.PrivAES192;
+import org.snmp4j.security.AuthSHA;
+import org.snmp4j.security.nonstandard.PrivAES192With3DESKeyExtension;
+import org.snmp4j.security.nonstandard.PrivAES256With3DESKeyExtension;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.UdpAddress;
+import org.snmp4j.util.DefaultPDUFactory;
+import org.snmp4j.util.PDUFactory;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the SNMPv3 configuration.
+ */
+public class V3SnmpConfiguration extends SnmpConfiguration implements Snmpv3Configuration {
+
+    private IpAddress address;
+
+    private String securityName;
+
+    private SecurityLevel securityLevel;
+
+    private OID authProtocol;
+
+    private String authPassword;
+
+    private OID privacyProtocol;
+
+    private String privacyPassword;
+
+    private String contextName;
+
+    private byte[] authoritativeEngineId;
+
+    private static Snmp snmp;
+
+    private static final USM USM_USER;
+
+    private static final int ENGINE_BOOT = 0;
+
+    private static final int PASS_CODE_LENGTH = 8;
+
+    private static final int DISCOVERY_TIMEOUT = 5000;
+
+    private static final String SLASH = "/";
+
+    private static final byte[] LOCALENGINEID = MPv3.createLocalEngineID();
+
+    private final Logger log = getLogger(V3SnmpConfiguration.class);
+
+    static {
+        SecurityProtocols securityProtocols = SecurityProtocols.getInstance();
+        securityProtocols.addPrivacyProtocol(new PrivAES128());
+        securityProtocols.addPrivacyProtocol(new
+                PrivAES192With3DESKeyExtension());
+        securityProtocols.addPrivacyProtocol(new
+                PrivAES256With3DESKeyExtension());
+        securityProtocols.addPrivacyProtocol(new PrivDES());
+        securityProtocols.addPrivacyProtocol(new Priv3DES());
+
+        USM_USER = new USM(SecurityProtocols.getInstance(),
+                new OctetString(LOCALENGINEID), ENGINE_BOOT);
+        USM_USER.setEngineDiscoveryEnabled(true);
+        SecurityModels.getInstance().addSecurityModel(USM_USER);
+    }
+
+    public V3SnmpConfiguration() {
+        this.setVersion(SnmpConstants.version3);
+    }
+
+    private V3SnmpConfiguration(Builder builder) {
+        this();
+        this.address = builder.address;
+        this.securityName = builder.securityName;
+        this.securityLevel = builder.securityLevel;
+        this.authProtocol = builder.authProtocol;
+        this.authPassword = builder.authPassword;
+        this.privacyProtocol = builder.privacyProtocol;
+        this.privacyPassword = builder.privacyPassword;
+        this.contextName = builder.contextName;
+    }
+
+    @Override
+    public Target createTarget(Address address) {
+        setUsm();
+        UserTarget target = new UserTarget();
+        target.setSecurityLevel(this.getSecurityLevel().getSnmpValue());
+        target.setSecurityName(new OctetString(this.getSecurityName()));
+        target.setVersion(this.getVersion());
+        target.setAddress(address);
+        target.setRetries(this.getRetries());
+        target.setTimeout(this.getTimeout());
+        target.setAuthoritativeEngineID(this.getAuthoritativeEngineId());
+        return target;
+    }
+
+    /**
+     * Create snmp session PDU factory.
+     *
+     * @return session PDU factory
+     */
+    @Override
+    public PDUFactory createPduFactory() {
+        DefaultPDUFactory pduFactory = new DefaultPDUFactory(PDU.GETBULK);
+        if (!StringUtils.isEmpty(this.getContextName())) {
+            pduFactory.setContextName(new OctetString(this.getContextName()));
+        }
+        return pduFactory;
+    }
+
+    @Override
+    public PDU createPDU(int type) {
+        ScopedPDU pdu = new ScopedPDU();
+        pdu.setType(type);
+        if (!StringUtils.isEmpty(this.getContextName())) {
+            pdu.setContextName(new OctetString(this.getContextName()));
+        }
+        switch (type) {
+            case PDU.GETBULK:
+                pdu.setMaxRepetitions(this.getMaxRepetitions());
+                pdu.setNonRepeaters(this.getNonRepeaters());
+                break;
+            default:
+                log.debug("Not setting up non-default configuration for PDU type {}.", type);
+        }
+
+        return pdu;
+    }
+
+    /**
+     * Create snmp session to the device.
+     *
+     * @return snmp session object
+     */
+    @Override
+    public Snmp createSnmpSession(TransportMapping transportMapping) throws IOException {
+        synchronized (USM_USER) {
+            if (Objects.isNull(snmp)) {
+                ThreadPool threadPool = ThreadPool.create("SnmpDispatcherPool", getDispatcherPoolSize());
+                MessageDispatcher mtDispatcher = new MultiThreadedMessageDispatcher(
+                        threadPool, new MessageDispatcherImpl());
+
+                // Add message processing models
+                mtDispatcher.addMessageProcessingModel(new MPv3());
+
+                snmp = new Snmp(mtDispatcher, transportMapping);
+
+                snmp.listen();
+            }
+        }
+
+        return snmp;
+    }
+
+    /**
+     * Set User Security Model to the snmpv3 session.
+     * SNMPv3 is a security model in which an authentication
+     * strategy is set up for a user in which the user resides.
+     * Security level is the permitted level of security within a security model.
+     * A combination of a security model and a security level determines
+     * which security mechanism is used when handling an SNMP packet.
+     */
+    private void setUsm() {
+
+        OctetString securityName = new OctetString(this.getSecurityName());
+        OctetString authPassphrase = StringUtils.isEmpty(this.getAuthenticationPassword()) ?
+                null : new OctetString(this.getAuthenticationPassword());
+        OctetString privPassphrase = StringUtils.isEmpty(this.getPrivacyPassword()) ?
+                null : new OctetString(this.getPrivacyPassword());
+
+
+        discoverAuthoritativeEngineId();
+
+        if (USM_USER.hasUser(new OctetString(this.getAuthoritativeEngineId()), securityName)) {
+            return;
+        }
+
+        USM_USER.addUser(securityName,
+                new UsmUser(securityName,
+                        this.getAuthenticationProtocol(),
+                        authPassphrase,
+                        this.getPrivacyProtocol(),
+                        privPassphrase));
+
+    }
+
+    /**
+     * Remove Snmp user security model when close connection to device.
+     */
+    @Override
+    public void removeUsm() {
+        OctetString securityName = new OctetString(this.getSecurityName());
+        OctetString engineId = new OctetString(this.getAuthoritativeEngineId());
+        if (USM_USER.hasUser(engineId, securityName)) {
+            USM_USER.removeAllUsers(securityName, engineId);
+        }
+
+    }
+
+    /**
+     * Discover snmp agent authoritative engineId.
+     * The Engine ID is used by SNMPv3 entities to uniquely identify them.
+     * An SNMP agent is considered an authoritative SNMP engine.
+     * This means that the agent responds to incoming messages (Get, GetNext, GetBulk, Set)
+     * and sends trap messages to a manager.
+     * The agent's local information is encapsulated in fields in the message.
+     * <p>
+     * Each SNMP agent maintains local information that is used in SNMPv3 message exchanges.
+     * The default SNMP Engine ID is comprised of the enterprise number and the default MAC address.
+     * This engine ID must be unique for the administrative domain,
+     * so that no two devices in a network have the same engine ID.
+     */
+    private void discoverAuthoritativeEngineId() {
+        this.authoritativeEngineId = Optional.ofNullable(snmp.discoverAuthoritativeEngineID(
+                        getTransportAddress(), DISCOVERY_TIMEOUT))
+                .orElseThrow(() -> new SnmpException(String.format("Snmp agent %s is not configured with" +
+                                " Snmpv3 user or not reachable",
+                        getAddress().toString())));
+    }
+
+    /**
+     * Returns the security level of SNMPv3 device.
+     *
+     * @return security level
+     */
+    @Override
+    public SecurityLevel getSecurityLevel() {
+        return this.securityLevel;
+    }
+
+    /**
+     * Returns the ip address.
+     *
+     * @return ip address
+     */
+    @Override
+    public IpAddress getAddress() {
+        return this.address;
+    }
+
+    /**
+     * Returns the security name of SNMPv3 device.
+     *
+     * @return security name
+     */
+    @Override
+    public String getSecurityName() {
+        return this.securityName;
+    }
+
+    /**
+     * Returns the authentication password of SNMPv3 device.
+     *
+     * @return authentication password
+     */
+    @Override
+    public String getAuthenticationPassword() {
+        return this.authPassword;
+    }
+
+    /**
+     * Returns the authentication protocol of SNMPv3 device.
+     *
+     * @return authentication protocol
+     */
+    @Override
+    public OID getAuthenticationProtocol() {
+        return this.authProtocol;
+    }
+
+    /**
+     * Returns the snmpv3 privacy password of the device.
+     *
+     * @return privacy password
+     */
+    @Override
+    public String getPrivacyPassword() {
+        return this.privacyPassword;
+    }
+
+    /**
+     * Returns the snmpv3 privacy protocol of the device.
+     *
+     * @return privacy protocol
+     */
+    @Override
+    public OID getPrivacyProtocol() {
+        return this.privacyProtocol;
+    }
+
+    /**
+     * Returns the snmpv3 context name of the device.
+     *
+     * @return snmpv3 context name
+     */
+    @Override
+    public String getContextName() {
+        return this.contextName;
+    }
+
+    /**
+     * Returns the snmpv3 authoritative engine id of the device.
+     *
+     * @return authoritative engine id
+     */
+    @Override
+    public byte[] getAuthoritativeEngineId() {
+        return this.authoritativeEngineId;
+    }
+
+
+    private Address getTransportAddress() {
+        return new UdpAddress(getAddress().toString() + SLASH + getPort());
+    }
+
+    /**
+     * Creates a new v3snmp configuration builder.
+     *
+     * @return new v3snmp configuration builder
+     */
+    public static V3SnmpConfiguration.Builder builder() {
+        return new V3SnmpConfiguration.Builder();
+    }
+
+    /**
+     * Convert the Snmpv3 configuration to string.
+     */
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("address", address)
+                .add("securityName", securityName)
+                .add("securityLevel", securityLevel)
+                .add("authProtocol", authProtocol)
+                .add("authPassword", authPassword)
+                .add("privacyProtocol", privacyProtocol)
+                .add("privacyPassword", privacyPassword)
+                .add("contextName", contextName)
+                .add("authoritativeEngineId", Arrays.toString(authoritativeEngineId))
+                .toString();
+    }
+
+    /**
+     * Facility for gradually building snmpv3 configuration.
+     */
+    public static final class Builder {
+
+        private IpAddress address;
+
+        private String securityName;
+
+        private SecurityLevel securityLevel;
+
+        private OID authProtocol;
+
+        private String authPassword;
+
+        private OID privacyProtocol;
+
+        private String privacyPassword;
+
+        private String contextName;
+
+
+        // Private construction is forbidden.
+        private Builder() {
+        }
+
+        /**
+         * Set ip address of the device.
+         *
+         * @param address ip address
+         * @return This builder
+         */
+        public Builder setAddress(String address) {
+            this.address = IpAddress.valueOf(address);
+            return this;
+        }
+
+        /**
+         * Set the security level of the SNMPv3 device.
+         *
+         * @param securityLevel securityLevel of the SNMPv3 device
+         * @return This builder
+         */
+        public Builder setSecurityLevel(String securityLevel) {
+            this.securityLevel = getSecurityLevel(securityLevel);
+            return this;
+        }
+
+        /**
+         * Set the security name of the SNMPv3 device.
+         *
+         * @param securityName securityName of the SNMPv3 device.
+         * @return This builder
+         */
+        public Builder setSecurityName(String securityName) {
+            this.securityName = securityName;
+            return this;
+        }
+
+        /**
+         * Set the authentication password of the SNMPv3 device.
+         *
+         * @param authPassword authPassword of the SNMPv3 device
+         * @return This builder
+         */
+        public Builder setAuthenticationPassword(String authPassword) {
+            this.authPassword = StringUtils.isEmpty(authPassword) ? null : authPassword;
+            return this;
+        }
+
+        /**
+         * Set the authentication protocol of the SNMPv3 device.
+         *
+         * @param authProtocol authProtocol of the SNMPv3 device
+         * @return This builder
+         */
+        public Builder setAuthenticationProtocol(String authProtocol) {
+            this.authProtocol = getAuthProtocol(authProtocol);
+            return this;
+        }
+
+        /**
+         * Set the privacy password of the SNMPv3 device.
+         *
+         * @param privacyPassword privacy protocol of the SNMPv3 device
+         * @return This builder
+         */
+        public Builder setPrivacyPassword(String privacyPassword) {
+            this.privacyPassword = StringUtils.isEmpty(privacyPassword) ? null : privacyPassword;
+            return this;
+        }
+
+        /**
+         * Set the privacy protocol of the SNMPv3 device.
+         *
+         * @param privacyProtocol privacyProtocol of the SNMPv3 device
+         * @return This builder
+         */
+        public Builder setPrivacyProtocol(String privacyProtocol) {
+            this.privacyProtocol = getPrivProtocol(privacyProtocol);
+            return this;
+        }
+
+        /**
+         * Set the context name of the SNMPv3 device.
+         *
+         * @param contextName context name of the SNMPv3 device
+         * @return This builder
+         */
+        public Builder setContextName(String contextName) {
+            this.contextName = contextName;
+            return this;
+        }
+
+        /**
+         * Convert privacy protocol string to privacy protocol object identifier.
+         *
+         * @param priv privacy protocol string
+         * @return privacy protocol object identifier
+         */
+        private OID getPrivProtocol(String priv) {
+
+            switch (priv) {
+
+                case "DES":
+                    return PrivDES.ID;
+
+                case "AES":
+                case "AES128":
+                    return PrivAES128.ID;
+
+                case "AES192":
+                    return PrivAES192.ID;
+
+                case "AES256":
+                    return PrivAES256.ID;
+
+                case "3DES":
+                case "DESEDE":
+                    return Priv3DES.ID;
+
+                default:
+                    throw new SnmpException("Invalid privacy protocol");
+
+            }
+        }
+
+        /**
+         * Convert authentication string to authentication protocol object identifier.
+         *
+         * @param auth privacy protocol string
+         * @return authentication protocol object identifier
+         */
+        private OID getAuthProtocol(String auth) {
+
+            switch (auth) {
+
+                case "MD5":
+                    return AuthMD5.ID;
+
+                case "SHA":
+                    return AuthSHA.ID;
+
+                default:
+                    throw new SnmpException("Invalid Authentication protocol");
+
+            }
+        }
+
+        /**
+         * Convert security level string to security level number.
+         *
+         * @param securityLevel snmpv3 security level string
+         * @return snmpv3 security level
+         */
+        private SecurityLevel getSecurityLevel(String securityLevel) {
+
+            switch (securityLevel) {
+
+                case "noAuthNoPriv":
+                    return SecurityLevel.noAuthNoPriv;
+
+                case "authNoPriv":
+                    return SecurityLevel.authNoPriv;
+
+                case "authPriv":
+                    return SecurityLevel.authPriv;
+
+                default:
+                    throw new SnmpException("Invalid Security level");
+            }
+        }
+
+        /**
+         * Validate snmpv3 configuration.
+         */
+        private void validateUsmConfiguration() {
+
+            validateSecurityName();
+
+            switch (securityLevel.getSnmpValue()) {
+
+                case SecurityLevel.AUTH_PRIV:
+                    validateAuth();
+                    validatePriv();
+                    return;
+
+                case SecurityLevel.AUTH_NOPRIV:
+                    validateAuth();
+                    return;
+
+                case SecurityLevel.NOAUTH_NOPRIV:
+                    return;
+
+                default:
+                    throw new SnmpException("Invalid security level");
+
+            }
+        }
+
+        /**
+         * Validate snmpv3 authentication protocol and password.
+         */
+        private void validateAuth() {
+            checkNotNull(authProtocol, "Authentication protocol must be provided");
+            checkNotNull(authPassword, "Authentication password must be provided");
+            checkState(authPassword.length() >= PASS_CODE_LENGTH,
+                    "Invalid authentication password");
+        }
+
+        private void validatePriv() {
+            checkNotNull(privacyProtocol, "Privacy protocol must be provided");
+            checkNotNull(privacyPassword, "Privacy password must be provided");
+            checkState(privacyPassword.length() >= PASS_CODE_LENGTH,
+                    "Invalid privacy password");
+        }
+
+        /**
+         * Validate snmpv3 privacy protocol and password.
+         */
+        private void validateSecurityName() {
+            checkNotNull(securityName, "Security name must be provided");
+            checkState(!privacyPassword.isEmpty(), "Invalid security name");
+        }
+
+        /**
+         * Create snmpv3 configuration instance.
+         *
+         * @return v3snmp configuration object
+         */
+        public V3SnmpConfiguration build() {
+            validateUsmConfiguration();
+            return new V3SnmpConfiguration(this);
+        }
+
+    }
+
+}
diff --git a/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/DefaultSnmpControllerTest.java b/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/DefaultSnmpControllerTest.java
index 41b4123..db7a877 100644
--- a/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/DefaultSnmpControllerTest.java
+++ b/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/DefaultSnmpControllerTest.java
@@ -20,6 +20,7 @@
 import com.btisystems.pronx.ems.core.snmp.ISnmpConfigurationFactory;
 import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
 import com.btisystems.pronx.ems.core.snmp.ISnmpSessionFactory;
+import com.google.common.collect.Maps;
 import org.junit.Before;
 import org.junit.Test;
 import org.onosproject.alarm.Alarm;
@@ -52,13 +53,15 @@
 
     @Before
     public void setUp() {
-        snmpController.sessionFactory = mockSnmpSessionFactory;
+        snmpController.factoryMap = Maps.newHashMap();
+        snmpController.factoryMap.put(1, mockSnmpSessionFactory);
     }
 
     @Test
     public void testActivate() {
         snmpController.activate(null);
-        assertNotNull("Incorrect sessionFactory", snmpController.sessionFactory);
+        assertTrue("Snmp session factory map should contain atleast one factory object",
+                snmpController.factoryMap.size() > 0);
     }
 
     @Test
diff --git a/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/DefaultSnmpv3DeviceTest.java b/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/DefaultSnmpv3DeviceTest.java
new file mode 100644
index 0000000..d663b5a
--- /dev/null
+++ b/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/DefaultSnmpv3DeviceTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.snmp.ctl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.snmp.SnmpDeviceConfig;
+
+import java.io.InputStream;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test class for DefaultSnmpv3Device.
+ */
+public class DefaultSnmpv3DeviceTest {
+
+    private final SnmpDeviceConfig config = new SnmpDeviceConfig();
+    private final InputStream jsonStream = DefaultSnmpv3DeviceTest.class
+            .getResourceAsStream("/device.json");
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    private static final String KEY = "snmp";
+    private final String snmpHost = "1.1.1.1";
+    private final int snmpPort = 1;
+    private final String username = "test";
+    private final String community = "test";
+    private final DeviceId deviceId = DeviceId.deviceId("snmp:1.1.1.1:1");
+    private final String defaultProtocol = "udp";
+    private final String securityLevel = "authPriv";
+    private final String securityName = "sdnonos";
+    private final String authProtocol = "SHA";
+    private final String authPassword = "sdn@1234";
+    private final String privProtocol = "AES";
+    private final String privPassword = "sdn@1234";
+    private final String contextName = "sdn-context";
+
+    private DefaultSnmpv3Device defaultSnmpv3Device;
+
+
+    @Before
+    public void setUp() throws Exception {
+        JsonNode jsonNode = mapper.readTree(jsonStream);
+        ConfigApplyDelegate delegate = new DefaultSnmpv3DeviceTest.MockDelegate();
+        config.init(deviceId, KEY, jsonNode, mapper, delegate);
+        defaultSnmpv3Device = new DefaultSnmpv3Device(config);
+    }
+
+    /**
+     * Tests fetching snmp host.
+     */
+    @Test
+    public void testGetSnmpHost() {
+        assertEquals(snmpHost, defaultSnmpv3Device.getSnmpHost());
+    }
+
+    /**
+     * Tests fetching snmp port.
+     */
+    @Test
+    public void testGetSnmpPort() {
+        assertEquals(snmpPort, defaultSnmpv3Device.getSnmpPort());
+    }
+
+    /**
+     * Tests fetching username.
+     */
+    @Test
+    public void testGetUsername() {
+        assertEquals(username, defaultSnmpv3Device.getUsername());
+    }
+
+    /**
+     * Tests fetching community string.
+     */
+    @Test
+    public void testGetCommunity() {
+        assertEquals(community, defaultSnmpv3Device.getCommunity());
+    }
+
+    /**
+     * Tests fetching protocol.
+     */
+    @Test
+    public void testGetProtocol() {
+        assertEquals(defaultProtocol, defaultSnmpv3Device.getProtocol());
+    }
+
+    /**
+     * Tests fetching security name.
+     */
+    @Test
+    public void testGetSecurityName() {
+        assertEquals(securityName, defaultSnmpv3Device.getSecurityName());
+    }
+
+    /**
+     * Tests fetching security level.
+     */
+    @Test
+    public void testGetSecurityLevel() {
+        assertEquals(securityLevel, defaultSnmpv3Device.getSecurityLevel());
+    }
+
+    /**
+     * Tests fetching authentication protocol.
+     */
+    @Test
+    public void testGetAuthProtocol() {
+        assertEquals(authProtocol, defaultSnmpv3Device.getAuthProtocol());
+    }
+
+    /**
+     * Tests fetching authentication password.
+     */
+    @Test
+    public void testGetAuthPassword() {
+        assertEquals(authPassword, defaultSnmpv3Device.getAuthPassword());
+    }
+
+    /**
+     * Tests fetching privacy protocol.
+     */
+    @Test
+    public void testGetPrivProtocol() {
+        assertEquals(privProtocol, defaultSnmpv3Device.getPrivProtocol());
+    }
+
+    /**
+     * Tests fetching privacy password.
+     */
+    @Test
+    public void testGetPrivPassword() {
+        assertEquals(privPassword, defaultSnmpv3Device.getPrivPassword());
+    }
+
+    /**
+     * Tests fetching context name.
+     */
+    @Test
+    public void testGetContextName() {
+        assertEquals(contextName, defaultSnmpv3Device.getContextName());
+    }
+
+    private class MockDelegate implements ConfigApplyDelegate {
+        @Override
+        public void onApply(Config config) {
+
+        }
+    }
+
+}
diff --git a/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/V3SnmpConfigurationTest.java b/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/V3SnmpConfigurationTest.java
new file mode 100644
index 0000000..5ea4dc4
--- /dev/null
+++ b/protocols/snmp/ctl/src/test/java/org/onosproject/snmp/ctl/V3SnmpConfigurationTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.snmp.ctl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.snmp.SnmpDeviceConfig;
+import org.onosproject.snmp.SnmpException;
+import org.snmp4j.TransportMapping;
+import org.snmp4j.security.AuthSHA;
+import org.snmp4j.security.PrivAES128;
+import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.smi.Address;
+import org.snmp4j.smi.GenericAddress;
+import org.snmp4j.transport.DefaultUdpTransportMapping;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test class for V3SnmpConfiguration.
+ */
+public class V3SnmpConfigurationTest {
+
+    private final SnmpDeviceConfig config = new SnmpDeviceConfig();
+    private final InputStream jsonStream = DefaultSnmpv3DeviceTest.class
+            .getResourceAsStream("/device.json");
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    private static final String KEY = "snmp";
+    private final String snmpHost = "1.1.1.1";
+    private final int snmpPort = 1;
+    private final String username = "test";
+    private final String community = "test";
+    private final DeviceId deviceId = DeviceId.deviceId("snmp:1.1.1.1:1");
+    private final String defaultProtocol = "udp";
+    private final String securityName = "sdnonos";
+    private final String authProtocol = "SHA";
+    private final String authPassword = "sdn@1234";
+    private final String privPassword = "sdn@1234";
+    private final String contextName = "sdn-context";
+
+    private static final String SLASH = "/";
+
+    private DefaultSnmpv3Device defaultSnmpv3Device;
+    private V3SnmpConfiguration v3SnmpConfiguration;
+
+    @Before
+    public void setUp() throws Exception {
+        JsonNode jsonNode = mapper.readTree(jsonStream);
+        ConfigApplyDelegate delegate = new MockDelegate();
+        config.init(deviceId, KEY, jsonNode, mapper, delegate);
+        defaultSnmpv3Device = new DefaultSnmpv3Device(config);
+
+        v3SnmpConfiguration = V3SnmpConfiguration.builder()
+                .setAddress(defaultSnmpv3Device.getSnmpHost())
+                .setSecurityName(defaultSnmpv3Device.getSecurityName())
+                .setSecurityLevel(defaultSnmpv3Device.getSecurityLevel())
+                .setAuthenticationProtocol(defaultSnmpv3Device.getAuthProtocol())
+                .setAuthenticationPassword(defaultSnmpv3Device.getAuthPassword())
+                .setPrivacyProtocol(defaultSnmpv3Device.getPrivProtocol())
+                .setPrivacyPassword(defaultSnmpv3Device.getPrivPassword())
+                .setContextName(defaultSnmpv3Device.getContextName())
+                .build();
+
+        v3SnmpConfiguration.setPort(defaultSnmpv3Device.getSnmpPort());
+    }
+
+    /**
+     * Test snmp create target exception case.
+     *
+     * @throws IOException
+     */
+    @Test(expected = SnmpException.class)
+    public void testCreateTargetException() throws IOException {
+        Address targetAddress = GenericAddress.parse(
+                defaultSnmpv3Device.getProtocol() +
+                        ":" + defaultSnmpv3Device.getSnmpHost() +
+                        "/" + defaultSnmpv3Device.getSnmpPort());
+
+        TransportMapping transport = new DefaultUdpTransportMapping();
+        v3SnmpConfiguration.createSnmpSession(transport);
+        v3SnmpConfiguration.createTarget(targetAddress);
+
+    }
+
+    /**
+     * Test fetching security level.
+     */
+    @Test
+    public void testGetSecurityLevel() {
+        assertEquals(SecurityLevel.AUTH_PRIV, v3SnmpConfiguration.getSecurityLevel().getSnmpValue());
+    }
+
+    /**
+     * Test fetching snmp host address.
+     */
+    @Test
+    public void testGetAddress() {
+        assertEquals(snmpHost, v3SnmpConfiguration.getAddress().toString());
+    }
+
+    /**
+     * Test fetching security name.
+     */
+    @Test
+    public void testGetSecurityName() {
+        assertEquals(securityName, v3SnmpConfiguration.getSecurityName());
+    }
+
+    /**
+     * Test fetching authentication password.
+     */
+    @Test
+    public void testGetAuthenticationPassword() {
+        assertEquals(authPassword, v3SnmpConfiguration.getAuthenticationPassword());
+    }
+
+    /**
+     * Test fetching authentication protocol.
+     */
+    @Test
+    public void testGetAuthenticationProtocol() {
+        assertEquals(AuthSHA.ID, v3SnmpConfiguration.getAuthenticationProtocol());
+    }
+
+    /**
+     * Test fetching privacy password.
+     */
+    @Test
+    public void testGetPrivacyPassword() {
+        assertEquals(privPassword, v3SnmpConfiguration.getPrivacyPassword());
+    }
+
+    /**
+     * Test fetching privacy protocol.
+     */
+    @Test
+    public void testGetPrivacyProtocol() {
+        assertEquals(PrivAES128.ID, v3SnmpConfiguration.getPrivacyProtocol());
+    }
+
+    /**
+     * Test fetching context name.
+     */
+    @Test
+    public void testGetContextName() {
+        assertEquals(contextName, v3SnmpConfiguration.getContextName());
+    }
+
+    private class MockDelegate implements ConfigApplyDelegate {
+        @Override
+        public void onApply(Config config) {
+
+        }
+    }
+
+}
diff --git a/protocols/snmp/ctl/src/test/resources/device.json b/protocols/snmp/ctl/src/test/resources/device.json
new file mode 100644
index 0000000..0902b47
--- /dev/null
+++ b/protocols/snmp/ctl/src/test/resources/device.json
@@ -0,0 +1,15 @@
+{
+  "ip": "1.1.1.1",
+  "port": 1,
+  "username": "test",
+  "password": "test",
+  "version": 3,
+  "communityString": "sdnonos",
+  "securityLevel": "authPriv",
+  "securityName": "sdnonos",
+  "authProtocol": "SHA",
+  "authPassword": "sdn@1234",
+  "privacyProtocol": "AES",
+  "privacyPassword": "sdn@1234",
+  "contextName": "sdn-context"
+}
diff --git a/providers/snmp/device/src/main/java/org/onosproject/provider/snmp/device/impl/SnmpDeviceProvider.java b/providers/snmp/device/src/main/java/org/onosproject/provider/snmp/device/impl/SnmpDeviceProvider.java
index ddd81c7..6a06a98 100644
--- a/providers/snmp/device/src/main/java/org/onosproject/provider/snmp/device/impl/SnmpDeviceProvider.java
+++ b/providers/snmp/device/src/main/java/org/onosproject/provider/snmp/device/impl/SnmpDeviceProvider.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.provider.snmp.device.impl;
 
+import org.onosproject.snmp.SnmpException;
+import org.onosproject.snmp.ctl.DefaultSnmpv3Device;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
@@ -52,6 +54,7 @@
 import org.onosproject.snmp.ctl.DefaultSnmpDevice;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
+import org.snmp4j.mp.SnmpConstants;
 
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
@@ -166,7 +169,14 @@
         deviceSubjects.forEach(deviceId -> {
             SnmpDeviceConfig config =
                     netCfgService.getConfig(deviceId, SnmpDeviceConfig.class);
-            buildDevice(new DefaultSnmpDevice(config));
+            if (config.version() == SnmpConstants.version2c) {
+                buildDevice(new DefaultSnmpDevice(config));
+            } else if (config.version() == SnmpConstants.version3) {
+                buildDevice(new DefaultSnmpv3Device(config));
+            } else {
+                throw new SnmpException(
+                        String.format("Invalid snmp version %d", config.version()));
+            }
         });
     }