From 5a72479951211013a3a2e9e410e7fc2c8cef4934 Mon Sep 17 00:00:00 2001
From: vkukhtyn <37999482+vkukhtyn@users.noreply.github.com>
Date: Wed, 10 Mar 2021 14:56:25 +0200
Subject: [PATCH 01/32] [WIP] SNMP initial implementation (#3755)
* Add SNMP type to transport profiles
* Update reference config for transport profiles
* Initial implementation to retrieve a value using SNMP GET message
* Fix license header
* Move config files
* Use async handler for SNMP response
* Send SNMP agent response to attributes and telemetry
* UI: add SNMP option for device profile
* UI: use JSON field to set SNMP profile configuration
* Handle device profile update event
* Use concurrent hash map for SNMP device sessions
* UI: Add device transport configuration control
* Cancel async request to avoid memory leak and timeout handling
* Start SNMP pooling after application started
* Move OID per profile mapping to SNMP transport context
* Fix build after merge with 3.2.1-SNAPSHOT
* Init device sessions on TB start
* Fix build error, refactoring
* Update session context on device update
* Set device info on session context creating
* Refresh pooling params on device or profile update
* Update license header
* Process device and profile transport config update
* Process SNMP response asynchronously
* Change polling implementation
---
application/pom.xml | 4 +
.../src/main/resources/thingsboard.yml | 2 +
common/data/pom.xml | 4 +
.../common/data/DeviceTransportType.java | 3 +-
.../JsonBasedTransportConfiguration.java | 39 ++++
.../data/DeviceTransportConfiguration.java | 3 +-
.../SnmpDeviceTransportConfiguration.java | 43 ++++
.../DeviceProfileTransportConfiguration.java | 8 +-
.../profile/SnmpDeviceProfileKvMapping.java | 28 +++
.../SnmpProfileTransportConfiguration.java | 44 ++++
common/transport/pom.xml | 1 +
common/transport/snmp/pom.xml | 66 ++++++
.../transport/snmp/SnmpTransportContext.java | 142 ++++++++++++
.../transport/snmp/SnmpTransportService.java | 147 +++++++++++++
.../snmp/session/DeviceSessionCtx.java | 206 ++++++++++++++++++
.../snmp/session/SnmpSessionListener.java | 173 +++++++++++++++
.../server/dao/device/DeviceServiceImpl.java | 3 +
msa/transport/pom.xml | 1 +
msa/transport/snmp/docker/Dockerfile | 33 +++
.../snmp/docker/start-tb-snmp-transport.sh | 33 +++
msa/transport/snmp/pom.xml | 190 ++++++++++++++++
pom.xml | 11 +
transport/pom.xml | 1 +
transport/snmp/pom.xml | 112 ++++++++++
.../ThingsboardSnmpTransportApplication.java | 48 ++++
.../main/resources/device-data-config.json | 9 +
.../main/resources/device-profile-config.json | 21 ++
.../home/components/home-components.module.ts | 3 +
...ile-transport-configuration.component.html | 6 +
...ile-transport-configuration.component.html | 23 ++
...ofile-transport-configuration.component.ts | 100 +++++++++
...ice-transport-configuration.component.html | 6 +
...ice-transport-configuration.component.html | 24 ++
...evice-transport-configuration.component.ts | 100 +++++++++
.../home/pages/device/device.module.ts | 2 +
ui-ngx/src/app/shared/models/device.models.ts | 42 +++-
.../assets/locale/locale.constant-en_US.json | 9 +
37 files changed, 1681 insertions(+), 9 deletions(-)
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
create mode 100644 common/transport/snmp/pom.xml
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/SnmpSessionListener.java
create mode 100644 msa/transport/snmp/docker/Dockerfile
create mode 100644 msa/transport/snmp/docker/start-tb-snmp-transport.sh
create mode 100644 msa/transport/snmp/pom.xml
create mode 100644 transport/snmp/pom.xml
create mode 100644 transport/snmp/src/main/java/org/thingsboard/server/snmp/ThingsboardSnmpTransportApplication.java
create mode 100644 transport/snmp/src/main/resources/device-data-config.json
create mode 100644 transport/snmp/src/main/resources/device-profile-config.json
create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html
create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts
create mode 100644 ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html
create mode 100644 ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts
diff --git a/application/pom.xml b/application/pom.xml
index c1a49b09e3..e3dcd9f0f9 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -85,6 +85,10 @@
org.thingsboard.common.transport
coap
+
+ org.thingsboard.common.transport
+ snmp
+
org.thingsboard
dao
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 0cc7c08669..437a672c54 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -566,6 +566,8 @@ transport:
bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
bind_port: "${COAP_BIND_PORT:5683}"
timeout: "${COAP_TIMEOUT:10000}"
+ snmp:
+ enabled: "${SNMP_ENABLED:true}"
swagger:
api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}"
diff --git a/common/data/pom.xml b/common/data/pom.xml
index 4454e628c0..bc977b1c52 100644
--- a/common/data/pom.xml
+++ b/common/data/pom.xml
@@ -79,6 +79,10 @@
org.thingsboard
protobuf-dynamic
+
+ org.apache.commons
+ commons-lang3
+
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java
index 9b9a021fcf..579cdde968 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java
@@ -18,5 +18,6 @@ package org.thingsboard.server.common.data;
public enum DeviceTransportType {
DEFAULT,
MQTT,
- LWM2M
+ LWM2M,
+ SNMP
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java
new file mode 100644
index 0000000000..c65e9dd972
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.device;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class JsonBasedTransportConfiguration {
+
+ @JsonIgnore
+ private final Map properties = new HashMap<>();
+
+ @JsonAnyGetter
+ public Map properties() {
+ return this.properties;
+ }
+
+ @JsonAnySetter
+ public void put(String name, Object value) {
+ this.properties.put(name, value);
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
index 5409dcd339..0ae9f26cf3 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
@@ -29,7 +29,8 @@ import org.thingsboard.server.common.data.DeviceTransportType;
@JsonSubTypes({
@JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"),
@JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"),
- @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M")})
+ @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"),
+ @JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")})
public interface DeviceTransportConfiguration {
@JsonIgnore
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
new file mode 100644
index 0000000000..3db31b4f2a
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.device.data;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+import org.thingsboard.server.common.data.DeviceTransportType;
+
+@Data
+public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
+
+ private String address;
+ private int port;
+ private String community;
+ private String protocolVersion;
+
+ @Override
+ public DeviceTransportType getType() {
+ return DeviceTransportType.SNMP;
+ }
+
+ @JsonIgnore
+ public boolean isValid() {
+ return StringUtils.isNotEmpty(this.address)
+ && this.port > 0
+ && StringUtils.isNotEmpty(this.community)
+ && StringUtils.isNotEmpty(this.protocolVersion);
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
index 2feba45d34..ffbe5f7048 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
@@ -19,7 +19,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -28,9 +27,10 @@ import org.thingsboard.server.common.data.DeviceTransportType;
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
- @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
- @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
- @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M")})
+ @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
+ @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
+ @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"),
+ @JsonSubTypes.Type(value = SnmpProfileTransportConfiguration.class, name = "SNMP")})
public interface DeviceProfileTransportConfiguration {
@JsonIgnore
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java
new file mode 100644
index 0000000000..6eff7cbd34
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.device.profile;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.kv.DataType;
+
+//TODO: rename class
+@Data
+public class SnmpDeviceProfileKvMapping {
+ private String key;
+ private DataType type;
+ private String method;
+ private String oid;
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
new file mode 100644
index 0000000000..a2529e74e9
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.device.profile;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import org.thingsboard.server.common.data.DeviceTransportType;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Data
+public class SnmpProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
+
+ private int poolPeriodMs;
+ private int timeoutMs;
+ private int retries;
+ private List attributes;
+ private List telemetry;
+
+ @Override
+ public DeviceTransportType getType() {
+ return DeviceTransportType.SNMP;
+ }
+
+ @JsonIgnore
+ public List getKvMappings() {
+ return Stream.concat(attributes.stream(), telemetry.stream()).collect(Collectors.toList());
+ }
+}
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index 957b0d518c..35722cdd1d 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -39,6 +39,7 @@
mqtt
http
coap
+ snmp
diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml
new file mode 100644
index 0000000000..1f334b9699
--- /dev/null
+++ b/common/transport/snmp/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+
+ org.thingsboard.common
+ 3.2.1-SNAPSHOT
+ transport
+
+
+ org.thingsboard.common.transport
+ snmp
+ jar
+
+ Thingsboard SNMP Transport Common
+ https://thingsboard.io
+
+
+ UTF-8
+ ${basedir}/../../..
+
+
+
+
+ org.thingsboard.common.transport
+ transport-api
+
+
+ org.springframework
+ spring-context-support
+
+
+ org.springframework
+ spring-context
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.snmp4j
+ snmp4j
+
+
+ org.thingsboard.common
+ dao-api
+
+
+
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
new file mode 100644
index 0000000000..66f852a209
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.VariableBinding;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileKvMapping;
+import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.DeviceProfileId;
+import org.thingsboard.server.common.data.security.DeviceCredentials;
+import org.thingsboard.server.common.data.security.DeviceCredentialsType;
+import org.thingsboard.server.common.transport.TransportContext;
+import org.thingsboard.server.dao.device.DeviceCredentialsService;
+import org.thingsboard.server.transport.snmp.session.DeviceSessionCtx;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+@Service("SnmpTransportContext")
+@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
+@Slf4j
+public class SnmpTransportContext extends TransportContext {
+ @Autowired
+ DeviceCredentialsService deviceCredentialsService;
+
+ @Autowired
+ SnmpTransportService snmpTransportService;
+
+ @Getter
+ private final Map profileTransportConfig = new ConcurrentHashMap<>();
+ @Getter
+ private final Map> pdusPerProfile = new ConcurrentHashMap<>();
+ @Getter
+ private final Map deviceSessions = new ConcurrentHashMap<>();
+
+ public Optional findAttributesMapping(DeviceProfileId deviceProfileId, OID responseOid) {
+ if (profileTransportConfig.containsKey(deviceProfileId)) {
+ return findMapping(responseOid, profileTransportConfig.get(deviceProfileId).getAttributes());
+ }
+ return Optional.empty();
+ }
+
+ public Optional findTelemetryMapping(DeviceProfileId deviceProfileId, OID responseOid) {
+ if (profileTransportConfig.containsKey(deviceProfileId)) {
+ return findMapping(responseOid, profileTransportConfig.get(deviceProfileId).getTelemetry());
+ }
+ return Optional.empty();
+ }
+
+ private Optional findMapping(OID responseOid, List mappings) {
+ return mappings.stream()
+ .filter(kvMapping -> new OID(kvMapping.getOid()).equals(responseOid))
+ //TODO: OID shouldn't be duplicated in the config, add backend and UI verification
+ .findFirst();
+ }
+
+ public void initPduListPerProfile() {
+ profileTransportConfig.forEach(this::updatePduListPerProfile);
+ }
+
+ public void updatePduListPerProfile(DeviceProfileId id, SnmpProfileTransportConfiguration config) {
+ pdusPerProfile.put(id, createPduList(config));
+ }
+
+ public void updateDeviceSessionCtx(Device device, DeviceProfile deviceProfile, Snmp snmp) {
+ DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId());
+ if (DeviceCredentialsType.ACCESS_TOKEN.equals(credentials.getCredentialsType())) {
+ SnmpDeviceTransportConfiguration snmpDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
+ if (snmpDeviceTransportConfiguration.isValid()) {
+ DeviceSessionCtx deviceSessionCtx = new DeviceSessionCtx(this, credentials.getCredentialsId(), snmpDeviceTransportConfiguration, snmp, device.getId(), deviceProfile);
+ deviceSessionCtx.createSessionInfo(ctx -> getTransportService().registerAsyncSession(deviceSessionCtx.getSessionInfo(), deviceSessionCtx));
+ this.deviceSessions.put(device.getId(), deviceSessionCtx);
+ }
+ } else {
+ log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
+ }
+ }
+
+ public ExecutorService getSnmpCallbackExecutor() {
+ return snmpTransportService.getSnmpCallbackExecutor();
+ }
+
+ private List createPduList(SnmpProfileTransportConfiguration deviceProfileConfig) {
+ Map> varBindingPerMethod = new HashMap<>();
+
+ deviceProfileConfig.getKvMappings().forEach(mapping -> varBindingPerMethod
+ .computeIfAbsent(mapping.getMethod(), v -> new ArrayList<>())
+ .add(new VariableBinding(new OID(mapping.getOid()))));
+
+ return varBindingPerMethod.keySet().stream()
+ .map(method -> {
+ PDU request = new PDU();
+ request.setType(getSnmpMethod(method));
+ request.addAll(varBindingPerMethod.get(method));
+ return request;
+ })
+ .collect(Collectors.toList());
+ }
+
+ //TODO: Extract SNMP methods to enum
+ private int getSnmpMethod(String configMethod) {
+ switch (configMethod) {
+ case "get":
+ return PDU.GET;
+ case "getNext":
+ case "response":
+ case "set":
+ default:
+ return -1;
+ }
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java
new file mode 100644
index 0000000000..5d8e4d84b4
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java
@@ -0,0 +1,147 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.snmp4j.Snmp;
+import org.snmp4j.transport.DefaultUdpTransportMapping;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+import org.thingsboard.common.util.ThingsBoardThreadFactory;
+import org.thingsboard.server.common.data.DeviceInfo;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.PageDataIterable;
+import org.thingsboard.server.dao.device.DeviceProfileService;
+import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.tenant.TenantService;
+import org.thingsboard.server.transport.snmp.session.DeviceSessionCtx;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Service("SnmpTransportService")
+@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
+@Slf4j
+public class SnmpTransportService {
+
+ private static final int ENTITY_PACK_LIMIT = 1024;
+
+ @Autowired
+ private SnmpTransportContext snmpTransportContext;
+
+ @Autowired
+ DeviceProfileService deviceProfileService;
+
+ @Autowired
+ TenantService tenantService;
+
+ @Autowired
+ DeviceService deviceService;
+
+ @Getter
+ private ExecutorService snmpCallbackExecutor;
+ private Snmp snmp;
+ private ScheduledExecutorService pollingExecutor;
+
+ @PostConstruct
+ public void init() {
+ log.info("Starting SNMP transport...");
+ pollingExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("snmp-polling"));
+ //TODO: Set parallelism value in the config
+ snmpCallbackExecutor = Executors.newWorkStealingPool(20);
+ initializeSnmp();
+ log.info("SNMP transport started!");
+ }
+
+ @PreDestroy
+ public void shutdown() {
+ log.info("Stopping SNMP transport!");
+ if (pollingExecutor != null) {
+ pollingExecutor.shutdownNow();
+ }
+ if (snmpCallbackExecutor != null) {
+ snmpCallbackExecutor.shutdownNow();
+ }
+ if (snmp != null) {
+ try {
+ snmp.close();
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ log.info("SNMP transport stopped!");
+ }
+
+ @EventListener(ApplicationReadyEvent.class)
+ @Order(value = 2)
+ public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
+ log.info("Received application ready event. Starting SNMP polling.");
+ initSessionCtxList();
+ startPolling();
+ }
+
+ private void initializeSnmp() {
+ try {
+ this.snmp = new Snmp(new DefaultUdpTransportMapping());
+ this.snmp.listen();
+ } catch (IOException e) {
+ //TODO: what should be done if transport wasn't initialized?
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ private void initSessionCtxList() {
+ //TODO: This approach works for monolith, in cluster the same data will be fetched by each node.
+ for (Tenant tenant : new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT)) {
+ TenantId tenantId = tenant.getTenantId();
+ for (DeviceProfile deviceProfile : new PageDataIterable<>(pageLink -> deviceProfileService.findDeviceProfiles(tenantId, pageLink), ENTITY_PACK_LIMIT)) {
+ if (DeviceTransportType.SNMP.equals(deviceProfile.getTransportType())) {
+ snmpTransportContext.getProfileTransportConfig().put(deviceProfile.getId(),
+ (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration());
+ initDeviceSessions(deviceProfile);
+ }
+ }
+ }
+ snmpTransportContext.initPduListPerProfile();
+ }
+
+ private void initDeviceSessions(DeviceProfile deviceProfile) {
+ for (DeviceInfo deviceInfo : new PageDataIterable<>(pageLink -> deviceService.findDeviceInfosByTenantIdAndDeviceProfileId(deviceProfile.getTenantId(), deviceProfile.getId(), pageLink), ENTITY_PACK_LIMIT)) {
+ snmpTransportContext.updateDeviceSessionCtx(deviceInfo, deviceProfile, snmp);
+ }
+ }
+
+ private void startPolling() {
+ //TODO: Get poll period from configuration;
+ int poolPeriodSeconds = 1;
+ pollingExecutor.scheduleAtFixedRate(() -> snmpTransportContext.getDeviceSessions().values().forEach(DeviceSessionCtx::executeSnmpRequest),
+ 0, poolPeriodSeconds, TimeUnit.SECONDS);
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java
new file mode 100644
index 0000000000..424b964387
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java
@@ -0,0 +1,206 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp.session;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.snmp4j.CommunityTarget;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.GenericAddress;
+import org.snmp4j.smi.OctetString;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
+import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
+import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
+import org.thingsboard.server.transport.snmp.SnmpTransportContext;
+
+import java.io.IOException;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+@Slf4j
+public class DeviceSessionCtx extends DeviceAwareSessionContext implements SessionMsgListener {
+ private final AtomicInteger msgIdSeq = new AtomicInteger(0);
+
+ @Getter
+ @Setter
+ private SnmpDeviceTransportConfiguration deviceTransportConfig;
+ @Getter
+ @Setter
+ private SnmpSessionListener snmpSessionListener;
+ @Getter
+ @Setter
+ private Target target;
+ @Getter
+ @Setter
+ private volatile TransportProtos.SessionInfoProto sessionInfo;
+
+ private Snmp snmp;
+ private SnmpProfileTransportConfiguration snmpProfileTransportConfiguration;
+ private long previousRequestExecutedAt = 0;
+
+ public DeviceSessionCtx(SnmpTransportContext transportContext, String token, SnmpDeviceTransportConfiguration deviceTransportConfig,
+ Snmp snmp, DeviceId deviceId, DeviceProfile deviceProfile) {
+ super(UUID.randomUUID());
+ this.snmpSessionListener = new SnmpSessionListener(transportContext, token);
+ super.setDeviceId(deviceId);
+ super.setDeviceProfile(deviceProfile);
+ //TODO: What should be done if snmp null?
+ if (snmp != null) {
+ this.snmp = snmp;
+ }
+ this.snmpProfileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ initTarget(this.snmpProfileTransportConfiguration, deviceTransportConfig);
+ }
+
+ @Override
+ public int nextMsgId() {
+ return msgIdSeq.incrementAndGet();
+ }
+
+ @Override
+ public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
+ }
+
+ @Override
+ public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
+ }
+
+ @Override
+ public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
+ }
+
+ @Override
+ public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
+ }
+
+ @Override
+ public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) {
+ }
+
+ @Override
+ public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto newSessionInfo, DeviceProfile deviceProfile) {
+ super.onDeviceProfileUpdate(sessionInfo, deviceProfile);
+ if (DeviceTransportType.SNMP.equals(deviceProfile.getTransportType())) {
+ snmpProfileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ snmpSessionListener.getSnmpTransportContext().getProfileTransportConfig().put(
+ deviceProfile.getId(),
+ snmpProfileTransportConfiguration);
+ snmpSessionListener.getSnmpTransportContext().updatePduListPerProfile(deviceProfile.getId(), snmpProfileTransportConfiguration);
+ } else {
+ //TODO: should the context be removed from the map?
+ }
+ }
+
+ @Override
+ public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional deviceProfileOpt) {
+ super.onDeviceUpdate(sessionInfo, device, deviceProfileOpt);
+ if (super.getDeviceProfile() != null && DeviceTransportType.SNMP.equals(super.getDeviceProfile().getTransportType())) {
+ snmpSessionListener.getSnmpTransportContext().updateDeviceSessionCtx(device, deviceProfile, null);
+ SnmpProfileTransportConfiguration profileTransportConfig = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ SnmpDeviceTransportConfiguration deviceTransportConfig = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
+ initTarget(profileTransportConfig, deviceTransportConfig);
+ } else {
+ //TODO: should the context be removed from the map?
+ }
+ }
+
+ public void createSessionInfo(Consumer registerSession) {
+ getSnmpSessionListener().getSnmpTransportContext().getTransportService().process(DeviceTransportType.SNMP,
+ TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(getSnmpSessionListener().getToken()).build(),
+ new TransportServiceCallback() {
+ @Override
+ public void onSuccess(ValidateDeviceCredentialsResponse msg) {
+ if (msg.hasDeviceInfo()) {
+ sessionInfo = SessionInfoCreator.create(msg, getSnmpSessionListener().getSnmpTransportContext(), UUID.randomUUID());
+ registerSession.accept(sessionInfo);
+ setDeviceInfo(msg.getDeviceInfo());
+ } else {
+ log.warn("[{}] Failed to process device auth", getDeviceId());
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.warn("[{}] Failed to process device auth", getDeviceId(), e);
+ }
+ });
+ }
+
+ public void executeSnmpRequest() {
+ long timeNow = System.currentTimeMillis();
+ long nextRequestExecutionTime = previousRequestExecutedAt + snmpProfileTransportConfiguration.getPoolPeriodMs();
+ if (nextRequestExecutionTime < timeNow) {
+ previousRequestExecutedAt = timeNow;
+
+ snmpSessionListener.getSnmpTransportContext().getPdusPerProfile().get(deviceProfile.getId()).forEach(pdu -> {
+ try {
+ log.debug("[{}] Sending SNMP message...", pdu.getRequestID());
+ snmp.send(pdu,
+ target,
+ deviceProfile.getId(),
+ snmpSessionListener);
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ });
+ }
+ }
+
+ private void initTarget(SnmpProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
+ this.deviceTransportConfig = deviceTransportConfig;
+ CommunityTarget communityTarget = new CommunityTarget();
+ communityTarget.setAddress(GenericAddress.parse(GenericAddress.TYPE_UDP + ":" + this.deviceTransportConfig.getAddress() + "/" + this.deviceTransportConfig.getPort()));
+ communityTarget.setVersion(getSnmpVersion(this.deviceTransportConfig.getProtocolVersion()));
+ communityTarget.setCommunity(new OctetString(this.deviceTransportConfig.getCommunity()));
+ communityTarget.setTimeout(profileTransportConfig.getTimeoutMs());
+ communityTarget.setRetries(profileTransportConfig.getRetries());
+ this.target = communityTarget;
+ log.info("SNMP target initialized: {}", this.target);
+ }
+
+ //TODO: replace with enum, wtih preliminary discussion of type version in config (string or integer)
+ private int getSnmpVersion(String configSnmpVersion) {
+ switch (configSnmpVersion) {
+ case ("v1"):
+ return SnmpConstants.version1;
+ case ("v2c"):
+ return SnmpConstants.version2c;
+ case ("v3"):
+ return SnmpConstants.version3;
+ default:
+ return -1;
+ }
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/SnmpSessionListener.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/SnmpSessionListener.java
new file mode 100644
index 0000000000..35aa1a9347
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/SnmpSessionListener.java
@@ -0,0 +1,173 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp.session;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.event.ResponseListener;
+import org.snmp4j.smi.VariableBinding;
+import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.id.DeviceProfileId;
+import org.thingsboard.server.common.data.kv.DataType;
+import org.thingsboard.server.common.transport.TransportContext;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.common.transport.adaptor.AdaptorException;
+import org.thingsboard.server.common.transport.adaptor.JsonConverter;
+import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
+import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.transport.snmp.SnmpTransportContext;
+
+import java.util.UUID;
+import java.util.function.Consumer;
+
+@Slf4j
+@AllArgsConstructor
+public class SnmpSessionListener implements ResponseListener {
+
+ @Getter
+ private final SnmpTransportContext snmpTransportContext;
+
+ @Getter
+ private final String token;
+
+ @Override
+ public void onResponse(ResponseEvent event) {
+ ((Snmp) event.getSource()).cancel(event.getRequest(), this);
+ snmpTransportContext.getSnmpCallbackExecutor().submit(() -> processSnmpResponse(event));
+ }
+
+ private void processSnmpResponse(ResponseEvent event) {
+ PDU response = event.getResponse();
+ if (event.getError() != null) {
+ log.warn("Response error: {}", event.getError().getMessage(), event.getError());
+ }
+
+ if (response != null) {
+ log.debug("[{}] Processing SNMP response: {}", response.getRequestID(), response);
+
+ DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();
+ TransportService transportService = snmpTransportContext.getTransportService();
+ for (int i = 0; i < response.size(); i++) {
+ VariableBinding vb = response.get(i);
+ snmpTransportContext.findAttributesMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
+ TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(),
+ new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
+ try {
+ transportService.process(sessionInfo,
+ convertToPostAttributes(kvMapping.getKey(), kvMapping.getType(), vb.toValueString()),
+ TransportServiceCallback.EMPTY);
+ reportActivity(sessionInfo);
+ } catch (Exception e) {
+ log.warn("Failed to process SNMP response: {}", e.getMessage(), e);
+ }
+ })));
+ snmpTransportContext.findTelemetryMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
+ TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(),
+ new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
+ try {
+ transportService.process(sessionInfo,
+ convertToPostTelemetry(kvMapping.getKey(), kvMapping.getType(), vb.toValueString()),
+ TransportServiceCallback.EMPTY);
+ reportActivity(sessionInfo);
+
+ } catch (Exception e) {
+ log.warn("Failed to process SNMP response: {}", e.getMessage(), e);
+ }
+ })));
+ }
+ } else {
+ log.warn("No SNMP response, requestId: {}", event.getRequest().getRequestID());
+ }
+ }
+
+ private TransportProtos.PostAttributeMsg convertToPostAttributes(String keyName, DataType dataType, String payload) throws AdaptorException {
+ try {
+ return JsonConverter.convertToAttributesProto(getKvJson(keyName, dataType, payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ //TODO: change the exception type
+ throw new AdaptorException(ex);
+ }
+ }
+
+ private TransportProtos.PostTelemetryMsg convertToPostTelemetry(String keyName, DataType dataType, String payload) throws AdaptorException {
+ try {
+ return JsonConverter.convertToTelemetryProto(getKvJson(keyName, dataType, payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ //TODO: change the exception type
+ throw new AdaptorException(ex);
+ }
+ }
+
+ private JsonElement getKvJson(String keyName, DataType dataType, String payload) throws AdaptorException {
+ JsonObject result = new JsonObject();
+ switch (dataType) {
+ case LONG:
+ result.addProperty(keyName, Long.parseLong(payload));
+ break;
+ case BOOLEAN:
+ result.addProperty(keyName, Boolean.parseBoolean(payload));
+ break;
+ case DOUBLE:
+ result.addProperty(keyName, Double.parseDouble(payload));
+ break;
+ case STRING:
+ result.addProperty(keyName, payload);
+ break;
+ default:
+ //TODO: change the exception type
+ throw new AdaptorException("Unsupported data type");
+ }
+ return new JsonParser().parse(result.toString());
+ }
+
+ private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
+ snmpTransportContext.getTransportService().process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
+ .setAttributeSubscription(false)
+ .setRpcSubscription(false)
+ .setLastActivityTime(System.currentTimeMillis())
+ .build(), TransportServiceCallback.EMPTY);
+ }
+
+ @AllArgsConstructor
+ private static class DeviceAuthCallback implements TransportServiceCallback {
+ private final TransportContext transportContext;
+ private final Consumer onSuccess;
+
+ @Override
+ public void onSuccess(ValidateDeviceCredentialsResponse msg) {
+ if (msg.hasDeviceInfo()) {
+ onSuccess.accept(SessionInfoCreator.create(msg, transportContext, UUID.randomUUID()));
+ } else {
+ log.warn("Failed to process device auth");
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.warn("Failed to process device auth", e);
+ }
+ }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
index cd161d650e..c088d18d3c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
@@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConf
import org.thingsboard.server.common.data.device.data.DeviceData;
import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
+import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -240,6 +241,8 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
case LWM2M:
deviceData.setTransportConfiguration(new Lwm2mDeviceTransportConfiguration());
break;
+ case SNMP:
+ deviceData.setTransportConfiguration(new SnmpDeviceTransportConfiguration());
}
}
return deviceData;
diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml
index 6fdedfbfcf..6a9fe17fc2 100644
--- a/msa/transport/pom.xml
+++ b/msa/transport/pom.xml
@@ -38,6 +38,7 @@
mqtt
http
coap
+ snmp
diff --git a/msa/transport/snmp/docker/Dockerfile b/msa/transport/snmp/docker/Dockerfile
new file mode 100644
index 0000000000..e39624d538
--- /dev/null
+++ b/msa/transport/snmp/docker/Dockerfile
@@ -0,0 +1,33 @@
+#
+# Copyright © 2016-2021 The Thingsboard Authors
+#
+# 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.
+#
+
+FROM thingsboard/openjdk8
+
+COPY start-tb-snmp-transport.sh ${pkg.name}.deb /tmp/
+
+RUN chmod a+x /tmp/*.sh \
+ && mv /tmp/start-tb-snmp-transport.sh /usr/bin
+
+RUN yes | dpkg -i /tmp/${pkg.name}.deb
+RUN rm /tmp/${pkg.name}.deb
+
+RUN systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
+
+RUN chmod 555 ${pkg.installFolder}/bin/${pkg.name}.jar
+
+USER ${pkg.user}
+
+CMD ["start-tb-snmp-transport.sh"]
diff --git a/msa/transport/snmp/docker/start-tb-snmp-transport.sh b/msa/transport/snmp/docker/start-tb-snmp-transport.sh
new file mode 100644
index 0000000000..2e69cf26f9
--- /dev/null
+++ b/msa/transport/snmp/docker/start-tb-snmp-transport.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Copyright © 2016-2021 The Thingsboard Authors
+#
+# 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.
+#
+
+CONF_FOLDER="/config"
+jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
+configfile=${pkg.name}.conf
+
+source "${CONF_FOLDER}/${configfile}"
+
+export LOADER_PATH=/config,${LOADER_PATH}
+
+echo "Starting '${project.name}' ..."
+
+cd ${pkg.installFolder}/bin
+
+exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.snmp.ThingsboardSnmpTransportApplication \
+ -Dspring.jpa.hibernate.ddl-auto=none \
+ -Dlogging.config=/config/logback.xml \
+ org.springframework.boot.loader.PropertiesLauncher
diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml
new file mode 100644
index 0000000000..df5caa658c
--- /dev/null
+++ b/msa/transport/snmp/pom.xml
@@ -0,0 +1,190 @@
+
+
+ 4.0.0
+
+ org.thingsboard.msa
+ transport
+ 3.2.1-SNAPSHOT
+
+
+ org.thingsboard.msa.transport
+ snmp
+ pom
+
+ ThingsBoard SNMP Transport Microservice
+ https://thingsboard.io
+ ThingsBoard SNMP Transport Microservice
+
+
+ UTF-8
+ ${basedir}/../../..
+ tb-snmp-transport
+ tb-snmp-transport
+ /var/log/${pkg.name}
+ /usr/share/${pkg.name}
+
+
+
+
+ org.thingsboard.transport
+ snmp
+ ${project.version}
+ deb
+ deb
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-tb-snmp-transport-deb
+ package
+
+ copy
+
+
+
+
+ org.thingsboard.transport
+ snmp
+ deb
+ deb
+ ${pkg.name}.deb
+ ${project.build.directory}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-docker-config
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}
+
+
+ docker
+ true
+
+
+
+
+
+
+
+ com.spotify
+ dockerfile-maven-plugin
+
+
+ build-docker-image
+ pre-integration-test
+
+ build
+
+
+ ${dockerfile.skip}
+ ${docker.repo}/${docker.name}
+ true
+ false
+ ${project.build.directory}
+
+
+
+ tag-docker-image
+ pre-integration-test
+
+ tag
+
+
+ ${dockerfile.skip}
+ ${docker.repo}/${docker.name}
+ ${project.version}
+
+
+
+
+
+
+
+
+ push-docker-image
+
+
+ push-docker-image
+
+
+
+
+
+ com.spotify
+ dockerfile-maven-plugin
+
+
+ push-latest-docker-image
+ pre-integration-test
+
+ push
+
+
+ latest
+ ${docker.repo}/${docker.name}
+
+
+
+ push-version-docker-image
+ pre-integration-test
+
+ push
+
+
+ ${project.version}
+ ${docker.repo}/${docker.name}
+
+
+
+
+
+
+
+
+
+
+ jenkins
+ Jenkins Repository
+ https://repo.jenkins-ci.org/releases
+
+ false
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 4ec162be8f..063cb027cb 100755
--- a/pom.xml
+++ b/pom.xml
@@ -109,6 +109,7 @@
1.0.2TB
3.4.0
7.54.2
+ 2.8.5
@@ -838,6 +839,11 @@
coap
${project.version}
+
+ org.thingsboard.common.transport
+ snmp
+ ${project.version}
+
org.thingsboard
dao
@@ -1408,6 +1414,11 @@
+
+ org.snmp4j
+ snmp4j
+ ${snmp4j.version}
+
diff --git a/transport/pom.xml b/transport/pom.xml
index bcafb9a184..98429f2329 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -37,6 +37,7 @@
http
mqtt
coap
+ snmp
diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml
new file mode 100644
index 0000000000..fbcca16149
--- /dev/null
+++ b/transport/snmp/pom.xml
@@ -0,0 +1,112 @@
+
+
+ 4.0.0
+
+
+ org.thingsboard
+ 3.2.1-SNAPSHOT
+ transport
+
+
+ org.thingsboard.transport
+ snmp
+ jar
+
+ Thingsboard SNMP Transport Service
+ https://thingsboard.io
+
+
+ UTF-8
+ ${basedir}/../..
+ java
+ false
+ process-resources
+ package
+ tb-snmp-transport
+ false
+ ${project.build.directory}/windows
+ ThingsBoard SNMP Transport Service
+ org.thingsboard.server.snmp.ThingsboardSnmpTransportApplication
+
+
+
+
+ org.thingsboard.common.transport
+ snmp
+
+
+ org.thingsboard.common
+ queue
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ ${pkg.name}-${project.version}
+
+
+ ${project.basedir}/src/main/resources
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.thingsboard
+ gradle-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+
+
+
+
+
+ jenkins
+ Jenkins Repository
+ https://repo.jenkins-ci.org/releases
+
+ false
+
+
+
+
diff --git a/transport/snmp/src/main/java/org/thingsboard/server/snmp/ThingsboardSnmpTransportApplication.java b/transport/snmp/src/main/java/org/thingsboard/server/snmp/ThingsboardSnmpTransportApplication.java
new file mode 100644
index 0000000000..47d96db8c5
--- /dev/null
+++ b/transport/snmp/src/main/java/org/thingsboard/server/snmp/ThingsboardSnmpTransportApplication.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.snmp;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.util.Arrays;
+
+@SpringBootConfiguration
+@EnableAsync
+@EnableScheduling
+@ComponentScan({"org.thingsboard.server.snmp", "org.thingsboard.server.common", "org.thingsboard.server.transport.snmp", "org.thingsboard.server.queue"})
+public class ThingsboardSnmpTransportApplication {
+
+ private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
+ private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-snmp-transport";
+
+ public static void main(String[] args) {
+ SpringApplication.run(ThingsboardSnmpTransportApplication.class, updateArguments(args));
+ }
+
+ private static String[] updateArguments(String[] args) {
+ if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
+ String[] modifiedArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, modifiedArgs, 0, args.length);
+ modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
+ return modifiedArgs;
+ }
+ return args;
+ }
+}
diff --git a/transport/snmp/src/main/resources/device-data-config.json b/transport/snmp/src/main/resources/device-data-config.json
new file mode 100644
index 0000000000..e425ccc3a6
--- /dev/null
+++ b/transport/snmp/src/main/resources/device-data-config.json
@@ -0,0 +1,9 @@
+{
+ "deviceName": "Thermostat T1",
+ "snmpConfig": {
+ "address": "192.168.1.2",
+ "port": 161,
+ "community": "U5J=$HWj6f@7",
+ "protocolVersion": "v2c"
+ }
+}
diff --git a/transport/snmp/src/main/resources/device-profile-config.json b/transport/snmp/src/main/resources/device-profile-config.json
new file mode 100644
index 0000000000..b272913045
--- /dev/null
+++ b/transport/snmp/src/main/resources/device-profile-config.json
@@ -0,0 +1,21 @@
+{
+ "poolPeriodMs": 10000,
+ "timeoutMs": 5000,
+ "retries": 5,
+ "attributes": [
+ {
+ "key": "snmpNodeManagerEmail",
+ "type": "STRING",
+ "method": "get",
+ "oid": ".1.3.6.1.2.1.1.4.0"
+ }
+ ],
+ "telemetry": [
+ {
+ "key": "snmpNodeSysUpTime",
+ "type": "LONG",
+ "method": "get",
+ "oid": ".1.3.6.1.2.1.1.3.0"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts
index 3552821a8b..abcbb5989f 100644
--- a/ui-ngx/src/app/modules/home/components/home-components.module.ts
+++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts
@@ -97,6 +97,7 @@ import { DeviceProfileDialogComponent } from '@home/components/profile/device-pr
import { DeviceProfileAutocompleteComponent } from '@home/components/profile/device-profile-autocomplete.component';
import { MqttDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/mqtt-device-profile-transport-configuration.component';
import { Lwm2mDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/lwm2m-device-profile-transport-configuration.component';
+import { SnmpDeviceProfileTransportConfigurationComponent } from './profile/device/snmp-device-profile-transport-configuration.component';
import { DeviceProfileAlarmsComponent } from '@home/components/profile/alarm/device-profile-alarms.component';
import { DeviceProfileAlarmComponent } from '@home/components/profile/alarm/device-profile-alarm.component';
import { CreateAlarmRulesComponent } from '@home/components/profile/alarm/create-alarm-rules.component';
@@ -199,6 +200,7 @@ import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-dev
DefaultDeviceProfileTransportConfigurationComponent,
MqttDeviceProfileTransportConfigurationComponent,
Lwm2mDeviceProfileTransportConfigurationComponent,
+ SnmpDeviceProfileTransportConfigurationComponent,
DeviceProfileTransportConfigurationComponent,
CreateAlarmRulesComponent,
AlarmRuleComponent,
@@ -288,6 +290,7 @@ import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-dev
DefaultDeviceProfileTransportConfigurationComponent,
MqttDeviceProfileTransportConfigurationComponent,
Lwm2mDeviceProfileTransportConfigurationComponent,
+ SnmpDeviceProfileTransportConfigurationComponent,
DeviceProfileTransportConfigurationComponent,
CreateAlarmRulesComponent,
AlarmRuleComponent,
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html
index 78707a9221..b877ef0015 100644
--- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html
+++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html
@@ -35,5 +35,11 @@
formControlName="configuration">
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html
new file mode 100644
index 0000000000..f57b1ac716
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html
@@ -0,0 +1,23 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts
new file mode 100644
index 0000000000..96f7454cba
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts
@@ -0,0 +1,100 @@
+///
+/// Copyright © 2016-2021 The Thingsboard Authors
+///
+/// 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.
+///
+
+import {Component, forwardRef, Input, OnInit} from '@angular/core';
+import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
+import {Store} from '@ngrx/store';
+import {AppState} from '@app/core/core.state';
+import {coerceBooleanProperty} from '@angular/cdk/coercion';
+import {
+ DeviceProfileTransportConfiguration,
+ DeviceTransportType,
+ SnmpDeviceProfileTransportConfiguration
+} from '@shared/models/device.models';
+import {isDefinedAndNotNull} from "@core/utils";
+
+export interface OidMappingConfiguration {
+ isAttribute: boolean;
+ key: string;
+ type: string;
+ method: string;
+ oid: string;
+}
+
+@Component({
+ selector: 'tb-snmp-device-profile-transport-configuration',
+ templateUrl: './snmp-device-profile-transport-configuration.component.html',
+ styleUrls: [],
+ providers: [{
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SnmpDeviceProfileTransportConfigurationComponent),
+ multi: true
+ }]
+})
+export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit {
+ snmpDeviceProfileTransportConfigurationFormGroup: FormGroup;
+ private requiredValue: boolean;
+ private configuration = [];
+
+ get required(): boolean {
+ return this.requiredValue;
+ }
+
+ @Input()
+ set required(value: boolean) {
+ this.requiredValue = coerceBooleanProperty(value);
+ }
+
+ @Input()
+ disabled: boolean;
+
+ private propagateChange = (v: any) => {
+ }
+
+ constructor(private store: Store, private fb: FormBuilder) {
+ }
+
+ ngOnInit(): void {
+ this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({
+ configuration: [null, Validators.required]
+ });
+ this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => {
+ this.updateModel();
+ });
+ }
+
+ registerOnChange(fn: any): void {
+ this.propagateChange = fn;
+ }
+
+ registerOnTouched(fn: any): void {
+ }
+
+ writeValue(value: SnmpDeviceProfileTransportConfiguration | null): void {
+ if (isDefinedAndNotNull(value)) {
+ this.snmpDeviceProfileTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
+ }
+ }
+
+ private updateModel() {
+ let configuration: DeviceProfileTransportConfiguration = null;
+ if (this.snmpDeviceProfileTransportConfigurationFormGroup.valid) {
+ configuration = this.snmpDeviceProfileTransportConfigurationFormGroup.getRawValue().configuration;
+ configuration.type = DeviceTransportType.SNMP;
+ }
+ this.propagateChange(configuration);
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html
index 73cceba5bc..02799a840c 100644
--- a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html
+++ b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html
@@ -35,5 +35,11 @@
formControlName="configuration">
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html
new file mode 100644
index 0000000000..fc9f615db9
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html
@@ -0,0 +1,24 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts
new file mode 100644
index 0000000000..165a2906f4
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts
@@ -0,0 +1,100 @@
+///
+/// Copyright © 2016-2021 The Thingsboard Authors
+///
+/// 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.
+///
+
+import {Component, forwardRef, Input, OnInit} from '@angular/core';
+import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
+import {Store} from '@ngrx/store';
+import {AppState} from '@app/core/core.state';
+import {coerceBooleanProperty} from '@angular/cdk/coercion';
+import {
+ DeviceTransportConfiguration,
+ DeviceTransportType,
+ SnmpDeviceTransportConfiguration
+} from '@shared/models/device.models';
+
+@Component({
+ selector: 'tb-snmp-device-transport-configuration',
+ templateUrl: './snmp-device-transport-configuration.component.html',
+ styleUrls: [],
+ providers: [{
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SnmpDeviceTransportConfigurationComponent),
+ multi: true
+ }]
+})
+export class SnmpDeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit {
+
+ snmpDeviceTransportConfigurationFormGroup: FormGroup;
+
+ private requiredValue: boolean;
+
+ get required(): boolean {
+ return this.requiredValue;
+ }
+
+ @Input()
+ set required(value: boolean) {
+ this.requiredValue = coerceBooleanProperty(value);
+ }
+
+ @Input()
+ disabled: boolean;
+
+ private propagateChange = (v: any) => {
+ };
+
+ constructor(private store: Store,
+ private fb: FormBuilder) {
+ }
+
+ registerOnChange(fn: any): void {
+ this.propagateChange = fn;
+ }
+
+ registerOnTouched(fn: any): void {
+ }
+
+ ngOnInit() {
+ this.snmpDeviceTransportConfigurationFormGroup = this.fb.group({
+ configuration: [null, Validators.required]
+ });
+ this.snmpDeviceTransportConfigurationFormGroup.valueChanges.subscribe(() => {
+ this.updateModel();
+ });
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ if (this.disabled) {
+ this.snmpDeviceTransportConfigurationFormGroup.disable({emitEvent: false});
+ } else {
+ this.snmpDeviceTransportConfigurationFormGroup.enable({emitEvent: false});
+ }
+ }
+
+ writeValue(value: SnmpDeviceTransportConfiguration | null): void {
+ this.snmpDeviceTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
+ }
+
+ private updateModel() {
+ let configuration: DeviceTransportConfiguration = null;
+ if (this.snmpDeviceTransportConfigurationFormGroup.valid) {
+ configuration = this.snmpDeviceTransportConfigurationFormGroup.getRawValue().configuration;
+ configuration.type = DeviceTransportType.SNMP;
+ }
+ this.propagateChange(configuration);
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/pages/device/device.module.ts b/ui-ngx/src/app/modules/home/pages/device/device.module.ts
index 7ba9a5c0fe..9347fab505 100644
--- a/ui-ngx/src/app/modules/home/pages/device/device.module.ts
+++ b/ui-ngx/src/app/modules/home/pages/device/device.module.ts
@@ -31,6 +31,7 @@ import { DefaultDeviceTransportConfigurationComponent } from './data/default-dev
import { DeviceTransportConfigurationComponent } from './data/device-transport-configuration.component';
import { MqttDeviceTransportConfigurationComponent } from './data/mqtt-device-transport-configuration.component';
import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-transport-configuration.component';
+import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-transport-configuration.component';
@NgModule({
declarations: [
@@ -39,6 +40,7 @@ import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-
DefaultDeviceTransportConfigurationComponent,
MqttDeviceTransportConfigurationComponent,
Lwm2mDeviceTransportConfigurationComponent,
+ SnmpDeviceTransportConfigurationComponent,
DeviceTransportConfigurationComponent,
DeviceDataComponent,
DeviceComponent,
diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts
index dcf3f34093..fd293aae64 100644
--- a/ui-ngx/src/app/shared/models/device.models.ts
+++ b/ui-ngx/src/app/shared/models/device.models.ts
@@ -29,13 +29,15 @@ import * as _moment from 'moment';
import { AbstractControl, ValidationErrors } from '@angular/forms';
export enum DeviceProfileType {
- DEFAULT = 'DEFAULT'
+ DEFAULT = 'DEFAULT',
+ SNMP = 'SNMP'
}
export enum DeviceTransportType {
DEFAULT = 'DEFAULT',
MQTT = 'MQTT',
// LWM2M = 'LWM2M'
+ SNMP = 'SNMP'
}
export enum MqttTransportPayloadType {
@@ -68,6 +70,13 @@ export const deviceProfileTypeConfigurationInfoMap = new Map(
[DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'],
[DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt-hint'],
// [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m-hint']
+ [DeviceTransportType.SNMP, 'device-profile.transport-type-snmp-hint'],
]
);
@@ -128,6 +139,13 @@ export const deviceTransportTypeConfigurationInfoMap = new Map[+] and multi-level [#] wildcards supported.",
"telemetry-topic-filter": "Telemetry topic filter",
From 37b88908fd12bb80f5c2a3756feefa009bd9e8f9 Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Wed, 10 Mar 2021 15:25:35 +0200
Subject: [PATCH 02/32] Fix merge errors
---
common/transport/snmp/pom.xml | 2 +-
msa/transport/snmp/pom.xml | 2 +-
transport/snmp/pom.xml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml
index 1f334b9699..c7e22ecc80 100644
--- a/common/transport/snmp/pom.xml
+++ b/common/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard.common
- 3.2.1-SNAPSHOT
+ 3.3.0-SNAPSHOT
transport
diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml
index df5caa658c..62448d8763 100644
--- a/msa/transport/snmp/pom.xml
+++ b/msa/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard.msa
transport
- 3.2.1-SNAPSHOT
+ 3.3.0-SNAPSHOT
org.thingsboard.msa.transport
diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml
index fbcca16149..ed446a5612 100644
--- a/transport/snmp/pom.xml
+++ b/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard
- 3.2.1-SNAPSHOT
+ 3.3.0-SNAPSHOT
transport
From 8f2438d6ab57ca22b940a001e9a05e68225e6c69 Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Mon, 22 Mar 2021 17:17:42 +0200
Subject: [PATCH 03/32] SNMP devices balancing (#4254)
* Fix merge errors
* Implement SNMP transports balancing
* Refactor; implement transport device cache
* Refactor
* Finish up device lifecycle handling implementing; refactor
* Refactor
* Change base image to thingsboard/openjdk11 for msa snmp transport
* Refactor
* Change transport services names to upper-case
---
.../actors/service/DefaultActorService.java | 4 +-
.../DefaultTbApiUsageStateService.java | 2 +-
.../apiusage/TbApiUsageStateService.java | 2 +-
.../queue/DefaultTbCoreConsumerService.java | 2 +-
.../DefaultTbRuleEngineConsumerService.java | 2 +-
.../service/queue/TbCoreConsumerService.java | 2 +-
.../queue/TbRuleEngineConsumerService.java | 2 +-
.../processing/AbstractConsumerService.java | 2 +-
.../state/DefaultDeviceStateService.java | 2 +-
.../service/state/DeviceStateService.java | 2 +-
.../DefaultSubscriptionManagerService.java | 2 +-
.../DefaultTbLocalSubscriptionService.java | 5 +-
.../SubscriptionManagerService.java | 2 +-
.../TbLocalSubscriptionService.java | 4 +-
.../AbstractSubscriptionService.java | 19 +-
.../telemetry/AlarmSubscriptionService.java | 3 +-
.../DefaultAlarmSubscriptionService.java | 23 -
.../TelemetrySubscriptionService.java | 3 +-
.../transport/DefaultTransportApiService.java | 94 ++++-
.../server/dao/device/DeviceService.java | 3 +
.../common/data/TbTransportService.java | 20 +
.../data/DeviceTransportConfiguration.java | 4 +-
.../SnmpProfileTransportConfiguration.java | 10 +-
.../DefaultTbServiceInfoProvider.java | 20 +
.../queue/discovery/HashPartitionService.java | 20 +-
.../queue/discovery/PartitionService.java | 4 +
.../discovery/TbApplicationEventListener.java | 1 +
.../queue/discovery/ZkDiscoveryService.java | 12 +-
.../ClusterTopologyChangeEvent.java | 3 +-
.../{ => event}/PartitionChangeEvent.java | 3 +-
.../event/ServiceListChangedEvent.java | 35 ++
.../{ => event}/TbApplicationEvent.java | 2 +-
.../queue/util/TbSnmpTransportComponent.java | 29 ++
common/queue/src/main/proto/queue.proto | 35 ++
.../transport/coap/CoapTransportService.java | 9 +-
.../transport/http/DeviceApiController.java | 10 +-
.../lwm2m/server/LwM2mTransportService.java | 3 +-
.../server/LwM2mTransportServiceImpl.java | 6 +
.../transport/mqtt/MqttTransportService.java | 8 +-
common/transport/snmp/pom.xml | 4 -
.../transport/snmp/SnmpDeviceSimulator.java | 81 ++++
.../transport/snmp/SnmpTransportContext.java | 311 +++++++++++---
.../transport/snmp/SnmpTransportService.java | 147 -------
.../ServiceListChangedEventListener.java | 35 ++
.../event/SnmpTransportListChangedEvent.java | 24 ++
...SnmpTransportListChangedEventListener.java | 34 ++
.../service/ProtoTransportEntityService.java | 92 ++++
.../SnmpTransportBalancingService.java | 92 ++++
.../SnmpTransportService.java} | 149 +++++--
.../snmp/session/DeviceSessionContext.java | 166 ++++++++
.../snmp/session/DeviceSessionCtx.java | 206 ---------
.../common/transport/DeviceUpdatedEvent.java | 28 ++
.../common/transport/SessionMsgListener.java | 4 +
.../common/transport/TransportContext.java | 1 +
.../common/transport/TransportService.java | 12 +
.../DefaultTransportDeviceProfileCache.java | 4 +-
.../service/DefaultTransportService.java | 77 +++-
.../server/dao/device/DeviceDao.java | 4 +
.../server/dao/device/DeviceServiceImpl.java | 7 +
.../dao/sql/device/DeviceRepository.java | 6 +
.../server/dao/sql/device/JpaDeviceDao.java | 6 +
docker/tb-transports/snmp/conf/logback.xml | 50 +++
.../snmp/conf/tb-snmp-transport.conf | 23 +
msa/transport/snmp/docker/Dockerfile | 2 +-
transport/snmp/src/main/conf/logback.xml | 45 ++
.../snmp/src/main/conf/tb-snmp-transport.conf | 22 +
transport/snmp/src/main/resources/logback.xml | 36 ++
.../src/main/resources/tb-snmp-transport.yml | 394 ++++++++++++++++++
68 files changed, 1923 insertions(+), 553 deletions(-)
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/TbTransportService.java
rename common/queue/src/main/java/org/thingsboard/server/queue/discovery/{ => event}/ClusterTopologyChangeEvent.java (91%)
rename common/queue/src/main/java/org/thingsboard/server/queue/discovery/{ => event}/PartitionChangeEvent.java (93%)
create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ServiceListChangedEvent.java
rename common/queue/src/main/java/org/thingsboard/server/queue/discovery/{ => event}/TbApplicationEvent.java (95%)
create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/util/TbSnmpTransportComponent.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
delete mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/ServiceListChangedEventListener.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEvent.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEventListener.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/ProtoTransportEntityService.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportBalancingService.java
rename common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/{session/SnmpSessionListener.java => service/SnmpTransportService.java} (60%)
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
delete mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java
create mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/DeviceUpdatedEvent.java
create mode 100644 docker/tb-transports/snmp/conf/logback.xml
create mode 100644 docker/tb-transports/snmp/conf/tb-snmp-transport.conf
create mode 100644 transport/snmp/src/main/conf/logback.xml
create mode 100644 transport/snmp/src/main/conf/tb-snmp-transport.conf
create mode 100644 transport/snmp/src/main/resources/logback.xml
create mode 100644 transport/snmp/src/main/resources/tb-snmp-transport.yml
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index 05363dfd59..f3a07eb727 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -25,7 +25,6 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.DefaultTbActorSystem;
-import org.thingsboard.server.actors.TbActorId;
import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.actors.TbActorSystem;
import org.thingsboard.server.actors.TbActorSystemSettings;
@@ -33,14 +32,13 @@ import org.thingsboard.server.actors.app.AppActor;
import org.thingsboard.server.actors.app.AppInitMsg;
import org.thingsboard.server.actors.stats.StatsActor;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
@Service
@Slf4j
diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
index d0a3984660..ccac9b4a16 100644
--- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
@@ -52,7 +52,7 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/TbApiUsageStateService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/TbApiUsageStateService.java
index 117607ae9f..f97188597b 100644
--- a/application/src/main/java/org/thingsboard/server/service/apiusage/TbApiUsageStateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/apiusage/TbApiUsageStateService.java
@@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
public interface TbApiUsageStateService extends ApplicationListener {
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
index 02f69ce10d..9d3e2b5a64 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
@@ -52,7 +52,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
index 390798a3e2..bd29b6c431 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
@@ -38,7 +38,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerService.java
index 0a965413cf..222f58fd78 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerService.java
@@ -16,7 +16,7 @@
package org.thingsboard.server.service.queue;
import org.springframework.context.ApplicationListener;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
public interface TbCoreConsumerService extends ApplicationListener {
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerService.java
index e4a88de575..582dbd1355 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerService.java
@@ -16,7 +16,7 @@
package org.thingsboard.server.service.queue;
import org.springframework.context.ApplicationListener;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
public interface TbRuleEngineConsumerService extends ApplicationListener {
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
index 02378eb557..28cce50bf5 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
@@ -34,7 +34,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
index c8a4e80e0e..5c358c1b77 100644
--- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
@@ -54,7 +54,7 @@ import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.util.TbCoreComponent;
diff --git a/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java
index 932ed4720d..b1bbb9cdfa 100644
--- a/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java
@@ -18,7 +18,7 @@ package org.thingsboard.server.service.state;
import org.springframework.context.ApplicationListener;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.common.msg.queue.TbCallback;
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
index ce8838aaa8..657bdf6f75 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
@@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdate
import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateValueListProto;
import org.thingsboard.server.queue.TbQueueProducer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
index 0220a94964..710d613b37 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
@@ -20,10 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
-import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java
index fd40364fe5..37850b701f 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java
@@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.msg.queue.TbCallback;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import java.util.List;
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java
index d244ef136b..f1bf6c0da0 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java
@@ -15,8 +15,8 @@
*/
package org.thingsboard.server.service.subscription;
-import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
index 168e1271fd..23abba15f3 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
@@ -22,35 +22,18 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
-import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
-import org.thingsboard.server.common.data.id.EntityId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.kv.AttributeKvEntry;
-import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
-import org.thingsboard.server.common.data.kv.BooleanDataEntry;
-import org.thingsboard.server.common.data.kv.DoubleDataEntry;
-import org.thingsboard.server.common.data.kv.LongDataEntry;
-import org.thingsboard.server.common.data.kv.StringDataEntry;
-import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.msg.queue.ServiceType;
-import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
-import org.thingsboard.server.dao.attributes.AttributesService;
-import org.thingsboard.server.dao.timeseries.TimeseriesService;
-import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.subscription.SubscriptionManagerService;
-import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
-import java.util.Collections;
-import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/AlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/AlarmSubscriptionService.java
index fa44906cc3..6ed3860341 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/AlarmSubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/AlarmSubscriptionService.java
@@ -17,8 +17,7 @@ package org.thingsboard.server.service.telemetry;
import org.springframework.context.ApplicationListener;
import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
-import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
/**
* Created by ashvayka on 27.03.18.
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
index 107b97dbb3..dde71ef53f 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
@@ -22,9 +22,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
-import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
@@ -35,43 +33,22 @@ import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.kv.AttributeKvEntry;
-import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
-import org.thingsboard.server.common.data.kv.BooleanDataEntry;
-import org.thingsboard.server.common.data.kv.DoubleDataEntry;
-import org.thingsboard.server.common.data.kv.LongDataEntry;
-import org.thingsboard.server.common.data.kv.StringDataEntry;
-import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.query.AlarmData;
-import org.thingsboard.server.common.data.query.AlarmDataPageLink;
import org.thingsboard.server.common.data.query.AlarmDataQuery;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.alarm.AlarmOperationResult;
import org.thingsboard.server.dao.alarm.AlarmService;
-import org.thingsboard.server.dao.attributes.AttributesService;
-import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.subscription.SubscriptionManagerService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
-import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.function.Consumer;
/**
* Created by ashvayka on 27.03.18.
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
index 015a58495f..0ac9e79ff1 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
@@ -16,8 +16,7 @@
package org.thingsboard.server.service.telemetry;
import org.springframework.context.ApplicationListener;
-import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
-import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
/**
* Created by ashvayka on 27.03.18.
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
index eea7af582c..4f58bd0f63 100644
--- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
+++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
@@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
@@ -61,11 +62,15 @@ import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetResourcesRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
@@ -84,6 +89,7 @@ import org.thingsboard.server.service.state.DeviceStateService;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -139,40 +145,41 @@ public class DefaultTransportApiService implements TransportApiService {
@Override
public ListenableFuture> handle(TbProtoQueueMsg tbProtoQueueMsg) {
TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue();
+ ListenableFuture result = null;
+
if (transportApiRequestMsg.hasValidateTokenRequestMsg()) {
ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg();
- return Futures.transform(validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+ result = validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN);
} else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) {
TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg();
- return Futures.transform(validateCredentials(msg),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+ result = validateCredentials(msg);
} else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg();
- return Futures.transform(validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+ result = validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE);
} else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) {
- return Futures.transform(handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+ result = handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg());
} else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) {
- return Futures.transform(handle(transportApiRequestMsg.getEntityProfileRequestMsg()),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+ result = handle(transportApiRequestMsg.getEntityProfileRequestMsg());
} else if (transportApiRequestMsg.hasLwM2MRequestMsg()) {
- return Futures.transform(handle(transportApiRequestMsg.getLwM2MRequestMsg()),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+ result = handle(transportApiRequestMsg.getLwM2MRequestMsg());
} else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) {
ValidateDeviceLwM2MCredentialsRequestMsg msg = transportApiRequestMsg.getValidateDeviceLwM2MCredentialsRequestMsg();
- return Futures.transform(validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+ result = validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS);
} else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) {
- return Futures.transform(handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+ result = handle(transportApiRequestMsg.getProvisionDeviceRequestMsg());
} else if (transportApiRequestMsg.hasResourcesRequestMsg()) {
- return Futures.transform(handle(transportApiRequestMsg.getResourcesRequestMsg()),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+ result = handle(transportApiRequestMsg.getResourcesRequestMsg());
+ } else if (transportApiRequestMsg.hasSnmpDevicesRequestMsg()) {
+ result = handle(transportApiRequestMsg.getSnmpDevicesRequestMsg());
+ } else if (transportApiRequestMsg.hasDeviceRequestMsg()) {
+ result = handle(transportApiRequestMsg.getDeviceRequestMsg());
+ } else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) {
+ result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg());
}
- return Futures.transform(getEmptyTransportApiResponseFuture(),
- value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
+
+ return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture),
+ value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()),
+ MoreExecutors.directExecutor());
}
private ListenableFuture validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) {
@@ -366,6 +373,39 @@ public class DefaultTransportApiService implements TransportApiService {
return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build());
}
+ private ListenableFuture handle(GetDeviceRequestMsg requestMsg) {
+ DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
+ Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, deviceId);
+
+ TransportApiResponseMsg responseMsg;
+ if (device != null) {
+ UUID deviceProfileId = device.getDeviceProfileId().getId();
+ responseMsg = TransportApiResponseMsg.newBuilder()
+ .setDeviceResponseMsg(TransportProtos.GetDeviceResponseMsg.newBuilder()
+ .setDeviceProfileIdMSB(deviceProfileId.getMostSignificantBits())
+ .setDeviceProfileIdLSB(deviceProfileId.getLeastSignificantBits())
+ .setDeviceTransportConfiguration(ByteString.copyFrom(
+ dataDecodingEncodingService.encode(device.getDeviceData().getTransportConfiguration())
+ )))
+ .build();
+ } else {
+ responseMsg = TransportApiResponseMsg.getDefaultInstance();
+ }
+
+ return Futures.immediateFuture(responseMsg);
+ }
+
+ private ListenableFuture handle(GetDeviceCredentialsRequestMsg requestMsg) {
+ DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
+ DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, deviceId);
+
+ return Futures.immediateFuture(TransportApiResponseMsg.newBuilder()
+ .setDeviceCredentialsResponseMsg(TransportProtos.GetDeviceCredentialsResponseMsg.newBuilder()
+ .setDeviceCredentialsData(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceCredentials))))
+ .build());
+ }
+
+
private ListenableFuture handle(GetResourcesRequestMsg requestMsg) {
TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
TransportProtos.GetResourcesResponseMsg.Builder builder = TransportProtos.GetResourcesResponseMsg.newBuilder();
@@ -400,6 +440,20 @@ public class DefaultTransportApiService implements TransportApiService {
.build();
}
+ // TODO: request snmp devices with pagination
+ private ListenableFuture handle(GetSnmpDevicesRequestMsg requestMsg) {
+ List result = deviceService.findDevicesIdsByDeviceProfileTransportType(DeviceTransportType.SNMP);
+ GetSnmpDevicesResponseMsg responseMsg = GetSnmpDevicesResponseMsg.newBuilder()
+ .addAllIds(result.stream()
+ .map(UUID::toString)
+ .collect(Collectors.toList()))
+ .build();
+
+ return Futures.immediateFuture(TransportApiResponseMsg.newBuilder()
+ .setSnmpDevicesResponseMsg(responseMsg)
+ .build());
+ }
+
private ListenableFuture getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) {
return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> {
if (device == null) {
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
index f98c56d89a..1a893f5388 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
@@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId;
@@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import java.util.List;
+import java.util.UUID;
public interface DeviceService {
@@ -90,4 +92,5 @@ public interface DeviceService {
Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
+ List findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType);
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbTransportService.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbTransportService.java
new file mode 100644
index 0000000000..6195022aba
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbTransportService.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data;
+
+public interface TbTransportService {
+ String getName();
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
index 0ae9f26cf3..49a547371f 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
@@ -21,6 +21,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.DeviceTransportType;
+import java.io.Serializable;
+
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
@@ -31,7 +33,7 @@ import org.thingsboard.server.common.data.DeviceTransportType;
@JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"),
@JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"),
@JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")})
-public interface DeviceTransportConfiguration {
+public interface DeviceTransportConfiguration extends Serializable {
@JsonIgnore
DeviceTransportType getType();
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
index a2529e74e9..50f6c4351c 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
@@ -19,14 +19,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.thingsboard.server.common.data.DeviceTransportType;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Data
public class SnmpProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
-
- private int poolPeriodMs;
+ private int pollPeriodMs;
private int timeoutMs;
private int retries;
private List attributes;
@@ -39,6 +39,10 @@ public class SnmpProfileTransportConfiguration implements DeviceProfileTransport
@JsonIgnore
public List getKvMappings() {
- return Stream.concat(attributes.stream(), telemetry.stream()).collect(Collectors.toList());
+ if (attributes != null && telemetry != null) {
+ return Stream.concat(attributes.stream(), telemetry.stream()).collect(Collectors.toList());
+ } else {
+ return Collections.emptyList();
+ }
}
}
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
index fc4ec33eb1..3ba360cd8e 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
@@ -19,8 +19,12 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.transport.TransportProtos;
@@ -32,6 +36,7 @@ import javax.annotation.PostConstruct;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -56,6 +61,8 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
@Autowired(required = false)
private TbQueueRuleEngineSettings ruleEngineSettings;
+ @Autowired
+ private ApplicationContext applicationContext;
private List serviceTypes;
private ServiceInfo serviceInfo;
@@ -102,6 +109,19 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
serviceInfo = builder.build();
}
+ @EventListener(ContextRefreshedEvent.class)
+ public void setTransports() {
+ serviceInfo = ServiceInfo.newBuilder(serviceInfo)
+ .addAllTransports(getTransportServices().stream()
+ .map(TbTransportService::getName)
+ .collect(Collectors.toSet()))
+ .build();
+ }
+
+ private Collection getTransportServices() {
+ return applicationContext.getBeansOfType(TbTransportService.class).values();
+ }
+
@Override
public ServiceInfo getServiceInfo() {
return serviceInfo;
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java
index 2da438417a..c833b314e6 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java
@@ -15,26 +15,27 @@
*/
package org.thingsboard.server.queue.discovery;
-import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
-import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import org.thingsboard.server.common.msg.queue.ServiceQueue;
+import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
+import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
+import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import javax.annotation.PostConstruct;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -46,7 +47,6 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ConcurrentNavigableMap;
import java.util.stream.Collectors;
@Service
@@ -186,6 +186,8 @@ public class HashPartitionService implements PartitionService {
applicationEventPublisher.publishEvent(new ClusterTopologyChangeEvent(this, changes));
}
}
+
+ applicationEventPublisher.publishEvent(new ServiceListChangedEvent(otherServices, currentService));
}
@Override
@@ -219,6 +221,14 @@ public class HashPartitionService implements PartitionService {
}
}
+ @Override
+ public int resolvePartitionIndex(UUID entityId, int partitions) {
+ int hash = hashFunction.newHasher()
+ .putLong(entityId.getMostSignificantBits())
+ .putLong(entityId.getLeastSignificantBits()).hash().asInt();
+ return Math.abs(hash % partitions);
+ }
+
private Map> getServiceKeyListMap(List services) {
final Map> currentMap = new HashMap<>();
services.forEach(serviceInfo -> {
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java
index ebb260bc57..20c59378e8 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java
@@ -20,9 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import java.util.List;
import java.util.Set;
+import java.util.UUID;
/**
* Once application is ready or cluster topology changes, this Service will produce {@link PartitionChangeEvent}
@@ -55,4 +57,6 @@ public interface PartitionService {
* @return
*/
TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId);
+
+ int resolvePartitionIndex(UUID entityId, int partitions);
}
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java
index 9158d8f0c8..70484a892b 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.queue.discovery;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
+import org.thingsboard.server.queue.discovery.event.TbApplicationEvent;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java
index 5253f57248..0802f0f52c 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java
@@ -33,12 +33,14 @@ import org.apache.zookeeper.KeeperException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -77,7 +79,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
private volatile boolean stopped = true;
- public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider, PartitionService partitionService) {
+ public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider,
+ PartitionService partitionService) {
this.serviceInfoProvider = serviceInfoProvider;
this.partitionService = partitionService;
}
@@ -126,7 +129,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
return;
}
publishCurrentServer();
- partitionService.recalculatePartitions(serviceInfoProvider.getServiceInfo(), getOtherServers());
+ TransportProtos.ServiceInfo currentService = serviceInfoProvider.getServiceInfo();
+ partitionService.recalculatePartitions(currentService, getOtherServers());
}
public synchronized void publishCurrentServer() {
@@ -281,11 +285,11 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
case CHILD_ADDED:
case CHILD_UPDATED:
case CHILD_REMOVED:
- partitionService.recalculatePartitions(serviceInfoProvider.getServiceInfo(), getOtherServers());
+ TransportProtos.ServiceInfo currentService = serviceInfoProvider.getServiceInfo();
+ partitionService.recalculatePartitions(currentService, getOtherServers());
break;
default:
break;
}
}
-
}
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ClusterTopologyChangeEvent.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ClusterTopologyChangeEvent.java
similarity index 91%
rename from common/queue/src/main/java/org/thingsboard/server/queue/discovery/ClusterTopologyChangeEvent.java
rename to common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ClusterTopologyChangeEvent.java
index 1e5b90b5fe..6b3a3ff93f 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ClusterTopologyChangeEvent.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ClusterTopologyChangeEvent.java
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.queue.discovery;
+package org.thingsboard.server.queue.discovery.event;
import lombok.Getter;
-import org.springframework.context.ApplicationEvent;
import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import java.util.Set;
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionChangeEvent.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java
similarity index 93%
rename from common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionChangeEvent.java
rename to common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java
index 2edcd2ceca..e11e19db61 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionChangeEvent.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.queue.discovery;
+package org.thingsboard.server.queue.discovery.event;
import lombok.Getter;
-import org.springframework.context.ApplicationEvent;
import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ServiceListChangedEvent.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ServiceListChangedEvent.java
new file mode 100644
index 0000000000..dd7d384640
--- /dev/null
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ServiceListChangedEvent.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.queue.discovery.event;
+
+import lombok.Getter;
+import lombok.ToString;
+import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
+
+import java.util.List;
+
+@Getter
+@ToString
+public class ServiceListChangedEvent extends TbApplicationEvent {
+ private final List otherServices;
+ private final ServiceInfo currentService;
+
+ public ServiceListChangedEvent(List otherServices, ServiceInfo currentService) {
+ super(otherServices);
+ this.otherServices = otherServices;
+ this.currentService = currentService;
+ }
+}
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEvent.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/TbApplicationEvent.java
similarity index 95%
rename from common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEvent.java
rename to common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/TbApplicationEvent.java
index face2d36d6..1a75b4e4e4 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEvent.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/TbApplicationEvent.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.queue.discovery;
+package org.thingsboard.server.queue.discovery.event;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/TbSnmpTransportComponent.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/TbSnmpTransportComponent.java
new file mode 100644
index 0000000000..36080afaab
--- /dev/null
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/TbSnmpTransportComponent.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.queue.util;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface TbSnmpTransportComponent {
+}
diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto
index 42f6267d42..59fcdad36a 100644
--- a/common/queue/src/main/proto/queue.proto
+++ b/common/queue/src/main/proto/queue.proto
@@ -34,6 +34,7 @@ message ServiceInfo {
int64 tenantIdMSB = 3;
int64 tenantIdLSB = 4;
repeated QueueInfo ruleEngineQueues = 5;
+ repeated string transports = 6;
}
/**
@@ -250,6 +251,34 @@ message GetEntityProfileResponseMsg {
bytes apiState = 3;
}
+message GetDeviceRequestMsg {
+ int64 deviceIdMSB = 1;
+ int64 deviceIdLSB = 2;
+}
+
+message GetDeviceResponseMsg {
+ int64 deviceProfileIdMSB = 1;
+ int64 deviceProfileIdLSB = 2;
+ bytes deviceTransportConfiguration = 3;
+}
+
+message GetDeviceCredentialsRequestMsg {
+ int64 deviceIdMSB = 1;
+ int64 deviceIdLSB = 2;
+}
+
+message GetDeviceCredentialsResponseMsg {
+ bytes deviceCredentialsData = 1;
+}
+
+message GetSnmpDevicesRequestMsg {
+
+}
+
+message GetSnmpDevicesResponseMsg {
+ repeated string ids = 1;
+}
+
message EntityUpdateMsg {
string entityType = 1;
bytes data = 2;
@@ -559,6 +588,9 @@ message TransportApiRequestMsg {
ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7;
ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8;
GetResourcesRequestMsg resourcesRequestMsg = 9;
+ GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 10;
+ GetDeviceRequestMsg deviceRequestMsg = 11;
+ GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 12;
}
/* Response from ThingsBoard Core Service to Transport Service */
@@ -567,8 +599,11 @@ message TransportApiResponseMsg {
GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2;
GetEntityProfileResponseMsg entityProfileResponseMsg = 3;
ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4;
+ GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5;
LwM2MResponseMsg lwM2MResponseMsg = 6;
GetResourcesResponseMsg resourcesResponseMsg = 7;
+ GetDeviceResponseMsg deviceResponseMsg = 8;
+ GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 9;
}
/* Messages that are handled by ThingsBoard Core Service */
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
index 8abc859b21..dd4a76dcc8 100644
--- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
@@ -18,12 +18,12 @@ package org.thingsboard.server.transport.coap;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer;
-
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.CoapEndpoint.Builder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.TbTransportService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -34,7 +34,7 @@ import java.net.UnknownHostException;
@Service("CoapTransportService")
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')")
@Slf4j
-public class CoapTransportService {
+public class CoapTransportService implements TbTransportService {
private static final String V1 = "v1";
private static final String API = "api";
@@ -73,4 +73,9 @@ public class CoapTransportService {
this.server.destroy();
log.info("CoAP transport stopped!");
}
+
+ @Override
+ public String getName() {
+ return "COAP";
+ }
}
diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
index e222b690ef..011093149e 100644
--- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
+++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
@@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.TransportContext;
@@ -41,7 +42,6 @@ import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
@@ -53,7 +53,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMs
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
import javax.servlet.http.HttpServletRequest;
@@ -69,7 +68,7 @@ import java.util.function.Consumer;
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.http.enabled}'=='true')")
@RequestMapping("/api/v1")
@Slf4j
-public class DeviceApiController {
+public class DeviceApiController implements TbTransportService {
@Autowired
private HttpTransportContext transportContext;
@@ -338,4 +337,9 @@ public class DeviceApiController {
.build(), TransportServiceCallback.EMPTY);
}
+ @Override
+ public String getName() {
+ return "HTTP";
+ }
+
}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java
index 8d1aff37b5..db419eca9f 100644
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java
@@ -20,12 +20,13 @@ import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.Collection;
import java.util.Optional;
-public interface LwM2mTransportService {
+public interface LwM2mTransportService extends TbTransportService {
void onRegistered(Registration registration, Collection previousObsersations);
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
index 548d750a3f..bae6a0af1d 100644
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
@@ -40,6 +40,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
@@ -1118,4 +1119,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
return new ArrayList<>(namesIsWritable);
}
+
+ @Override
+ public String getName() {
+ return "LWM2M";
+ }
}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
index 0a0c0cbe70..3e53dec227 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
@@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.TbTransportService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -38,7 +39,7 @@ import javax.annotation.PreDestroy;
@Service("MqttTransportService")
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')")
@Slf4j
-public class MqttTransportService {
+public class MqttTransportService implements TbTransportService {
@Value("${transport.mqtt.bind_address}")
private String host;
@@ -90,4 +91,9 @@ public class MqttTransportService {
}
log.info("MQTT transport stopped!");
}
+
+ @Override
+ public String getName() {
+ return "MQTT";
+ }
}
diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml
index c7e22ecc80..ab83ab69a7 100644
--- a/common/transport/snmp/pom.xml
+++ b/common/transport/snmp/pom.xml
@@ -58,9 +58,5 @@
org.snmp4j
snmp4j
-
- org.thingsboard.common
- dao-api
-
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
new file mode 100644
index 0000000000..7109ef4e3f
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp;
+
+import org.snmp4j.CommunityTarget;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.GenericAddress;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.transport.DefaultUdpTransportMapping;
+import org.snmp4j.transport.UdpTransportMapping;
+
+import java.io.IOException;
+
+/**
+ * For testing purposes. Will be removed when the time comes
+ */
+public class SnmpDeviceSimulator {
+ private final Target target;
+ private final OID oid = new OID(".1.3.6.1.2.1.1.1.0");
+ private Snmp snmp;
+
+ public SnmpDeviceSimulator(int port) {
+ String address = "udp:127.0.0.1/" + port;
+
+ CommunityTarget target = new CommunityTarget();
+ target.setCommunity(new OctetString("public"));
+ target.setAddress(GenericAddress.parse(address));
+ target.setRetries(2);
+ target.setTimeout(1500);
+ target.setVersion(SnmpConstants.version2c);
+
+ this.target = target;
+ }
+
+ public static void main(String[] args) throws IOException {
+ SnmpDeviceSimulator deviceSimulator = new SnmpDeviceSimulator(161);
+
+ deviceSimulator.start();
+ String response = deviceSimulator.sendRequest(PDU.GET);
+
+ System.out.println(response);
+ }
+
+ public void start() throws IOException {
+ UdpTransportMapping transport = new DefaultUdpTransportMapping();
+ transport.addTransportListener((sourceTransport, incomingAddress, wholeMessage, tmStateReference) -> {
+ System.out.println();
+ });
+ snmp = new Snmp(transport);
+
+ transport.listen();
+ }
+
+ public String sendRequest(int pduType) throws IOException {
+ PDU pdu = new PDU();
+ pdu.add(new VariableBinding(oid));
+ pdu.setType(pduType);
+
+ ResponseEvent responseEvent = snmp.send(pdu, target);
+ return responseEvent.getResponse().get(0).getVariable().toString();
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index 66f852a209..a9fa8380fa 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -15,17 +15,19 @@
*/
package org.thingsboard.server.transport.snmp;
-import lombok.Getter;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
-import org.snmp4j.Snmp;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.stereotype.Service;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileKvMapping;
import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
@@ -33,84 +35,180 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
+import org.thingsboard.server.common.transport.DeviceUpdatedEvent;
import org.thingsboard.server.common.transport.TransportContext;
-import org.thingsboard.server.dao.device.DeviceCredentialsService;
-import org.thingsboard.server.transport.snmp.session.DeviceSessionCtx;
+import org.thingsboard.server.common.transport.TransportDeviceProfileCache;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
+import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
+import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService;
+import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
+import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
+import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
-@Service("SnmpTransportContext")
-@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
+@TbSnmpTransportComponent
+@Component
@Slf4j
+@RequiredArgsConstructor
public class SnmpTransportContext extends TransportContext {
- @Autowired
- DeviceCredentialsService deviceCredentialsService;
-
- @Autowired
- SnmpTransportService snmpTransportService;
-
- @Getter
- private final Map profileTransportConfig = new ConcurrentHashMap<>();
- @Getter
- private final Map> pdusPerProfile = new ConcurrentHashMap<>();
- @Getter
- private final Map deviceSessions = new ConcurrentHashMap<>();
-
- public Optional findAttributesMapping(DeviceProfileId deviceProfileId, OID responseOid) {
- if (profileTransportConfig.containsKey(deviceProfileId)) {
- return findMapping(responseOid, profileTransportConfig.get(deviceProfileId).getAttributes());
- }
- return Optional.empty();
+ private final SnmpTransportService snmpTransportService;
+ private final TransportDeviceProfileCache deviceProfileCache;
+ private final TransportService transportService;
+ private final ProtoTransportEntityService protoEntityService;
+ private final SnmpTransportBalancingService balancingService;
+
+ private final Map sessions = new ConcurrentHashMap<>();
+ private final Map profilesTransportConfigs = new ConcurrentHashMap<>();
+ private final Map> profilesPdus = new ConcurrentHashMap<>();
+ private Collection allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
+
+ @EventListener(ApplicationReadyEvent.class)
+ @Order(2)
+ public void initDevicesSessions() {
+ log.info("Initializing SNMP devices sessions");
+ allSnmpDevicesIds = protoEntityService.getAllSnmpDevicesIds().stream()
+ .map(DeviceId::new)
+ .collect(Collectors.toList());
+ log.trace("Found all SNMP devices ids: {}", allSnmpDevicesIds);
+
+ List managedDevicesIds = allSnmpDevicesIds.stream()
+ .filter(deviceId -> balancingService.isManagedByCurrentTransport(deviceId.getId()))
+ .collect(Collectors.toList());
+ log.info("SNMP devices managed by current SNMP transport: {}", managedDevicesIds);
+
+ managedDevicesIds.stream()
+ .map(protoEntityService::getDeviceById)
+ .collect(Collectors.toList())
+ .forEach(this::establishDeviceSession);
}
- public Optional findTelemetryMapping(DeviceProfileId deviceProfileId, OID responseOid) {
- if (profileTransportConfig.containsKey(deviceProfileId)) {
- return findMapping(responseOid, profileTransportConfig.get(deviceProfileId).getTelemetry());
+ private void establishDeviceSession(Device device) {
+ if (device == null) return;
+ log.info("Establishing SNMP device session for device {}", device.getId());
+
+ DeviceProfileId deviceProfileId = device.getDeviceProfileId();
+ DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
+
+ DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
+ if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
+ log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
+ return;
}
- return Optional.empty();
- }
- private Optional findMapping(OID responseOid, List mappings) {
- return mappings.stream()
- .filter(kvMapping -> new OID(kvMapping.getOid()).equals(responseOid))
- //TODO: OID shouldn't be duplicated in the config, add backend and UI verification
- .findFirst();
- }
+ SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
+ if (!deviceTransportConfiguration.isValid()) {
+ log.warn("SNMP device transport configuration is not valid");
+ return;
+ }
- public void initPduListPerProfile() {
- profileTransportConfig.forEach(this::updatePduListPerProfile);
- }
+ SnmpProfileTransportConfiguration profileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ profilesPdus.computeIfAbsent(deviceProfileId, id -> createPdus(profileTransportConfiguration));
- public void updatePduListPerProfile(DeviceProfileId id, SnmpProfileTransportConfiguration config) {
- pdusPerProfile.put(id, createPduList(config));
+ DeviceSessionContext deviceSessionContext = new DeviceSessionContext(
+ device, deviceProfile,
+ credentials.getCredentialsId(), deviceTransportConfiguration,
+ this, snmpTransportService
+ );
+ registerSessionMsgListener(deviceSessionContext);
+ sessions.put(device.getId(), deviceSessionContext);
+ log.info("Established SNMP device session for device {}", device.getId());
}
- public void updateDeviceSessionCtx(Device device, DeviceProfile deviceProfile, Snmp snmp) {
- DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId());
- if (DeviceCredentialsType.ACCESS_TOKEN.equals(credentials.getCredentialsType())) {
- SnmpDeviceTransportConfiguration snmpDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
- if (snmpDeviceTransportConfiguration.isValid()) {
- DeviceSessionCtx deviceSessionCtx = new DeviceSessionCtx(this, credentials.getCredentialsId(), snmpDeviceTransportConfiguration, snmp, device.getId(), deviceProfile);
- deviceSessionCtx.createSessionInfo(ctx -> getTransportService().registerAsyncSession(deviceSessionCtx.getSessionInfo(), deviceSessionCtx));
- this.deviceSessions.put(device.getId(), deviceSessionCtx);
- }
- } else {
+ private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) {
+ log.info("Updating SNMP device session for device {}", device.getId());
+ DeviceProfileId deviceProfileId = deviceProfile.getId();
+
+ DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
+ if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
+ destroyDeviceSession(sessionContext);
+ return;
+ }
+
+ SnmpProfileTransportConfiguration profileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
+ sessionContext.setProfileTransportConfiguration(profileTransportConfiguration);
+ sessionContext.setDeviceTransportConfiguration(deviceTransportConfiguration);
+ if (!deviceTransportConfiguration.isValid()) {
+ log.warn("SNMP device transport configuration is not valid");
+ destroyDeviceSession(sessionContext);
+ return;
+ }
+
+ if (!profileTransportConfiguration.equals(profilesTransportConfigs.get(deviceProfileId))) {
+ profilesPdus.put(deviceProfileId, createPdus(profileTransportConfiguration));
+ profilesTransportConfigs.put(deviceProfileId, profileTransportConfiguration);
+ sessionContext.initTarget(profileTransportConfiguration, deviceTransportConfiguration);
+ } else if (!deviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
+ sessionContext.initTarget(profileTransportConfiguration, deviceTransportConfiguration);
+ } else {
+ log.trace("Configuration of the device {} was not updated", device);
}
}
- public ExecutorService getSnmpCallbackExecutor() {
- return snmpTransportService.getSnmpCallbackExecutor();
+ private void destroyDeviceSession(DeviceSessionContext sessionContext) {
+ if (sessionContext == null) return;
+ log.info("Destroying SNMP device session for device {}", sessionContext.getDevice().getId());
+ sessionContext.close();
+ transportService.deregisterSession(sessionContext.getSessionInfo());
+ sessions.remove(sessionContext.getDeviceId());
+ log.trace("Deregistered and removed session");
+
+ DeviceProfileId deviceProfileId = sessionContext.getDeviceProfile().getId();
+ if (sessions.values().stream()
+ .map(DeviceAwareSessionContext::getDeviceProfile)
+ .noneMatch(deviceProfile -> deviceProfile.getId().equals(deviceProfileId))) {
+ log.trace("Removed values for device profile {} from configs and pdus caches", deviceProfileId);
+ profilesTransportConfigs.remove(deviceProfileId);
+ profilesPdus.remove(deviceProfileId);
+ }
}
- private List createPduList(SnmpProfileTransportConfiguration deviceProfileConfig) {
+ private void registerSessionMsgListener(DeviceSessionContext deviceSessionContext) {
+ transportService.process(DeviceTransportType.SNMP,
+ TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceSessionContext.getToken()).build(),
+ new TransportServiceCallback() {
+ @Override
+ public void onSuccess(ValidateDeviceCredentialsResponse msg) {
+ if (msg.hasDeviceInfo()) {
+ SessionInfoProto sessionInfo = SessionInfoCreator.create(
+ msg, SnmpTransportContext.this, UUID.randomUUID()
+ );
+
+ transportService.registerAsyncSession(sessionInfo, deviceSessionContext);
+ deviceSessionContext.setSessionInfo(sessionInfo);
+ deviceSessionContext.setDeviceInfo(msg.getDeviceInfo());
+ } else {
+ log.warn("[{}] Failed to process device auth", deviceSessionContext.getDeviceId());
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.warn("[{}] Failed to process device auth: {}", deviceSessionContext.getDeviceId(), e);
+ }
+ });
+ }
+
+ private List createPdus(SnmpProfileTransportConfiguration deviceProfileConfig) {
Map> varBindingPerMethod = new HashMap<>();
deviceProfileConfig.getKvMappings().forEach(mapping -> varBindingPerMethod
@@ -127,7 +225,106 @@ public class SnmpTransportContext extends TransportContext {
.collect(Collectors.toList());
}
- //TODO: Extract SNMP methods to enum
+ @EventListener(DeviceUpdatedEvent.class)
+ public void onDeviceUpdatedOrCreated(DeviceUpdatedEvent deviceUpdatedEvent) {
+ Device device = deviceUpdatedEvent.getDevice();
+ log.trace("Got creating or updating device event for device {}", device);
+ DeviceTransportType transportType = Optional.ofNullable(device.getDeviceData().getTransportConfiguration())
+ .map(DeviceTransportConfiguration::getType)
+ .orElse(null);
+ if (!allSnmpDevicesIds.contains(device.getId())) {
+ if (transportType != DeviceTransportType.SNMP) {
+ return;
+ }
+ allSnmpDevicesIds.add(device.getId());
+ if (balancingService.isManagedByCurrentTransport(device.getId().getId())) {
+ establishDeviceSession(device);
+ }
+ } else {
+ if (balancingService.isManagedByCurrentTransport(device.getId().getId())) {
+ DeviceSessionContext sessionContext = sessions.get(device.getId());
+ if (transportType == DeviceTransportType.SNMP) {
+ if (sessionContext != null) {
+ updateDeviceSession(sessionContext, device, deviceProfileCache.get(device.getDeviceProfileId()));
+ } else {
+ establishDeviceSession(device);
+ }
+ } else {
+ log.trace("Transport type was changed to {}", transportType);
+ destroyDeviceSession(sessionContext);
+ }
+ }
+ }
+ }
+
+ public void onDeviceDeleted(DeviceSessionContext sessionContext) {
+ destroyDeviceSession(sessionContext);
+ }
+
+ public void onDeviceProfileUpdated(DeviceProfile deviceProfile, DeviceSessionContext sessionContext) {
+ updateDeviceSession(sessionContext, sessionContext.getDevice(), deviceProfile);
+ }
+
+ public void onSnmpTransportListChanged() {
+ log.trace("SNMP transport list changed. Updating sessions");
+ List deleted = new LinkedList<>();
+ for (DeviceId deviceId : allSnmpDevicesIds) {
+ if (balancingService.isManagedByCurrentTransport(deviceId.getId())) {
+ if (!sessions.containsKey(deviceId)) {
+ Device device = protoEntityService.getDeviceById(deviceId);
+ if (device != null) {
+ log.info("SNMP device {} is now managed by current transport node", deviceId);
+ establishDeviceSession(device);
+ } else {
+ deleted.add(deviceId);
+ }
+ }
+ } else {
+ Optional.ofNullable(sessions.get(deviceId))
+ .ifPresent(sessionContext -> {
+ log.info("SNMP session for device {} is not managed by current transport node anymore", deviceId);
+ destroyDeviceSession(sessionContext);
+ });
+ }
+ }
+ log.trace("Removing deleted SNMP devices: {}", deleted);
+ allSnmpDevicesIds.removeAll(deleted);
+ }
+
+
+ public Collection getSessions() {
+ return sessions.values();
+ }
+
+ public Map> getProfilesPdus() {
+ return profilesPdus;
+ }
+
+ public Optional getAttributesMapping(DeviceProfileId deviceProfileId, OID responseOid) {
+ if (profilesTransportConfigs.containsKey(deviceProfileId)) {
+ return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getAttributes());
+ }
+ return Optional.empty();
+ }
+
+ public Optional getTelemetryMapping(DeviceProfileId deviceProfileId, OID responseOid) {
+ if (profilesTransportConfigs.containsKey(deviceProfileId)) {
+ return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getTelemetry());
+ }
+ return Optional.empty();
+ }
+
+ private Optional getMapping(OID responseOid, List mappings) {
+ return mappings.stream()
+ .filter(kvMapping -> new OID(kvMapping.getOid()).equals(responseOid))
+ //TODO: OID shouldn't be duplicated in the config, add backend and UI verification
+ .findFirst();
+ }
+
+ public ExecutorService getSnmpCallbackExecutor() {
+ return snmpTransportService.getSnmpCallbackExecutor();
+ }
+
private int getSnmpMethod(String configMethod) {
switch (configMethod) {
case "get":
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java
deleted file mode 100644
index 5d8e4d84b4..0000000000
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * Copyright © 2016-2021 The Thingsboard Authors
- *
- * 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.thingsboard.server.transport.snmp;
-
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-import org.snmp4j.Snmp;
-import org.snmp4j.transport.DefaultUdpTransportMapping;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
-import org.springframework.context.event.EventListener;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-import org.thingsboard.common.util.ThingsBoardThreadFactory;
-import org.thingsboard.server.common.data.DeviceInfo;
-import org.thingsboard.server.common.data.DeviceProfile;
-import org.thingsboard.server.common.data.DeviceTransportType;
-import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.page.PageDataIterable;
-import org.thingsboard.server.dao.device.DeviceProfileService;
-import org.thingsboard.server.dao.device.DeviceService;
-import org.thingsboard.server.dao.tenant.TenantService;
-import org.thingsboard.server.transport.snmp.session.DeviceSessionCtx;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import java.io.IOException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-@Service("SnmpTransportService")
-@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
-@Slf4j
-public class SnmpTransportService {
-
- private static final int ENTITY_PACK_LIMIT = 1024;
-
- @Autowired
- private SnmpTransportContext snmpTransportContext;
-
- @Autowired
- DeviceProfileService deviceProfileService;
-
- @Autowired
- TenantService tenantService;
-
- @Autowired
- DeviceService deviceService;
-
- @Getter
- private ExecutorService snmpCallbackExecutor;
- private Snmp snmp;
- private ScheduledExecutorService pollingExecutor;
-
- @PostConstruct
- public void init() {
- log.info("Starting SNMP transport...");
- pollingExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("snmp-polling"));
- //TODO: Set parallelism value in the config
- snmpCallbackExecutor = Executors.newWorkStealingPool(20);
- initializeSnmp();
- log.info("SNMP transport started!");
- }
-
- @PreDestroy
- public void shutdown() {
- log.info("Stopping SNMP transport!");
- if (pollingExecutor != null) {
- pollingExecutor.shutdownNow();
- }
- if (snmpCallbackExecutor != null) {
- snmpCallbackExecutor.shutdownNow();
- }
- if (snmp != null) {
- try {
- snmp.close();
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- }
- }
- log.info("SNMP transport stopped!");
- }
-
- @EventListener(ApplicationReadyEvent.class)
- @Order(value = 2)
- public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
- log.info("Received application ready event. Starting SNMP polling.");
- initSessionCtxList();
- startPolling();
- }
-
- private void initializeSnmp() {
- try {
- this.snmp = new Snmp(new DefaultUdpTransportMapping());
- this.snmp.listen();
- } catch (IOException e) {
- //TODO: what should be done if transport wasn't initialized?
- log.error(e.getMessage(), e);
- }
- }
-
- private void initSessionCtxList() {
- //TODO: This approach works for monolith, in cluster the same data will be fetched by each node.
- for (Tenant tenant : new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT)) {
- TenantId tenantId = tenant.getTenantId();
- for (DeviceProfile deviceProfile : new PageDataIterable<>(pageLink -> deviceProfileService.findDeviceProfiles(tenantId, pageLink), ENTITY_PACK_LIMIT)) {
- if (DeviceTransportType.SNMP.equals(deviceProfile.getTransportType())) {
- snmpTransportContext.getProfileTransportConfig().put(deviceProfile.getId(),
- (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration());
- initDeviceSessions(deviceProfile);
- }
- }
- }
- snmpTransportContext.initPduListPerProfile();
- }
-
- private void initDeviceSessions(DeviceProfile deviceProfile) {
- for (DeviceInfo deviceInfo : new PageDataIterable<>(pageLink -> deviceService.findDeviceInfosByTenantIdAndDeviceProfileId(deviceProfile.getTenantId(), deviceProfile.getId(), pageLink), ENTITY_PACK_LIMIT)) {
- snmpTransportContext.updateDeviceSessionCtx(deviceInfo, deviceProfile, snmp);
- }
- }
-
- private void startPolling() {
- //TODO: Get poll period from configuration;
- int poolPeriodSeconds = 1;
- pollingExecutor.scheduleAtFixedRate(() -> snmpTransportContext.getDeviceSessions().values().forEach(DeviceSessionCtx::executeSnmpRequest),
- 0, poolPeriodSeconds, TimeUnit.SECONDS);
- }
-}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/ServiceListChangedEventListener.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/ServiceListChangedEventListener.java
new file mode 100644
index 0000000000..90b82abf96
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/ServiceListChangedEventListener.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp.event;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
+import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
+import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
+
+@TbSnmpTransportComponent
+@Component
+@RequiredArgsConstructor
+public class ServiceListChangedEventListener extends TbApplicationEventListener {
+ private final SnmpTransportBalancingService snmpTransportBalancingService;
+
+ @Override
+ protected void onTbApplicationEvent(ServiceListChangedEvent event) {
+ snmpTransportBalancingService.onServiceListChanged(event);
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEvent.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEvent.java
new file mode 100644
index 0000000000..f7a4259b91
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEvent.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp.event;
+
+import org.thingsboard.server.queue.discovery.event.TbApplicationEvent;
+
+public class SnmpTransportListChangedEvent extends TbApplicationEvent {
+ public SnmpTransportListChangedEvent() {
+ super(new Object());
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEventListener.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEventListener.java
new file mode 100644
index 0000000000..1387461037
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEventListener.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp.event;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
+import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+import org.thingsboard.server.transport.snmp.SnmpTransportContext;
+
+@TbSnmpTransportComponent
+@Component
+@RequiredArgsConstructor
+public class SnmpTransportListChangedEventListener extends TbApplicationEventListener {
+ private final SnmpTransportContext snmpTransportContext;
+
+ @Override
+ protected void onTbApplicationEvent(SnmpTransportListChangedEvent event) {
+ snmpTransportContext.onSnmpTransportListChanged();
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/ProtoTransportEntityService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/ProtoTransportEntityService.java
new file mode 100644
index 0000000000..1f639daa2a
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/ProtoTransportEntityService.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.device.data.DeviceData;
+import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.DeviceProfileId;
+import org.thingsboard.server.common.data.security.DeviceCredentials;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+@TbSnmpTransportComponent
+@Service
+@RequiredArgsConstructor
+public class ProtoTransportEntityService {
+ private final TransportService transportService;
+ private final DataDecodingEncodingService dataDecodingEncodingService;
+
+ public Device getDeviceById(DeviceId id) {
+ TransportProtos.GetDeviceResponseMsg deviceProto = transportService.getDevice(TransportProtos.GetDeviceRequestMsg.newBuilder()
+ .setDeviceIdMSB(id.getId().getMostSignificantBits())
+ .setDeviceIdLSB(id.getId().getLeastSignificantBits())
+ .build());
+
+ if (deviceProto == null) {
+ return null;
+ }
+
+ DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(
+ deviceProto.getDeviceProfileIdMSB(), deviceProto.getDeviceProfileIdLSB())
+ );
+
+ Device device = new Device();
+ device.setId(id);
+ device.setDeviceProfileId(deviceProfileId);
+
+ DeviceTransportConfiguration deviceTransportConfiguration = (DeviceTransportConfiguration) dataDecodingEncodingService.decode(
+ deviceProto.getDeviceTransportConfiguration().toByteArray()
+ ).orElseThrow(() -> new IllegalStateException("Can't find device transport configuration"));
+
+ DeviceData deviceData = new DeviceData();
+ deviceData.setTransportConfiguration(deviceTransportConfiguration);
+ device.setDeviceData(deviceData);
+
+ return device;
+ }
+
+ public DeviceCredentials getDeviceCredentialsByDeviceId(DeviceId deviceId) {
+ TransportProtos.GetDeviceCredentialsResponseMsg deviceCredentialsResponse = transportService.getDeviceCredentials(
+ TransportProtos.GetDeviceCredentialsRequestMsg.newBuilder()
+ .setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
+ .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
+ .build()
+ );
+
+ return (DeviceCredentials) dataDecodingEncodingService.decode(deviceCredentialsResponse.getDeviceCredentialsData().toByteArray())
+ .orElseThrow(() -> new IllegalArgumentException("Device credentials not found"));
+ }
+
+ public List getAllSnmpDevicesIds() {
+ TransportProtos.GetSnmpDevicesResponseMsg devicesIdsResponse = transportService.getSnmpDevicesIds(
+ TransportProtos.GetSnmpDevicesRequestMsg.getDefaultInstance()
+ );
+
+ return devicesIdsResponse.getIdsList().stream()
+ .map(UUID::fromString)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportBalancingService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportBalancingService.java
new file mode 100644
index 0000000000..3564ab319e
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportBalancingService.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp.service;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
+import org.thingsboard.server.queue.discovery.PartitionService;
+import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
+import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+import org.thingsboard.server.transport.snmp.event.SnmpTransportListChangedEvent;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@TbSnmpTransportComponent
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class SnmpTransportBalancingService {
+ private final PartitionService partitionService;
+ private final ApplicationEventPublisher eventPublisher;
+ private final SnmpTransportService snmpTransportService;
+
+ private int snmpTransportsCount = 1;
+ private Integer currentTransportPartitionIndex = 0;
+
+ public void onServiceListChanged(ServiceListChangedEvent event) {
+ log.trace("Got service list changed event: {}", event);
+ recalculatePartitions(event.getOtherServices(), event.getCurrentService());
+ }
+
+ public boolean isManagedByCurrentTransport(UUID entityId) {
+ boolean isManaged = resolvePartitionIndexForEntity(entityId) == currentTransportPartitionIndex;
+ if (!isManaged) {
+ log.trace("Entity {} is not managed by current SNMP transport node", entityId);
+ }
+ return isManaged;
+ }
+
+ private int resolvePartitionIndexForEntity(UUID entityId) {
+ return partitionService.resolvePartitionIndex(entityId, snmpTransportsCount);
+ }
+
+ private void recalculatePartitions(List otherServices, ServiceInfo currentService) {
+ log.info("Recalculating partitions for SNMP transports");
+ List snmpTransports = Stream.concat(otherServices.stream(), Stream.of(currentService))
+ .filter(service -> service.getTransportsList().contains(snmpTransportService.getName()))
+ .sorted(Comparator.comparing(ServiceInfo::getServiceId))
+ .collect(Collectors.toList());
+ log.trace("Found SNMP transports: {}", snmpTransports);
+
+ int previousCurrentTransportPartitionIndex = currentTransportPartitionIndex;
+ int previousSnmpTransportsCount = snmpTransportsCount;
+
+ if (!snmpTransports.isEmpty()) {
+ for (int i = 0; i < snmpTransports.size(); i++) {
+ if (snmpTransports.get(i).equals(currentService)) {
+ currentTransportPartitionIndex = i;
+ break;
+ }
+ }
+ snmpTransportsCount = snmpTransports.size();
+ }
+
+ if (snmpTransportsCount != previousSnmpTransportsCount || currentTransportPartitionIndex != previousCurrentTransportPartitionIndex) {
+ log.info("SNMP transports partitions have changed: transports count = {}, current transport partition index = {}", snmpTransportsCount, currentTransportPartitionIndex);
+ eventPublisher.publishEvent(new SnmpTransportListChangedEvent());
+ } else {
+ log.info("SNMP transports partitions have not changed");
+ }
+ }
+
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/SnmpSessionListener.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
similarity index 60%
rename from common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/SnmpSessionListener.java
rename to common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index 35aa1a9347..22e60c98ee 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/SnmpSessionListener.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.transport.snmp.session;
+package org.thingsboard.server.transport.snmp.service;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -25,9 +25,16 @@ import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
-import org.snmp4j.event.ResponseListener;
import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.transport.DefaultUdpTransportMapping;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.transport.TransportContext;
@@ -39,27 +46,116 @@ import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
+import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
+import javax.annotation.PreDestroy;
+import java.io.IOException;
import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
+@TbSnmpTransportComponent
+@Service
@Slf4j
-@AllArgsConstructor
-public class SnmpSessionListener implements ResponseListener {
-
- @Getter
+public class SnmpTransportService implements TbTransportService {
private final SnmpTransportContext snmpTransportContext;
@Getter
- private final String token;
+ private ExecutorService snmpCallbackExecutor;
+ @Getter
+ private Snmp snmp;
+ private ScheduledExecutorService pollingExecutor;
- @Override
- public void onResponse(ResponseEvent event) {
- ((Snmp) event.getSource()).cancel(event.getRequest(), this);
- snmpTransportContext.getSnmpCallbackExecutor().submit(() -> processSnmpResponse(event));
+ public SnmpTransportService(@Lazy SnmpTransportContext snmpTransportContext) {
+ this.snmpTransportContext = snmpTransportContext;
}
- private void processSnmpResponse(ResponseEvent event) {
+ // @PostConstruct
+ private void init() {
+ log.info("Starting SNMP transport...");
+ pollingExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("snmp-polling"));
+ //TODO: Set parallelism value in the config
+ snmpCallbackExecutor = Executors.newWorkStealingPool(20);
+
+ initializeSnmp();
+
+ log.info("SNMP transport started!");
+ }
+
+ @PreDestroy
+ public void shutdown() {
+ log.info("Stopping SNMP transport!");
+ if (pollingExecutor != null) {
+ pollingExecutor.shutdownNow();
+ }
+ if (snmpCallbackExecutor != null) {
+ snmpCallbackExecutor.shutdownNow();
+ }
+ if (snmp != null) {
+ try {
+ snmp.close();
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ log.info("SNMP transport stopped!");
+ }
+
+ @EventListener(ApplicationReadyEvent.class)
+ @Order(value = 10)
+ public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
+ log.info("Received application ready event. Starting SNMP polling.");
+// startPolling();
+ }
+
+ private void initializeSnmp() {
+ try {
+ this.snmp = new Snmp(new DefaultUdpTransportMapping());
+ this.snmp.listen();
+ } catch (IOException e) {
+ //TODO: what should be done if transport wasn't initialized?
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ private void startPolling() {
+ //TODO: Get poll period from configuration;
+ int pollPeriodSeconds = 1;
+
+ pollingExecutor.scheduleWithFixedDelay(() -> {
+ snmpTransportContext.getSessions().forEach(this::executeSnmpRequest);
+ }, 0, pollPeriodSeconds, TimeUnit.SECONDS);
+ }
+
+ private void executeSnmpRequest(DeviceSessionContext sessionContext) {
+ long timeNow = System.currentTimeMillis();
+ long nextRequestExecutionTime = sessionContext.getPreviousRequestExecutedAt() + sessionContext.getProfileTransportConfiguration().getPollPeriodMs();
+
+ if (nextRequestExecutionTime < timeNow) {
+ sessionContext.setPreviousRequestExecutedAt(timeNow);
+
+ DeviceProfileId deviceProfileId = sessionContext.getDeviceProfile().getId();
+ snmpTransportContext.getProfilesPdus().get(deviceProfileId).forEach(pdu -> {
+ try {
+ log.debug("[{}] Sending SNMP message...", pdu.getRequestID());
+ snmp.send(pdu, sessionContext.getTarget(), deviceProfileId, sessionContext);
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ });
+ }
+ }
+
+ public void onNewDeviceResponse(ResponseEvent responseEvent, DeviceSessionContext sessionContext) {
+ ((Snmp) responseEvent.getSource()).cancel(responseEvent.getRequest(), sessionContext);
+ snmpTransportContext.getSnmpCallbackExecutor().submit(() -> processSnmpResponse(responseEvent, sessionContext));
+ }
+
+ private void processSnmpResponse(ResponseEvent event, DeviceSessionContext sessionContext) {
PDU response = event.getResponse();
if (event.getError() != null) {
log.warn("Response error: {}", event.getError().getMessage(), event.getError());
@@ -72,8 +168,8 @@ public class SnmpSessionListener implements ResponseListener {
TransportService transportService = snmpTransportContext.getTransportService();
for (int i = 0; i < response.size(); i++) {
VariableBinding vb = response.get(i);
- snmpTransportContext.findAttributesMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
- TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(),
+ snmpTransportContext.getAttributesMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
+ TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(sessionContext.getToken()).build(),
new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
try {
transportService.process(sessionInfo,
@@ -84,8 +180,8 @@ public class SnmpSessionListener implements ResponseListener {
log.warn("Failed to process SNMP response: {}", e.getMessage(), e);
}
})));
- snmpTransportContext.findTelemetryMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
- TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(),
+ snmpTransportContext.getTelemetryMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
+ TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(sessionContext.getToken()).build(),
new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
try {
transportService.process(sessionInfo,
@@ -103,6 +199,14 @@ public class SnmpSessionListener implements ResponseListener {
}
}
+ private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
+ snmpTransportContext.getTransportService().process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
+ .setAttributeSubscription(false)
+ .setRpcSubscription(false)
+ .setLastActivityTime(System.currentTimeMillis())
+ .build(), TransportServiceCallback.EMPTY);
+ }
+
private TransportProtos.PostAttributeMsg convertToPostAttributes(String keyName, DataType dataType, String payload) throws AdaptorException {
try {
return JsonConverter.convertToAttributesProto(getKvJson(keyName, dataType, payload));
@@ -143,14 +247,6 @@ public class SnmpSessionListener implements ResponseListener {
return new JsonParser().parse(result.toString());
}
- private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
- snmpTransportContext.getTransportService().process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
- .setAttributeSubscription(false)
- .setRpcSubscription(false)
- .setLastActivityTime(System.currentTimeMillis())
- .build(), TransportServiceCallback.EMPTY);
- }
-
@AllArgsConstructor
private static class DeviceAuthCallback implements TransportServiceCallback {
private final TransportContext transportContext;
@@ -170,4 +266,9 @@ public class SnmpSessionListener implements ResponseListener {
log.warn("Failed to process device auth", e);
}
}
+
+ @Override
+ public String getName() {
+ return "SNMP";
+ }
}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
new file mode 100644
index 0000000000..ed87bbc726
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
@@ -0,0 +1,166 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp.session;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.snmp4j.CommunityTarget;
+import org.snmp4j.Target;
+import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.event.ResponseListener;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.GenericAddress;
+import org.snmp4j.smi.OctetString;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
+import org.thingsboard.server.transport.snmp.SnmpTransportContext;
+import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
+
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Slf4j
+public class DeviceSessionContext extends DeviceAwareSessionContext implements SessionMsgListener, ResponseListener {
+ @Getter
+ private Target target;
+ private final String token;
+ @Getter
+ @Setter
+ private SnmpProfileTransportConfiguration profileTransportConfiguration;
+ @Getter
+ @Setter
+ private SnmpDeviceTransportConfiguration deviceTransportConfiguration;
+ @Getter
+ private final Device device;
+
+ private final SnmpTransportContext snmpTransportContext;
+ private final SnmpTransportService snmpTransportService;
+
+ @Getter
+ @Setter
+ private long previousRequestExecutedAt = 0;
+ private final AtomicInteger msgIdSeq = new AtomicInteger(0);
+ private boolean isActive = true;
+
+ public DeviceSessionContext(Device device, DeviceProfile deviceProfile,
+ String token, SnmpDeviceTransportConfiguration deviceTransportConfiguration,
+ SnmpTransportContext snmpTransportContext, SnmpTransportService snmpTransportService) {
+ super(UUID.randomUUID());
+ super.setDeviceId(device.getId());
+ super.setDeviceProfile(deviceProfile);
+ this.device = device;
+
+ this.token = token;
+ this.snmpTransportContext = snmpTransportContext;
+ this.snmpTransportService = snmpTransportService;
+
+ this.profileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ this.deviceTransportConfiguration = deviceTransportConfiguration;
+
+ initTarget(this.profileTransportConfiguration, this.deviceTransportConfiguration);
+ }
+
+ @Override
+ public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto newSessionInfo, DeviceProfile deviceProfile) {
+ super.onDeviceProfileUpdate(newSessionInfo, deviceProfile);
+ if (isActive) {
+ snmpTransportContext.onDeviceProfileUpdated(deviceProfile, this);
+ }
+ }
+
+ @Override
+ public void onDeviceDeleted(DeviceId deviceId) {
+ snmpTransportContext.onDeviceDeleted(this);
+ }
+
+ @Override
+ public void onResponse(ResponseEvent event) {
+ if (isActive) {
+ snmpTransportService.onNewDeviceResponse(event, this);
+ }
+ }
+
+ public void initTarget(SnmpProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
+ log.trace("Initializing target for SNMP session of device {}", device);
+ CommunityTarget communityTarget = new CommunityTarget();
+ communityTarget.setAddress(GenericAddress.parse(GenericAddress.TYPE_UDP + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
+ communityTarget.setVersion(getSnmpVersion(deviceTransportConfig.getProtocolVersion()));
+ communityTarget.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
+ communityTarget.setTimeout(profileTransportConfig.getTimeoutMs());
+ communityTarget.setRetries(profileTransportConfig.getRetries());
+ this.target = communityTarget;
+ log.info("SNMP target initialized: {}", this.target);
+ }
+
+ public void close() {
+ isActive = false;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ //TODO: replace with enum, wtih preliminary discussion of type version in config (string or integer)
+ private int getSnmpVersion(String configSnmpVersion) {
+ switch (configSnmpVersion) {
+ case ("v1"):
+ return SnmpConstants.version1;
+ case ("v2c"):
+ return SnmpConstants.version2c;
+ case ("v3"):
+ return SnmpConstants.version3;
+ default:
+ return -1;
+ }
+ }
+
+ @Override
+ public int nextMsgId() {
+ return msgIdSeq.incrementAndGet();
+ }
+
+ @Override
+ public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
+ }
+
+ @Override
+ public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
+ }
+
+ @Override
+ public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
+ }
+
+ @Override
+ public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
+ }
+
+ @Override
+ public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) {
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java
deleted file mode 100644
index 424b964387..0000000000
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/**
- * Copyright © 2016-2021 The Thingsboard Authors
- *
- * 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.thingsboard.server.transport.snmp.session;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
-import org.snmp4j.CommunityTarget;
-import org.snmp4j.Snmp;
-import org.snmp4j.Target;
-import org.snmp4j.mp.SnmpConstants;
-import org.snmp4j.smi.GenericAddress;
-import org.snmp4j.smi.OctetString;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.DeviceProfile;
-import org.thingsboard.server.common.data.DeviceTransportType;
-import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
-import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
-import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.transport.SessionMsgListener;
-import org.thingsboard.server.common.transport.TransportServiceCallback;
-import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
-import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
-import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
-import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
-import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
-import org.thingsboard.server.transport.snmp.SnmpTransportContext;
-
-import java.io.IOException;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-@Slf4j
-public class DeviceSessionCtx extends DeviceAwareSessionContext implements SessionMsgListener {
- private final AtomicInteger msgIdSeq = new AtomicInteger(0);
-
- @Getter
- @Setter
- private SnmpDeviceTransportConfiguration deviceTransportConfig;
- @Getter
- @Setter
- private SnmpSessionListener snmpSessionListener;
- @Getter
- @Setter
- private Target target;
- @Getter
- @Setter
- private volatile TransportProtos.SessionInfoProto sessionInfo;
-
- private Snmp snmp;
- private SnmpProfileTransportConfiguration snmpProfileTransportConfiguration;
- private long previousRequestExecutedAt = 0;
-
- public DeviceSessionCtx(SnmpTransportContext transportContext, String token, SnmpDeviceTransportConfiguration deviceTransportConfig,
- Snmp snmp, DeviceId deviceId, DeviceProfile deviceProfile) {
- super(UUID.randomUUID());
- this.snmpSessionListener = new SnmpSessionListener(transportContext, token);
- super.setDeviceId(deviceId);
- super.setDeviceProfile(deviceProfile);
- //TODO: What should be done if snmp null?
- if (snmp != null) {
- this.snmp = snmp;
- }
- this.snmpProfileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
- initTarget(this.snmpProfileTransportConfiguration, deviceTransportConfig);
- }
-
- @Override
- public int nextMsgId() {
- return msgIdSeq.incrementAndGet();
- }
-
- @Override
- public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
- }
-
- @Override
- public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
- }
-
- @Override
- public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
- }
-
- @Override
- public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
- }
-
- @Override
- public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) {
- }
-
- @Override
- public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto newSessionInfo, DeviceProfile deviceProfile) {
- super.onDeviceProfileUpdate(sessionInfo, deviceProfile);
- if (DeviceTransportType.SNMP.equals(deviceProfile.getTransportType())) {
- snmpProfileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
- snmpSessionListener.getSnmpTransportContext().getProfileTransportConfig().put(
- deviceProfile.getId(),
- snmpProfileTransportConfiguration);
- snmpSessionListener.getSnmpTransportContext().updatePduListPerProfile(deviceProfile.getId(), snmpProfileTransportConfiguration);
- } else {
- //TODO: should the context be removed from the map?
- }
- }
-
- @Override
- public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional deviceProfileOpt) {
- super.onDeviceUpdate(sessionInfo, device, deviceProfileOpt);
- if (super.getDeviceProfile() != null && DeviceTransportType.SNMP.equals(super.getDeviceProfile().getTransportType())) {
- snmpSessionListener.getSnmpTransportContext().updateDeviceSessionCtx(device, deviceProfile, null);
- SnmpProfileTransportConfiguration profileTransportConfig = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
- SnmpDeviceTransportConfiguration deviceTransportConfig = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
- initTarget(profileTransportConfig, deviceTransportConfig);
- } else {
- //TODO: should the context be removed from the map?
- }
- }
-
- public void createSessionInfo(Consumer registerSession) {
- getSnmpSessionListener().getSnmpTransportContext().getTransportService().process(DeviceTransportType.SNMP,
- TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(getSnmpSessionListener().getToken()).build(),
- new TransportServiceCallback() {
- @Override
- public void onSuccess(ValidateDeviceCredentialsResponse msg) {
- if (msg.hasDeviceInfo()) {
- sessionInfo = SessionInfoCreator.create(msg, getSnmpSessionListener().getSnmpTransportContext(), UUID.randomUUID());
- registerSession.accept(sessionInfo);
- setDeviceInfo(msg.getDeviceInfo());
- } else {
- log.warn("[{}] Failed to process device auth", getDeviceId());
- }
- }
-
- @Override
- public void onError(Throwable e) {
- log.warn("[{}] Failed to process device auth", getDeviceId(), e);
- }
- });
- }
-
- public void executeSnmpRequest() {
- long timeNow = System.currentTimeMillis();
- long nextRequestExecutionTime = previousRequestExecutedAt + snmpProfileTransportConfiguration.getPoolPeriodMs();
- if (nextRequestExecutionTime < timeNow) {
- previousRequestExecutedAt = timeNow;
-
- snmpSessionListener.getSnmpTransportContext().getPdusPerProfile().get(deviceProfile.getId()).forEach(pdu -> {
- try {
- log.debug("[{}] Sending SNMP message...", pdu.getRequestID());
- snmp.send(pdu,
- target,
- deviceProfile.getId(),
- snmpSessionListener);
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- }
- });
- }
- }
-
- private void initTarget(SnmpProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
- this.deviceTransportConfig = deviceTransportConfig;
- CommunityTarget communityTarget = new CommunityTarget();
- communityTarget.setAddress(GenericAddress.parse(GenericAddress.TYPE_UDP + ":" + this.deviceTransportConfig.getAddress() + "/" + this.deviceTransportConfig.getPort()));
- communityTarget.setVersion(getSnmpVersion(this.deviceTransportConfig.getProtocolVersion()));
- communityTarget.setCommunity(new OctetString(this.deviceTransportConfig.getCommunity()));
- communityTarget.setTimeout(profileTransportConfig.getTimeoutMs());
- communityTarget.setRetries(profileTransportConfig.getRetries());
- this.target = communityTarget;
- log.info("SNMP target initialized: {}", this.target);
- }
-
- //TODO: replace with enum, wtih preliminary discussion of type version in config (string or integer)
- private int getSnmpVersion(String configSnmpVersion) {
- switch (configSnmpVersion) {
- case ("v1"):
- return SnmpConstants.version1;
- case ("v2c"):
- return SnmpConstants.version2c;
- case ("v3"):
- return SnmpConstants.version3;
- default:
- return -1;
- }
- }
-}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/DeviceUpdatedEvent.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/DeviceUpdatedEvent.java
new file mode 100644
index 0000000000..ad4819fbb1
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/DeviceUpdatedEvent.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.transport;
+
+import lombok.Getter;
+import org.thingsboard.server.common.data.Device;
+
+@Getter
+public class DeviceUpdatedEvent {
+ private final Device device;
+
+ public DeviceUpdatedEvent(Device device) {
+ this.device = device;
+ }
+}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java
index 8209fe2531..9aff642708 100644
--- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.common.transport;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
@@ -49,4 +50,7 @@ public interface SessionMsgListener {
default void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional deviceProfileOpt) {
}
+
+ default void onDeviceDeleted(DeviceId deviceId) {
+ }
}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
index 75f20cc825..eabcb1e8e6 100644
--- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
@@ -21,6 +21,7 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
import org.thingsboard.server.queue.util.TbTransportComponent;
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java
index ab728af00d..24469dc599 100644
--- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java
@@ -22,11 +22,17 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
import org.thingsboard.server.common.transport.service.SessionMetaData;
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetResourcesRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetResourcesResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.LwM2MRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.LwM2MResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
@@ -55,6 +61,12 @@ public interface TransportService {
GetResourcesResponseMsg getResources(GetResourcesRequestMsg msg);
+ GetSnmpDevicesResponseMsg getSnmpDevicesIds(GetSnmpDevicesRequestMsg requestMsg);
+
+ GetDeviceResponseMsg getDevice(GetDeviceRequestMsg requestMsg);
+
+ GetDeviceCredentialsResponseMsg getDeviceCredentials(GetDeviceCredentialsRequestMsg requestMsg);
+
void process(DeviceTransportType transportType, ValidateDeviceTokenRequestMsg msg,
TransportServiceCallback callback);
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportDeviceProfileCache.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportDeviceProfileCache.java
index 5c36e9b3e0..ad289a7e16 100644
--- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportDeviceProfileCache.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportDeviceProfileCache.java
@@ -112,8 +112,8 @@ public class DefaultTransportDeviceProfileCache implements TransportDeviceProfil
profile = profileOpt.get();
this.put(profile);
} else {
- log.warn("[{}] Can't device profile: {}", id, entityProfileMsg.getData());
- throw new RuntimeException("Can't device profile!");
+ log.warn("[{}] Can't find device profile: {}", id, entityProfileMsg.getData());
+ throw new RuntimeException("Can't find device profile!");
}
} finally {
deviceProfileFetchLock.unlock();
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java
index b273ee48cf..903de94020 100644
--- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java
@@ -23,6 +23,7 @@ import com.google.gson.JsonObject;
import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
@@ -47,6 +48,7 @@ import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.common.stats.MessagesStats;
import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.common.stats.StatsType;
+import org.thingsboard.server.common.transport.DeviceUpdatedEvent;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.TransportDeviceProfileCache;
import org.thingsboard.server.common.transport.TransportService;
@@ -61,7 +63,6 @@ import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@@ -131,6 +132,7 @@ public class DefaultTransportService implements TransportService {
private final TransportRateLimitService rateLimitService;
private final DataDecodingEncodingService dataDecodingEncodingService;
private final SchedulerComponent scheduler;
+ private final ApplicationEventPublisher eventPublisher;
protected TbQueueRequestTemplate, TbProtoQueueMsg> transportApiRequestTemplate;
protected TbQueueProducer> ruleEngineMsgProducer;
@@ -157,7 +159,8 @@ public class DefaultTransportService implements TransportService {
TransportDeviceProfileCache deviceProfileCache,
TransportTenantProfileCache tenantProfileCache,
TbApiUsageClient apiUsageClient, TransportRateLimitService rateLimitService,
- DataDecodingEncodingService dataDecodingEncodingService, SchedulerComponent scheduler) {
+ DataDecodingEncodingService dataDecodingEncodingService, SchedulerComponent scheduler,
+ ApplicationEventPublisher eventPublisher) {
this.serviceInfoProvider = serviceInfoProvider;
this.queueProvider = queueProvider;
this.producerProvider = producerProvider;
@@ -169,6 +172,7 @@ public class DefaultTransportService implements TransportService {
this.rateLimitService = rateLimitService;
this.dataDecodingEncodingService = dataDecodingEncodingService;
this.scheduler = scheduler;
+ this.eventPublisher = eventPublisher;
}
@PostConstruct
@@ -266,6 +270,58 @@ public class DefaultTransportService implements TransportService {
}
}
+ @Override
+ public TransportProtos.GetSnmpDevicesResponseMsg getSnmpDevicesIds(TransportProtos.GetSnmpDevicesRequestMsg requestMsg) {
+ TbProtoQueueMsg protoMsg = new TbProtoQueueMsg<>(
+ UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder()
+ .setSnmpDevicesRequestMsg(requestMsg)
+ .build()
+ );
+
+ try {
+ TbProtoQueueMsg response = transportApiRequestTemplate.send(protoMsg).get();
+ return response.getValue().getSnmpDevicesResponseMsg();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public TransportProtos.GetDeviceResponseMsg getDevice(TransportProtos.GetDeviceRequestMsg requestMsg) {
+ TbProtoQueueMsg protoMsg = new TbProtoQueueMsg<>(
+ UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder()
+ .setDeviceRequestMsg(requestMsg)
+ .build()
+ );
+
+ try {
+ TransportApiResponseMsg response = transportApiRequestTemplate.send(protoMsg).get().getValue();
+ if (response.hasDeviceResponseMsg()) {
+ return response.getDeviceResponseMsg();
+ } else {
+ return null;
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public TransportProtos.GetDeviceCredentialsResponseMsg getDeviceCredentials(TransportProtos.GetDeviceCredentialsRequestMsg requestMsg) {
+ TbProtoQueueMsg protoMsg = new TbProtoQueueMsg<>(
+ UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder()
+ .setDeviceCredentialsRequestMsg(requestMsg)
+ .build()
+ );
+
+ try {
+ TbProtoQueueMsg response = transportApiRequestTemplate.send(protoMsg).get();
+ return response.getValue().getDeviceCredentialsResponseMsg();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@Override
public void process(DeviceTransportType transportType, TransportProtos.ValidateDeviceTokenRequestMsg msg,
TransportServiceCallback callback) {
@@ -685,7 +741,10 @@ public class DefaultTransportService implements TransportService {
}
} else if (EntityType.DEVICE.equals(entityType)) {
Optional deviceOpt = dataDecodingEncodingService.decode(msg.getData().toByteArray());
- deviceOpt.ifPresent(this::onDeviceUpdate);
+ deviceOpt.ifPresent(device -> {
+ onDeviceUpdate(device);
+ eventPublisher.publishEvent(new DeviceUpdatedEvent(device));
+ });
}
} else if (toSessionMsg.hasEntityDeleteMsg()) {
TransportProtos.EntityDeleteMsg msg = toSessionMsg.getEntityDeleteMsg();
@@ -699,6 +758,7 @@ public class DefaultTransportService implements TransportService {
rateLimitService.remove(new TenantId(entityUuid));
} else if (EntityType.DEVICE.equals(entityType)) {
rateLimitService.remove(new DeviceId(entityUuid));
+ onDeviceDeleted(new DeviceId(entityUuid));
}
} else if (toSessionMsg.hasResourceUpdateMsg()) {
//TODO: update resource cache
@@ -764,6 +824,17 @@ public class DefaultTransportService implements TransportService {
});
}
+ private void onDeviceDeleted(DeviceId deviceId) {
+ sessions.forEach((id, md) -> {
+ DeviceId sessionDeviceId = new DeviceId(new UUID(md.getSessionInfo().getDeviceIdMSB(), md.getSessionInfo().getDeviceIdLSB()));
+ if (sessionDeviceId.equals(deviceId)) {
+ transportCallbackExecutor.submit(() -> {
+ md.getListener().onDeviceDeleted(deviceId);
+ });
+ }
+ });
+ }
+
protected UUID toSessionId(TransportProtos.SessionInfoProto sessionInfo) {
return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
index 7f8d512c70..547725b635 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao.device;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
+import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@@ -215,4 +216,7 @@ public interface DeviceDao extends Dao, TenantEntityDao {
* @return the list of device objects
*/
PageData findDevicesByTenantIdAndProfileId(UUID tenantId, UUID profileId, PageLink pageLink);
+
+ List findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType);
+
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
index f933cbd543..64b44db686 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
@@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
@@ -80,6 +81,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
@@ -554,6 +556,11 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
return savedDevice;
}
+ @Override
+ public List findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType) {
+ return deviceDao.findDevicesIdsByDeviceProfileTransportType(transportType);
+ }
+
private DataValidator deviceValidator =
new DataValidator() {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
index c39e481638..708e0f38e3 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
@@ -20,6 +20,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
+import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.dao.model.sql.DeviceEntity;
import org.thingsboard.server.dao.model.sql.DeviceInfoEntity;
@@ -170,4 +171,9 @@ public interface DeviceRepository extends PagingAndSortingRepository findIdsByDeviceProfileTransportType(@Param("transportType") DeviceTransportType transportType);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
index 18979eba91..cb91ffac4f 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
@@ -22,6 +22,7 @@ import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
+import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.TenantId;
@@ -114,6 +115,11 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao
DaoUtil.toPageable(pageLink)));
}
+ @Override
+ public List findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType) {
+ return deviceRepository.findIdsByDeviceProfileTransportType(transportType);
+ }
+
@Override
public PageData findDeviceInfosByTenantIdAndCustomerId(UUID tenantId, UUID customerId, PageLink pageLink) {
return DaoUtil.toPageData(
diff --git a/docker/tb-transports/snmp/conf/logback.xml b/docker/tb-transports/snmp/conf/logback.xml
new file mode 100644
index 0000000000..e581a97c42
--- /dev/null
+++ b/docker/tb-transports/snmp/conf/logback.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+ /var/log/tb-snmp-transport/${TB_SERVICE_ID}/tb-snmp-transport.log
+
+ /var/log/tb-snmp-transport/${TB_SERVICE_ID}/tb-snmp-transport.%d{yyyy-MM-dd}.%i.log
+ 100MB
+ 30
+ 3GB
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docker/tb-transports/snmp/conf/tb-snmp-transport.conf b/docker/tb-transports/snmp/conf/tb-snmp-transport.conf
new file mode 100644
index 0000000000..2aceae797c
--- /dev/null
+++ b/docker/tb-transports/snmp/conf/tb-snmp-transport.conf
@@ -0,0 +1,23 @@
+#
+# Copyright © 2016-2021 The Thingsboard Authors
+#
+# 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.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,heap*,age*,safepoint=debug:file=/var/log/tb-snmp-transport/${TB_SERVICE_ID}-gc.log:time,uptime,level,tags:filecount=10,filesize=10M"
+export JAVA_OPTS="$JAVA_OPTS -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-snmp-transport/${TB_SERVICE_ID}-heapdump.bin"
+export JAVA_OPTS="$JAVA_OPTS -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:+ExitOnOutOfMemoryError"
+export LOG_FILENAME=tb-snmp-transport.out
+export LOADER_PATH=/usr/share/tb-snmp-transport/conf
diff --git a/msa/transport/snmp/docker/Dockerfile b/msa/transport/snmp/docker/Dockerfile
index e39624d538..b808932482 100644
--- a/msa/transport/snmp/docker/Dockerfile
+++ b/msa/transport/snmp/docker/Dockerfile
@@ -14,7 +14,7 @@
# limitations under the License.
#
-FROM thingsboard/openjdk8
+FROM thingsboard/openjdk11
COPY start-tb-snmp-transport.sh ${pkg.name}.deb /tmp/
diff --git a/transport/snmp/src/main/conf/logback.xml b/transport/snmp/src/main/conf/logback.xml
new file mode 100644
index 0000000000..d9818c2257
--- /dev/null
+++ b/transport/snmp/src/main/conf/logback.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ ${pkg.logFolder}/${pkg.name}.log
+
+ ${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log
+ 100MB
+ 30
+ 3GB
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/transport/snmp/src/main/conf/tb-snmp-transport.conf b/transport/snmp/src/main/conf/tb-snmp-transport.conf
new file mode 100644
index 0000000000..73b6e04f68
--- /dev/null
+++ b/transport/snmp/src/main/conf/tb-snmp-transport.conf
@@ -0,0 +1,22 @@
+#
+# Copyright © 2016-2021 The Thingsboard Authors
+#
+# 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.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,heap*,age*,safepoint=debug:file=@pkg.logFolder@/gc.log:time,uptime,level,tags:filecount=10,filesize=10M"
+export JAVA_OPTS="$JAVA_OPTS -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError"
+export JAVA_OPTS="$JAVA_OPTS -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/transport/snmp/src/main/resources/logback.xml b/transport/snmp/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7a3d9f6f0d
--- /dev/null
+++ b/transport/snmp/src/main/resources/logback.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml
new file mode 100644
index 0000000000..9f6bd131ac
--- /dev/null
+++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml
@@ -0,0 +1,394 @@
+#
+# Copyright © 2016-2021 The Thingsboard Authors
+#
+# 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.
+#
+
+server:
+ # Server bind address
+ address: "${HTTP_BIND_ADDRESS:0.0.0.0}"
+ # Server bind port
+ port: "${HTTP_BIND_PORT:8085}"
+ # Server SSL configuration
+
+# Zookeeper connection parameters. Used for service discovery.
+zk:
+ # Enable/disable zookeeper discovery service.
+ enabled: "${ZOOKEEPER_ENABLED:false}"
+ # Zookeeper connect string
+ url: "${ZOOKEEPER_URL:localhost:2181}"
+ # Zookeeper retry interval in milliseconds
+ retry_interval_ms: "${ZOOKEEPER_RETRY_INTERVAL_MS:3000}"
+ # Zookeeper connection timeout in milliseconds
+ connection_timeout_ms: "${ZOOKEEPER_CONNECTION_TIMEOUT_MS:3000}"
+ # Zookeeper session timeout in milliseconds
+ session_timeout_ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}"
+ # Name of the directory in zookeeper 'filesystem'
+ zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}"
+
+cluster:
+ stats:
+ enabled: "${TB_CLUSTER_STATS_ENABLED:false}"
+ print_interval_ms: "${TB_CLUSTER_STATS_PRINT_INTERVAL_MS:10000}"
+
+cache:
+ # caffeine or redis
+ type: "${CACHE_TYPE:caffeine}"
+ attributes:
+ # make sure that if cache.type is 'redis' and cache.attributes.enabled is 'true' that you change 'maxmemory-policy' Redis config property to 'allkeys-lru', 'allkeys-lfu' or 'allkeys-random'
+ enabled: "${CACHE_ATTRIBUTES_ENABLED:true}"
+
+caffeine:
+ specs:
+ relations:
+ timeToLiveInMinutes: 1440
+ maxSize: 0
+ deviceCredentials:
+ timeToLiveInMinutes: 1440
+ maxSize: 0
+ devices:
+ timeToLiveInMinutes: 1440
+ maxSize: 0
+ sessions:
+ timeToLiveInMinutes: 1440
+ maxSize: 0
+ assets:
+ timeToLiveInMinutes: 1440
+ maxSize: 0
+ entityViews:
+ timeToLiveInMinutes: 1440
+ maxSize: 0
+ claimDevices:
+ timeToLiveInMinutes: 1
+ maxSize: 0
+ securitySettings:
+ timeToLiveInMinutes: 1440
+ maxSize: 0
+ tenantProfiles:
+ timeToLiveInMinutes: 1440
+ maxSize: 0
+ deviceProfiles:
+ timeToLiveInMinutes: 1440
+ maxSize: 0
+ attributes:
+ timeToLiveInMinutes: 1440
+ maxSize: 100000
+
+redis:
+ # standalone or cluster
+ connection:
+ type: "${REDIS_CONNECTION_TYPE:standalone}"
+ standalone:
+ host: "${REDIS_HOST:localhost}"
+ port: "${REDIS_PORT:6379}"
+ useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}"
+ # this value may be used only if you used not default ClientConfig
+ clientName: "${REDIS_CLIENT_NAME:standalone}"
+ # this value may be used only if you used not default ClientConfig
+ connectTimeout: "${REDIS_CLIENT_CONNECT_TIMEOUT:30000}"
+ # this value may be used only if you used not default ClientConfig
+ readTimeout: "${REDIS_CLIENT_READ_TIMEOUT:60000}"
+ # this value may be used only if you used not default ClientConfig
+ usePoolConfig: "${REDIS_CLIENT_USE_POOL_CONFIG:false}"
+ cluster:
+ # Comma-separated list of "host:port" pairs to bootstrap from.
+ nodes: "${REDIS_NODES:}"
+ # Maximum number of redirects to follow when executing commands across the cluster.
+ max-redirects: "${REDIS_MAX_REDIRECTS:12}"
+ useDefaultPoolConfig: "${REDIS_USE_DEFAULT_POOL_CONFIG:true}"
+ # db index
+ db: "${REDIS_DB:0}"
+ # db password
+ password: "${REDIS_PASSWORD:}"
+ # pool config
+ pool_config:
+ maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}"
+ maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}"
+ minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}"
+ testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}"
+ testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}"
+ testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}"
+ minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}"
+ evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}"
+ maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}"
+ numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}"
+ blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}"
+
+# Check new version updates parameters
+updates:
+ # Enable/disable updates checking.
+ enabled: "${UPDATES_ENABLED:true}"
+
+# spring freemarker configuration
+spring.freemarker.checkTemplateLocation: "false"
+
+audit-log:
+ # Enable/disable audit log functionality.
+ enabled: "${AUDIT_LOG_ENABLED:true}"
+ # Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS
+ by_tenant_partitioning: "${AUDIT_LOG_BY_TENANT_PARTITIONING:MONTHS}"
+ # Number of days as history period if startTime and endTime are not specified
+ default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}"
+ # Logging levels per each entity type.
+ # Allowed values: OFF (disable), W (log write operations), RW (log read and write operations)
+ logging-level:
+ mask:
+ "device": "${AUDIT_LOG_MASK_DEVICE:W}"
+ "asset": "${AUDIT_LOG_MASK_ASSET:W}"
+ "dashboard": "${AUDIT_LOG_MASK_DASHBOARD:W}"
+ "customer": "${AUDIT_LOG_MASK_CUSTOMER:W}"
+ "user": "${AUDIT_LOG_MASK_USER:W}"
+ "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
+ "alarm": "${AUDIT_LOG_MASK_ALARM:W}"
+ "entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}"
+ "device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}"
+ sink:
+ # Type of external sink. possible options: none, elasticsearch
+ type: "${AUDIT_LOG_SINK_TYPE:none}"
+ # Name of the index where audit logs stored
+ # Index name could contain next placeholders (not mandatory):
+ # @{TENANT} - substituted by tenant ID
+ # @{DATE} - substituted by current date in format provided in audit_log.sink.date_format
+ index_pattern: "${AUDIT_LOG_SINK_INDEX_PATTERN:@{TENANT}_AUDIT_LOG_@{DATE}}"
+ # Date format. Details of the pattern could be found here:
+ # https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
+ date_format: "${AUDIT_LOG_SINK_DATE_FORMAT:YYYY.MM.DD}"
+ scheme_name: "${AUDIT_LOG_SINK_SCHEME_NAME:http}" # http or https
+ host: "${AUDIT_LOG_SINK_HOST:localhost}"
+ port: "${AUDIT_LOG_SINK_PORT:9200}"
+ user_name: "${AUDIT_LOG_SINK_USER_NAME:}"
+ password: "${AUDIT_LOG_SINK_PASSWORD:}"
+
+state:
+ # Should be greater then transport.sessions.report_timeout
+ defaultInactivityTimeoutInSec: "${DEFAULT_INACTIVITY_TIMEOUT:600}"
+ defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:60}"
+ persistToTelemetry: "${PERSIST_STATE_TO_TELEMETRY:false}"
+
+transport:
+ sessions:
+ inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}"
+ report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:30000}"
+ json:
+ # Cast String data types to Numeric if possible when processing Telemetry/Attributes JSON
+ type_cast_enabled: "${JSON_TYPE_CAST_ENABLED:true}"
+ # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check)
+ max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}"
+ # Enable/disable http/mqtt/coap transport protocols (has higher priority than certain protocol's 'enabled' property)
+ api_enabled: "${TB_TRANSPORT_API_ENABLED:true}"
+ # Local LwM2M transport parameters
+ snmp:
+ enabled: "${SNMP_ENABLED:true}"
+
+queue:
+ type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ)
+ in_memory:
+ stats:
+ # For debug lvl
+ print-interval-ms: "${TB_QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}"
+ kafka:
+ bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
+ acks: "${TB_KAFKA_ACKS:all}"
+ retries: "${TB_KAFKA_RETRIES:1}"
+ batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
+ linger.ms: "${TB_KAFKA_LINGER_MS:1}"
+ buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
+ replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}"
+ max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}"
+ max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}"
+ max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}"
+ fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}"
+ use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}"
+ confluent:
+ ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}"
+ sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}"
+ sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}"
+ security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}"
+ other:
+ topic-properties:
+ rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
+ core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
+ transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
+ notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
+ js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
+ consumer-stats:
+ enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
+ print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
+ kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}"
+ aws_sqs:
+ use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
+ access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
+ secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}"
+ region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}"
+ threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}"
+ queue-properties:
+ rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
+ core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
+ transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
+ notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
+ js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
+ # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
+ pubsub:
+ project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
+ service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}"
+ max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes
+ max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}"
+ queue-properties:
+ rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
+ core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
+ transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
+ notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
+ js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
+ service_bus:
+ namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
+ sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
+ sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}"
+ max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}"
+ queue-properties:
+ rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
+ core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
+ transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
+ notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
+ js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
+ rabbitmq:
+ exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
+ host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
+ port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}"
+ virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}"
+ username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}"
+ password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}"
+ automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}"
+ connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}"
+ handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}"
+ queue-properties:
+ rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
+ core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
+ transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
+ notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
+ js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
+ partitions:
+ hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256
+ transport_api:
+ requests_topic: "${TB_QUEUE_TRANSPORT_API_REQUEST_TOPIC:tb_transport.api.requests}"
+ responses_topic: "${TB_QUEUE_TRANSPORT_API_RESPONSE_TOPIC:tb_transport.api.responses}"
+ max_pending_requests: "${TB_QUEUE_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
+ max_requests_timeout: "${TB_QUEUE_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ max_callback_threads: "${TB_QUEUE_TRANSPORT_MAX_CALLBACK_THREADS:100}"
+ request_poll_interval: "${TB_QUEUE_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}"
+ response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
+ core:
+ topic: "${TB_QUEUE_CORE_TOPIC:tb_core}"
+ poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
+ partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
+ pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}"
+ usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
+ stats:
+ enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}"
+ print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:60000}"
+ js:
+ # JS Eval request topic
+ request_topic: "${REMOTE_JS_EVAL_REQUEST_TOPIC:js_eval.requests}"
+ # JS Eval responses topic prefix that is combined with node id
+ response_topic_prefix: "${REMOTE_JS_EVAL_RESPONSE_TOPIC:js_eval.responses}"
+ # JS Eval max pending requests
+ max_pending_requests: "${REMOTE_JS_MAX_PENDING_REQUESTS:10000}"
+ # JS Eval max request timeout
+ max_eval_requests_timeout: "${REMOTE_JS_MAX_EVAL_REQUEST_TIMEOUT:60000}"
+ # JS max request timeout
+ max_requests_timeout: "${REMOTE_JS_MAX_REQUEST_TIMEOUT:10000}"
+ # JS response poll interval
+ response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}"
+ rule-engine:
+ topic: "${TB_QUEUE_RULE_ENGINE_TOPIC:tb_rule_engine}"
+ poll-interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}"
+ pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:2000}"
+ stats:
+ enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}"
+ print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}"
+ queues:
+ - name: "${TB_QUEUE_RE_MAIN_QUEUE_NAME:Main}"
+ topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}"
+ poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}"
+ partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}"
+ pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:2000}"
+ submit-strategy:
+ type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
+ # For BATCH only
+ batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
+ processing-strategy:
+ type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
+ # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
+ retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
+ failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
+ pause-between-retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRY_PAUSE:3}"# Time in seconds to wait in consumer thread before retries;
+ max-pause-between-retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_MAX_RETRY_PAUSE:3}"# Max allowed time in seconds for pause between retries.
+ - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}"
+ topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}"
+ poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}"
+ partitions: "${TB_QUEUE_RE_HP_PARTITIONS:10}"
+ pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:2000}"
+ submit-strategy:
+ type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
+ # For BATCH only
+ batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
+ processing-strategy:
+ type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
+ # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
+ retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
+ failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
+ pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
+ max-pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_MAX_RETRY_PAUSE:5}"# Max allowed time in seconds for pause between retries.
+ - name: "${TB_QUEUE_RE_SQ_QUEUE_NAME:SequentialByOriginator}"
+ topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}"
+ poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}"
+ partitions: "${TB_QUEUE_RE_SQ_PARTITIONS:10}"
+ pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:2000}"
+ submit-strategy:
+ type: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_BY_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
+ # For BATCH only
+ batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
+ processing-strategy:
+ type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
+ # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
+ retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
+ failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
+ pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
+ max-pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_MAX_RETRY_PAUSE:5}"# Max allowed time in seconds for pause between retries.
+ transport:
+ # For high priority notifications that require minimum latency and processing time
+ notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}"
+ poll_interval: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
+
+event:
+ debug:
+ max-symbols: "${TB_MAX_DEBUG_EVENT_SYMBOLS:4096}"
+
+service:
+ type: "${TB_SERVICE_TYPE:tb-transport}"
+ # Unique id for this service (autogenerated if empty)
+ id: "${TB_SERVICE_ID:}"
+ tenant_id: "${TB_SERVICE_TENANT_ID:}" # empty or specific tenant id.
+
+metrics:
+ # Enable/disable actuator metrics.
+ enabled: "${METRICS_ENABLED:false}"
+ timer:
+ # Metrics percentiles returned by actuator for timer metrics. List of double values (divided by ,).
+ percentiles: "${METRICS_TIMER_PERCENTILES:0.5}"
+
+management:
+ endpoints:
+ web:
+ exposure:
+ # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
+ include: '${METRICS_ENDPOINTS_EXPOSE:info}'
From 0532d22d9dfeede30fd206b41ea1398629c654e5 Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Wed, 24 Mar 2021 12:05:43 +0200
Subject: [PATCH 04/32] Refactor
---
...ProfileKvMapping.java => SnmpMapping.java} | 9 +-
.../SnmpProfileTransportConfiguration.java | 10 +-
.../DefaultTbServiceInfoProvider.java | 5 +-
.../server/queue/util/AfterContextReady.java | 35 +++
.../server/queue/util/AfterStartUp.java | 35 +++
.../transport/snmp/SnmpTransportContext.java | 31 +--
.../snmp/service/SnmpTransportService.java | 237 +++++++-----------
7 files changed, 184 insertions(+), 178 deletions(-)
rename common/data/src/main/java/org/thingsboard/server/common/data/device/profile/{SnmpDeviceProfileKvMapping.java => SnmpMapping.java} (90%)
create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java
create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
similarity index 90%
rename from common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
index 6eff7cbd34..6bd0ca6b4d 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
@@ -18,11 +18,10 @@ package org.thingsboard.server.common.data.device.profile;
import lombok.Data;
import org.thingsboard.server.common.data.kv.DataType;
-//TODO: rename class
@Data
-public class SnmpDeviceProfileKvMapping {
- private String key;
- private DataType type;
- private String method;
+public class SnmpMapping {
private String oid;
+ private String method;
+ private String key;
+ private DataType dataType;
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
index 50f6c4351c..fb6b46fbb6 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
@@ -29,8 +29,8 @@ public class SnmpProfileTransportConfiguration implements DeviceProfileTransport
private int pollPeriodMs;
private int timeoutMs;
private int retries;
- private List attributes;
- private List telemetry;
+ private List attributesMappings;
+ private List telemetryMappings;
@Override
public DeviceTransportType getType() {
@@ -38,9 +38,9 @@ public class SnmpProfileTransportConfiguration implements DeviceProfileTransport
}
@JsonIgnore
- public List getKvMappings() {
- if (attributes != null && telemetry != null) {
- return Stream.concat(attributes.stream(), telemetry.stream()).collect(Collectors.toList());
+ public List getAllMappings() {
+ if (attributesMappings != null && telemetryMappings != null) {
+ return Stream.concat(attributesMappings.stream(), telemetryMappings.stream()).collect(Collectors.toList());
} else {
return Collections.emptyList();
}
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
index 3ba360cd8e..6988bef9e1 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
@@ -20,8 +20,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
-import org.springframework.context.event.ContextRefreshedEvent;
-import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.TbTransportService;
@@ -31,6 +29,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
+import org.thingsboard.server.queue.util.AfterContextReady;
import javax.annotation.PostConstruct;
import java.net.InetAddress;
@@ -109,7 +108,7 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
serviceInfo = builder.build();
}
- @EventListener(ContextRefreshedEvent.class)
+ @AfterContextReady
public void setTransports() {
serviceInfo = ServiceInfo.newBuilder(serviceInfo)
.addAllTransports(getTransportServices().stream()
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java
new file mode 100644
index 0000000000..5c68a7609a
--- /dev/null
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.queue.util;
+
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.annotation.Order;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@EventListener(ContextRefreshedEvent.class)
+@Order
+public @interface AfterContextReady {
+ @AliasFor(annotation = Order.class, attribute = "value")
+ int order() default Integer.MAX_VALUE;
+}
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java
new file mode 100644
index 0000000000..5d2e94ee68
--- /dev/null
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.queue.util;
+
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.annotation.Order;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@EventListener(ApplicationReadyEvent.class)
+@Order
+public @interface AfterStartUp {
+ @AliasFor(annotation = Order.class, attribute = "value")
+ int order() default Integer.MAX_VALUE;
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index a9fa8380fa..df2582005a 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -20,16 +20,14 @@ import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
-import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
-import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileKvMapping;
+import org.thingsboard.server.common.data.device.profile.SnmpMapping;
import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -45,6 +43,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService;
import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
@@ -61,7 +60,6 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
@TbSnmpTransportComponent
@@ -80,8 +78,7 @@ public class SnmpTransportContext extends TransportContext {
private final Map> profilesPdus = new ConcurrentHashMap<>();
private Collection allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
- @EventListener(ApplicationReadyEvent.class)
- @Order(2)
+ @AfterStartUp(order = 2)
public void initDevicesSessions() {
log.info("Initializing SNMP devices sessions");
allSnmpDevicesIds = protoEntityService.getAllSnmpDevicesIds().stream()
@@ -209,17 +206,17 @@ public class SnmpTransportContext extends TransportContext {
}
private List createPdus(SnmpProfileTransportConfiguration deviceProfileConfig) {
- Map> varBindingPerMethod = new HashMap<>();
+ Map> bindingsPerMethod = new HashMap<>();
- deviceProfileConfig.getKvMappings().forEach(mapping -> varBindingPerMethod
+ deviceProfileConfig.getAllMappings().forEach(mapping -> bindingsPerMethod
.computeIfAbsent(mapping.getMethod(), v -> new ArrayList<>())
.add(new VariableBinding(new OID(mapping.getOid()))));
- return varBindingPerMethod.keySet().stream()
+ return bindingsPerMethod.keySet().stream()
.map(method -> {
PDU request = new PDU();
request.setType(getSnmpMethod(method));
- request.addAll(varBindingPerMethod.get(method));
+ request.addAll(bindingsPerMethod.get(method));
return request;
})
.collect(Collectors.toList());
@@ -300,31 +297,27 @@ public class SnmpTransportContext extends TransportContext {
return profilesPdus;
}
- public Optional getAttributesMapping(DeviceProfileId deviceProfileId, OID responseOid) {
+ public Optional getAttributeMapping(DeviceProfileId deviceProfileId, OID responseOid) {
if (profilesTransportConfigs.containsKey(deviceProfileId)) {
- return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getAttributes());
+ return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getAttributesMappings());
}
return Optional.empty();
}
- public Optional getTelemetryMapping(DeviceProfileId deviceProfileId, OID responseOid) {
+ public Optional getTelemetryMapping(DeviceProfileId deviceProfileId, OID responseOid) {
if (profilesTransportConfigs.containsKey(deviceProfileId)) {
- return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getTelemetry());
+ return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getTelemetryMappings());
}
return Optional.empty();
}
- private Optional getMapping(OID responseOid, List mappings) {
+ private Optional getMapping(OID responseOid, List mappings) {
return mappings.stream()
.filter(kvMapping -> new OID(kvMapping.getOid()).equals(responseOid))
//TODO: OID shouldn't be duplicated in the config, add backend and UI verification
.findFirst();
}
- public ExecutorService getSnmpCallbackExecutor() {
- return snmpTransportService.getSnmpCallbackExecutor();
- }
-
private int getSnmpMethod(String configMethod) {
switch (configMethod) {
case "get":
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index 22e60c98ee..3e9eb63943 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -15,114 +15,74 @@
*/
package org.thingsboard.server.transport.snmp.service;
-import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonSyntaxException;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Lazy;
-import org.springframework.context.event.EventListener;
-import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
-import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.kv.DataType;
-import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
-import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
-import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.transport.snmp.SnmpTransportContext;
+import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
+import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
-import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
@TbSnmpTransportComponent
@Service
@Slf4j
public class SnmpTransportService implements TbTransportService {
private final SnmpTransportContext snmpTransportContext;
+ private final TransportService transportService;
- @Getter
- private ExecutorService snmpCallbackExecutor;
- @Getter
private Snmp snmp;
private ScheduledExecutorService pollingExecutor;
+ private ExecutorService snmpResponseProcessingExecutor;
- public SnmpTransportService(@Lazy SnmpTransportContext snmpTransportContext) {
+ public SnmpTransportService(@Lazy SnmpTransportContext snmpTransportContext,
+ TransportService transportService) {
this.snmpTransportContext = snmpTransportContext;
+ this.transportService = transportService;
}
- // @PostConstruct
- private void init() {
- log.info("Starting SNMP transport...");
+ @PostConstruct
+ private void init() throws IOException {
+ log.info("Initializing SNMP transport service");
+
pollingExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("snmp-polling"));
//TODO: Set parallelism value in the config
- snmpCallbackExecutor = Executors.newWorkStealingPool(20);
+ snmpResponseProcessingExecutor = Executors.newWorkStealingPool(20);
initializeSnmp();
- log.info("SNMP transport started!");
- }
-
- @PreDestroy
- public void shutdown() {
- log.info("Stopping SNMP transport!");
- if (pollingExecutor != null) {
- pollingExecutor.shutdownNow();
- }
- if (snmpCallbackExecutor != null) {
- snmpCallbackExecutor.shutdownNow();
- }
- if (snmp != null) {
- try {
- snmp.close();
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- }
- }
- log.info("SNMP transport stopped!");
- }
-
- @EventListener(ApplicationReadyEvent.class)
- @Order(value = 10)
- public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
- log.info("Received application ready event. Starting SNMP polling.");
-// startPolling();
+ log.info("SNMP transport service initialized");
}
- private void initializeSnmp() {
- try {
- this.snmp = new Snmp(new DefaultUdpTransportMapping());
- this.snmp.listen();
- } catch (IOException e) {
- //TODO: what should be done if transport wasn't initialized?
- log.error(e.getMessage(), e);
- }
+ private void initializeSnmp() throws IOException {
+ snmp = new Snmp(new DefaultUdpTransportMapping());
+ snmp.listen();
}
- private void startPolling() {
+ @AfterStartUp(order = 10)
+ public void startPolling() {
+ log.info("Starting SNMP polling");
//TODO: Get poll period from configuration;
int pollPeriodSeconds = 1;
@@ -141,7 +101,7 @@ public class SnmpTransportService implements TbTransportService {
DeviceProfileId deviceProfileId = sessionContext.getDeviceProfile().getId();
snmpTransportContext.getProfilesPdus().get(deviceProfileId).forEach(pdu -> {
try {
- log.debug("[{}] Sending SNMP message...", pdu.getRequestID());
+ log.debug("[{}] Sending SNMP message for device {}", pdu.getRequestID(), sessionContext.getDeviceId());
snmp.send(pdu, sessionContext.getTarget(), deviceProfileId, sessionContext);
} catch (IOException e) {
log.error(e.getMessage(), e);
@@ -152,118 +112,84 @@ public class SnmpTransportService implements TbTransportService {
public void onNewDeviceResponse(ResponseEvent responseEvent, DeviceSessionContext sessionContext) {
((Snmp) responseEvent.getSource()).cancel(responseEvent.getRequest(), sessionContext);
- snmpTransportContext.getSnmpCallbackExecutor().submit(() -> processSnmpResponse(responseEvent, sessionContext));
+ snmpResponseProcessingExecutor.submit(() -> processSnmpResponse(responseEvent, sessionContext));
}
private void processSnmpResponse(ResponseEvent event, DeviceSessionContext sessionContext) {
- PDU response = event.getResponse();
if (event.getError() != null) {
log.warn("Response error: {}", event.getError().getMessage(), event.getError());
+ return;
+ }
+
+ PDU response = event.getResponse();
+ if (response == null) {
+ log.warn("No SNMP response, requestId: {}", event.getRequest().getRequestID());
+ return;
}
- if (response != null) {
- log.debug("[{}] Processing SNMP response: {}", response.getRequestID(), response);
+ DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();
+ log.debug("[{}] Processing SNMP response for device {} with device profile {}: {}",
+ response.getRequestID(), sessionContext.getDeviceId(), deviceProfileId, response);
- DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();
- TransportService transportService = snmpTransportContext.getTransportService();
- for (int i = 0; i < response.size(); i++) {
- VariableBinding vb = response.get(i);
- snmpTransportContext.getAttributesMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
- TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(sessionContext.getToken()).build(),
- new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
- try {
- transportService.process(sessionInfo,
- convertToPostAttributes(kvMapping.getKey(), kvMapping.getType(), vb.toValueString()),
- TransportServiceCallback.EMPTY);
- reportActivity(sessionInfo);
- } catch (Exception e) {
- log.warn("Failed to process SNMP response: {}", e.getMessage(), e);
- }
- })));
- snmpTransportContext.getTelemetryMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
- TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(sessionContext.getToken()).build(),
- new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
- try {
- transportService.process(sessionInfo,
- convertToPostTelemetry(kvMapping.getKey(), kvMapping.getType(), vb.toValueString()),
- TransportServiceCallback.EMPTY);
- reportActivity(sessionInfo);
+ JsonObject telemetry = new JsonObject();
+ JsonObject attributes = new JsonObject();
- } catch (Exception e) {
- log.warn("Failed to process SNMP response: {}", e.getMessage(), e);
- }
- })));
- }
- } else {
- log.warn("No SNMP response, requestId: {}", event.getRequest().getRequestID());
+ for (int i = 0; i < response.size(); i++) {
+ VariableBinding variableBinding = response.get(i);
+ log.trace("Processing variable binding {}: {}", i, variableBinding);
+
+ snmpTransportContext.getTelemetryMapping(deviceProfileId, variableBinding.getOid()).ifPresent(mapping -> {
+ log.trace("Found telemetry mapping for oid {}: {}", variableBinding.getOid(), mapping);
+ processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), telemetry);
+ });
+
+ snmpTransportContext.getAttributeMapping(deviceProfileId, variableBinding.getOid()).ifPresent(mapping -> {
+ log.trace("Found attribute mapping for oid {}: {}", variableBinding.getOid(), mapping);
+ processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), attributes);
+ });
+ }
+
+ if (telemetry.entrySet().isEmpty() && attributes.entrySet().isEmpty()) {
+ log.warn("No telemetry or attribute values is the SNMP response for device {}", sessionContext.getDeviceId());
+ return;
}
+
+ if (!telemetry.entrySet().isEmpty()) {
+ TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(telemetry);
+ transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, TransportServiceCallback.EMPTY);
+ log.debug("Posted telemetry for device {}: {}", sessionContext.getDeviceId(), telemetry);
+ }
+
+ if (!attributes.entrySet().isEmpty()) {
+ TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(attributes);
+ transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, TransportServiceCallback.EMPTY);
+ log.debug("Posted attributes for device {}: {}", sessionContext.getDeviceId(), attributes);
+ }
+
+ reportActivity(sessionContext.getSessionInfo());
}
private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
- snmpTransportContext.getTransportService().process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
+ transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(false)
.setRpcSubscription(false)
.setLastActivityTime(System.currentTimeMillis())
.build(), TransportServiceCallback.EMPTY);
}
- private TransportProtos.PostAttributeMsg convertToPostAttributes(String keyName, DataType dataType, String payload) throws AdaptorException {
- try {
- return JsonConverter.convertToAttributesProto(getKvJson(keyName, dataType, payload));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- //TODO: change the exception type
- throw new AdaptorException(ex);
- }
- }
-
- private TransportProtos.PostTelemetryMsg convertToPostTelemetry(String keyName, DataType dataType, String payload) throws AdaptorException {
- try {
- return JsonConverter.convertToTelemetryProto(getKvJson(keyName, dataType, payload));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- //TODO: change the exception type
- throw new AdaptorException(ex);
- }
- }
-
- private JsonElement getKvJson(String keyName, DataType dataType, String payload) throws AdaptorException {
- JsonObject result = new JsonObject();
+ private void processValue(String key, DataType dataType, String value, JsonObject result) {
switch (dataType) {
case LONG:
- result.addProperty(keyName, Long.parseLong(payload));
+ result.addProperty(key, Long.parseLong(value));
break;
case BOOLEAN:
- result.addProperty(keyName, Boolean.parseBoolean(payload));
+ result.addProperty(key, Boolean.parseBoolean(value));
break;
case DOUBLE:
- result.addProperty(keyName, Double.parseDouble(payload));
- break;
- case STRING:
- result.addProperty(keyName, payload);
+ result.addProperty(key, Double.parseDouble(value));
break;
default:
- //TODO: change the exception type
- throw new AdaptorException("Unsupported data type");
- }
- return new JsonParser().parse(result.toString());
- }
-
- @AllArgsConstructor
- private static class DeviceAuthCallback implements TransportServiceCallback {
- private final TransportContext transportContext;
- private final Consumer onSuccess;
-
- @Override
- public void onSuccess(ValidateDeviceCredentialsResponse msg) {
- if (msg.hasDeviceInfo()) {
- onSuccess.accept(SessionInfoCreator.create(msg, transportContext, UUID.randomUUID()));
- } else {
- log.warn("Failed to process device auth");
- }
- }
-
- @Override
- public void onError(Throwable e) {
- log.warn("Failed to process device auth", e);
+ result.addProperty(key, value);
}
}
@@ -271,4 +197,23 @@ public class SnmpTransportService implements TbTransportService {
public String getName() {
return "SNMP";
}
+
+ @PreDestroy
+ public void shutdown() {
+ log.info("Stopping SNMP transport!");
+ if (pollingExecutor != null) {
+ pollingExecutor.shutdownNow();
+ }
+ if (snmpResponseProcessingExecutor != null) {
+ snmpResponseProcessingExecutor.shutdownNow();
+ }
+ if (snmp != null) {
+ try {
+ snmp.close();
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ log.info("SNMP transport stopped!");
+ }
}
From afab5150b8b39aac81a3c7f34a6f6fd438965d3b Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Wed, 24 Mar 2021 16:43:28 +0200
Subject: [PATCH 05/32] Provide SNMP transport configuration validation;
refactor
---
.../data/DeviceTransportConfiguration.java | 3 +
.../SnmpDeviceTransportConfiguration.java | 19 +++--
.../DeviceProfileTransportConfiguration.java | 6 +-
...pDeviceProfileTransportConfiguration.java} | 24 +++++--
.../data/transport/snmp/SnmpMapping.java | 39 ++++++++++
.../data/transport/snmp/SnmpMethod.java | 32 +++++++++
.../snmp/SnmpProtocolVersion.java} | 23 +++---
.../transport/snmp/SnmpTransportContext.java | 71 ++++++++-----------
.../snmp/service/SnmpTransportService.java | 12 +++-
.../snmp/session/DeviceSessionContext.java | 25 ++-----
.../dao/device/DeviceProfileServiceImpl.java | 1 +
.../server/dao/device/DeviceServiceImpl.java | 4 ++
12 files changed, 173 insertions(+), 86 deletions(-)
rename common/data/src/main/java/org/thingsboard/server/common/data/device/profile/{SnmpProfileTransportConfiguration.java => SnmpDeviceProfileTransportConfiguration.java} (60%)
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java
rename common/data/src/main/java/org/thingsboard/server/common/data/{device/profile/SnmpMapping.java => transport/snmp/SnmpProtocolVersion.java} (68%)
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
index 49a547371f..63b31eb3a2 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
@@ -38,4 +38,7 @@ public interface DeviceTransportConfiguration extends Serializable {
@JsonIgnore
DeviceTransportType getType();
+ default void validate() {
+ }
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
index 3db31b4f2a..551bf2fcfb 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
@@ -19,25 +19,30 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
@Data
public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
-
private String address;
private int port;
private String community;
- private String protocolVersion;
+ private SnmpProtocolVersion protocolVersion;
@Override
public DeviceTransportType getType() {
return DeviceTransportType.SNMP;
}
+ @Override
+ public void validate() {
+ if (!isValid()) {
+ throw new IllegalArgumentException("Transport configuration is not valid");
+ }
+ }
+
@JsonIgnore
- public boolean isValid() {
- return StringUtils.isNotEmpty(this.address)
- && this.port > 0
- && StringUtils.isNotEmpty(this.community)
- && StringUtils.isNotEmpty(this.protocolVersion);
+ private boolean isValid() {
+ return StringUtils.isNotBlank(address) && port > 0 &&
+ StringUtils.isNotBlank(community) && protocolVersion != null;
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
index ffbe5f7048..2fea8d34ad 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
@@ -30,10 +31,13 @@ import org.thingsboard.server.common.data.DeviceTransportType;
@JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
@JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
@JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"),
- @JsonSubTypes.Type(value = SnmpProfileTransportConfiguration.class, name = "SNMP")})
+ @JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP")})
public interface DeviceProfileTransportConfiguration {
@JsonIgnore
DeviceTransportType getType();
+ default void validate() {
+ }
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
similarity index 60%
rename from common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
index fb6b46fbb6..a4f7cc57de 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
@@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.device.profile;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import java.util.Collections;
import java.util.List;
@@ -25,12 +26,12 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
@Data
-public class SnmpProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
+public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
private int pollPeriodMs;
private int timeoutMs;
private int retries;
- private List attributesMappings;
private List telemetryMappings;
+ private List attributesMappings;
@Override
public DeviceTransportType getType() {
@@ -39,10 +40,25 @@ public class SnmpProfileTransportConfiguration implements DeviceProfileTransport
@JsonIgnore
public List getAllMappings() {
- if (attributesMappings != null && telemetryMappings != null) {
- return Stream.concat(attributesMappings.stream(), telemetryMappings.stream()).collect(Collectors.toList());
+ if (telemetryMappings != null && attributesMappings != null) {
+ return Stream.concat(telemetryMappings.stream(), attributesMappings.stream()).collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}
+
+ @Override
+ public void validate() {
+ if (!isValid()) {
+ throw new IllegalArgumentException("Transport configuration is not valid");
+ }
+ }
+
+ @JsonIgnore
+ private boolean isValid() {
+ List mappings = getAllMappings();
+ return pollPeriodMs > 0 && timeoutMs > 0 && retries >= 0 &&
+ !mappings.isEmpty() && mappings.stream().allMatch(SnmpMapping::isValid) &&
+ mappings.stream().map(SnmpMapping::getOid).distinct().count() == mappings.size();
+ }
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
new file mode 100644
index 0000000000..32a3f2a25b
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+import org.thingsboard.server.common.data.kv.DataType;
+
+import java.util.regex.Pattern;
+
+@Data
+public class SnmpMapping {
+ private String oid;
+ private SnmpMethod method;
+ private String key;
+ private DataType dataType;
+
+ private static final Pattern OID_PATTERN = Pattern.compile("^\\.?([0-2])((\\.0)|(\\.[1-9][0-9]*))*$");
+
+ @JsonIgnore
+ public boolean isValid() {
+ return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && method != null &&
+ StringUtils.isNotBlank(key) && dataType != null;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java
new file mode 100644
index 0000000000..2a04ce427b
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp;
+
+public enum SnmpMethod {
+ GET(-96),
+ SET(-93);
+
+ // codes taken from org.snmp4j.PDU class
+ private final int code;
+
+ SnmpMethod(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java
similarity index 68%
rename from common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java
index 6bd0ca6b4d..d16836cf1a 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java
@@ -13,15 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.data.device.profile;
+package org.thingsboard.server.common.data.transport.snmp;
-import lombok.Data;
-import org.thingsboard.server.common.data.kv.DataType;
+public enum SnmpProtocolVersion {
+ V1(0),
+ V2C(1),
+ V3(3);
-@Data
-public class SnmpMapping {
- private String oid;
- private String method;
- private String key;
- private DataType dataType;
+ private final int code;
+
+ SnmpProtocolVersion(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index df2582005a..beaaef829f 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -27,12 +27,13 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
-import org.thingsboard.server.common.data.device.profile.SnmpMapping;
-import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.transport.DeviceUpdatedEvent;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportDeviceProfileCache;
@@ -74,7 +75,7 @@ public class SnmpTransportContext extends TransportContext {
private final SnmpTransportBalancingService balancingService;
private final Map sessions = new ConcurrentHashMap<>();
- private final Map profilesTransportConfigs = new ConcurrentHashMap<>();
+ private final Map profilesTransportConfigs = new ConcurrentHashMap<>();
private final Map> profilesPdus = new ConcurrentHashMap<>();
private Collection allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
@@ -94,7 +95,13 @@ public class SnmpTransportContext extends TransportContext {
managedDevicesIds.stream()
.map(protoEntityService::getDeviceById)
.collect(Collectors.toList())
- .forEach(this::establishDeviceSession);
+ .forEach(device -> {
+ try {
+ establishDeviceSession(device);
+ } catch (Exception e) {
+ log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.getMessage());
+ }
+ });
}
private void establishDeviceSession(Device device) {
@@ -110,13 +117,10 @@ public class SnmpTransportContext extends TransportContext {
return;
}
+ SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
- if (!deviceTransportConfiguration.isValid()) {
- log.warn("SNMP device transport configuration is not valid");
- return;
- }
- SnmpProfileTransportConfiguration profileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ profilesTransportConfigs.put(deviceProfileId, profileTransportConfiguration);
profilesPdus.computeIfAbsent(deviceProfileId, id -> createPdus(profileTransportConfiguration));
DeviceSessionContext deviceSessionContext = new DeviceSessionContext(
@@ -140,22 +144,18 @@ public class SnmpTransportContext extends TransportContext {
return;
}
- SnmpProfileTransportConfiguration profileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
- SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
- sessionContext.setProfileTransportConfiguration(profileTransportConfiguration);
- sessionContext.setDeviceTransportConfiguration(deviceTransportConfiguration);
- if (!deviceTransportConfiguration.isValid()) {
- log.warn("SNMP device transport configuration is not valid");
- destroyDeviceSession(sessionContext);
- return;
- }
+ SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
+
+ if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
+ profilesPdus.put(deviceProfileId, createPdus(newProfileTransportConfiguration));
+ profilesTransportConfigs.put(deviceProfileId, newProfileTransportConfiguration);
- if (!profileTransportConfiguration.equals(profilesTransportConfigs.get(deviceProfileId))) {
- profilesPdus.put(deviceProfileId, createPdus(profileTransportConfiguration));
- profilesTransportConfigs.put(deviceProfileId, profileTransportConfiguration);
- sessionContext.initTarget(profileTransportConfiguration, deviceTransportConfiguration);
- } else if (!deviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
- sessionContext.initTarget(profileTransportConfiguration, deviceTransportConfiguration);
+ sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
+ sessionContext.initTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
+ } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
+ sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
+ sessionContext.initTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
} else {
log.trace("Configuration of the device {} was not updated", device);
}
@@ -205,8 +205,8 @@ public class SnmpTransportContext extends TransportContext {
});
}
- private List createPdus(SnmpProfileTransportConfiguration deviceProfileConfig) {
- Map> bindingsPerMethod = new HashMap<>();
+ private List createPdus(SnmpDeviceProfileTransportConfiguration deviceProfileConfig) {
+ Map> bindingsPerMethod = new HashMap<>();
deviceProfileConfig.getAllMappings().forEach(mapping -> bindingsPerMethod
.computeIfAbsent(mapping.getMethod(), v -> new ArrayList<>())
@@ -215,7 +215,7 @@ public class SnmpTransportContext extends TransportContext {
return bindingsPerMethod.keySet().stream()
.map(method -> {
PDU request = new PDU();
- request.setType(getSnmpMethod(method));
+ request.setType(method.getCode());
request.addAll(bindingsPerMethod.get(method));
return request;
})
@@ -311,22 +311,9 @@ public class SnmpTransportContext extends TransportContext {
return Optional.empty();
}
- private Optional getMapping(OID responseOid, List mappings) {
+ private Optional getMapping(OID oid, List mappings) {
return mappings.stream()
- .filter(kvMapping -> new OID(kvMapping.getOid()).equals(responseOid))
- //TODO: OID shouldn't be duplicated in the config, add backend and UI verification
+ .filter(mapping -> new OID(mapping.getOid()).equals(oid))
.findFirst();
}
-
- private int getSnmpMethod(String configMethod) {
- switch (configMethod) {
- case "get":
- return PDU.GET;
- case "getNext":
- case "response":
- case "set":
- default:
- return -1;
- }
- }
}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index 3e9eb63943..86d2cead95 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.smi.Null;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.context.annotation.Lazy;
@@ -103,8 +104,8 @@ public class SnmpTransportService implements TbTransportService {
try {
log.debug("[{}] Sending SNMP message for device {}", pdu.getRequestID(), sessionContext.getDeviceId());
snmp.send(pdu, sessionContext.getTarget(), deviceProfileId, sessionContext);
- } catch (IOException e) {
- log.error(e.getMessage(), e);
+ } catch (Exception e) {
+ log.error("Failed to send SNMP request: {}", e.getMessage());
}
});
}
@@ -123,7 +124,7 @@ public class SnmpTransportService implements TbTransportService {
PDU response = event.getResponse();
if (response == null) {
- log.warn("No SNMP response, requestId: {}", event.getRequest().getRequestID());
+ log.warn("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
return;
}
@@ -138,6 +139,11 @@ public class SnmpTransportService implements TbTransportService {
VariableBinding variableBinding = response.get(i);
log.trace("Processing variable binding {}: {}", i, variableBinding);
+ if (variableBinding.getVariable() instanceof Null) {
+ log.debug("Response variable is empty");
+ continue;
+ }
+
snmpTransportContext.getTelemetryMapping(deviceProfileId, variableBinding.getOid()).ifPresent(mapping -> {
log.trace("Found telemetry mapping for oid {}: {}", variableBinding.getOid(), mapping);
processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), telemetry);
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
index ed87bbc726..3cfc9bc041 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
@@ -22,13 +22,12 @@ import org.snmp4j.CommunityTarget;
import org.snmp4j.Target;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
-import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OctetString;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
-import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
@@ -51,7 +50,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
private final String token;
@Getter
@Setter
- private SnmpProfileTransportConfiguration profileTransportConfiguration;
+ private SnmpDeviceProfileTransportConfiguration profileTransportConfiguration;
@Getter
@Setter
private SnmpDeviceTransportConfiguration deviceTransportConfiguration;
@@ -79,7 +78,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
this.snmpTransportContext = snmpTransportContext;
this.snmpTransportService = snmpTransportService;
- this.profileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ this.profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
this.deviceTransportConfiguration = deviceTransportConfiguration;
initTarget(this.profileTransportConfiguration, this.deviceTransportConfiguration);
@@ -105,11 +104,11 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
}
}
- public void initTarget(SnmpProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
+ public void initTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
log.trace("Initializing target for SNMP session of device {}", device);
CommunityTarget communityTarget = new CommunityTarget();
communityTarget.setAddress(GenericAddress.parse(GenericAddress.TYPE_UDP + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
- communityTarget.setVersion(getSnmpVersion(deviceTransportConfig.getProtocolVersion()));
+ communityTarget.setVersion(deviceTransportConfig.getProtocolVersion().getCode());
communityTarget.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
communityTarget.setTimeout(profileTransportConfig.getTimeoutMs());
communityTarget.setRetries(profileTransportConfig.getRetries());
@@ -125,20 +124,6 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
return token;
}
- //TODO: replace with enum, wtih preliminary discussion of type version in config (string or integer)
- private int getSnmpVersion(String configSnmpVersion) {
- switch (configSnmpVersion) {
- case ("v1"):
- return SnmpConstants.version1;
- case ("v2c"):
- return SnmpConstants.version2c;
- case ("v3"):
- return SnmpConstants.version3;
- default:
- return -1;
- }
- }
-
@Override
public int nextMsgId() {
return msgIdSeq.incrementAndGet();
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
index 95f52cf55e..fea97bc903 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
@@ -350,6 +350,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
}
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
+ transportConfiguration.validate();
if (transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) {
MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
if (mqttTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
index 64b44db686..f3644d80ac 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
@@ -46,6 +46,7 @@ import org.thingsboard.server.common.data.device.credentials.BasicMqttCredential
import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration;
import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.DeviceData;
+import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
@@ -604,6 +605,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
throw new DataValidationException("Can't assign device to customer from different tenant!");
}
}
+ Optional.ofNullable(device.getDeviceData())
+ .flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration()))
+ .ifPresent(DeviceTransportConfiguration::validate);
}
};
From 039508bfbd3d155a5091a130dd4f1701bba9c223 Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Wed, 24 Mar 2021 12:05:43 +0200
Subject: [PATCH 06/32] Refactor
---
...ProfileKvMapping.java => SnmpMapping.java} | 9 +-
.../SnmpProfileTransportConfiguration.java | 10 +-
.../DefaultTbServiceInfoProvider.java | 5 +-
.../server/queue/util/AfterContextReady.java | 35 +++
.../server/queue/util/AfterStartUp.java | 35 +++
.../transport/snmp/SnmpTransportContext.java | 31 +--
.../snmp/service/SnmpTransportService.java | 237 +++++++-----------
7 files changed, 184 insertions(+), 178 deletions(-)
rename common/data/src/main/java/org/thingsboard/server/common/data/device/profile/{SnmpDeviceProfileKvMapping.java => SnmpMapping.java} (90%)
create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java
create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
similarity index 90%
rename from common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
index 6eff7cbd34..6bd0ca6b4d 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
@@ -18,11 +18,10 @@ package org.thingsboard.server.common.data.device.profile;
import lombok.Data;
import org.thingsboard.server.common.data.kv.DataType;
-//TODO: rename class
@Data
-public class SnmpDeviceProfileKvMapping {
- private String key;
- private DataType type;
- private String method;
+public class SnmpMapping {
private String oid;
+ private String method;
+ private String key;
+ private DataType dataType;
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
index 50f6c4351c..fb6b46fbb6 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
@@ -29,8 +29,8 @@ public class SnmpProfileTransportConfiguration implements DeviceProfileTransport
private int pollPeriodMs;
private int timeoutMs;
private int retries;
- private List attributes;
- private List telemetry;
+ private List attributesMappings;
+ private List telemetryMappings;
@Override
public DeviceTransportType getType() {
@@ -38,9 +38,9 @@ public class SnmpProfileTransportConfiguration implements DeviceProfileTransport
}
@JsonIgnore
- public List getKvMappings() {
- if (attributes != null && telemetry != null) {
- return Stream.concat(attributes.stream(), telemetry.stream()).collect(Collectors.toList());
+ public List getAllMappings() {
+ if (attributesMappings != null && telemetryMappings != null) {
+ return Stream.concat(attributesMappings.stream(), telemetryMappings.stream()).collect(Collectors.toList());
} else {
return Collections.emptyList();
}
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
index 3ba360cd8e..6988bef9e1 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
@@ -20,8 +20,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
-import org.springframework.context.event.ContextRefreshedEvent;
-import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.TbTransportService;
@@ -31,6 +29,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
+import org.thingsboard.server.queue.util.AfterContextReady;
import javax.annotation.PostConstruct;
import java.net.InetAddress;
@@ -109,7 +108,7 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
serviceInfo = builder.build();
}
- @EventListener(ContextRefreshedEvent.class)
+ @AfterContextReady
public void setTransports() {
serviceInfo = ServiceInfo.newBuilder(serviceInfo)
.addAllTransports(getTransportServices().stream()
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java
new file mode 100644
index 0000000000..5c68a7609a
--- /dev/null
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.queue.util;
+
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.annotation.Order;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@EventListener(ContextRefreshedEvent.class)
+@Order
+public @interface AfterContextReady {
+ @AliasFor(annotation = Order.class, attribute = "value")
+ int order() default Integer.MAX_VALUE;
+}
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java
new file mode 100644
index 0000000000..5d2e94ee68
--- /dev/null
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.queue.util;
+
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.annotation.Order;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@EventListener(ApplicationReadyEvent.class)
+@Order
+public @interface AfterStartUp {
+ @AliasFor(annotation = Order.class, attribute = "value")
+ int order() default Integer.MAX_VALUE;
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index a9fa8380fa..df2582005a 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -20,16 +20,14 @@ import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
-import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
-import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileKvMapping;
+import org.thingsboard.server.common.data.device.profile.SnmpMapping;
import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -45,6 +43,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService;
import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
@@ -61,7 +60,6 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
@TbSnmpTransportComponent
@@ -80,8 +78,7 @@ public class SnmpTransportContext extends TransportContext {
private final Map> profilesPdus = new ConcurrentHashMap<>();
private Collection allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
- @EventListener(ApplicationReadyEvent.class)
- @Order(2)
+ @AfterStartUp(order = 2)
public void initDevicesSessions() {
log.info("Initializing SNMP devices sessions");
allSnmpDevicesIds = protoEntityService.getAllSnmpDevicesIds().stream()
@@ -209,17 +206,17 @@ public class SnmpTransportContext extends TransportContext {
}
private List createPdus(SnmpProfileTransportConfiguration deviceProfileConfig) {
- Map> varBindingPerMethod = new HashMap<>();
+ Map> bindingsPerMethod = new HashMap<>();
- deviceProfileConfig.getKvMappings().forEach(mapping -> varBindingPerMethod
+ deviceProfileConfig.getAllMappings().forEach(mapping -> bindingsPerMethod
.computeIfAbsent(mapping.getMethod(), v -> new ArrayList<>())
.add(new VariableBinding(new OID(mapping.getOid()))));
- return varBindingPerMethod.keySet().stream()
+ return bindingsPerMethod.keySet().stream()
.map(method -> {
PDU request = new PDU();
request.setType(getSnmpMethod(method));
- request.addAll(varBindingPerMethod.get(method));
+ request.addAll(bindingsPerMethod.get(method));
return request;
})
.collect(Collectors.toList());
@@ -300,31 +297,27 @@ public class SnmpTransportContext extends TransportContext {
return profilesPdus;
}
- public Optional getAttributesMapping(DeviceProfileId deviceProfileId, OID responseOid) {
+ public Optional getAttributeMapping(DeviceProfileId deviceProfileId, OID responseOid) {
if (profilesTransportConfigs.containsKey(deviceProfileId)) {
- return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getAttributes());
+ return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getAttributesMappings());
}
return Optional.empty();
}
- public Optional getTelemetryMapping(DeviceProfileId deviceProfileId, OID responseOid) {
+ public Optional getTelemetryMapping(DeviceProfileId deviceProfileId, OID responseOid) {
if (profilesTransportConfigs.containsKey(deviceProfileId)) {
- return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getTelemetry());
+ return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getTelemetryMappings());
}
return Optional.empty();
}
- private Optional getMapping(OID responseOid, List mappings) {
+ private Optional getMapping(OID responseOid, List mappings) {
return mappings.stream()
.filter(kvMapping -> new OID(kvMapping.getOid()).equals(responseOid))
//TODO: OID shouldn't be duplicated in the config, add backend and UI verification
.findFirst();
}
- public ExecutorService getSnmpCallbackExecutor() {
- return snmpTransportService.getSnmpCallbackExecutor();
- }
-
private int getSnmpMethod(String configMethod) {
switch (configMethod) {
case "get":
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index 22e60c98ee..3e9eb63943 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -15,114 +15,74 @@
*/
package org.thingsboard.server.transport.snmp.service;
-import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonSyntaxException;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Lazy;
-import org.springframework.context.event.EventListener;
-import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
-import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.kv.DataType;
-import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
-import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
-import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.transport.snmp.SnmpTransportContext;
+import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
+import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
-import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
@TbSnmpTransportComponent
@Service
@Slf4j
public class SnmpTransportService implements TbTransportService {
private final SnmpTransportContext snmpTransportContext;
+ private final TransportService transportService;
- @Getter
- private ExecutorService snmpCallbackExecutor;
- @Getter
private Snmp snmp;
private ScheduledExecutorService pollingExecutor;
+ private ExecutorService snmpResponseProcessingExecutor;
- public SnmpTransportService(@Lazy SnmpTransportContext snmpTransportContext) {
+ public SnmpTransportService(@Lazy SnmpTransportContext snmpTransportContext,
+ TransportService transportService) {
this.snmpTransportContext = snmpTransportContext;
+ this.transportService = transportService;
}
- // @PostConstruct
- private void init() {
- log.info("Starting SNMP transport...");
+ @PostConstruct
+ private void init() throws IOException {
+ log.info("Initializing SNMP transport service");
+
pollingExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("snmp-polling"));
//TODO: Set parallelism value in the config
- snmpCallbackExecutor = Executors.newWorkStealingPool(20);
+ snmpResponseProcessingExecutor = Executors.newWorkStealingPool(20);
initializeSnmp();
- log.info("SNMP transport started!");
- }
-
- @PreDestroy
- public void shutdown() {
- log.info("Stopping SNMP transport!");
- if (pollingExecutor != null) {
- pollingExecutor.shutdownNow();
- }
- if (snmpCallbackExecutor != null) {
- snmpCallbackExecutor.shutdownNow();
- }
- if (snmp != null) {
- try {
- snmp.close();
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- }
- }
- log.info("SNMP transport stopped!");
- }
-
- @EventListener(ApplicationReadyEvent.class)
- @Order(value = 10)
- public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
- log.info("Received application ready event. Starting SNMP polling.");
-// startPolling();
+ log.info("SNMP transport service initialized");
}
- private void initializeSnmp() {
- try {
- this.snmp = new Snmp(new DefaultUdpTransportMapping());
- this.snmp.listen();
- } catch (IOException e) {
- //TODO: what should be done if transport wasn't initialized?
- log.error(e.getMessage(), e);
- }
+ private void initializeSnmp() throws IOException {
+ snmp = new Snmp(new DefaultUdpTransportMapping());
+ snmp.listen();
}
- private void startPolling() {
+ @AfterStartUp(order = 10)
+ public void startPolling() {
+ log.info("Starting SNMP polling");
//TODO: Get poll period from configuration;
int pollPeriodSeconds = 1;
@@ -141,7 +101,7 @@ public class SnmpTransportService implements TbTransportService {
DeviceProfileId deviceProfileId = sessionContext.getDeviceProfile().getId();
snmpTransportContext.getProfilesPdus().get(deviceProfileId).forEach(pdu -> {
try {
- log.debug("[{}] Sending SNMP message...", pdu.getRequestID());
+ log.debug("[{}] Sending SNMP message for device {}", pdu.getRequestID(), sessionContext.getDeviceId());
snmp.send(pdu, sessionContext.getTarget(), deviceProfileId, sessionContext);
} catch (IOException e) {
log.error(e.getMessage(), e);
@@ -152,118 +112,84 @@ public class SnmpTransportService implements TbTransportService {
public void onNewDeviceResponse(ResponseEvent responseEvent, DeviceSessionContext sessionContext) {
((Snmp) responseEvent.getSource()).cancel(responseEvent.getRequest(), sessionContext);
- snmpTransportContext.getSnmpCallbackExecutor().submit(() -> processSnmpResponse(responseEvent, sessionContext));
+ snmpResponseProcessingExecutor.submit(() -> processSnmpResponse(responseEvent, sessionContext));
}
private void processSnmpResponse(ResponseEvent event, DeviceSessionContext sessionContext) {
- PDU response = event.getResponse();
if (event.getError() != null) {
log.warn("Response error: {}", event.getError().getMessage(), event.getError());
+ return;
+ }
+
+ PDU response = event.getResponse();
+ if (response == null) {
+ log.warn("No SNMP response, requestId: {}", event.getRequest().getRequestID());
+ return;
}
- if (response != null) {
- log.debug("[{}] Processing SNMP response: {}", response.getRequestID(), response);
+ DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();
+ log.debug("[{}] Processing SNMP response for device {} with device profile {}: {}",
+ response.getRequestID(), sessionContext.getDeviceId(), deviceProfileId, response);
- DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();
- TransportService transportService = snmpTransportContext.getTransportService();
- for (int i = 0; i < response.size(); i++) {
- VariableBinding vb = response.get(i);
- snmpTransportContext.getAttributesMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
- TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(sessionContext.getToken()).build(),
- new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
- try {
- transportService.process(sessionInfo,
- convertToPostAttributes(kvMapping.getKey(), kvMapping.getType(), vb.toValueString()),
- TransportServiceCallback.EMPTY);
- reportActivity(sessionInfo);
- } catch (Exception e) {
- log.warn("Failed to process SNMP response: {}", e.getMessage(), e);
- }
- })));
- snmpTransportContext.getTelemetryMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
- TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(sessionContext.getToken()).build(),
- new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
- try {
- transportService.process(sessionInfo,
- convertToPostTelemetry(kvMapping.getKey(), kvMapping.getType(), vb.toValueString()),
- TransportServiceCallback.EMPTY);
- reportActivity(sessionInfo);
+ JsonObject telemetry = new JsonObject();
+ JsonObject attributes = new JsonObject();
- } catch (Exception e) {
- log.warn("Failed to process SNMP response: {}", e.getMessage(), e);
- }
- })));
- }
- } else {
- log.warn("No SNMP response, requestId: {}", event.getRequest().getRequestID());
+ for (int i = 0; i < response.size(); i++) {
+ VariableBinding variableBinding = response.get(i);
+ log.trace("Processing variable binding {}: {}", i, variableBinding);
+
+ snmpTransportContext.getTelemetryMapping(deviceProfileId, variableBinding.getOid()).ifPresent(mapping -> {
+ log.trace("Found telemetry mapping for oid {}: {}", variableBinding.getOid(), mapping);
+ processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), telemetry);
+ });
+
+ snmpTransportContext.getAttributeMapping(deviceProfileId, variableBinding.getOid()).ifPresent(mapping -> {
+ log.trace("Found attribute mapping for oid {}: {}", variableBinding.getOid(), mapping);
+ processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), attributes);
+ });
+ }
+
+ if (telemetry.entrySet().isEmpty() && attributes.entrySet().isEmpty()) {
+ log.warn("No telemetry or attribute values is the SNMP response for device {}", sessionContext.getDeviceId());
+ return;
}
+
+ if (!telemetry.entrySet().isEmpty()) {
+ TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(telemetry);
+ transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, TransportServiceCallback.EMPTY);
+ log.debug("Posted telemetry for device {}: {}", sessionContext.getDeviceId(), telemetry);
+ }
+
+ if (!attributes.entrySet().isEmpty()) {
+ TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(attributes);
+ transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, TransportServiceCallback.EMPTY);
+ log.debug("Posted attributes for device {}: {}", sessionContext.getDeviceId(), attributes);
+ }
+
+ reportActivity(sessionContext.getSessionInfo());
}
private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
- snmpTransportContext.getTransportService().process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
+ transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(false)
.setRpcSubscription(false)
.setLastActivityTime(System.currentTimeMillis())
.build(), TransportServiceCallback.EMPTY);
}
- private TransportProtos.PostAttributeMsg convertToPostAttributes(String keyName, DataType dataType, String payload) throws AdaptorException {
- try {
- return JsonConverter.convertToAttributesProto(getKvJson(keyName, dataType, payload));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- //TODO: change the exception type
- throw new AdaptorException(ex);
- }
- }
-
- private TransportProtos.PostTelemetryMsg convertToPostTelemetry(String keyName, DataType dataType, String payload) throws AdaptorException {
- try {
- return JsonConverter.convertToTelemetryProto(getKvJson(keyName, dataType, payload));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- //TODO: change the exception type
- throw new AdaptorException(ex);
- }
- }
-
- private JsonElement getKvJson(String keyName, DataType dataType, String payload) throws AdaptorException {
- JsonObject result = new JsonObject();
+ private void processValue(String key, DataType dataType, String value, JsonObject result) {
switch (dataType) {
case LONG:
- result.addProperty(keyName, Long.parseLong(payload));
+ result.addProperty(key, Long.parseLong(value));
break;
case BOOLEAN:
- result.addProperty(keyName, Boolean.parseBoolean(payload));
+ result.addProperty(key, Boolean.parseBoolean(value));
break;
case DOUBLE:
- result.addProperty(keyName, Double.parseDouble(payload));
- break;
- case STRING:
- result.addProperty(keyName, payload);
+ result.addProperty(key, Double.parseDouble(value));
break;
default:
- //TODO: change the exception type
- throw new AdaptorException("Unsupported data type");
- }
- return new JsonParser().parse(result.toString());
- }
-
- @AllArgsConstructor
- private static class DeviceAuthCallback implements TransportServiceCallback {
- private final TransportContext transportContext;
- private final Consumer onSuccess;
-
- @Override
- public void onSuccess(ValidateDeviceCredentialsResponse msg) {
- if (msg.hasDeviceInfo()) {
- onSuccess.accept(SessionInfoCreator.create(msg, transportContext, UUID.randomUUID()));
- } else {
- log.warn("Failed to process device auth");
- }
- }
-
- @Override
- public void onError(Throwable e) {
- log.warn("Failed to process device auth", e);
+ result.addProperty(key, value);
}
}
@@ -271,4 +197,23 @@ public class SnmpTransportService implements TbTransportService {
public String getName() {
return "SNMP";
}
+
+ @PreDestroy
+ public void shutdown() {
+ log.info("Stopping SNMP transport!");
+ if (pollingExecutor != null) {
+ pollingExecutor.shutdownNow();
+ }
+ if (snmpResponseProcessingExecutor != null) {
+ snmpResponseProcessingExecutor.shutdownNow();
+ }
+ if (snmp != null) {
+ try {
+ snmp.close();
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ log.info("SNMP transport stopped!");
+ }
}
From fc06e8ac998ae426af4a21b0fe713b5f9a380285 Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Wed, 24 Mar 2021 16:43:28 +0200
Subject: [PATCH 07/32] Provide SNMP transport configuration validation;
refactor
---
.../data/DeviceTransportConfiguration.java | 3 +
.../SnmpDeviceTransportConfiguration.java | 19 +++--
.../DeviceProfileTransportConfiguration.java | 6 +-
...pDeviceProfileTransportConfiguration.java} | 24 +++++--
.../data/transport/snmp/SnmpMapping.java | 39 ++++++++++
.../data/transport/snmp/SnmpMethod.java | 32 +++++++++
.../snmp/SnmpProtocolVersion.java} | 23 +++---
.../transport/snmp/SnmpTransportContext.java | 71 ++++++++-----------
.../snmp/service/SnmpTransportService.java | 12 +++-
.../snmp/session/DeviceSessionContext.java | 25 ++-----
.../dao/device/DeviceProfileServiceImpl.java | 1 +
.../server/dao/device/DeviceServiceImpl.java | 4 ++
12 files changed, 173 insertions(+), 86 deletions(-)
rename common/data/src/main/java/org/thingsboard/server/common/data/device/profile/{SnmpProfileTransportConfiguration.java => SnmpDeviceProfileTransportConfiguration.java} (60%)
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java
rename common/data/src/main/java/org/thingsboard/server/common/data/{device/profile/SnmpMapping.java => transport/snmp/SnmpProtocolVersion.java} (68%)
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
index 49a547371f..63b31eb3a2 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
@@ -38,4 +38,7 @@ public interface DeviceTransportConfiguration extends Serializable {
@JsonIgnore
DeviceTransportType getType();
+ default void validate() {
+ }
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
index 3db31b4f2a..551bf2fcfb 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
@@ -19,25 +19,30 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
@Data
public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
-
private String address;
private int port;
private String community;
- private String protocolVersion;
+ private SnmpProtocolVersion protocolVersion;
@Override
public DeviceTransportType getType() {
return DeviceTransportType.SNMP;
}
+ @Override
+ public void validate() {
+ if (!isValid()) {
+ throw new IllegalArgumentException("Transport configuration is not valid");
+ }
+ }
+
@JsonIgnore
- public boolean isValid() {
- return StringUtils.isNotEmpty(this.address)
- && this.port > 0
- && StringUtils.isNotEmpty(this.community)
- && StringUtils.isNotEmpty(this.protocolVersion);
+ private boolean isValid() {
+ return StringUtils.isNotBlank(address) && port > 0 &&
+ StringUtils.isNotBlank(community) && protocolVersion != null;
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
index ffbe5f7048..2fea8d34ad 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
@@ -30,10 +31,13 @@ import org.thingsboard.server.common.data.DeviceTransportType;
@JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
@JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
@JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"),
- @JsonSubTypes.Type(value = SnmpProfileTransportConfiguration.class, name = "SNMP")})
+ @JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP")})
public interface DeviceProfileTransportConfiguration {
@JsonIgnore
DeviceTransportType getType();
+ default void validate() {
+ }
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
similarity index 60%
rename from common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
index fb6b46fbb6..a4f7cc57de 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
@@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.device.profile;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import java.util.Collections;
import java.util.List;
@@ -25,12 +26,12 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
@Data
-public class SnmpProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
+public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
private int pollPeriodMs;
private int timeoutMs;
private int retries;
- private List attributesMappings;
private List telemetryMappings;
+ private List attributesMappings;
@Override
public DeviceTransportType getType() {
@@ -39,10 +40,25 @@ public class SnmpProfileTransportConfiguration implements DeviceProfileTransport
@JsonIgnore
public List getAllMappings() {
- if (attributesMappings != null && telemetryMappings != null) {
- return Stream.concat(attributesMappings.stream(), telemetryMappings.stream()).collect(Collectors.toList());
+ if (telemetryMappings != null && attributesMappings != null) {
+ return Stream.concat(telemetryMappings.stream(), attributesMappings.stream()).collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}
+
+ @Override
+ public void validate() {
+ if (!isValid()) {
+ throw new IllegalArgumentException("Transport configuration is not valid");
+ }
+ }
+
+ @JsonIgnore
+ private boolean isValid() {
+ List mappings = getAllMappings();
+ return pollPeriodMs > 0 && timeoutMs > 0 && retries >= 0 &&
+ !mappings.isEmpty() && mappings.stream().allMatch(SnmpMapping::isValid) &&
+ mappings.stream().map(SnmpMapping::getOid).distinct().count() == mappings.size();
+ }
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
new file mode 100644
index 0000000000..32a3f2a25b
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+import org.thingsboard.server.common.data.kv.DataType;
+
+import java.util.regex.Pattern;
+
+@Data
+public class SnmpMapping {
+ private String oid;
+ private SnmpMethod method;
+ private String key;
+ private DataType dataType;
+
+ private static final Pattern OID_PATTERN = Pattern.compile("^\\.?([0-2])((\\.0)|(\\.[1-9][0-9]*))*$");
+
+ @JsonIgnore
+ public boolean isValid() {
+ return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && method != null &&
+ StringUtils.isNotBlank(key) && dataType != null;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java
new file mode 100644
index 0000000000..2a04ce427b
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp;
+
+public enum SnmpMethod {
+ GET(-96),
+ SET(-93);
+
+ // codes taken from org.snmp4j.PDU class
+ private final int code;
+
+ SnmpMethod(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java
similarity index 68%
rename from common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java
index 6bd0ca6b4d..d16836cf1a 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpMapping.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java
@@ -13,15 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.data.device.profile;
+package org.thingsboard.server.common.data.transport.snmp;
-import lombok.Data;
-import org.thingsboard.server.common.data.kv.DataType;
+public enum SnmpProtocolVersion {
+ V1(0),
+ V2C(1),
+ V3(3);
-@Data
-public class SnmpMapping {
- private String oid;
- private String method;
- private String key;
- private DataType dataType;
+ private final int code;
+
+ SnmpProtocolVersion(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index df2582005a..beaaef829f 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -27,12 +27,13 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
-import org.thingsboard.server.common.data.device.profile.SnmpMapping;
-import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.transport.DeviceUpdatedEvent;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportDeviceProfileCache;
@@ -74,7 +75,7 @@ public class SnmpTransportContext extends TransportContext {
private final SnmpTransportBalancingService balancingService;
private final Map sessions = new ConcurrentHashMap<>();
- private final Map profilesTransportConfigs = new ConcurrentHashMap<>();
+ private final Map profilesTransportConfigs = new ConcurrentHashMap<>();
private final Map> profilesPdus = new ConcurrentHashMap<>();
private Collection allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
@@ -94,7 +95,13 @@ public class SnmpTransportContext extends TransportContext {
managedDevicesIds.stream()
.map(protoEntityService::getDeviceById)
.collect(Collectors.toList())
- .forEach(this::establishDeviceSession);
+ .forEach(device -> {
+ try {
+ establishDeviceSession(device);
+ } catch (Exception e) {
+ log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.getMessage());
+ }
+ });
}
private void establishDeviceSession(Device device) {
@@ -110,13 +117,10 @@ public class SnmpTransportContext extends TransportContext {
return;
}
+ SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
- if (!deviceTransportConfiguration.isValid()) {
- log.warn("SNMP device transport configuration is not valid");
- return;
- }
- SnmpProfileTransportConfiguration profileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ profilesTransportConfigs.put(deviceProfileId, profileTransportConfiguration);
profilesPdus.computeIfAbsent(deviceProfileId, id -> createPdus(profileTransportConfiguration));
DeviceSessionContext deviceSessionContext = new DeviceSessionContext(
@@ -140,22 +144,18 @@ public class SnmpTransportContext extends TransportContext {
return;
}
- SnmpProfileTransportConfiguration profileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
- SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
- sessionContext.setProfileTransportConfiguration(profileTransportConfiguration);
- sessionContext.setDeviceTransportConfiguration(deviceTransportConfiguration);
- if (!deviceTransportConfiguration.isValid()) {
- log.warn("SNMP device transport configuration is not valid");
- destroyDeviceSession(sessionContext);
- return;
- }
+ SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
+
+ if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
+ profilesPdus.put(deviceProfileId, createPdus(newProfileTransportConfiguration));
+ profilesTransportConfigs.put(deviceProfileId, newProfileTransportConfiguration);
- if (!profileTransportConfiguration.equals(profilesTransportConfigs.get(deviceProfileId))) {
- profilesPdus.put(deviceProfileId, createPdus(profileTransportConfiguration));
- profilesTransportConfigs.put(deviceProfileId, profileTransportConfiguration);
- sessionContext.initTarget(profileTransportConfiguration, deviceTransportConfiguration);
- } else if (!deviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
- sessionContext.initTarget(profileTransportConfiguration, deviceTransportConfiguration);
+ sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
+ sessionContext.initTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
+ } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
+ sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
+ sessionContext.initTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
} else {
log.trace("Configuration of the device {} was not updated", device);
}
@@ -205,8 +205,8 @@ public class SnmpTransportContext extends TransportContext {
});
}
- private List createPdus(SnmpProfileTransportConfiguration deviceProfileConfig) {
- Map> bindingsPerMethod = new HashMap<>();
+ private List createPdus(SnmpDeviceProfileTransportConfiguration deviceProfileConfig) {
+ Map> bindingsPerMethod = new HashMap<>();
deviceProfileConfig.getAllMappings().forEach(mapping -> bindingsPerMethod
.computeIfAbsent(mapping.getMethod(), v -> new ArrayList<>())
@@ -215,7 +215,7 @@ public class SnmpTransportContext extends TransportContext {
return bindingsPerMethod.keySet().stream()
.map(method -> {
PDU request = new PDU();
- request.setType(getSnmpMethod(method));
+ request.setType(method.getCode());
request.addAll(bindingsPerMethod.get(method));
return request;
})
@@ -311,22 +311,9 @@ public class SnmpTransportContext extends TransportContext {
return Optional.empty();
}
- private Optional getMapping(OID responseOid, List mappings) {
+ private Optional getMapping(OID oid, List mappings) {
return mappings.stream()
- .filter(kvMapping -> new OID(kvMapping.getOid()).equals(responseOid))
- //TODO: OID shouldn't be duplicated in the config, add backend and UI verification
+ .filter(mapping -> new OID(mapping.getOid()).equals(oid))
.findFirst();
}
-
- private int getSnmpMethod(String configMethod) {
- switch (configMethod) {
- case "get":
- return PDU.GET;
- case "getNext":
- case "response":
- case "set":
- default:
- return -1;
- }
- }
}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index 3e9eb63943..86d2cead95 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.smi.Null;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.context.annotation.Lazy;
@@ -103,8 +104,8 @@ public class SnmpTransportService implements TbTransportService {
try {
log.debug("[{}] Sending SNMP message for device {}", pdu.getRequestID(), sessionContext.getDeviceId());
snmp.send(pdu, sessionContext.getTarget(), deviceProfileId, sessionContext);
- } catch (IOException e) {
- log.error(e.getMessage(), e);
+ } catch (Exception e) {
+ log.error("Failed to send SNMP request: {}", e.getMessage());
}
});
}
@@ -123,7 +124,7 @@ public class SnmpTransportService implements TbTransportService {
PDU response = event.getResponse();
if (response == null) {
- log.warn("No SNMP response, requestId: {}", event.getRequest().getRequestID());
+ log.warn("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
return;
}
@@ -138,6 +139,11 @@ public class SnmpTransportService implements TbTransportService {
VariableBinding variableBinding = response.get(i);
log.trace("Processing variable binding {}: {}", i, variableBinding);
+ if (variableBinding.getVariable() instanceof Null) {
+ log.debug("Response variable is empty");
+ continue;
+ }
+
snmpTransportContext.getTelemetryMapping(deviceProfileId, variableBinding.getOid()).ifPresent(mapping -> {
log.trace("Found telemetry mapping for oid {}: {}", variableBinding.getOid(), mapping);
processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), telemetry);
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
index ed87bbc726..3cfc9bc041 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
@@ -22,13 +22,12 @@ import org.snmp4j.CommunityTarget;
import org.snmp4j.Target;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
-import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OctetString;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
-import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
@@ -51,7 +50,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
private final String token;
@Getter
@Setter
- private SnmpProfileTransportConfiguration profileTransportConfiguration;
+ private SnmpDeviceProfileTransportConfiguration profileTransportConfiguration;
@Getter
@Setter
private SnmpDeviceTransportConfiguration deviceTransportConfiguration;
@@ -79,7 +78,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
this.snmpTransportContext = snmpTransportContext;
this.snmpTransportService = snmpTransportService;
- this.profileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ this.profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
this.deviceTransportConfiguration = deviceTransportConfiguration;
initTarget(this.profileTransportConfiguration, this.deviceTransportConfiguration);
@@ -105,11 +104,11 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
}
}
- public void initTarget(SnmpProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
+ public void initTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
log.trace("Initializing target for SNMP session of device {}", device);
CommunityTarget communityTarget = new CommunityTarget();
communityTarget.setAddress(GenericAddress.parse(GenericAddress.TYPE_UDP + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
- communityTarget.setVersion(getSnmpVersion(deviceTransportConfig.getProtocolVersion()));
+ communityTarget.setVersion(deviceTransportConfig.getProtocolVersion().getCode());
communityTarget.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
communityTarget.setTimeout(profileTransportConfig.getTimeoutMs());
communityTarget.setRetries(profileTransportConfig.getRetries());
@@ -125,20 +124,6 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
return token;
}
- //TODO: replace with enum, wtih preliminary discussion of type version in config (string or integer)
- private int getSnmpVersion(String configSnmpVersion) {
- switch (configSnmpVersion) {
- case ("v1"):
- return SnmpConstants.version1;
- case ("v2c"):
- return SnmpConstants.version2c;
- case ("v3"):
- return SnmpConstants.version3;
- default:
- return -1;
- }
- }
-
@Override
public int nextMsgId() {
return msgIdSeq.incrementAndGet();
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
index 95f52cf55e..fea97bc903 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
@@ -350,6 +350,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
}
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
+ transportConfiguration.validate();
if (transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) {
MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
if (mqttTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
index 64b44db686..f3644d80ac 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
@@ -46,6 +46,7 @@ import org.thingsboard.server.common.data.device.credentials.BasicMqttCredential
import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration;
import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.DeviceData;
+import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
@@ -604,6 +605,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
throw new DataValidationException("Can't assign device to customer from different tenant!");
}
}
+ Optional.ofNullable(device.getDeviceData())
+ .flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration()))
+ .ifPresent(DeviceTransportConfiguration::validate);
}
};
From 306ace040cb94fdd777e392af98f772f31bb2ee5 Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Thu, 25 Mar 2021 10:06:06 +0200
Subject: [PATCH 08/32] Refactor
---
.../src/main/resources/thingsboard.yml | 5 +
.../JsonBasedTransportConfiguration.java | 39 ----
common/transport/snmp/pom.xml | 6 +
.../transport/snmp/SnmpDeviceSimulator.java | 81 ---------
.../transport/snmp/SnmpTransportContext.java | 10 +-
.../snmp/service/SnmpTransportService.java | 34 +++-
.../snmp/session/DeviceSessionContext.java | 13 +-
.../transport/snmp/SnmpDeviceSimulator.java | 168 ++++++++++++++++++
.../snmp-device-profile-transport-config.json | 27 +++
.../snmp-device-transport-config.json | 6 +
.../main/resources/device-data-config.json | 9 -
.../main/resources/device-profile-config.json | 21 ---
.../src/main/resources/tb-snmp-transport.yml | 5 +
13 files changed, 257 insertions(+), 167 deletions(-)
delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java
delete mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
create mode 100644 common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
create mode 100644 common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
create mode 100644 common/transport/snmp/src/test/resources/snmp-device-transport-config.json
delete mode 100644 transport/snmp/src/main/resources/device-data-config.json
delete mode 100644 transport/snmp/src/main/resources/device-profile-config.json
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 3f7ef449ed..2cedd30bc8 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -647,6 +647,11 @@ transport:
redis.enabled: "${LWM2M_REDIS_ENABLED:false}"
snmp:
enabled: "${SNMP_ENABLED:true}"
+ response_processing:
+ # parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices
+ parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}"
+ # to configure SNMP to work over UDP or TCP
+ underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}"
swagger:
api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}"
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java
deleted file mode 100644
index c65e9dd972..0000000000
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright © 2016-2021 The Thingsboard Authors
- *
- * 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.thingsboard.server.common.data.device;
-
-import com.fasterxml.jackson.annotation.JsonAnyGetter;
-import com.fasterxml.jackson.annotation.JsonAnySetter;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public abstract class JsonBasedTransportConfiguration {
-
- @JsonIgnore
- private final Map properties = new HashMap<>();
-
- @JsonAnyGetter
- public Map properties() {
- return this.properties;
- }
-
- @JsonAnySetter
- public void put(String name, Object value) {
- this.properties.put(name, value);
- }
-}
diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml
index ab83ab69a7..afa35b518c 100644
--- a/common/transport/snmp/pom.xml
+++ b/common/transport/snmp/pom.xml
@@ -58,5 +58,11 @@
org.snmp4j
snmp4j
+
+ org.snmp4j
+ snmp4j-agent
+ 3.3.6
+ test
+
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
deleted file mode 100644
index 7109ef4e3f..0000000000
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Copyright © 2016-2021 The Thingsboard Authors
- *
- * 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.thingsboard.server.transport.snmp;
-
-import org.snmp4j.CommunityTarget;
-import org.snmp4j.PDU;
-import org.snmp4j.Snmp;
-import org.snmp4j.Target;
-import org.snmp4j.event.ResponseEvent;
-import org.snmp4j.mp.SnmpConstants;
-import org.snmp4j.smi.GenericAddress;
-import org.snmp4j.smi.OID;
-import org.snmp4j.smi.OctetString;
-import org.snmp4j.smi.VariableBinding;
-import org.snmp4j.transport.DefaultUdpTransportMapping;
-import org.snmp4j.transport.UdpTransportMapping;
-
-import java.io.IOException;
-
-/**
- * For testing purposes. Will be removed when the time comes
- */
-public class SnmpDeviceSimulator {
- private final Target target;
- private final OID oid = new OID(".1.3.6.1.2.1.1.1.0");
- private Snmp snmp;
-
- public SnmpDeviceSimulator(int port) {
- String address = "udp:127.0.0.1/" + port;
-
- CommunityTarget target = new CommunityTarget();
- target.setCommunity(new OctetString("public"));
- target.setAddress(GenericAddress.parse(address));
- target.setRetries(2);
- target.setTimeout(1500);
- target.setVersion(SnmpConstants.version2c);
-
- this.target = target;
- }
-
- public static void main(String[] args) throws IOException {
- SnmpDeviceSimulator deviceSimulator = new SnmpDeviceSimulator(161);
-
- deviceSimulator.start();
- String response = deviceSimulator.sendRequest(PDU.GET);
-
- System.out.println(response);
- }
-
- public void start() throws IOException {
- UdpTransportMapping transport = new DefaultUdpTransportMapping();
- transport.addTransportListener((sourceTransport, incomingAddress, wholeMessage, tmStateReference) -> {
- System.out.println();
- });
- snmp = new Snmp(transport);
-
- transport.listen();
- }
-
- public String sendRequest(int pduType) throws IOException {
- PDU pdu = new PDU();
- pdu.add(new VariableBinding(oid));
- pdu.setType(pduType);
-
- ResponseEvent responseEvent = snmp.send(pdu, target);
- return responseEvent.getResponse().get(0).getVariable().toString();
- }
-}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index beaaef829f..2c1896a505 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
@@ -79,6 +80,9 @@ public class SnmpTransportContext extends TransportContext {
private final Map> profilesPdus = new ConcurrentHashMap<>();
private Collection allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
+ @Value("${transport.snmp.underlying_protocol}")
+ private String snmpUnderlyingProtocol;
+
@AfterStartUp(order = 2)
public void initDevicesSessions() {
log.info("Initializing SNMP devices sessions");
@@ -124,9 +128,9 @@ public class SnmpTransportContext extends TransportContext {
profilesPdus.computeIfAbsent(deviceProfileId, id -> createPdus(profileTransportConfiguration));
DeviceSessionContext deviceSessionContext = new DeviceSessionContext(
- device, deviceProfile,
- credentials.getCredentialsId(), deviceTransportConfiguration,
- this, snmpTransportService
+ device, deviceProfile, credentials.getCredentialsId(),
+ deviceTransportConfiguration, this,
+ snmpTransportService, snmpUnderlyingProtocol
);
registerSessionMsgListener(deviceSessionContext);
sessions.put(device.getId(), deviceSessionContext);
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index 86d2cead95..fd386e612a 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -19,10 +19,13 @@ import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
+import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
@@ -57,6 +60,11 @@ public class SnmpTransportService implements TbTransportService {
private ScheduledExecutorService pollingExecutor;
private ExecutorService snmpResponseProcessingExecutor;
+ @Value("${transport.snmp.response_processing.parallelism_level}")
+ private Integer responseProcessingParallelismLevel;
+ @Value("${transport.snmp.underlying_protocol}")
+ private String snmpUnderlyingProtocol;
+
public SnmpTransportService(@Lazy SnmpTransportContext snmpTransportContext,
TransportService transportService) {
this.snmpTransportContext = snmpTransportContext;
@@ -68,8 +76,7 @@ public class SnmpTransportService implements TbTransportService {
log.info("Initializing SNMP transport service");
pollingExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("snmp-polling"));
- //TODO: Set parallelism value in the config
- snmpResponseProcessingExecutor = Executors.newWorkStealingPool(20);
+ snmpResponseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
initializeSnmp();
@@ -77,19 +84,28 @@ public class SnmpTransportService implements TbTransportService {
}
private void initializeSnmp() throws IOException {
- snmp = new Snmp(new DefaultUdpTransportMapping());
+ TransportMapping> transportMapping;
+ switch (snmpUnderlyingProtocol) {
+ case "udp":
+ transportMapping = new DefaultUdpTransportMapping();
+ break;
+ case "tcp":
+ transportMapping = new DefaultTcpTransportMapping();
+ break;
+ default:
+ throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported");
+ }
+ snmp = new Snmp(transportMapping);
snmp.listen();
}
@AfterStartUp(order = 10)
public void startPolling() {
log.info("Starting SNMP polling");
- //TODO: Get poll period from configuration;
- int pollPeriodSeconds = 1;
pollingExecutor.scheduleWithFixedDelay(() -> {
snmpTransportContext.getSessions().forEach(this::executeSnmpRequest);
- }, 0, pollPeriodSeconds, TimeUnit.SECONDS);
+ }, 0, 1, TimeUnit.SECONDS);
}
private void executeSnmpRequest(DeviceSessionContext sessionContext) {
@@ -111,12 +127,12 @@ public class SnmpTransportService implements TbTransportService {
}
}
- public void onNewDeviceResponse(ResponseEvent responseEvent, DeviceSessionContext sessionContext) {
+ public void onNewDeviceResponse(DeviceSessionContext sessionContext, ResponseEvent responseEvent) {
((Snmp) responseEvent.getSource()).cancel(responseEvent.getRequest(), sessionContext);
- snmpResponseProcessingExecutor.submit(() -> processSnmpResponse(responseEvent, sessionContext));
+ snmpResponseProcessingExecutor.submit(() -> processSnmpResponse(sessionContext, responseEvent));
}
- private void processSnmpResponse(ResponseEvent event, DeviceSessionContext sessionContext) {
+ private void processSnmpResponse(DeviceSessionContext sessionContext, ResponseEvent event) {
if (event.getError() != null) {
log.warn("Response error: {}", event.getError().getMessage(), event.getError());
return;
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
index 3cfc9bc041..57e89ad034 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
@@ -65,10 +65,12 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
private long previousRequestExecutedAt = 0;
private final AtomicInteger msgIdSeq = new AtomicInteger(0);
private boolean isActive = true;
+ private final String snmpUnderlyingProtocol;
- public DeviceSessionContext(Device device, DeviceProfile deviceProfile,
- String token, SnmpDeviceTransportConfiguration deviceTransportConfiguration,
- SnmpTransportContext snmpTransportContext, SnmpTransportService snmpTransportService) {
+ public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token,
+ SnmpDeviceTransportConfiguration deviceTransportConfiguration,
+ SnmpTransportContext snmpTransportContext, SnmpTransportService snmpTransportService,
+ String snmpUnderlyingProtocol) {
super(UUID.randomUUID());
super.setDeviceId(device.getId());
super.setDeviceProfile(deviceProfile);
@@ -81,6 +83,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
this.profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
this.deviceTransportConfiguration = deviceTransportConfiguration;
+ this.snmpUnderlyingProtocol = snmpUnderlyingProtocol;
initTarget(this.profileTransportConfiguration, this.deviceTransportConfiguration);
}
@@ -100,14 +103,14 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
@Override
public void onResponse(ResponseEvent event) {
if (isActive) {
- snmpTransportService.onNewDeviceResponse(event, this);
+ snmpTransportService.onNewDeviceResponse(this, event);
}
}
public void initTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
log.trace("Initializing target for SNMP session of device {}", device);
CommunityTarget communityTarget = new CommunityTarget();
- communityTarget.setAddress(GenericAddress.parse(GenericAddress.TYPE_UDP + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
+ communityTarget.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
communityTarget.setVersion(deviceTransportConfig.getProtocolVersion().getCode());
communityTarget.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
communityTarget.setTimeout(profileTransportConfig.getTimeoutMs());
diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
new file mode 100644
index 0000000000..29fa35dcab
--- /dev/null
+++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
@@ -0,0 +1,168 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp;
+
+import org.snmp4j.CommunityTarget;
+import org.snmp4j.Target;
+import org.snmp4j.TransportMapping;
+import org.snmp4j.agent.BaseAgent;
+import org.snmp4j.agent.CommandProcessor;
+import org.snmp4j.agent.DuplicateRegistrationException;
+import org.snmp4j.agent.MOGroup;
+import org.snmp4j.agent.ManagedObject;
+import org.snmp4j.agent.mo.MOAccessImpl;
+import org.snmp4j.agent.mo.MOScalar;
+import org.snmp4j.agent.mo.snmp.RowStatus;
+import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
+import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
+import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
+import org.snmp4j.agent.mo.snmp.StorageType;
+import org.snmp4j.agent.mo.snmp.VacmMIB;
+import org.snmp4j.agent.security.MutableVACM;
+import org.snmp4j.mp.MPv3;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.security.SecurityModel;
+import org.snmp4j.security.USM;
+import org.snmp4j.smi.Address;
+import org.snmp4j.smi.GenericAddress;
+import org.snmp4j.smi.Integer32;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.Variable;
+import org.snmp4j.transport.TransportMappings;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Scanner;
+
+public class SnmpDeviceSimulator extends BaseAgent {
+
+ public static void main(String[] args) throws IOException {
+ SnmpDeviceSimulator device = new SnmpDeviceSimulator(1610);
+
+ device.start();
+ device.setUpMappings(Map.of(
+ ".1.3.6.1.2.1.1.1.50", "12",
+ ".1.3.6.1.2.1.2.1.52", "56",
+ ".1.3.6.1.2.1.3.1.54", "yes"
+ ));
+
+ new Scanner(System.in).nextLine();
+ }
+
+
+ private final Target target;
+ private final Address address;
+
+ public SnmpDeviceSimulator(int port) {
+ super(new File("conf.agent"), new File("bootCounter.agent"),
+ new CommandProcessor(new OctetString(MPv3.createLocalEngineID())));
+
+ CommunityTarget target = new CommunityTarget();
+ target.setCommunity(new OctetString("public"));
+ address = GenericAddress.parse("udp:0.0.0.0/" + port);
+ target.setAddress(address);
+ target.setRetries(2);
+ target.setTimeout(1500);
+ target.setVersion(SnmpConstants.version2c);
+ this.target = target;
+ }
+
+ public void start() throws IOException {
+ init();
+ addShutdownHook();
+ getServer().addContext(new OctetString("public"));
+ finishInit();
+ run();
+ sendColdStartNotification();
+ }
+
+ public void setUpMappings(Map oidToResponseMappings) {
+ unregisterManagedObject(getSnmpv2MIB());
+ oidToResponseMappings.forEach((oid, response) -> {
+ registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(response)));
+ });
+ }
+
+ public Target getTarget() {
+ return target;
+ }
+
+ @Override
+ protected void registerManagedObjects() {
+ }
+
+ protected void registerManagedObject(ManagedObject mo) {
+ try {
+ server.register(mo, null);
+ } catch (DuplicateRegistrationException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ protected void unregisterManagedObject(MOGroup moGroup) {
+ moGroup.unregisterMOs(server, getContext(moGroup));
+ }
+
+ @Override
+ protected void addNotificationTargets(SnmpTargetMIB targetMIB,
+ SnmpNotificationMIB notificationMIB) {
+ }
+
+ @Override
+ protected void addViews(VacmMIB vacm) {
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c, new OctetString(
+ "cpublic"), new OctetString("v1v2group"),
+ StorageType.nonVolatile);
+
+ vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"),
+ SecurityModel.SECURITY_MODEL_ANY, SecurityLevel.NOAUTH_NOPRIV,
+ MutableVACM.VACM_MATCH_EXACT, new OctetString("fullReadView"),
+ new OctetString("fullWriteView"), new OctetString(
+ "fullNotifyView"), StorageType.nonVolatile);
+
+ vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"),
+ new OctetString(), VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+ }
+
+ protected void addUsmUser(USM usm) {
+ }
+
+ protected void initTransportMappings() {
+ transportMappings = new TransportMapping[]{TransportMappings.getInstance().createTransportMapping(address)};
+ }
+
+ protected void unregisterManagedObjects() {
+ }
+
+ protected void addCommunities(SnmpCommunityMIB communityMIB) {
+ Variable[] com2sec = new Variable[]{
+ new OctetString("public"),
+ new OctetString("cpublic"),
+ getAgent().getContextEngineID(),
+ new OctetString("public"),
+ new OctetString(),
+ new Integer32(StorageType.nonVolatile),
+ new Integer32(RowStatus.active)
+ };
+ SnmpCommunityMIB.SnmpCommunityEntryRow row = communityMIB.getSnmpCommunityEntry().createRow(
+ new OctetString("public2public").toSubIndex(true), com2sec);
+ communityMIB.getSnmpCommunityEntry().addRow(row);
+ }
+}
diff --git a/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json b/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
new file mode 100644
index 0000000000..78b66c4c0e
--- /dev/null
+++ b/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
@@ -0,0 +1,27 @@
+{
+ "pollPeriodMs": 3000,
+ "timeoutMs": 500,
+ "retries": 0,
+ "telemetryMappings": [
+ {
+ "oid": ".1.3.6.1.2.1.1.1.50",
+ "method": "GET",
+ "key": "temperature",
+ "dataType": "LONG"
+ },
+ {
+ "oid": ".1.3.6.1.2.1.2.1.52",
+ "method": "GET",
+ "key": "humidity",
+ "dataType": "LONG"
+ }
+ ],
+ "attributesMappings": [
+ {
+ "oid": ".1.3.6.1.2.1.3.1.54",
+ "method": "GET",
+ "key": "isCool",
+ "dataType": "STRING"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/common/transport/snmp/src/test/resources/snmp-device-transport-config.json b/common/transport/snmp/src/test/resources/snmp-device-transport-config.json
new file mode 100644
index 0000000000..c73d817bfb
--- /dev/null
+++ b/common/transport/snmp/src/test/resources/snmp-device-transport-config.json
@@ -0,0 +1,6 @@
+{
+ "address": "127.0.0.1",
+ "port": 1610,
+ "community": "public",
+ "protocolVersion": "V2C"
+}
\ No newline at end of file
diff --git a/transport/snmp/src/main/resources/device-data-config.json b/transport/snmp/src/main/resources/device-data-config.json
deleted file mode 100644
index e425ccc3a6..0000000000
--- a/transport/snmp/src/main/resources/device-data-config.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "deviceName": "Thermostat T1",
- "snmpConfig": {
- "address": "192.168.1.2",
- "port": 161,
- "community": "U5J=$HWj6f@7",
- "protocolVersion": "v2c"
- }
-}
diff --git a/transport/snmp/src/main/resources/device-profile-config.json b/transport/snmp/src/main/resources/device-profile-config.json
deleted file mode 100644
index b272913045..0000000000
--- a/transport/snmp/src/main/resources/device-profile-config.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "poolPeriodMs": 10000,
- "timeoutMs": 5000,
- "retries": 5,
- "attributes": [
- {
- "key": "snmpNodeManagerEmail",
- "type": "STRING",
- "method": "get",
- "oid": ".1.3.6.1.2.1.1.4.0"
- }
- ],
- "telemetry": [
- {
- "key": "snmpNodeSysUpTime",
- "type": "LONG",
- "method": "get",
- "oid": ".1.3.6.1.2.1.1.3.0"
- }
- ]
-}
\ No newline at end of file
diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml
index 9f6bd131ac..db9f79b51a 100644
--- a/transport/snmp/src/main/resources/tb-snmp-transport.yml
+++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml
@@ -189,6 +189,11 @@ transport:
# Local LwM2M transport parameters
snmp:
enabled: "${SNMP_ENABLED:true}"
+ response_processing:
+ # parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices
+ parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}"
+ # to configure SNMP to work over UDP or TCP
+ underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}"
queue:
type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ)
From 61341a7cafdf9cbbe59fc8b31bc7827b1ad19382 Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Thu, 1 Apr 2021 15:50:37 +0300
Subject: [PATCH 09/32] Refactor, implement SNMP traps, add support for SNMP v3
targets
---
.../SnmpDeviceTransportConfiguration.java | 6 +-
...mpDeviceProfileTransportConfiguration.java | 23 +-
.../transport/snmp/SnmpCommunicationSpec.java | 40 +++
.../data/transport/snmp/SnmpMapping.java | 3 +-
...ibutesQueryingSnmpCommunicationConfig.java | 25 ++
...TrapsReceivingSnmpCommunicationConfig.java | 25 ++
...eatingQueryingSnmpCommunicationConfig.java | 36 +++
...ributesSettingSnmpCommunicationConfig.java | 36 +++
.../snmp/configs/SnmpCommunicationConfig.java | 62 +++++
...emetryQueryingSnmpCommunicationConfig.java | 29 ++
...TrapsReceivingSnmpCommunicationConfig.java | 30 ++
.../transport/snmp/SnmpTransportContext.java | 83 +-----
.../snmp/service/SnmpTransportService.java | 263 ++++++++++++++----
.../snmp/session/DeviceSessionContext.java | 105 ++++++-
...ulator.java => SnmpDeviceSimulatorV2.java} | 76 ++++-
.../transport/snmp/SnmpDeviceSimulatorV3.java | 52 ++++
.../server/transport/snmp/SnmpTest.java | 49 ++++
.../snmp-device-profile-transport-config.json | 62 +++--
.../snmp-device-transport-config.json | 2 +-
.../transport/adaptor/JsonConverter.java | 2 +-
20 files changed, 810 insertions(+), 199 deletions(-)
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesQueryingSnmpCommunicationConfig.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryQueryingSnmpCommunicationConfig.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java
rename common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/{SnmpDeviceSimulator.java => SnmpDeviceSimulatorV2.java} (69%)
create mode 100644 common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java
create mode 100644 common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTest.java
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
index 551bf2fcfb..a6056b4188 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
@@ -25,8 +25,10 @@ import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
private String address;
private int port;
- private String community;
private SnmpProtocolVersion protocolVersion;
+ private String securityName;
+ private String authenticationPassphrase; // for SNMP v3
+ private String privacyPassphrase; // for SNMP v3
@Override
public DeviceTransportType getType() {
@@ -43,6 +45,6 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur
@JsonIgnore
private boolean isValid() {
return StringUtils.isNotBlank(address) && port > 0 &&
- StringUtils.isNotBlank(community) && protocolVersion != null;
+ StringUtils.isNotBlank(securityName) && protocolVersion != null;
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
index a4f7cc57de..843a51304e 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
@@ -18,35 +18,21 @@ package org.thingsboard.server.common.data.device.profile;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.thingsboard.server.common.data.DeviceTransportType;
-import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig;
-import java.util.Collections;
import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
@Data
public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
- private int pollPeriodMs;
private int timeoutMs;
private int retries;
- private List telemetryMappings;
- private List attributesMappings;
+ private List communicationConfigs;
@Override
public DeviceTransportType getType() {
return DeviceTransportType.SNMP;
}
- @JsonIgnore
- public List getAllMappings() {
- if (telemetryMappings != null && attributesMappings != null) {
- return Stream.concat(telemetryMappings.stream(), attributesMappings.stream()).collect(Collectors.toList());
- } else {
- return Collections.emptyList();
- }
- }
-
@Override
public void validate() {
if (!isValid()) {
@@ -56,9 +42,6 @@ public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTra
@JsonIgnore
private boolean isValid() {
- List mappings = getAllMappings();
- return pollPeriodMs > 0 && timeoutMs > 0 && retries >= 0 &&
- !mappings.isEmpty() && mappings.stream().allMatch(SnmpMapping::isValid) &&
- mappings.stream().map(SnmpMapping::getOid).distinct().count() == mappings.size();
+ return true;
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
new file mode 100644
index 0000000000..f6ff7d3109
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp;
+
+public enum SnmpCommunicationSpec {
+ TELEMETRY_QUERYING(true),
+ CLIENT_ATTRIBUTES_QUERYING(true),
+
+ SHARED_ATTRIBUTES_SETTING,
+
+ TELEMETRY_TRAPS_RECEIVING,
+ CLIENT_ATTRIBUTES_TRAPS_RECEIVING;
+
+ private final boolean isRepeatingQuerying;
+
+ SnmpCommunicationSpec() {
+ this.isRepeatingQuerying = false;
+ }
+
+ SnmpCommunicationSpec(boolean isRepeatingQuerying) {
+ this.isRepeatingQuerying = isRepeatingQuerying;
+ }
+
+ public boolean isRepeatingQuerying() {
+ return isRepeatingQuerying;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
index 32a3f2a25b..0269491461 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
@@ -25,7 +25,6 @@ import java.util.regex.Pattern;
@Data
public class SnmpMapping {
private String oid;
- private SnmpMethod method;
private String key;
private DataType dataType;
@@ -33,7 +32,7 @@ public class SnmpMapping {
@JsonIgnore
public boolean isValid() {
- return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && method != null &&
+ return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() &&
StringUtils.isNotBlank(key) && dataType != null;
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesQueryingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesQueryingSnmpCommunicationConfig.java
new file mode 100644
index 0000000000..16198f8ef6
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesQueryingSnmpCommunicationConfig.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.configs;
+
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+
+public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
+ @Override
+ public SnmpCommunicationSpec getSpec() {
+ return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java
new file mode 100644
index 0000000000..9a11b01f1b
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.configs;
+
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+
+public class ClientAttributesTrapsReceivingSnmpCommunicationConfig extends SnmpCommunicationConfig {
+ @Override
+ public SnmpCommunicationSpec getSpec() {
+ return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_TRAPS_RECEIVING;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java
new file mode 100644
index 0000000000..764bf97b15
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.configs;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public abstract class RepeatingQueryingSnmpCommunicationConfig extends SnmpCommunicationConfig {
+ private Long queryingFrequencyMs;
+
+ @Override
+ public SnmpMethod getMethod() {
+ return SnmpMethod.GET;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java
new file mode 100644
index 0000000000..1c63dae618
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.configs;
+
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
+
+public class SharedAttributesSettingSnmpCommunicationConfig extends SnmpCommunicationConfig {
+ @Override
+ public SnmpCommunicationSpec getSpec() {
+ return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING;
+ }
+
+ @Override
+ public SnmpMethod getMethod() {
+ return SnmpMethod.SET;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
new file mode 100644
index 0000000000..bfc194669b
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.configs;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
+
+import java.util.List;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "spec")
+@JsonSubTypes({
+ @Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"),
+ @Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"),
+ @Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING"),
+ @Type(value = TelemetryTrapsReceivingSnmpCommunicationConfig.class, name = "TELEMETRY_TRAPS_RECEIVING"),
+ @Type(value = ClientAttributesTrapsReceivingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_TRAPS_RECEIVING")
+})
+public abstract class SnmpCommunicationConfig {
+ protected List mappings;
+
+ public List getMappings() {
+ return mappings;
+ }
+
+ public abstract SnmpCommunicationSpec getSpec();
+
+ @JsonIgnore
+ public SnmpMethod getMethod() {
+ return null;
+ }
+
+ @JsonIgnore
+ public boolean isValid() {
+ return true;
+ }
+
+ public void validate() {
+ if (!isValid()) {
+ throw new IllegalArgumentException("Communication config is not valid");
+ }
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryQueryingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryQueryingSnmpCommunicationConfig.java
new file mode 100644
index 0000000000..68d3c81ed2
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryQueryingSnmpCommunicationConfig.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.configs;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
+ @Override
+ public SnmpCommunicationSpec getSpec() {
+ return SnmpCommunicationSpec.TELEMETRY_QUERYING;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java
new file mode 100644
index 0000000000..66be4d0cf8
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.configs;
+
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+
+public class TelemetryTrapsReceivingSnmpCommunicationConfig extends SnmpCommunicationConfig {
+ @Override
+ public SnmpCommunicationSpec getSpec() {
+ return SnmpCommunicationSpec.TELEMETRY_TRAPS_RECEIVING;
+ }
+
+ @Override
+ public boolean isValid() {
+ return false;
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index 2c1896a505..877d4f1652 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -17,9 +17,6 @@ package org.thingsboard.server.transport.snmp;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.snmp4j.PDU;
-import org.snmp4j.smi.OID;
-import org.snmp4j.smi.VariableBinding;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@@ -33,8 +30,6 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
-import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
-import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.transport.DeviceUpdatedEvent;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportDeviceProfileCache;
@@ -42,7 +37,6 @@ import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
-import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.queue.util.AfterStartUp;
@@ -52,9 +46,7 @@ import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingServi
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -76,8 +68,6 @@ public class SnmpTransportContext extends TransportContext {
private final SnmpTransportBalancingService balancingService;
private final Map sessions = new ConcurrentHashMap<>();
- private final Map profilesTransportConfigs = new ConcurrentHashMap<>();
- private final Map> profilesPdus = new ConcurrentHashMap<>();
private Collection allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
@Value("${transport.snmp.underlying_protocol}")
@@ -124,22 +114,19 @@ public class SnmpTransportContext extends TransportContext {
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
- profilesTransportConfigs.put(deviceProfileId, profileTransportConfiguration);
- profilesPdus.computeIfAbsent(deviceProfileId, id -> createPdus(profileTransportConfiguration));
-
DeviceSessionContext deviceSessionContext = new DeviceSessionContext(
device, deviceProfile, credentials.getCredentialsId(),
- deviceTransportConfiguration, this,
- snmpTransportService, snmpUnderlyingProtocol
+ profileTransportConfiguration, deviceTransportConfiguration,
+ this, snmpTransportService, snmpUnderlyingProtocol
);
registerSessionMsgListener(deviceSessionContext);
sessions.put(device.getId(), deviceSessionContext);
+ snmpTransportService.createQueryingTasks(deviceSessionContext);
log.info("Established SNMP device session for device {}", device.getId());
}
private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) {
log.info("Updating SNMP device session for device {}", device.getId());
- DeviceProfileId deviceProfileId = deviceProfile.getId();
DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
@@ -152,14 +139,13 @@ public class SnmpTransportContext extends TransportContext {
SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
- profilesPdus.put(deviceProfileId, createPdus(newProfileTransportConfiguration));
- profilesTransportConfigs.put(deviceProfileId, newProfileTransportConfiguration);
-
sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
- sessionContext.initTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
+ sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
+ snmpTransportService.cancelQueryingTasks(sessionContext);
+ snmpTransportService.createQueryingTasks(sessionContext);
} else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
- sessionContext.initTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
+ sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
} else {
log.trace("Configuration of the device {} was not updated", device);
}
@@ -171,22 +157,14 @@ public class SnmpTransportContext extends TransportContext {
sessionContext.close();
transportService.deregisterSession(sessionContext.getSessionInfo());
sessions.remove(sessionContext.getDeviceId());
- log.trace("Deregistered and removed session");
-
- DeviceProfileId deviceProfileId = sessionContext.getDeviceProfile().getId();
- if (sessions.values().stream()
- .map(DeviceAwareSessionContext::getDeviceProfile)
- .noneMatch(deviceProfile -> deviceProfile.getId().equals(deviceProfileId))) {
- log.trace("Removed values for device profile {} from configs and pdus caches", deviceProfileId);
- profilesTransportConfigs.remove(deviceProfileId);
- profilesPdus.remove(deviceProfileId);
- }
+ snmpTransportService.cancelQueryingTasks(sessionContext);
+ log.trace("Unregistered and removed session");
}
private void registerSessionMsgListener(DeviceSessionContext deviceSessionContext) {
transportService.process(DeviceTransportType.SNMP,
TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceSessionContext.getToken()).build(),
- new TransportServiceCallback() {
+ new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
if (msg.hasDeviceInfo()) {
@@ -195,6 +173,7 @@ public class SnmpTransportContext extends TransportContext {
);
transportService.registerAsyncSession(sessionInfo, deviceSessionContext);
+ transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
deviceSessionContext.setSessionInfo(sessionInfo);
deviceSessionContext.setDeviceInfo(msg.getDeviceInfo());
} else {
@@ -209,23 +188,6 @@ public class SnmpTransportContext extends TransportContext {
});
}
- private List createPdus(SnmpDeviceProfileTransportConfiguration deviceProfileConfig) {
- Map> bindingsPerMethod = new HashMap<>();
-
- deviceProfileConfig.getAllMappings().forEach(mapping -> bindingsPerMethod
- .computeIfAbsent(mapping.getMethod(), v -> new ArrayList<>())
- .add(new VariableBinding(new OID(mapping.getOid()))));
-
- return bindingsPerMethod.keySet().stream()
- .map(method -> {
- PDU request = new PDU();
- request.setType(method.getCode());
- request.addAll(bindingsPerMethod.get(method));
- return request;
- })
- .collect(Collectors.toList());
- }
-
@EventListener(DeviceUpdatedEvent.class)
public void onDeviceUpdatedOrCreated(DeviceUpdatedEvent deviceUpdatedEvent) {
Device device = deviceUpdatedEvent.getDevice();
@@ -297,27 +259,4 @@ public class SnmpTransportContext extends TransportContext {
return sessions.values();
}
- public Map> getProfilesPdus() {
- return profilesPdus;
- }
-
- public Optional getAttributeMapping(DeviceProfileId deviceProfileId, OID responseOid) {
- if (profilesTransportConfigs.containsKey(deviceProfileId)) {
- return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getAttributesMappings());
- }
- return Optional.empty();
- }
-
- public Optional getTelemetryMapping(DeviceProfileId deviceProfileId, OID responseOid) {
- if (profilesTransportConfigs.containsKey(deviceProfileId)) {
- return getMapping(responseOid, profilesTransportConfigs.get(deviceProfileId).getTelemetryMappings());
- }
- return Optional.empty();
- }
-
- private Optional getMapping(OID oid, List mappings) {
- return mappings.stream()
- .filter(mapping -> new OID(mapping.getOid()).equals(oid))
- .findFirst();
- }
}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index fd386e612a..963b5db382 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -16,12 +16,20 @@
package org.thingsboard.server.transport.snmp.service;
import com.google.gson.JsonObject;
+import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.snmp4j.CommandResponder;
+import org.snmp4j.CommandResponderEvent;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.Null;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.TcpAddress;
+import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
@@ -32,11 +40,14 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.kv.DataType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+import org.thingsboard.server.common.data.transport.snmp.configs.RepeatingQueryingSnmpCommunicationConfig;
+import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
@@ -44,21 +55,33 @@ import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
@TbSnmpTransportComponent
@Service
@Slf4j
-public class SnmpTransportService implements TbTransportService {
+public class SnmpTransportService implements TbTransportService, CommandResponder {
private final SnmpTransportContext snmpTransportContext;
private final TransportService transportService;
+ @Getter
private Snmp snmp;
- private ScheduledExecutorService pollingExecutor;
- private ExecutorService snmpResponseProcessingExecutor;
+ private ScheduledExecutorService queryingExecutor;
+ private ExecutorService responseProcessingExecutor;
+
+ private final Map> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class);
@Value("${transport.snmp.response_processing.parallelism_level}")
private Integer responseProcessingParallelismLevel;
@@ -75,10 +98,12 @@ public class SnmpTransportService implements TbTransportService {
private void init() throws IOException {
log.info("Initializing SNMP transport service");
- pollingExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("snmp-polling"));
- snmpResponseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
+ queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying"));
+ responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
initializeSnmp();
+ initializeTrapsListener();
+ configureResponseProcessors();
log.info("SNMP transport service initialized");
}
@@ -99,40 +124,118 @@ public class SnmpTransportService implements TbTransportService {
snmp.listen();
}
- @AfterStartUp(order = 10)
- public void startPolling() {
- log.info("Starting SNMP polling");
+ private void initializeTrapsListener() throws IOException {
+ int trapsListeningPort = 1062;
+ String bindingAddress = "0.0.0.0/" + trapsListeningPort;
- pollingExecutor.scheduleWithFixedDelay(() -> {
- snmpTransportContext.getSessions().forEach(this::executeSnmpRequest);
- }, 0, 1, TimeUnit.SECONDS);
- }
+ TransportMapping> transportMapping;
+ switch (snmpUnderlyingProtocol) {
+ case "udp":
+ transportMapping = new DefaultUdpTransportMapping(new UdpAddress(bindingAddress));
+ break;
+ case "tcp":
+ transportMapping = new DefaultTcpTransportMapping(new TcpAddress(bindingAddress));
+ break;
+ default:
+ throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported");
+ }
+
+
+ Snmp trapsSnmp = new Snmp(transportMapping);
+ trapsSnmp.addCommandResponder(this);
- private void executeSnmpRequest(DeviceSessionContext sessionContext) {
- long timeNow = System.currentTimeMillis();
- long nextRequestExecutionTime = sessionContext.getPreviousRequestExecutedAt() + sessionContext.getProfileTransportConfiguration().getPollPeriodMs();
+ transportMapping.listen();
+ }
- if (nextRequestExecutionTime < timeNow) {
- sessionContext.setPreviousRequestExecutedAt(timeNow);
+ public void createQueryingTasks(DeviceSessionContext sessionContext) {
+ List> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
+ .filter(config -> config.getSpec().isRepeatingQuerying())
+ .map(config -> {
+ RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config;
+ return createQueryingTaskForConfig(sessionContext, repeatingCommunicationConfig);
+ })
+ .collect(Collectors.toList());
+ sessionContext.setQueryingTasks(queryingTasks);
+ }
- DeviceProfileId deviceProfileId = sessionContext.getDeviceProfile().getId();
- snmpTransportContext.getProfilesPdus().get(deviceProfileId).forEach(pdu -> {
- try {
- log.debug("[{}] Sending SNMP message for device {}", pdu.getRequestID(), sessionContext.getDeviceId());
- snmp.send(pdu, sessionContext.getTarget(), deviceProfileId, sessionContext);
- } catch (Exception e) {
- log.error("Failed to send SNMP request: {}", e.getMessage());
+ private ScheduledFuture> createQueryingTaskForConfig(DeviceSessionContext sessionContext, RepeatingQueryingSnmpCommunicationConfig communicationConfig) {
+ Long queryingFrequency = communicationConfig.getQueryingFrequencyMs();
+ return queryingExecutor.scheduleWithFixedDelay(() -> {
+ try {
+ if (sessionContext.isActive()) {
+ sendRequest(sessionContext, communicationConfig);
}
- });
+ } catch (Exception e) {
+ log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.getMessage());
+ }
+ }, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
+ }
+
+ public void cancelQueryingTasks(DeviceSessionContext sessionContext) {
+ sessionContext.getQueryingTasks().forEach(task -> task.cancel(true));
+ sessionContext.getQueryingTasks().clear();
+ }
+
+ public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) throws IOException {
+ PDU request = createPdu(communicationConfig);
+ executeRequest(sessionContext, request);
+ }
+
+ public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) throws IOException {
+ PDU request = createPduWithValues(communicationConfig, values);
+ executeRequest(sessionContext, request);
+ }
+
+ private void executeRequest(DeviceSessionContext sessionContext, PDU request) throws IOException {
+ if (request.size() > 0) {
+ log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings());
+ snmp.send(request, sessionContext.getTarget(), sessionContext.getDeviceProfile().getId(), sessionContext);
}
}
- public void onNewDeviceResponse(DeviceSessionContext sessionContext, ResponseEvent responseEvent) {
- ((Snmp) responseEvent.getSource()).cancel(responseEvent.getRequest(), sessionContext);
- snmpResponseProcessingExecutor.submit(() -> processSnmpResponse(sessionContext, responseEvent));
+ private PDU createPdu(SnmpCommunicationConfig communicationConfig) {
+ PDU pdu = new PDU();
+ pdu.setType(communicationConfig.getMethod().getCode());
+ pdu.addAll(communicationConfig.getMappings().stream()
+ .map(mapping -> new VariableBinding(new OID(mapping.getOid())))
+ .collect(Collectors.toList()));
+ return pdu;
}
- private void processSnmpResponse(DeviceSessionContext sessionContext, ResponseEvent event) {
+ private PDU createPduWithValues(SnmpCommunicationConfig communicationConfig, Map values) {
+ PDU pdu = new PDU();
+ pdu.setType(communicationConfig.getMethod().getCode());
+ pdu.addAll(communicationConfig.getMappings().stream()
+ .filter(mapping -> values.containsKey(mapping.getKey()))
+ .map(mapping -> {
+ String value = values.get(mapping.getKey());
+ return new VariableBinding(new OID(mapping.getOid()), new OctetString(value));
+ })
+ .collect(Collectors.toList()));
+ return pdu;
+ }
+
+
+ private void processTrap(CommandResponderEvent event) {
+ if (event.getPDU().getType() != PDU.TRAP) return;
+
+ snmpTransportContext.getSessions().stream()
+ .filter(sessionContext -> {
+ // TODO: SNMP v3 support
+ return sessionContext.getTarget().getSecurityName().equals(OctetString.fromByteArray(event.getSecurityName())) &&
+ sessionContext.getTarget().getAddress().equals(event.getPeerAddress());
+ })
+ .findFirst()
+ .ifPresentOrElse(sessionContext -> {
+ responseProcessingExecutor.execute(() -> processResponse(sessionContext, event.getPDU()));
+ }, () -> {
+ log.debug("SNMP event is from unknown source: {}", event);
+ });
+ }
+
+ public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) {
+ ((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext);
+
if (event.getError() != null) {
log.warn("Response error: {}", event.getError().getMessage(), event.getError());
return;
@@ -143,16 +246,29 @@ public class SnmpTransportService implements TbTransportService {
log.warn("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
return;
}
-
DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();
log.debug("[{}] Processing SNMP response for device {} with device profile {}: {}",
response.getRequestID(), sessionContext.getDeviceId(), deviceProfileId, response);
- JsonObject telemetry = new JsonObject();
- JsonObject attributes = new JsonObject();
+ responseProcessingExecutor.execute(() -> processResponse(sessionContext, response));
+ }
- for (int i = 0; i < response.size(); i++) {
- VariableBinding variableBinding = response.get(i);
+ private void processResponse(DeviceSessionContext sessionContext, PDU responsePdu) {
+ Map mappings = new HashMap<>();
+ Map configs = new HashMap<>();
+ Map responses = new EnumMap<>(SnmpCommunicationSpec.class);
+
+ for (SnmpCommunicationConfig config : sessionContext.getProfileTransportConfiguration().getCommunicationConfigs()) {
+ for (SnmpMapping mapping : config.getMappings()) {
+ OID oid = new OID(mapping.getOid());
+ mappings.put(oid, mapping);
+ configs.put(oid, config);
+ }
+ responses.put(config.getSpec(), new JsonObject());
+ }
+
+ for (int i = 0; i < responsePdu.size(); i++) {
+ VariableBinding variableBinding = responsePdu.get(i);
log.trace("Processing variable binding {}: {}", i, variableBinding);
if (variableBinding.getVariable() instanceof Null) {
@@ -160,37 +276,57 @@ public class SnmpTransportService implements TbTransportService {
continue;
}
- snmpTransportContext.getTelemetryMapping(deviceProfileId, variableBinding.getOid()).ifPresent(mapping -> {
- log.trace("Found telemetry mapping for oid {}: {}", variableBinding.getOid(), mapping);
- processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), telemetry);
- });
+ OID oid = variableBinding.getOid();
+ if (!mappings.containsKey(oid)) {
+ log.debug("No SNMP mapping for oid {}", oid);
+ continue;
+ }
- snmpTransportContext.getAttributeMapping(deviceProfileId, variableBinding.getOid()).ifPresent(mapping -> {
- log.trace("Found attribute mapping for oid {}: {}", variableBinding.getOid(), mapping);
- processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), attributes);
- });
- }
+ SnmpCommunicationSpec spec = configs.get(oid).getSpec();
+ if (!responseProcessors.containsKey(spec)) {
+ log.debug("No response processor found for spec {}", spec);
+ continue;
+ }
- if (telemetry.entrySet().isEmpty() && attributes.entrySet().isEmpty()) {
- log.warn("No telemetry or attribute values is the SNMP response for device {}", sessionContext.getDeviceId());
- return;
+ SnmpMapping mapping = mappings.get(oid);
+ processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), responses.get(spec));
}
- if (!telemetry.entrySet().isEmpty()) {
- TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(telemetry);
- transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, TransportServiceCallback.EMPTY);
- log.debug("Posted telemetry for device {}: {}", sessionContext.getDeviceId(), telemetry);
+ if (responses.values().stream().allMatch(response -> response.entrySet().isEmpty())) {
+ log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), responsePdu.getRequestID());
+ return;
}
- if (!attributes.entrySet().isEmpty()) {
- TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(attributes);
- transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, TransportServiceCallback.EMPTY);
- log.debug("Posted attributes for device {}: {}", sessionContext.getDeviceId(), attributes);
- }
+ responses.forEach((spec, response) -> {
+ Optional.ofNullable(responseProcessors.get(spec))
+ .ifPresent(responseProcessor -> {
+ responseProcessor.accept(response, sessionContext);
+ });
+ });
reportActivity(sessionContext.getSessionInfo());
}
+ private void configureResponseProcessors() {
+ Stream.of(SnmpCommunicationSpec.TELEMETRY_QUERYING, SnmpCommunicationSpec.TELEMETRY_TRAPS_RECEIVING)
+ .forEach(telemetrySpec -> {
+ responseProcessors.put(telemetrySpec, (response, sessionContext) -> {
+ TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(response);
+ transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, TransportServiceCallback.EMPTY);
+ log.debug("Posted telemetry for device {}: {}", sessionContext.getDeviceId(), response);
+ });
+ });
+
+ Stream.of(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, SnmpCommunicationSpec.CLIENT_ATTRIBUTES_TRAPS_RECEIVING)
+ .forEach(clientAttributesSpec -> {
+ responseProcessors.put(clientAttributesSpec, (response, sessionContext) -> {
+ TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(response);
+ transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, TransportServiceCallback.EMPTY);
+ log.debug("Posted attributes for device {}: {}", sessionContext.getDeviceId(), response);
+ });
+ });
+ }
+
private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(false)
@@ -200,6 +336,8 @@ public class SnmpTransportService implements TbTransportService {
}
private void processValue(String key, DataType dataType, String value, JsonObject result) {
+ if (StringUtils.isEmpty(value)) return;
+
switch (dataType) {
case LONG:
result.addProperty(key, Long.parseLong(value));
@@ -215,6 +353,11 @@ public class SnmpTransportService implements TbTransportService {
}
}
+ @Override
+ public void processPdu(CommandResponderEvent event) {
+ processTrap(event);
+ }
+
@Override
public String getName() {
return "SNMP";
@@ -223,11 +366,11 @@ public class SnmpTransportService implements TbTransportService {
@PreDestroy
public void shutdown() {
log.info("Stopping SNMP transport!");
- if (pollingExecutor != null) {
- pollingExecutor.shutdownNow();
+ if (queryingExecutor != null) {
+ queryingExecutor.shutdownNow();
}
- if (snmpResponseProcessingExecutor != null) {
- snmpResponseProcessingExecutor.shutdownNow();
+ if (responseProcessingExecutor != null) {
+ responseProcessingExecutor.shutdownNow();
}
if (snmp != null) {
try {
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
index 57e89ad034..c0bbc843bc 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
@@ -18,18 +18,31 @@ package org.thingsboard.server.transport.snmp.session;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.snmp4j.AbstractTarget;
import org.snmp4j.CommunityTarget;
import org.snmp4j.Target;
+import org.snmp4j.UserTarget;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
+import org.snmp4j.security.AuthSHA;
+import org.snmp4j.security.PrivDES;
+import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.security.SecurityModel;
+import org.snmp4j.security.SecurityModels;
+import org.snmp4j.security.USM;
+import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.GenericAddress;
+import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
@@ -40,8 +53,13 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseM
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
@Slf4j
public class DeviceSessionContext extends DeviceAwareSessionContext implements SessionMsgListener, ResponseListener {
@@ -64,10 +82,16 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
@Setter
private long previousRequestExecutedAt = 0;
private final AtomicInteger msgIdSeq = new AtomicInteger(0);
+ @Getter
private boolean isActive = true;
private final String snmpUnderlyingProtocol;
+ @Getter
+ @Setter
+ private List> queryingTasks = new LinkedList<>();
+
public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token,
+ SnmpDeviceProfileTransportConfiguration profileTransportConfiguration,
SnmpDeviceTransportConfiguration deviceTransportConfiguration,
SnmpTransportContext snmpTransportContext, SnmpTransportService snmpTransportService,
String snmpUnderlyingProtocol) {
@@ -80,11 +104,11 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
this.snmpTransportContext = snmpTransportContext;
this.snmpTransportService = snmpTransportService;
- this.profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
+ this.profileTransportConfiguration = profileTransportConfiguration;
this.deviceTransportConfiguration = deviceTransportConfiguration;
this.snmpUnderlyingProtocol = snmpUnderlyingProtocol;
- initTarget(this.profileTransportConfiguration, this.deviceTransportConfiguration);
+ initializeTarget(profileTransportConfiguration, deviceTransportConfiguration);
}
@Override
@@ -103,20 +127,62 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
@Override
public void onResponse(ResponseEvent event) {
if (isActive) {
- snmpTransportService.onNewDeviceResponse(this, event);
+ snmpTransportService.processResponseEvent(this, event);
}
}
- public void initTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
+ public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
log.trace("Initializing target for SNMP session of device {}", device);
- CommunityTarget communityTarget = new CommunityTarget();
- communityTarget.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
- communityTarget.setVersion(deviceTransportConfig.getProtocolVersion().getCode());
- communityTarget.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
- communityTarget.setTimeout(profileTransportConfig.getTimeoutMs());
- communityTarget.setRetries(profileTransportConfig.getRetries());
- this.target = communityTarget;
- log.info("SNMP target initialized: {}", this.target);
+
+ AbstractTarget target;
+
+ SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();
+ switch (protocolVersion) {
+ case V1:
+ CommunityTarget communityTargetV1 = new CommunityTarget();
+ communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);
+ communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
+ communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getSecurityName()));
+ target = communityTargetV1;
+ break;
+ case V2C:
+ CommunityTarget communityTargetV2 = new CommunityTarget();
+ communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
+ communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
+ communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getSecurityName()));
+ target = communityTargetV2;
+ break;
+ case V3:
+ USM usm = new USM();
+ SecurityModels.getInstance().addSecurityModel(usm);
+
+ OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());
+ OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());
+ OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());
+
+ OID authenticationProtocol = AuthSHA.ID;
+ OID privacyProtocol = PrivDES.ID; // FIXME: to config
+
+ UsmUser user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase);
+ snmpTransportService.getSnmp().getUSM().addUser(user);
+
+ UserTarget userTarget = new UserTarget();
+ userTarget.setSecurityName(securityName);
+ userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
+
+ target = userTarget;
+ break;
+ default:
+ throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
+ }
+
+ target.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
+ target.setTimeout(profileTransportConfig.getTimeoutMs());
+ target.setRetries(profileTransportConfig.getRetries());
+ target.setVersion(protocolVersion.getCode());
+
+ this.target = target;
+ log.info("SNMP target initialized: {}", target);
}
public void close() {
@@ -138,6 +204,21 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
@Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
+ profileTransportConfiguration.getCommunicationConfigs().stream()
+ .filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING)
+ .findFirst()
+ .ifPresent(communicationConfig -> {
+ Map sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream()
+ .collect(Collectors.toMap(
+ Map.Entry::getKey,
+ entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString()
+ ));
+ try {
+ snmpTransportService.sendRequest(this, communicationConfig, sharedAttributes);
+ } catch (Exception e) {
+ log.error("Failed to send request with shared attributes to SNMP device {}: {}", getDeviceId(), e.getMessage());
+ }
+ });
}
@Override
diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java
similarity index 69%
rename from common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
rename to common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java
index 29fa35dcab..d48eaadce3 100644
--- a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulator.java
+++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java
@@ -15,7 +15,10 @@
*/
package org.thingsboard.server.transport.snmp;
+import org.snmp4j.CommandResponderEvent;
import org.snmp4j.CommunityTarget;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.agent.BaseAgent;
@@ -42,45 +45,81 @@ import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.Variable;
+import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.TransportMappings;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Scanner;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
-public class SnmpDeviceSimulator extends BaseAgent {
+public class SnmpDeviceSimulatorV2 extends BaseAgent {
+
+ public static class RequestProcessor extends CommandProcessor {
+ private final Consumer processor;
+
+ public RequestProcessor(Consumer processor) {
+ super(new OctetString(MPv3.createLocalEngineID()));
+ this.processor = processor;
+ }
+
+ @Override
+ public void processPdu(CommandResponderEvent event) {
+ processor.accept(event);
+ }
+ }
public static void main(String[] args) throws IOException {
- SnmpDeviceSimulator device = new SnmpDeviceSimulator(1610);
+ SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");
device.start();
device.setUpMappings(Map.of(
".1.3.6.1.2.1.1.1.50", "12",
".1.3.6.1.2.1.2.1.52", "56",
- ".1.3.6.1.2.1.3.1.54", "yes"
+ ".1.3.6.1.2.1.3.1.54", "yes",
+ ".1.3.6.1.2.1.7.1.58", ""
));
+
+// while (true) {
+// new Scanner(System.in).nextLine();
+// device.sendTrap("127.0.0.1", 1062, Map.of(".1.3.6.1.2.87.1.56", "12"));
+// System.out.println("sent");
+// }
+
+// Snmp snmp = new Snmp(device.transportMappings[0]);
+// device.snmp.addCommandResponder(event -> {
+// System.out.println(event);
+// });
+
new Scanner(System.in).nextLine();
}
private final Target target;
private final Address address;
+ private Snmp snmp;
- public SnmpDeviceSimulator(int port) {
- super(new File("conf.agent"), new File("bootCounter.agent"),
- new CommandProcessor(new OctetString(MPv3.createLocalEngineID())));
+ private final String password;
+ public SnmpDeviceSimulatorV2(int port, String password) throws IOException {
+ super(new File("conf.agent"), new File("bootCounter.agent"), new RequestProcessor(event -> {
+ System.out.println("aboba");
+ ((Snmp) event.getSource()).cancel(event.getPDU(), event1 -> System.out.println("canceled"));
+ }));
CommunityTarget target = new CommunityTarget();
- target.setCommunity(new OctetString("public"));
- address = GenericAddress.parse("udp:0.0.0.0/" + port);
+ target.setCommunity(new OctetString(password));
+ this.address = GenericAddress.parse("udp:0.0.0.0/" + port);
target.setAddress(address);
target.setRetries(2);
target.setTimeout(1500);
target.setVersion(SnmpConstants.version2c);
this.target = target;
+ this.password = password;
}
public void start() throws IOException {
@@ -90,17 +129,27 @@ public class SnmpDeviceSimulator extends BaseAgent {
finishInit();
run();
sendColdStartNotification();
+ snmp = new Snmp(transportMappings[0]);
}
public void setUpMappings(Map oidToResponseMappings) {
unregisterManagedObject(getSnmpv2MIB());
oidToResponseMappings.forEach((oid, response) -> {
- registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(response)));
+ registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response)));
});
}
- public Target getTarget() {
- return target;
+ public void sendTrap(String host, int port, Map values) throws IOException {
+ PDU pdu = new PDU();
+ pdu.addAll(values.entrySet().stream()
+ .map(entry -> new VariableBinding(new OID(entry.getKey()), new OctetString(entry.getValue())))
+ .collect(Collectors.toList()));
+ pdu.setType(PDU.TRAP);
+
+ CommunityTarget remoteTarget = (CommunityTarget) getTarget().clone();
+ remoteTarget.setAddress(new UdpAddress(host + "/" + port));
+
+ snmp.send(pdu, remoteTarget);
}
@Override
@@ -165,4 +214,9 @@ public class SnmpDeviceSimulator extends BaseAgent {
new OctetString("public2public").toSubIndex(true), com2sec);
communityMIB.getSnmpCommunityEntry().addRow(row);
}
+
+ public Target getTarget() {
+ return target;
+ }
+
}
diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java
new file mode 100644
index 0000000000..63ccd7021a
--- /dev/null
+++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp;
+
+import org.snmp4j.UserTarget;
+import org.snmp4j.security.AuthSHA;
+import org.snmp4j.security.PrivDES;
+import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.security.SecurityModels;
+import org.snmp4j.security.USM;
+import org.snmp4j.security.UsmUser;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+
+import java.io.IOException;
+
+public class SnmpDeviceSimulatorV3 extends SnmpDeviceSimulatorV2 {
+ public SnmpDeviceSimulatorV3(int port, String securityName, String authenticationPassphrase, String privacyPassphrase) throws IOException {
+ super(12, null);
+// super(new File("conf.agent"), new File("bootCounter.agent"));
+
+ USM usm = new USM();
+ SecurityModels.getInstance().addSecurityModel(usm);
+
+ OID authenticationProtocol = AuthSHA.ID;
+ OID privacyProtocol = PrivDES.ID;
+
+ UsmUser user = new UsmUser(new OctetString(securityName), authenticationProtocol, new OctetString(authenticationPassphrase), privacyProtocol, new OctetString(privacyPassphrase));
+ }
+
+ public void initV3(UsmUser user, String securityName) {
+// snmp.getUSM().addUser(user);
+
+ UserTarget userTarget = new UserTarget();
+ userTarget.setSecurityName(new OctetString(securityName));
+ userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
+ }
+
+}
diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTest.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTest.java
new file mode 100644
index 0000000000..29a8c085c8
--- /dev/null
+++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTest.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Scanner;
+
+public class SnmpTest {
+ public static void main(String[] args) throws IOException {
+ SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");
+
+ device.start();
+ device.setUpMappings(Map.of(
+ ".1.3.6.1.2.1.1.1.50", "12",
+ ".1.3.6.1.2.1.2.1.52", "56",
+ ".1.3.6.1.2.1.3.1.54", "yes",
+ ".1.3.6.1.2.1.7.1.58", ""
+ ));
+
+
+// while (true) {
+// new Scanner(System.in).nextLine();
+// device.sendTrap("127.0.0.1", 1062, Map.of(".1.3.6.1.2.87.1.56", "12"));
+// System.out.println("sent");
+// }
+
+// Snmp snmp = new Snmp(device.transportMappings[0]);
+// device.snmp.addCommandResponder(event -> {
+// System.out.println(event);
+// });
+
+ new Scanner(System.in).nextLine();
+ }
+
+}
diff --git a/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json b/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
index 78b66c4c0e..f8291a090b 100644
--- a/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
+++ b/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
@@ -1,27 +1,53 @@
{
- "pollPeriodMs": 3000,
"timeoutMs": 500,
"retries": 0,
- "telemetryMappings": [
+ "communicationConfigs": [
{
- "oid": ".1.3.6.1.2.1.1.1.50",
- "method": "GET",
- "key": "temperature",
- "dataType": "LONG"
+ "spec": "TELEMETRY_QUERYING",
+ "queryingFrequencyMs": 3000,
+ "mappings": [
+ {
+ "oid": ".1.3.6.1.2.1.1.1.50",
+ "key": "temperature",
+ "dataType": "LONG"
+ },
+ {
+ "oid": ".1.3.6.1.2.1.2.1.52",
+ "key": "humidity",
+ "dataType": "DOUBLE"
+ }
+ ]
},
{
- "oid": ".1.3.6.1.2.1.2.1.52",
- "method": "GET",
- "key": "humidity",
- "dataType": "LONG"
- }
- ],
- "attributesMappings": [
+ "spec": "CLIENT_ATTRIBUTES_QUERYING",
+ "queryingFrequencyMs": 5000,
+ "mappings": [
+ {
+ "oid": ".1.3.6.1.2.1.3.1.54",
+ "key": "isCool",
+ "dataType": "STRING"
+ }
+ ]
+ },
+ {
+ "spec": "SHARED_ATTRIBUTES_SETTING",
+ "mappings": [
+ {
+ "oid": ".1.3.6.1.2.1.7.1.58",
+ "key": "shared",
+ "dataType": "STRING"
+ }
+ ]
+ },
{
- "oid": ".1.3.6.1.2.1.3.1.54",
- "method": "GET",
- "key": "isCool",
- "dataType": "STRING"
+ "spec": "TELEMETRY_TRAPS_RECEIVING",
+ "mappings": [
+ {
+ "oid": ".1.3.6.1.2.8.7.1.56",
+ "key": "temperature_trap",
+ "dataType": "LONG"
+ }
+ ]
}
]
-}
\ No newline at end of file
+}
diff --git a/common/transport/snmp/src/test/resources/snmp-device-transport-config.json b/common/transport/snmp/src/test/resources/snmp-device-transport-config.json
index c73d817bfb..9a97b75e40 100644
--- a/common/transport/snmp/src/test/resources/snmp-device-transport-config.json
+++ b/common/transport/snmp/src/test/resources/snmp-device-transport-config.json
@@ -1,6 +1,6 @@
{
"address": "127.0.0.1",
"port": 1610,
- "community": "public",
+ "password": "public",
"protocolVersion": "V2C"
}
\ No newline at end of file
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
index 2c5dfafe4e..699e43e5ee 100644
--- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
@@ -285,7 +285,7 @@ public class JsonConverter {
return result;
}
- public static JsonElement toJson(AttributeUpdateNotificationMsg payload) {
+ public static JsonObject toJson(AttributeUpdateNotificationMsg payload) {
JsonObject result = new JsonObject();
if (payload.getSharedUpdatedCount() > 0) {
payload.getSharedUpdatedList().forEach(addToObjectFromProto(result));
From e52ac96c624fa2f2790d5a288683e675311e8a8a Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Tue, 6 Apr 2021 17:05:34 +0300
Subject: [PATCH 10/32] Implement SNMP v3 security support, remove traps
support
---
.../SnmpDeviceTransportConfiguration.java | 23 +-
.../snmp/AuthenticationProtocol.java | 45 ++
.../data/transport/snmp/PrivacyProtocol.java | 43 ++
.../transport/snmp/SnmpCommunicationSpec.java | 5 +-
...TrapsReceivingSnmpCommunicationConfig.java | 25 -
.../snmp/configs/SnmpCommunicationConfig.java | 4 +-
...TrapsReceivingSnmpCommunicationConfig.java | 30 -
.../transport/snmp/SnmpAuthService.java | 118 +++
.../transport/snmp/SnmpTransportContext.java | 12 +-
.../snmp/service/SnmpTransportService.java | 142 ++--
.../snmp/session/DeviceSessionContext.java | 74 +-
.../transport/snmp/SnmpDeviceSimulatorV2.java | 26 -
.../transport/snmp/SnmpDeviceSimulatorV3.java | 723 +++++++++++++++++-
.../snmp/{SnmpTest.java => SnmpTestV2.java} | 2 +-
.../server/transport/snmp/SnmpTestV3.java | 46 ++
.../snmp-device-profile-transport-config.json | 10 -
.../snmp-device-transport-config-v3.json | 13 +
.../snmp-device-transport-config.json | 2 +-
18 files changed, 1057 insertions(+), 286 deletions(-)
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java
delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java
delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java
rename common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/{SnmpTest.java => SnmpTestV2.java} (98%)
create mode 100644 common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV3.java
create mode 100644 common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
index a6056b4188..24ab463129 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
@@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.transport.snmp.AuthenticationProtocol;
+import org.thingsboard.server.common.data.transport.snmp.PrivacyProtocol;
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
@Data
@@ -26,9 +28,23 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur
private String address;
private int port;
private SnmpProtocolVersion protocolVersion;
+
+ /*
+ * For SNMP v1 and v2c
+ * */
+ private String community;
+
+ /*
+ * For SNMP v3 with User Based Security Model
+ * */
+ private String username;
private String securityName;
- private String authenticationPassphrase; // for SNMP v3
- private String privacyPassphrase; // for SNMP v3
+ private String contextName;
+ private AuthenticationProtocol authenticationProtocol;
+ private String authenticationPassphrase;
+ private PrivacyProtocol privacyProtocol;
+ private String privacyPassphrase;
+ private String engineId;
@Override
public DeviceTransportType getType() {
@@ -44,7 +60,6 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur
@JsonIgnore
private boolean isValid() {
- return StringUtils.isNotBlank(address) && port > 0 &&
- StringUtils.isNotBlank(securityName) && protocolVersion != null;
+ return true;
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java
new file mode 100644
index 0000000000..42d2e98798
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+public enum AuthenticationProtocol {
+ SHA_1("1.3.6.1.6.3.10.1.1.3"),
+ SHA_224("1.3.6.1.6.3.10.1.1.4"),
+ SHA_256("1.3.6.1.6.3.10.1.1.5"),
+ SHA_384("1.3.6.1.6.3.10.1.1.6"),
+ SHA_512("1.3.6.1.6.3.10.1.1.7"),
+ MD5("1.3.6.1.6.3.10.1.1.2");
+
+ // oids taken from org.snmp4j.security.SecurityProtocol implementations
+ private final String oid;
+
+ AuthenticationProtocol(String oid) {
+ this.oid = oid;
+ }
+
+ public String getOid() {
+ return oid;
+ }
+
+ public static Optional forName(String name) {
+ return Arrays.stream(values())
+ .filter(protocol -> protocol.name().equalsIgnoreCase(name))
+ .findFirst();
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java
new file mode 100644
index 0000000000..1c040eb83e
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+public enum PrivacyProtocol {
+ DES("1.3.6.1.6.3.10.1.2.2"),
+ AES_128("1.3.6.1.6.3.10.1.2.4"),
+ AES_192("1.3.6.1.4.1.4976.2.2.1.1.1"),
+ AES_256("1.3.6.1.4.1.4976.2.2.1.1.2");
+
+ // oids taken from org.snmp4j.security.SecurityProtocol implementations
+ private final String oid;
+
+ PrivacyProtocol(String oid) {
+ this.oid = oid;
+ }
+
+ public String getOid() {
+ return oid;
+ }
+
+ public static Optional forName(String name) {
+ return Arrays.stream(values())
+ .filter(protocol -> protocol.name().equalsIgnoreCase(name))
+ .findFirst();
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
index f6ff7d3109..a2edf75968 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
@@ -19,10 +19,7 @@ public enum SnmpCommunicationSpec {
TELEMETRY_QUERYING(true),
CLIENT_ATTRIBUTES_QUERYING(true),
- SHARED_ATTRIBUTES_SETTING,
-
- TELEMETRY_TRAPS_RECEIVING,
- CLIENT_ATTRIBUTES_TRAPS_RECEIVING;
+ SHARED_ATTRIBUTES_SETTING;
private final boolean isRepeatingQuerying;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java
deleted file mode 100644
index 9a11b01f1b..0000000000
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * Copyright © 2016-2021 The Thingsboard Authors
- *
- * 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.thingsboard.server.common.data.transport.snmp.configs;
-
-import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
-
-public class ClientAttributesTrapsReceivingSnmpCommunicationConfig extends SnmpCommunicationConfig {
- @Override
- public SnmpCommunicationSpec getSpec() {
- return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_TRAPS_RECEIVING;
- }
-}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
index bfc194669b..7e1ee27237 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
@@ -31,9 +31,7 @@ import java.util.List;
@JsonSubTypes({
@Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"),
@Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"),
- @Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING"),
- @Type(value = TelemetryTrapsReceivingSnmpCommunicationConfig.class, name = "TELEMETRY_TRAPS_RECEIVING"),
- @Type(value = ClientAttributesTrapsReceivingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_TRAPS_RECEIVING")
+ @Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING")
})
public abstract class SnmpCommunicationConfig {
protected List mappings;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java
deleted file mode 100644
index 66be4d0cf8..0000000000
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Copyright © 2016-2021 The Thingsboard Authors
- *
- * 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.thingsboard.server.common.data.transport.snmp.configs;
-
-import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
-
-public class TelemetryTrapsReceivingSnmpCommunicationConfig extends SnmpCommunicationConfig {
- @Override
- public SnmpCommunicationSpec getSpec() {
- return SnmpCommunicationSpec.TELEMETRY_TRAPS_RECEIVING;
- }
-
- @Override
- public boolean isValid() {
- return false;
- }
-}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java
new file mode 100644
index 0000000000..59b31d593c
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp;
+
+import lombok.RequiredArgsConstructor;
+import org.snmp4j.AbstractTarget;
+import org.snmp4j.CommunityTarget;
+import org.snmp4j.Target;
+import org.snmp4j.UserTarget;
+import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.security.SecurityModel;
+import org.snmp4j.security.SecurityProtocols;
+import org.snmp4j.security.USM;
+import org.snmp4j.security.UsmUser;
+import org.snmp4j.smi.GenericAddress;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
+import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
+import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
+import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
+
+@Service
+@TbSnmpTransportComponent
+@RequiredArgsConstructor
+public class SnmpAuthService {
+ private final SnmpTransportService snmpTransportService;
+
+ @Value("${transport.snmp.underlying_protocol}")
+ private String snmpUnderlyingProtocol;
+
+ public Target setUpSnmpTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
+ AbstractTarget target;
+
+ SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();
+ switch (protocolVersion) {
+ case V1:
+ CommunityTarget communityTargetV1 = new CommunityTarget();
+ communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);
+ communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
+ communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
+ target = communityTargetV1;
+ break;
+ case V2C:
+ CommunityTarget communityTargetV2 = new CommunityTarget();
+ communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
+ communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
+ communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
+ target = communityTargetV2;
+ break;
+ case V3:
+ OctetString username = new OctetString(deviceTransportConfig.getUsername());
+ OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());
+ OctetString engineId = new OctetString(deviceTransportConfig.getEngineId());
+
+ OID authenticationProtocol = new OID(deviceTransportConfig.getAuthenticationProtocol().getOid());
+ OID privacyProtocol = new OID(deviceTransportConfig.getPrivacyProtocol().getOid());
+ OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());
+ authenticationPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(authenticationProtocol, authenticationPassphrase, engineId.getValue()));
+ OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());
+ privacyPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(privacyProtocol, authenticationProtocol, privacyPassphrase, engineId.getValue()));
+
+ USM usm = snmpTransportService.getSnmp().getUSM();
+ if (usm.hasUser(engineId, securityName)) {
+ usm.removeAllUsers(username, engineId);
+ }
+ usm.addLocalizedUser(
+ engineId.getValue(), username,
+ authenticationProtocol, authenticationPassphrase.getValue(),
+ privacyProtocol, privacyPassphrase.getValue()
+ );
+
+ UserTarget userTarget = new UserTarget();
+ userTarget.setSecurityName(securityName);
+ userTarget.setAuthoritativeEngineID(engineId.getValue());
+ userTarget.setSecurityModel(SecurityModel.SECURITY_MODEL_USM);
+ userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
+ target = userTarget;
+ break;
+ default:
+ throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
+ }
+
+ target.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
+ target.setTimeout(profileTransportConfig.getTimeoutMs());
+ target.setRetries(profileTransportConfig.getRetries());
+ target.setVersion(protocolVersion.getCode());
+
+ return target;
+ }
+
+ public void cleanUpSnmpAuthInfo(DeviceSessionContext sessionContext) {
+ SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
+ if (deviceTransportConfiguration.getProtocolVersion() == SnmpProtocolVersion.V3) {
+ OctetString username = new OctetString(deviceTransportConfiguration.getUsername());
+ OctetString engineId = new OctetString(deviceTransportConfiguration.getEngineId());
+ snmpTransportService.getSnmp().getUSM().removeAllUsers(username, engineId);
+ }
+ }
+
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index 877d4f1652..2b0ce984aa 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -15,9 +15,9 @@
*/
package org.thingsboard.server.transport.snmp;
+import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
@@ -61,18 +61,18 @@ import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
public class SnmpTransportContext extends TransportContext {
+ @Getter
private final SnmpTransportService snmpTransportService;
private final TransportDeviceProfileCache deviceProfileCache;
private final TransportService transportService;
private final ProtoTransportEntityService protoEntityService;
private final SnmpTransportBalancingService balancingService;
+ @Getter
+ private final SnmpAuthService snmpAuthService;
private final Map sessions = new ConcurrentHashMap<>();
private Collection allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
- @Value("${transport.snmp.underlying_protocol}")
- private String snmpUnderlyingProtocol;
-
@AfterStartUp(order = 2)
public void initDevicesSessions() {
log.info("Initializing SNMP devices sessions");
@@ -116,8 +116,7 @@ public class SnmpTransportContext extends TransportContext {
DeviceSessionContext deviceSessionContext = new DeviceSessionContext(
device, deviceProfile, credentials.getCredentialsId(),
- profileTransportConfiguration, deviceTransportConfiguration,
- this, snmpTransportService, snmpUnderlyingProtocol
+ profileTransportConfiguration, deviceTransportConfiguration, this
);
registerSessionMsgListener(deviceSessionContext);
sessions.put(device.getId(), deviceSessionContext);
@@ -155,6 +154,7 @@ public class SnmpTransportContext extends TransportContext {
if (sessionContext == null) return;
log.info("Destroying SNMP device session for device {}", sessionContext.getDevice().getId());
sessionContext.close();
+ snmpAuthService.cleanUpSnmpAuthInfo(sessionContext);
transportService.deregisterSession(sessionContext.getSessionInfo());
sessions.remove(sessionContext.getDeviceId());
snmpTransportService.cancelQueryingTasks(sessionContext);
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index 963b5db382..2f26bfa98c 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -19,29 +19,31 @@ import com.google.gson.JsonObject;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
-import org.snmp4j.CommandResponder;
-import org.snmp4j.CommandResponderEvent;
import org.snmp4j.PDU;
+import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.mp.MPv3;
+import org.snmp4j.security.SecurityModels;
+import org.snmp4j.security.SecurityProtocols;
+import org.snmp4j.security.USM;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
-import org.snmp4j.smi.TcpAddress;
-import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.TbTransportService;
+import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import org.thingsboard.server.common.data.transport.snmp.configs.RepeatingQueryingSnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig;
import org.thingsboard.server.common.transport.TransportService;
@@ -49,12 +51,12 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
-import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
+import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
@@ -67,13 +69,11 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
@TbSnmpTransportComponent
@Service
@Slf4j
-public class SnmpTransportService implements TbTransportService, CommandResponder {
- private final SnmpTransportContext snmpTransportContext;
+public class SnmpTransportService implements TbTransportService {
private final TransportService transportService;
@Getter
@@ -88,9 +88,7 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
@Value("${transport.snmp.underlying_protocol}")
private String snmpUnderlyingProtocol;
- public SnmpTransportService(@Lazy SnmpTransportContext snmpTransportContext,
- TransportService transportService) {
- this.snmpTransportContext = snmpTransportContext;
+ public SnmpTransportService(TransportService transportService) {
this.transportService = transportService;
}
@@ -102,7 +100,6 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
initializeSnmp();
- initializeTrapsListener();
configureResponseProcessors();
log.info("SNMP transport service initialized");
@@ -122,29 +119,9 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
}
snmp = new Snmp(transportMapping);
snmp.listen();
- }
-
- private void initializeTrapsListener() throws IOException {
- int trapsListeningPort = 1062;
- String bindingAddress = "0.0.0.0/" + trapsListeningPort;
- TransportMapping> transportMapping;
- switch (snmpUnderlyingProtocol) {
- case "udp":
- transportMapping = new DefaultUdpTransportMapping(new UdpAddress(bindingAddress));
- break;
- case "tcp":
- transportMapping = new DefaultTcpTransportMapping(new TcpAddress(bindingAddress));
- break;
- default:
- throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported");
- }
-
-
- Snmp trapsSnmp = new Snmp(transportMapping);
- trapsSnmp.addCommandResponder(this);
-
- transportMapping.listen();
+ USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
+ SecurityModels.getInstance().addSecurityModel(usm);
}
public void createQueryingTasks(DeviceSessionContext sessionContext) {
@@ -177,12 +154,11 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
}
public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) throws IOException {
- PDU request = createPdu(communicationConfig);
- executeRequest(sessionContext, request);
+ sendRequest(sessionContext, communicationConfig, Collections.emptyMap());
}
public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) throws IOException {
- PDU request = createPduWithValues(communicationConfig, values);
+ PDU request = createPdu(sessionContext, communicationConfig, values);
executeRequest(sessionContext, request);
}
@@ -193,46 +169,37 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
}
}
- private PDU createPdu(SnmpCommunicationConfig communicationConfig) {
- PDU pdu = new PDU();
- pdu.setType(communicationConfig.getMethod().getCode());
- pdu.addAll(communicationConfig.getMappings().stream()
- .map(mapping -> new VariableBinding(new OID(mapping.getOid())))
- .collect(Collectors.toList()));
- return pdu;
- }
+ private PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) {
+ PDU pdu;
+ SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
+ SnmpProtocolVersion snmpVersion = deviceTransportConfiguration.getProtocolVersion();
+ switch (snmpVersion) {
+ case V1:
+ case V2C:
+ pdu = new PDU();
+ break;
+ case V3:
+ ScopedPDU scopedPdu = new ScopedPDU();
+ scopedPdu.setContextName(new OctetString(deviceTransportConfiguration.getContextName()));
+ scopedPdu.setContextEngineID(new OctetString(deviceTransportConfiguration.getEngineId()));
+ pdu = scopedPdu;
+ break;
+ default:
+ throw new UnsupportedOperationException("SNMP version " + snmpVersion + " is not supported");
+ }
- private PDU createPduWithValues(SnmpCommunicationConfig communicationConfig, Map values) {
- PDU pdu = new PDU();
pdu.setType(communicationConfig.getMethod().getCode());
pdu.addAll(communicationConfig.getMappings().stream()
- .filter(mapping -> values.containsKey(mapping.getKey()))
- .map(mapping -> {
- String value = values.get(mapping.getKey());
- return new VariableBinding(new OID(mapping.getOid()), new OctetString(value));
- })
+ .filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey()))
+ .map(mapping -> Optional.ofNullable(values.get(mapping.getKey()))
+ .map(value -> new VariableBinding(new OID(mapping.getOid()), new OctetString(values.get(mapping.getKey()))))
+ .orElseGet(() -> new VariableBinding(new OID(mapping.getOid()))))
.collect(Collectors.toList()));
+
return pdu;
}
- private void processTrap(CommandResponderEvent event) {
- if (event.getPDU().getType() != PDU.TRAP) return;
-
- snmpTransportContext.getSessions().stream()
- .filter(sessionContext -> {
- // TODO: SNMP v3 support
- return sessionContext.getTarget().getSecurityName().equals(OctetString.fromByteArray(event.getSecurityName())) &&
- sessionContext.getTarget().getAddress().equals(event.getPeerAddress());
- })
- .findFirst()
- .ifPresentOrElse(sessionContext -> {
- responseProcessingExecutor.execute(() -> processResponse(sessionContext, event.getPDU()));
- }, () -> {
- log.debug("SNMP event is from unknown source: {}", event);
- });
- }
-
public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) {
((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext);
@@ -243,7 +210,7 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
PDU response = event.getResponse();
if (response == null) {
- log.warn("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
+ log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
return;
}
DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();
@@ -308,23 +275,17 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
}
private void configureResponseProcessors() {
- Stream.of(SnmpCommunicationSpec.TELEMETRY_QUERYING, SnmpCommunicationSpec.TELEMETRY_TRAPS_RECEIVING)
- .forEach(telemetrySpec -> {
- responseProcessors.put(telemetrySpec, (response, sessionContext) -> {
- TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(response);
- transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, TransportServiceCallback.EMPTY);
- log.debug("Posted telemetry for device {}: {}", sessionContext.getDeviceId(), response);
- });
- });
-
- Stream.of(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, SnmpCommunicationSpec.CLIENT_ATTRIBUTES_TRAPS_RECEIVING)
- .forEach(clientAttributesSpec -> {
- responseProcessors.put(clientAttributesSpec, (response, sessionContext) -> {
- TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(response);
- transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, TransportServiceCallback.EMPTY);
- log.debug("Posted attributes for device {}: {}", sessionContext.getDeviceId(), response);
- });
- });
+ responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (response, sessionContext) -> {
+ TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(response);
+ transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, TransportServiceCallback.EMPTY);
+ log.debug("Posted telemetry for device {}: {}", sessionContext.getDeviceId(), response);
+ });
+
+ responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (response, sessionContext) -> {
+ TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(response);
+ transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, TransportServiceCallback.EMPTY);
+ log.debug("Posted attributes for device {}: {}", sessionContext.getDeviceId(), response);
+ });
}
private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
@@ -353,11 +314,6 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
}
}
- @Override
- public void processPdu(CommandResponderEvent event) {
- processTrap(event);
- }
-
@Override
public String getName() {
return "SNMP";
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
index c0bbc843bc..c5a329547f 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
@@ -18,29 +18,15 @@ package org.thingsboard.server.transport.snmp.session;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
-import org.snmp4j.AbstractTarget;
-import org.snmp4j.CommunityTarget;
import org.snmp4j.Target;
-import org.snmp4j.UserTarget;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
-import org.snmp4j.security.AuthSHA;
-import org.snmp4j.security.PrivDES;
-import org.snmp4j.security.SecurityLevel;
-import org.snmp4j.security.SecurityModel;
-import org.snmp4j.security.SecurityModels;
-import org.snmp4j.security.USM;
-import org.snmp4j.security.UsmUser;
-import org.snmp4j.smi.GenericAddress;
-import org.snmp4j.smi.OID;
-import org.snmp4j.smi.OctetString;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
-import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
@@ -50,6 +36,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponse
import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
+import org.thingsboard.server.transport.snmp.SnmpAuthService;
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
@@ -77,6 +64,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
private final SnmpTransportContext snmpTransportContext;
private final SnmpTransportService snmpTransportService;
+ private final SnmpAuthService snmpAuthService;
@Getter
@Setter
@@ -84,7 +72,6 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
private final AtomicInteger msgIdSeq = new AtomicInteger(0);
@Getter
private boolean isActive = true;
- private final String snmpUnderlyingProtocol;
@Getter
@Setter
@@ -93,8 +80,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token,
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration,
SnmpDeviceTransportConfiguration deviceTransportConfiguration,
- SnmpTransportContext snmpTransportContext, SnmpTransportService snmpTransportService,
- String snmpUnderlyingProtocol) {
+ SnmpTransportContext snmpTransportContext) {
super(UUID.randomUUID());
super.setDeviceId(device.getId());
super.setDeviceProfile(deviceProfile);
@@ -102,12 +88,12 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
this.token = token;
this.snmpTransportContext = snmpTransportContext;
- this.snmpTransportService = snmpTransportService;
+ this.snmpTransportService = snmpTransportContext.getSnmpTransportService();
+ this.snmpAuthService = snmpTransportContext.getSnmpAuthService();
this.profileTransportConfiguration = profileTransportConfiguration;
this.deviceTransportConfiguration = deviceTransportConfiguration;
- this.snmpUnderlyingProtocol = snmpUnderlyingProtocol;
initializeTarget(profileTransportConfiguration, deviceTransportConfiguration);
}
@@ -133,55 +119,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
log.trace("Initializing target for SNMP session of device {}", device);
-
- AbstractTarget target;
-
- SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();
- switch (protocolVersion) {
- case V1:
- CommunityTarget communityTargetV1 = new CommunityTarget();
- communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);
- communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
- communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getSecurityName()));
- target = communityTargetV1;
- break;
- case V2C:
- CommunityTarget communityTargetV2 = new CommunityTarget();
- communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
- communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
- communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getSecurityName()));
- target = communityTargetV2;
- break;
- case V3:
- USM usm = new USM();
- SecurityModels.getInstance().addSecurityModel(usm);
-
- OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());
- OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());
- OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());
-
- OID authenticationProtocol = AuthSHA.ID;
- OID privacyProtocol = PrivDES.ID; // FIXME: to config
-
- UsmUser user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase);
- snmpTransportService.getSnmp().getUSM().addUser(user);
-
- UserTarget userTarget = new UserTarget();
- userTarget.setSecurityName(securityName);
- userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
-
- target = userTarget;
- break;
- default:
- throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
- }
-
- target.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
- target.setTimeout(profileTransportConfig.getTimeoutMs());
- target.setRetries(profileTransportConfig.getRetries());
- target.setVersion(protocolVersion.getCode());
-
- this.target = target;
+ this.target = snmpAuthService.setUpSnmpTarget(profileTransportConfig, deviceTransportConfig);
log.info("SNMP target initialized: {}", target);
}
diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java
index d48eaadce3..c97f47d573 100644
--- a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java
+++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java
@@ -73,32 +73,6 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent {
}
}
- public static void main(String[] args) throws IOException {
- SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");
-
- device.start();
- device.setUpMappings(Map.of(
- ".1.3.6.1.2.1.1.1.50", "12",
- ".1.3.6.1.2.1.2.1.52", "56",
- ".1.3.6.1.2.1.3.1.54", "yes",
- ".1.3.6.1.2.1.7.1.58", ""
- ));
-
-
-// while (true) {
-// new Scanner(System.in).nextLine();
-// device.sendTrap("127.0.0.1", 1062, Map.of(".1.3.6.1.2.87.1.56", "12"));
-// System.out.println("sent");
-// }
-
-// Snmp snmp = new Snmp(device.transportMappings[0]);
-// device.snmp.addCommandResponder(event -> {
-// System.out.println(event);
-// });
-
- new Scanner(System.in).nextLine();
- }
-
private final Target target;
private final Address address;
diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java
index 63ccd7021a..da7f984267 100644
--- a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java
+++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java
@@ -15,38 +15,731 @@
*/
package org.thingsboard.server.transport.snmp;
-import org.snmp4j.UserTarget;
+import org.snmp4j.MessageDispatcherImpl;
+import org.snmp4j.TransportMapping;
+import org.snmp4j.agent.BaseAgent;
+import org.snmp4j.agent.CommandProcessor;
+import org.snmp4j.agent.DuplicateRegistrationException;
+import org.snmp4j.agent.MOGroup;
+import org.snmp4j.agent.ManagedObject;
+import org.snmp4j.agent.mo.DefaultMOMutableRow2PC;
+import org.snmp4j.agent.mo.DefaultMOTable;
+import org.snmp4j.agent.mo.MOAccessImpl;
+import org.snmp4j.agent.mo.MOColumn;
+import org.snmp4j.agent.mo.MOMutableColumn;
+import org.snmp4j.agent.mo.MOMutableTableModel;
+import org.snmp4j.agent.mo.MOScalar;
+import org.snmp4j.agent.mo.MOTableIndex;
+import org.snmp4j.agent.mo.MOTableRow;
+import org.snmp4j.agent.mo.MOTableSubIndex;
+import org.snmp4j.agent.mo.ext.AgentppSimulationMib;
+import org.snmp4j.agent.mo.snmp.RowStatus;
+import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
+import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
+import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
+import org.snmp4j.agent.mo.snmp.StorageType;
+import org.snmp4j.agent.mo.snmp.TransportDomains;
+import org.snmp4j.agent.mo.snmp.VacmMIB;
+import org.snmp4j.agent.mo.snmp4j.example.Snmp4jHeartbeatMib;
+import org.snmp4j.agent.security.MutableVACM;
+import org.snmp4j.mp.MPv1;
+import org.snmp4j.mp.MPv2c;
+import org.snmp4j.mp.MPv3;
+import org.snmp4j.mp.MessageProcessingModel;
+import org.snmp4j.security.AuthHMAC192SHA256;
+import org.snmp4j.security.AuthMD5;
import org.snmp4j.security.AuthSHA;
+import org.snmp4j.security.PrivAES128;
+import org.snmp4j.security.PrivAES192;
+import org.snmp4j.security.PrivAES256;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.SecurityModels;
+import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.security.UsmUser;
+import org.snmp4j.smi.Address;
+import org.snmp4j.smi.Gauge32;
+import org.snmp4j.smi.GenericAddress;
+import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.SMIConstants;
+import org.snmp4j.smi.TcpAddress;
+import org.snmp4j.smi.TimeTicks;
+import org.snmp4j.smi.UdpAddress;
+import org.snmp4j.smi.Variable;
+import org.snmp4j.transport.DefaultTcpTransportMapping;
+import org.snmp4j.transport.TransportMappings;
+import org.snmp4j.util.ThreadPool;
+import java.io.File;
import java.io.IOException;
+import java.util.Map;
-public class SnmpDeviceSimulatorV3 extends SnmpDeviceSimulatorV2 {
- public SnmpDeviceSimulatorV3(int port, String securityName, String authenticationPassphrase, String privacyPassphrase) throws IOException {
- super(12, null);
-// super(new File("conf.agent"), new File("bootCounter.agent"));
+/**
+ * The TestAgent is a sample SNMP agent implementation of all
+ * features (MIB implementations) provided by the SNMP4J-Agent framework.
+ *
+ * Note, for snmp4s, this code is mostly a copy from snmp4j.
+ * And don't remove snmp users
+ *
+ */
+public class SnmpDeviceSimulatorV3 extends BaseAgent {
+ protected String address;
+ private Snmp4jHeartbeatMib heartbeatMIB;
+ private AgentppSimulationMib agentppSimulationMIB;
+
+ public SnmpDeviceSimulatorV3(CommandProcessor processor) throws IOException {
+ super(new File("SNMP4JTestAgentBC.cfg"), new File("SNMP4JTestAgentConfig.cfg"),
+ processor);
+ agent.setWorkerPool(ThreadPool.create("RequestPool", 4));
+ }
+
+ public void setUpMappings(Map oidToResponseMappings) {
+ unregisterManagedObject(getSnmpv2MIB());
+ oidToResponseMappings.forEach((oid, response) -> {
+ registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response)));
+ });
+ }
+ protected void registerManagedObject(ManagedObject mo) {
+ try {
+ server.register(mo, null);
+ } catch (DuplicateRegistrationException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ protected void unregisterManagedObject(MOGroup moGroup) {
+ moGroup.unregisterMOs(server, getContext(moGroup));
+ }
+
+ protected void registerManagedObjects() {
+ try {
+ server.register(createStaticIfTable(), null);
+ server.register(createStaticIfXTable(), null);
+ agentppSimulationMIB.registerMOs(server, null);
+ heartbeatMIB.registerMOs(server, null);
+ } catch (DuplicateRegistrationException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ protected void addNotificationTargets(SnmpTargetMIB targetMIB,
+ SnmpNotificationMIB notificationMIB) {
+ targetMIB.addDefaultTDomains();
+
+ targetMIB.addTargetAddress(new OctetString("notificationV2c"),
+ TransportDomains.transportDomainUdpIpv4,
+ new OctetString(new UdpAddress("127.0.0.1/162").getValue()),
+ 200, 1,
+ new OctetString("notify"),
+ new OctetString("v2c"),
+ StorageType.permanent);
+ targetMIB.addTargetAddress(new OctetString("notificationV3"),
+ TransportDomains.transportDomainUdpIpv4,
+ new OctetString(new UdpAddress("127.0.0.1/1162").getValue()),
+ 200, 1,
+ new OctetString("notify"),
+ new OctetString("v3notify"),
+ StorageType.permanent);
+ targetMIB.addTargetParams(new OctetString("v2c"),
+ MessageProcessingModel.MPv2c,
+ SecurityModel.SECURITY_MODEL_SNMPv2c,
+ new OctetString("cpublic"),
+ SecurityLevel.AUTH_PRIV,
+ StorageType.permanent);
+ targetMIB.addTargetParams(new OctetString("v3notify"),
+ MessageProcessingModel.MPv3,
+ SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("v3notify"),
+ SecurityLevel.NOAUTH_NOPRIV,
+ StorageType.permanent);
+ notificationMIB.addNotifyEntry(new OctetString("default"),
+ new OctetString("notify"),
+ SnmpNotificationMIB.SnmpNotifyTypeEnum.inform,
+ StorageType.permanent);
+ }
+ protected void addViews(VacmMIB vacm) {
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv1,
+ new OctetString("cpublic"),
+ new OctetString("v1v2group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c,
+ new OctetString("cpublic"),
+ new OctetString("v1v2group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("SHADES"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("MD5DES"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("TEST"),
+ new OctetString("v3test"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("SHA"),
+ new OctetString("v3restricted"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("SHAAES128"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("SHAAES192"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("SHAAES256"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("MD5AES128"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("MD5AES192"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("MD5AES256"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("aboba"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ //============================================//
+ // agent5-auth-priv
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("agent5"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ //===========================================//
+ // agent002
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("agent002"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+ //===========================================//
+ // user001-auth-no-priv
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("user001"),
+ new OctetString("group001"),
+ StorageType.nonVolatile);
+ //===========================================//
+
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("v3notify"),
+ new OctetString("v3group"),
+ StorageType.nonVolatile);
+
+ //===========================================//
+ // group auth no priv
+ vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
+ new OctetString("v3notify-auth"),
+ new OctetString("group001"),
+ StorageType.nonVolatile);
+ //===========================================//
+
+
+
+ // my conf
+ vacm.addAccess(new OctetString("group001"), new OctetString("public"),
+ SecurityModel.SECURITY_MODEL_USM,
+ SecurityLevel.AUTH_NOPRIV,
+ MutableVACM.VACM_MATCH_EXACT,
+ new OctetString("fullReadView"),
+ new OctetString("fullWriteView"),
+ new OctetString("fullNotifyView"),
+ StorageType.nonVolatile);
+
+ vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"),
+ SecurityModel.SECURITY_MODEL_ANY,
+ SecurityLevel.NOAUTH_NOPRIV,
+ MutableVACM.VACM_MATCH_EXACT,
+ new OctetString("fullReadView"),
+ new OctetString("fullWriteView"),
+ new OctetString("fullNotifyView"),
+ StorageType.nonVolatile);
+ vacm.addAccess(new OctetString("v3group"), new OctetString(),
+ SecurityModel.SECURITY_MODEL_USM,
+ SecurityLevel.AUTH_PRIV,
+ MutableVACM.VACM_MATCH_EXACT,
+ new OctetString("fullReadView"),
+ new OctetString("fullWriteView"),
+ new OctetString("fullNotifyView"),
+ StorageType.nonVolatile);
+ vacm.addAccess(new OctetString("v3restricted"), new OctetString(),
+ SecurityModel.SECURITY_MODEL_USM,
+ SecurityLevel.NOAUTH_NOPRIV,
+ MutableVACM.VACM_MATCH_EXACT,
+ new OctetString("restrictedReadView"),
+ new OctetString("restrictedWriteView"),
+ new OctetString("restrictedNotifyView"),
+ StorageType.nonVolatile);
+ vacm.addAccess(new OctetString("v3test"), new OctetString(),
+ SecurityModel.SECURITY_MODEL_USM,
+ SecurityLevel.AUTH_PRIV,
+ MutableVACM.VACM_MATCH_EXACT,
+ new OctetString("testReadView"),
+ new OctetString("testWriteView"),
+ new OctetString("testNotifyView"),
+ StorageType.nonVolatile);
+
+ vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"),
+ new OctetString(), VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+ vacm.addViewTreeFamily(new OctetString("fullWriteView"), new OID("1.3"),
+ new OctetString(), VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+ vacm.addViewTreeFamily(new OctetString("fullNotifyView"), new OID("1.3"),
+ new OctetString(), VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+
+ vacm.addViewTreeFamily(new OctetString("restrictedReadView"),
+ new OID("1.3.6.1.2"),
+ new OctetString(), VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+ vacm.addViewTreeFamily(new OctetString("restrictedWriteView"),
+ new OID("1.3.6.1.2.1"),
+ new OctetString(),
+ VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+ vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
+ new OID("1.3.6.1.2"),
+ new OctetString(), VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+ vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
+ new OID("1.3.6.1.6.3.1"),
+ new OctetString(), VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
- USM usm = new USM();
- SecurityModels.getInstance().addSecurityModel(usm);
+ vacm.addViewTreeFamily(new OctetString("testReadView"),
+ new OID("1.3.6.1.2"),
+ new OctetString(), VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+ vacm.addViewTreeFamily(new OctetString("testReadView"),
+ new OID("1.3.6.1.2.1.1"),
+ new OctetString(), VacmMIB.vacmViewExcluded,
+ StorageType.nonVolatile);
+ vacm.addViewTreeFamily(new OctetString("testWriteView"),
+ new OID("1.3.6.1.2.1"),
+ new OctetString(),
+ VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+ vacm.addViewTreeFamily(new OctetString("testNotifyView"),
+ new OID("1.3.6.1.2"),
+ new OctetString(), VacmMIB.vacmViewIncluded,
+ StorageType.nonVolatile);
+ }
+
+ protected void addUsmUser(USM usm) {
+ UsmUser user = new UsmUser(new OctetString("SHADES"),
+ AuthSHA.ID,
+ new OctetString("SHADESAuthPassword"),
+ PrivDES.ID,
+ new OctetString("SHADESPrivPassword"));
+// usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ usm.addUser(user.getSecurityName(), null, user);
+ user = new UsmUser(new OctetString("TEST"),
+ AuthSHA.ID,
+ new OctetString("maplesyrup"),
+ PrivDES.ID,
+ new OctetString("maplesyrup"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ user = new UsmUser(new OctetString("SHA"),
+ AuthSHA.ID,
+ new OctetString("SHAAuthPassword"),
+ null,
+ null);
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ user = new UsmUser(new OctetString("SHADES"),
+ AuthSHA.ID,
+ new OctetString("SHADESAuthPassword"),
+ PrivDES.ID,
+ new OctetString("SHADESPrivPassword"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ user = new UsmUser(new OctetString("MD5DES"),
+ AuthMD5.ID,
+ new OctetString("MD5DESAuthPassword"),
+ PrivDES.ID,
+ new OctetString("MD5DESPrivPassword"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ user = new UsmUser(new OctetString("SHAAES128"),
+ AuthSHA.ID,
+ new OctetString("SHAAES128AuthPassword"),
+ PrivAES128.ID,
+ new OctetString("SHAAES128PrivPassword"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ user = new UsmUser(new OctetString("SHAAES192"),
+ AuthSHA.ID,
+ new OctetString("SHAAES192AuthPassword"),
+ PrivAES192.ID,
+ new OctetString("SHAAES192PrivPassword"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ user = new UsmUser(new OctetString("SHAAES256"),
+ AuthSHA.ID,
+ new OctetString("SHAAES256AuthPassword"),
+ PrivAES256.ID,
+ new OctetString("SHAAES256PrivPassword"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+
+ user = new UsmUser(new OctetString("MD5AES128"),
+ AuthMD5.ID,
+ new OctetString("MD5AES128AuthPassword"),
+ PrivAES128.ID,
+ new OctetString("MD5AES128PrivPassword"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ user = new UsmUser(new OctetString("MD5AES192"),
+ AuthHMAC192SHA256.ID,
+ new OctetString("MD5AES192AuthPassword"),
+ PrivAES192.ID,
+ new OctetString("MD5AES192PrivPassword"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ //==============================================================
+ user = new UsmUser(new OctetString("MD5AES256"),
+ AuthMD5.ID,
+ new OctetString("MD5AES256AuthPassword"),
+ PrivAES256.ID,
+ new OctetString("MD5AES256PrivPassword"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ user = new UsmUser(new OctetString("MD5AES256"),
+ AuthMD5.ID,
+ new OctetString("MD5AES256AuthPassword"),
+ PrivAES256.ID,
+ new OctetString("MD5AES256PrivPassword"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+
+ OctetString securityName = new OctetString("aboba");
+ OctetString authenticationPassphrase = new OctetString("abobaaboba");
+ OctetString privacyPassphrase = new OctetString("abobaaboba");
OID authenticationProtocol = AuthSHA.ID;
- OID privacyProtocol = PrivDES.ID;
+ OID privacyProtocol = PrivDES.ID; // FIXME: to config
+ user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase);
+ usm.addUser(user);
+
+ //===============================================================//
+ user = new UsmUser(new OctetString("agent5"),
+ AuthSHA.ID,
+ new OctetString("authpass"),
+ PrivDES.ID,
+ new OctetString("privpass"));
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ //===============================================================//
+ // user001
+ user = new UsmUser(new OctetString("user001"),
+ AuthSHA.ID,
+ new OctetString("authpass"),
+ null, null);
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ //===============================================================//
+ // user002
+ user = new UsmUser(new OctetString("user001"),
+ null,
+ null,
+ null, null);
+ usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
+ //===============================================================//
+
+ user = new UsmUser(new OctetString("v3notify"),
+ null,
+ null,
+ null,
+ null);
+ usm.addUser(user.getSecurityName(), null, user);
+
+ this.usm = usm;
+ }
+
+ private static DefaultMOTable createStaticIfXTable() {
+ MOTableSubIndex[] subIndexes =
+ new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
+ MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
+ MOColumn[] columns = new MOColumn[19];
+ int c = 0;
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifName
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifInMulticastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifInBroadcastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifOutMulticastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifOutBroadcastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifHCInOctets
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifHCInUcastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifHCInMulticastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifHCInBroadcastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutOctets
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutUcastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutMulticastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutBroadcastPkts
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
+ MOAccessImpl.ACCESS_READ_WRITE); // ifLinkUpDownTrapEnable
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifHighSpeed
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
+ MOAccessImpl.ACCESS_READ_WRITE); // ifPromiscuousMode
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifConnectorPresent
+ columns[c++] =
+ new MOMutableColumn(c, SMIConstants.SYNTAX_OCTET_STRING, // ifAlias
+ MOAccessImpl.ACCESS_READ_WRITE, null);
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_TIMETICKS,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifCounterDiscontinuityTime
+
+ DefaultMOTable ifXTable =
+ new DefaultMOTable(new OID("1.3.6.1.2.1.31.1.1.1"), indexDef, columns);
+ MOMutableTableModel model = (MOMutableTableModel) ifXTable.getModel();
+ Variable[] rowValues1 = new Variable[] {
+ new OctetString("Ethernet-0"),
+ new Integer32(1),
+ new Integer32(2),
+ new Integer32(3),
+ new Integer32(4),
+ new Integer32(5),
+ new Integer32(6),
+ new Integer32(7),
+ new Integer32(8),
+ new Integer32(9),
+ new Integer32(10),
+ new Integer32(11),
+ new Integer32(12),
+ new Integer32(13),
+ new Integer32(14),
+ new Integer32(15),
+ new Integer32(16),
+ new OctetString("My eth"),
+ new TimeTicks(1000)
+ };
+ Variable[] rowValues2 = new Variable[] {
+ new OctetString("Loopback"),
+ new Integer32(21),
+ new Integer32(22),
+ new Integer32(23),
+ new Integer32(24),
+ new Integer32(25),
+ new Integer32(26),
+ new Integer32(27),
+ new Integer32(28),
+ new Integer32(29),
+ new Integer32(30),
+ new Integer32(31),
+ new Integer32(32),
+ new Integer32(33),
+ new Integer32(34),
+ new Integer32(35),
+ new Integer32(36),
+ new OctetString("My loop"),
+ new TimeTicks(2000)
+ };
+ model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
+ model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
+ ifXTable.setVolatile(true);
+ return ifXTable;
+ }
+
+ private static DefaultMOTable createStaticIfTable() {
+ MOTableSubIndex[] subIndexes =
+ new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
+ MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
+ MOColumn[] columns = new MOColumn[8];
+ int c = 0;
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifIndex
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifDescr
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifType
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifPhysAddress
+ columns[c++] =
+ new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, // ifAdminStatus
+ MOAccessImpl.ACCESS_READ_WRITE, null);
+ columns[c++] =
+ new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
+ MOAccessImpl.ACCESS_READ_ONLY); // ifOperStatus
- UsmUser user = new UsmUser(new OctetString(securityName), authenticationProtocol, new OctetString(authenticationPassphrase), privacyProtocol, new OctetString(privacyPassphrase));
+ DefaultMOTable ifTable =
+ new DefaultMOTable(new OID("1.3.6.1.2.1.2.2.1"), indexDef, columns);
+ MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
+ Variable[] rowValues1 = new Variable[] {
+ new Integer32(1),
+ new OctetString("eth0"),
+ new Integer32(6),
+ new Integer32(1500),
+ new Gauge32(100000000),
+ new OctetString("00:00:00:00:01"),
+ new Integer32(1),
+ new Integer32(1)
+ };
+ Variable[] rowValues2 = new Variable[] {
+ new Integer32(2),
+ new OctetString("loopback"),
+ new Integer32(24),
+ new Integer32(1500),
+ new Gauge32(10000000),
+ new OctetString("00:00:00:00:02"),
+ new Integer32(1),
+ new Integer32(1)
+ };
+ model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
+ model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
+ ifTable.setVolatile(true);
+ return ifTable;
}
- public void initV3(UsmUser user, String securityName) {
-// snmp.getUSM().addUser(user);
+ private static DefaultMOTable createStaticSnmp4sTable() {
+ MOTableSubIndex[] subIndexes =
+ new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
+ MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
+ MOColumn[] columns = new MOColumn[8];
+ int c = 0;
+ columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_NULL, MOAccessImpl.ACCESS_READ_ONLY); // testNull
+ columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // testBoolean
+ columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifType
+ columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
+ columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_GAUGE32, MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
+ columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING, MOAccessImpl.ACCESS_READ_ONLY); //ifPhysAddress
+ columns[c++] = new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_WRITE,
+ null);
+ // ifAdminStatus
+ columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY);
+ // ifOperStatus
+
+ DefaultMOTable ifTable =
+ new DefaultMOTable(new OID("1.3.6.1.4.1.50000.1.1"), indexDef, columns);
+ MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
+ Variable[] rowValues1 = new Variable[] {
+ new Integer32(1),
+ new OctetString("eth0"),
+ new Integer32(6),
+ new Integer32(1500),
+ new Gauge32(100000000),
+ new OctetString("00:00:00:00:01"),
+ new Integer32(1),
+ new Integer32(1)
+ };
+ Variable[] rowValues2 = new Variable[] {
+ new Integer32(2),
+ new OctetString("loopback"),
+ new Integer32(24),
+ new Integer32(1500),
+ new Gauge32(10000000),
+ new OctetString("00:00:00:00:02"),
+ new Integer32(1),
+ new Integer32(1)
+ };
+ model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
+ model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
+ ifTable.setVolatile(true);
+ return ifTable;
+ }
+
+ protected void initTransportMappings() throws IOException {
+ transportMappings = new TransportMapping[2];
+ Address addr = GenericAddress.parse(address);
+ TransportMapping tm =
+ TransportMappings.getInstance().createTransportMapping(addr);
+ transportMappings[0] = tm;
+ transportMappings[1] = new DefaultTcpTransportMapping(new TcpAddress(address));
+ }
+
+ public void start(String ip, String port) throws IOException {
+ address = ip + "/" + port;
+ //BasicConfigurator.configure();
+ init();
+ addShutdownHook();
+// loadConfig(ImportModes.REPLACE_CREATE);
+ getServer().addContext(new OctetString("public"));
+ finishInit();
+ run();
+ sendColdStartNotification();
+ }
+
+ protected void unregisterManagedObjects() {
+ // here we should unregister those objects previously registered...
+ }
+
+ protected void addCommunities(SnmpCommunityMIB communityMIB) {
+ Variable[] com2sec = new Variable[] {
+ new OctetString("public"), // community name
+ new OctetString("cpublic"), // security name
+ getAgent().getContextEngineID(), // local engine ID
+ new OctetString("public"), // default context name
+ new OctetString(), // transport tag
+ new Integer32(StorageType.nonVolatile), // storage type
+ new Integer32(RowStatus.active) // row status
+ };
+ MOTableRow row =
+ communityMIB.getSnmpCommunityEntry().createRow(
+ new OctetString("public2public").toSubIndex(true), com2sec);
+ communityMIB.getSnmpCommunityEntry().addRow((SnmpCommunityMIB.SnmpCommunityEntryRow) row);
+// snmpCommunityMIB.setSourceAddressFiltering(true);
+ }
+
+ protected void registerSnmpMIBs() {
+ heartbeatMIB = new Snmp4jHeartbeatMib(super.getNotificationOriginator(),
+ new OctetString(),
+ super.snmpv2MIB.getSysUpTime());
+ agentppSimulationMIB = new AgentppSimulationMib();
+ super.registerSnmpMIBs();
+ }
- UserTarget userTarget = new UserTarget();
- userTarget.setSecurityName(new OctetString(securityName));
- userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
+ protected void initMessageDispatcher() {
+ this.dispatcher = new MessageDispatcherImpl();
+ this.mpv3 = new MPv3(this.agent.getContextEngineID().getValue());
+ this.usm = new USM(SecurityProtocols.getInstance(), this.agent.getContextEngineID(), this.updateEngineBoots());
+ SecurityModels.getInstance().addSecurityModel(this.usm);
+ SecurityProtocols.getInstance().addDefaultProtocols();
+ this.dispatcher.addMessageProcessingModel(new MPv1());
+ this.dispatcher.addMessageProcessingModel(new MPv2c());
+ this.dispatcher.addMessageProcessingModel(this.mpv3);
+ this.initSnmpSession();
}
-}
+}
\ No newline at end of file
diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTest.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java
similarity index 98%
rename from common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTest.java
rename to common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java
index 29a8c085c8..417204296c 100644
--- a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTest.java
+++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java
@@ -19,7 +19,7 @@ import java.io.IOException;
import java.util.Map;
import java.util.Scanner;
-public class SnmpTest {
+public class SnmpTestV2 {
public static void main(String[] args) throws IOException {
SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");
diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV3.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV3.java
new file mode 100644
index 0000000000..8e40deb17d
--- /dev/null
+++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV3.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp;
+
+import org.snmp4j.CommandResponderEvent;
+import org.snmp4j.agent.CommandProcessor;
+import org.snmp4j.mp.MPv3;
+import org.snmp4j.smi.OctetString;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Scanner;
+
+public class SnmpTestV3 {
+ public static void main(String[] args) throws IOException {
+ SnmpDeviceSimulatorV3 device = new SnmpDeviceSimulatorV3(new CommandProcessor(new OctetString(MPv3.createLocalEngineID())) {
+ @Override
+ public void processPdu(CommandResponderEvent event) {
+ System.out.println("event: " + event);
+ }
+ });
+ device.start("0.0.0.0", "1610");
+
+ device.setUpMappings(Map.of(
+ ".1.3.6.1.2.1.1.1.50", "12",
+ ".1.3.6.1.2.1.2.1.52", "56",
+ ".1.3.6.1.2.1.3.1.54", "yes",
+ ".1.3.6.1.2.1.7.1.58", ""
+ ));
+
+ new Scanner(System.in).nextLine();
+ }
+}
diff --git a/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json b/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
index f8291a090b..f74ebca0bf 100644
--- a/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
+++ b/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
@@ -38,16 +38,6 @@
"dataType": "STRING"
}
]
- },
- {
- "spec": "TELEMETRY_TRAPS_RECEIVING",
- "mappings": [
- {
- "oid": ".1.3.6.1.2.8.7.1.56",
- "key": "temperature_trap",
- "dataType": "LONG"
- }
- ]
}
]
}
diff --git a/common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json b/common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json
new file mode 100644
index 0000000000..039e03fa53
--- /dev/null
+++ b/common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json
@@ -0,0 +1,13 @@
+{
+ "address": "192.168.3.23",
+ "port": 1610,
+ "protocolVersion": "V3",
+
+ "username": "tb-user",
+ "engineId": "qwertyuioa",
+ "securityName": "tb-user",
+ "authenticationProtocol": "SHA_512",
+ "authenticationPassphrase": "sdfghjkloifgh",
+ "privacyProtocol": "DES",
+ "privacyPassphrase": "rtytguijokod"
+}
\ No newline at end of file
diff --git a/common/transport/snmp/src/test/resources/snmp-device-transport-config.json b/common/transport/snmp/src/test/resources/snmp-device-transport-config.json
index 9a97b75e40..c73d817bfb 100644
--- a/common/transport/snmp/src/test/resources/snmp-device-transport-config.json
+++ b/common/transport/snmp/src/test/resources/snmp-device-transport-config.json
@@ -1,6 +1,6 @@
{
"address": "127.0.0.1",
"port": 1610,
- "password": "public",
+ "community": "public",
"protocolVersion": "V2C"
}
\ No newline at end of file
From 415bf570bac6b422f880622163924464d6ca390b Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Wed, 7 Apr 2021 19:22:28 +0300
Subject: [PATCH 11/32] Refactor
---
.../SnmpDeviceTransportConfiguration.java | 26 +++++++--
...mpDeviceProfileTransportConfiguration.java | 18 +++++--
...eatingQueryingSnmpCommunicationConfig.java | 2 +-
...ributesSettingSnmpCommunicationConfig.java | 5 --
.../snmp/configs/SnmpCommunicationConfig.java | 8 +--
.../transport/snmp/SnmpAuthService.java | 3 +-
.../transport/snmp/SnmpTransportContext.java | 54 ++++++++++---------
.../snmp/service/SnmpTransportService.java | 27 ++++++++--
.../snmp/session/DeviceSessionContext.java | 14 ++---
9 files changed, 98 insertions(+), 59 deletions(-)
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
index 24ab463129..68a9d1f218 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
@@ -17,16 +17,21 @@ package org.thingsboard.server.common.data.device.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
+import lombok.ToString;
+import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.transport.snmp.AuthenticationProtocol;
import org.thingsboard.server.common.data.transport.snmp.PrivacyProtocol;
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
+import java.util.Objects;
+
@Data
+@ToString(of = {"host", "port", "protocolVersion"})
public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
- private String address;
- private int port;
+ private String host;
+ private Integer port;
private SnmpProtocolVersion protocolVersion;
/*
@@ -60,6 +65,21 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur
@JsonIgnore
private boolean isValid() {
- return true;
+ boolean isValid = StringUtils.isNotBlank(host) && port != null && protocolVersion != null;
+ if (isValid) {
+ switch (protocolVersion) {
+ case V1:
+ case V2C:
+ isValid = StringUtils.isNotEmpty(community);
+ break;
+ case V3:
+ isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName)
+ && contextName != null && authenticationProtocol != null
+ && StringUtils.isNotBlank(authenticationPassphrase)
+ && privacyProtocol != null && privacyPassphrase != null && engineId != null;
+ break;
+ }
+ }
+ return isValid;
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
index 843a51304e..5d6afc7155 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
@@ -17,15 +17,21 @@ package org.thingsboard.server.common.data.device.profile;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
+import org.apache.commons.lang3.ArrayUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig;
+import java.util.Collections;
import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
@Data
public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
- private int timeoutMs;
- private int retries;
+ private Integer timeoutMs;
+ private Integer retries;
private List communicationConfigs;
@Override
@@ -36,12 +42,16 @@ public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTra
@Override
public void validate() {
if (!isValid()) {
- throw new IllegalArgumentException("Transport configuration is not valid");
+ throw new IllegalArgumentException("SNMP transport configuration is not valid");
}
}
@JsonIgnore
private boolean isValid() {
- return true;
+ return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0
+ && communicationConfigs != null && !communicationConfigs.isEmpty()
+ && communicationConfigs.stream().allMatch(config -> config != null && config.isValid())
+ && communicationConfigs.stream().flatMap(config -> config.getMappings().stream()).map(SnmpMapping::getOid)
+ .distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getMappings().size()).sum();
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java
index 764bf97b15..19a009457a 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java
@@ -31,6 +31,6 @@ public abstract class RepeatingQueryingSnmpCommunicationConfig extends SnmpCommu
@Override
public boolean isValid() {
- return true;
+ return super.isValid() && queryingFrequencyMs != null && queryingFrequencyMs > 0;
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java
index 1c63dae618..74c67f957a 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java
@@ -28,9 +28,4 @@ public class SharedAttributesSettingSnmpCommunicationConfig extends SnmpCommunic
public SnmpMethod getMethod() {
return SnmpMethod.SET;
}
-
- @Override
- public boolean isValid() {
- return true;
- }
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
index 7e1ee27237..acc0df0dd7 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
@@ -49,12 +49,6 @@ public abstract class SnmpCommunicationConfig {
@JsonIgnore
public boolean isValid() {
- return true;
- }
-
- public void validate() {
- if (!isValid()) {
- throw new IllegalArgumentException("Communication config is not valid");
- }
+ return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid());
}
}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java
index 59b31d593c..81187fb33e 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java
@@ -24,7 +24,6 @@ import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
-import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
@@ -98,7 +97,7 @@ public class SnmpAuthService {
throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
}
- target.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
+ target.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getHost() + "/" + deviceTransportConfig.getPort()));
target.setTimeout(profileTransportConfig.getTimeoutMs());
target.setRetries(profileTransportConfig.getRetries());
target.setVersion(protocolVersion.getCode());
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index 2b0ce984aa..d7441c7c1d 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -89,18 +89,12 @@ public class SnmpTransportContext extends TransportContext {
managedDevicesIds.stream()
.map(protoEntityService::getDeviceById)
.collect(Collectors.toList())
- .forEach(device -> {
- try {
- establishDeviceSession(device);
- } catch (Exception e) {
- log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.getMessage());
- }
- });
+ .forEach(this::establishDeviceSession);
}
private void establishDeviceSession(Device device) {
if (device == null) return;
- log.info("Establishing SNMP device session for device {}", device.getId());
+ log.info("Establishing SNMP session for device {}", device.getId());
DeviceProfileId deviceProfileId = device.getDeviceProfileId();
DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
@@ -114,18 +108,24 @@ public class SnmpTransportContext extends TransportContext {
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
- DeviceSessionContext deviceSessionContext = new DeviceSessionContext(
- device, deviceProfile, credentials.getCredentialsId(),
- profileTransportConfiguration, deviceTransportConfiguration, this
- );
- registerSessionMsgListener(deviceSessionContext);
+ DeviceSessionContext deviceSessionContext;
+ try {
+ deviceSessionContext = new DeviceSessionContext(
+ device, deviceProfile, credentials.getCredentialsId(),
+ profileTransportConfiguration, deviceTransportConfiguration, this
+ );
+ registerSessionMsgListener(deviceSessionContext);
+ } catch (Exception e) {
+ log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.getMessage());
+ return;
+ }
sessions.put(device.getId(), deviceSessionContext);
snmpTransportService.createQueryingTasks(deviceSessionContext);
log.info("Established SNMP device session for device {}", device.getId());
}
private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) {
- log.info("Updating SNMP device session for device {}", device.getId());
+ log.info("Updating SNMP session for device {}", device.getId());
DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
@@ -137,16 +137,20 @@ public class SnmpTransportContext extends TransportContext {
SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
- if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
- sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
- sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
- snmpTransportService.cancelQueryingTasks(sessionContext);
- snmpTransportService.createQueryingTasks(sessionContext);
- } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
- sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
- sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
- } else {
- log.trace("Configuration of the device {} was not updated", device);
+ try {
+ if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
+ sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
+ sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
+ snmpTransportService.cancelQueryingTasks(sessionContext);
+ snmpTransportService.createQueryingTasks(sessionContext);
+ } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
+ sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
+ sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
+ } else {
+ log.trace("Configuration of the device {} was not updated", device);
+ }
+ } catch (Exception e) {
+ log.error("Failed to update session for SNMP device {}: {}", sessionContext.getDeviceId(), e.getMessage());
}
}
@@ -156,8 +160,8 @@ public class SnmpTransportContext extends TransportContext {
sessionContext.close();
snmpAuthService.cleanUpSnmpAuthInfo(sessionContext);
transportService.deregisterSession(sessionContext.getSessionInfo());
- sessions.remove(sessionContext.getDeviceId());
snmpTransportService.cancelQueryingTasks(sessionContext);
+ sessions.remove(sessionContext.getDeviceId());
log.trace("Unregistered and removed session");
}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index 2f26bfa98c..94a18f3d54 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -28,9 +28,11 @@ import org.snmp4j.mp.MPv3;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
+import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
@@ -143,7 +145,7 @@ public class SnmpTransportService implements TbTransportService {
sendRequest(sessionContext, communicationConfig);
}
} catch (Exception e) {
- log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.getMessage());
+ log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString());
}
}, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
}
@@ -192,7 +194,24 @@ public class SnmpTransportService implements TbTransportService {
pdu.addAll(communicationConfig.getMappings().stream()
.filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey()))
.map(mapping -> Optional.ofNullable(values.get(mapping.getKey()))
- .map(value -> new VariableBinding(new OID(mapping.getOid()), new OctetString(values.get(mapping.getKey()))))
+ .map(value -> {
+ Variable variable;
+ switch (mapping.getDataType()) {
+ case LONG:
+ try {
+ variable = new Integer32(Integer.parseInt(value));
+ break;
+ } catch (NumberFormatException ignored) {
+ }
+ case DOUBLE:
+ case BOOLEAN:
+ case STRING:
+ case JSON:
+ default:
+ variable = new OctetString(value);
+ }
+ return new VariableBinding(new OID(mapping.getOid()), variable);
+ })
.orElseGet(() -> new VariableBinding(new OID(mapping.getOid()))))
.collect(Collectors.toList()));
@@ -267,7 +286,9 @@ public class SnmpTransportService implements TbTransportService {
responses.forEach((spec, response) -> {
Optional.ofNullable(responseProcessors.get(spec))
.ifPresent(responseProcessor -> {
- responseProcessor.accept(response, sessionContext);
+ if (!response.entrySet().isEmpty()) {
+ responseProcessor.accept(response, sessionContext);
+ }
});
});
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
index c5a329547f..b3a38545a8 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
@@ -63,8 +63,6 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
private final Device device;
private final SnmpTransportContext snmpTransportContext;
- private final SnmpTransportService snmpTransportService;
- private final SnmpAuthService snmpAuthService;
@Getter
@Setter
@@ -80,7 +78,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token,
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration,
SnmpDeviceTransportConfiguration deviceTransportConfiguration,
- SnmpTransportContext snmpTransportContext) {
+ SnmpTransportContext snmpTransportContext) throws Exception {
super(UUID.randomUUID());
super.setDeviceId(device.getId());
super.setDeviceProfile(deviceProfile);
@@ -88,8 +86,6 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
this.token = token;
this.snmpTransportContext = snmpTransportContext;
- this.snmpTransportService = snmpTransportContext.getSnmpTransportService();
- this.snmpAuthService = snmpTransportContext.getSnmpAuthService();
this.profileTransportConfiguration = profileTransportConfiguration;
this.deviceTransportConfiguration = deviceTransportConfiguration;
@@ -113,13 +109,13 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
@Override
public void onResponse(ResponseEvent event) {
if (isActive) {
- snmpTransportService.processResponseEvent(this, event);
+ snmpTransportContext.getSnmpTransportService().processResponseEvent(this, event);
}
}
- public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
+ public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) throws Exception {
log.trace("Initializing target for SNMP session of device {}", device);
- this.target = snmpAuthService.setUpSnmpTarget(profileTransportConfig, deviceTransportConfig);
+ this.target = snmpTransportContext.getSnmpAuthService().setUpSnmpTarget(profileTransportConfig, deviceTransportConfig);
log.info("SNMP target initialized: {}", target);
}
@@ -152,7 +148,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString()
));
try {
- snmpTransportService.sendRequest(this, communicationConfig, sharedAttributes);
+ snmpTransportContext.getSnmpTransportService().sendRequest(this, communicationConfig, sharedAttributes);
} catch (Exception e) {
log.error("Failed to send request with shared attributes to SNMP device {}: {}", getDeviceId(), e.getMessage());
}
From 783b959577be49cbf020f379e09a0043dfcd95a9 Mon Sep 17 00:00:00 2001
From: Viacheslav Klimov
Date: Wed, 14 Apr 2021 15:51:13 +0300
Subject: [PATCH 12/32] Refactor
---
...mpDeviceProfileTransportConfiguration.java | 11 +-
.../transport/snmp/SnmpCommunicationSpec.java | 21 +-
.../data/transport/snmp/SnmpMapping.java | 7 +-
...ltipleMappingsSnmpCommunicationConfig.java | 36 +++
...eatingQueryingSnmpCommunicationConfig.java | 6 +-
.../SnmpCommunicationConfig.java | 24 +-
...ibutesQueryingSnmpCommunicationConfig.java | 5 +-
...ributesSettingSnmpCommunicationConfig.java | 7 +-
...emetryQueryingSnmpCommunicationConfig.java | 5 +-
...CommandSettingSnmpCommunicationConfig.java | 59 +++++
...sponseQueryingSnmpCommunicationConfig.java | 60 +++++
.../transport/snmp/SnmpTransportContext.java | 7 +-
.../transport/snmp/service/PduMapper.java | 150 +++++++++++
.../snmp/{ => service}/SnmpAuthService.java | 8 +-
.../snmp/service/SnmpTransportService.java | 244 ++++++------------
.../snmp/session/DeviceSessionContext.java | 38 +--
16 files changed, 454 insertions(+), 234 deletions(-)
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/MultipleMappingsSnmpCommunicationConfig.java
rename common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/{configs => config}/RepeatingQueryingSnmpCommunicationConfig.java (88%)
rename common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/{configs => config}/SnmpCommunicationConfig.java (75%)
rename common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/{configs => config/impl}/ClientAttributesQueryingSnmpCommunicationConfig.java (89%)
rename common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/{configs => config/impl}/SharedAttributesSettingSnmpCommunicationConfig.java (87%)
rename common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/{configs => config/impl}/TelemetryQueryingSnmpCommunicationConfig.java (90%)
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcCommandSettingSnmpCommunicationConfig.java
create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcResponseQueryingSnmpCommunicationConfig.java
create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduMapper.java
rename common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/{ => service}/SnmpAuthService.java (94%)
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
index 5d6afc7155..bbf074eeda 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
@@ -17,16 +17,11 @@ package org.thingsboard.server.common.data.device.profile;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
-import org.apache.commons.lang3.ArrayUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
-import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig;
+import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
-import java.util.Collections;
import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
@Data
public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
@@ -51,7 +46,7 @@ public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTra
return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0
&& communicationConfigs != null && !communicationConfigs.isEmpty()
&& communicationConfigs.stream().allMatch(config -> config != null && config.isValid())
- && communicationConfigs.stream().flatMap(config -> config.getMappings().stream()).map(SnmpMapping::getOid)
- .distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getMappings().size()).sum();
+ && communicationConfigs.stream().flatMap(config -> config.getAllMappings().stream()).map(SnmpMapping::getOid)
+ .distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getAllMappings().size()).sum();
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
index a2edf75968..a6643ecf1e 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
@@ -16,22 +16,11 @@
package org.thingsboard.server.common.data.transport.snmp;
public enum SnmpCommunicationSpec {
- TELEMETRY_QUERYING(true),
- CLIENT_ATTRIBUTES_QUERYING(true),
+ TELEMETRY_QUERYING,
- SHARED_ATTRIBUTES_SETTING;
+ CLIENT_ATTRIBUTES_QUERYING,
+ SHARED_ATTRIBUTES_SETTING,
- private final boolean isRepeatingQuerying;
-
- SnmpCommunicationSpec() {
- this.isRepeatingQuerying = false;
- }
-
- SnmpCommunicationSpec(boolean isRepeatingQuerying) {
- this.isRepeatingQuerying = isRepeatingQuerying;
- }
-
- public boolean isRepeatingQuerying() {
- return isRepeatingQuerying;
- }
+ TO_DEVICE_RPC_COMMAND_SETTING,
+ TO_DEVICE_RPC_RESPONSE_QUERYING
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
index 0269491461..d787f7446f 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
@@ -16,13 +16,17 @@
package org.thingsboard.server.common.data.transport.snmp;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.AllArgsConstructor;
import lombok.Data;
+import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.kv.DataType;
import java.util.regex.Pattern;
@Data
+@AllArgsConstructor
+@NoArgsConstructor
public class SnmpMapping {
private String oid;
private String key;
@@ -32,7 +36,6 @@ public class SnmpMapping {
@JsonIgnore
public boolean isValid() {
- return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() &&
- StringUtils.isNotBlank(key) && dataType != null;
+ return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && StringUtils.isNotBlank(key);
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/MultipleMappingsSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/MultipleMappingsSnmpCommunicationConfig.java
new file mode 100644
index 0000000000..f785c8788d
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/MultipleMappingsSnmpCommunicationConfig.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.config;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+
+import java.util.List;
+
+@Data
+public abstract class MultipleMappingsSnmpCommunicationConfig implements SnmpCommunicationConfig {
+ protected List mappings;
+
+ @Override
+ public boolean isValid() {
+ return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid());
+ }
+
+ @Override
+ public List getAllMappings() {
+ return mappings;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/RepeatingQueryingSnmpCommunicationConfig.java
similarity index 88%
rename from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/RepeatingQueryingSnmpCommunicationConfig.java
index 19a009457a..38451d4142 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/RepeatingQueryingSnmpCommunicationConfig.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.data.transport.snmp.configs;
+package org.thingsboard.server.common.data.transport.snmp.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
@EqualsAndHashCode(callSuper = true)
@Data
-public abstract class RepeatingQueryingSnmpCommunicationConfig extends SnmpCommunicationConfig {
+public abstract class RepeatingQueryingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
private Long queryingFrequencyMs;
@Override
@@ -31,6 +31,6 @@ public abstract class RepeatingQueryingSnmpCommunicationConfig extends SnmpCommu
@Override
public boolean isValid() {
- return super.isValid() && queryingFrequencyMs != null && queryingFrequencyMs > 0;
+ return queryingFrequencyMs != null && queryingFrequencyMs > 0 && super.isValid();
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java
similarity index 75%
rename from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java
index acc0df0dd7..e2ba29abfa 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.data.transport.snmp.configs;
+package org.thingsboard.server.common.data.transport.snmp.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -23,6 +23,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
+import org.thingsboard.server.common.data.transport.snmp.config.impl.ClientAttributesQueryingSnmpCommunicationConfig;
+import org.thingsboard.server.common.data.transport.snmp.config.impl.SharedAttributesSettingSnmpCommunicationConfig;
+import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig;
import java.util.List;
@@ -33,22 +36,19 @@ import java.util.List;
@Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"),
@Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING")
})
-public abstract class SnmpCommunicationConfig {
- protected List mappings;
+public interface SnmpCommunicationConfig {
- public List getMappings() {
- return mappings;
- }
-
- public abstract SnmpCommunicationSpec getSpec();
+ SnmpCommunicationSpec getSpec();
@JsonIgnore
- public SnmpMethod getMethod() {
+ default SnmpMethod getMethod() {
return null;
}
@JsonIgnore
- public boolean isValid() {
- return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid());
- }
+ List getAllMappings();
+
+ @JsonIgnore
+ boolean isValid();
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesQueryingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ClientAttributesQueryingSnmpCommunicationConfig.java
similarity index 89%
rename from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesQueryingSnmpCommunicationConfig.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ClientAttributesQueryingSnmpCommunicationConfig.java
index 16198f8ef6..51bd2198e9 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesQueryingSnmpCommunicationConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ClientAttributesQueryingSnmpCommunicationConfig.java
@@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.data.transport.snmp.configs;
+package org.thingsboard.server.common.data.transport.snmp.config.impl;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
+
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING;
}
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/SharedAttributesSettingSnmpCommunicationConfig.java
similarity index 87%
rename from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/SharedAttributesSettingSnmpCommunicationConfig.java
index 74c67f957a..441395d279 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/SharedAttributesSettingSnmpCommunicationConfig.java
@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.data.transport.snmp.configs;
+package org.thingsboard.server.common.data.transport.snmp.config.impl;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
+import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig;
+
+public class SharedAttributesSettingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
-public class SharedAttributesSettingSnmpCommunicationConfig extends SnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING;
@@ -28,4 +30,5 @@ public class SharedAttributesSettingSnmpCommunicationConfig extends SnmpCommunic
public SnmpMethod getMethod() {
return SnmpMethod.SET;
}
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryQueryingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/TelemetryQueryingSnmpCommunicationConfig.java
similarity index 90%
rename from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryQueryingSnmpCommunicationConfig.java
rename to common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/TelemetryQueryingSnmpCommunicationConfig.java
index 68d3c81ed2..c3f82d64ec 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryQueryingSnmpCommunicationConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/TelemetryQueryingSnmpCommunicationConfig.java
@@ -13,17 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.data.transport.snmp.configs;
+package org.thingsboard.server.common.data.transport.snmp.config.impl;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
@EqualsAndHashCode(callSuper = true)
@Data
public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
+
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.TELEMETRY_QUERYING;
}
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcCommandSettingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcCommandSettingSnmpCommunicationConfig.java
new file mode 100644
index 0000000000..97f3de047b
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcCommandSettingSnmpCommunicationConfig.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.config.impl;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.kv.DataType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
+import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@Data
+public class ToDeviceRpcCommandSettingSnmpCommunicationConfig implements SnmpCommunicationConfig {
+ private SnmpMapping mapping;
+
+ @Override
+ public SnmpCommunicationSpec getSpec() {
+ return SnmpCommunicationSpec.TO_DEVICE_RPC_COMMAND_SETTING;
+ }
+
+ @Override
+ public SnmpMethod getMethod() {
+ return SnmpMethod.SET;
+ }
+
+ public void setMapping(SnmpMapping mapping) {
+ this.mapping = mapping != null ? new SnmpMapping(mapping.getOid(), RPC_COMMAND_KEY_NAME, DataType.STRING) : null;
+ }
+
+ @Override
+ public List getAllMappings() {
+ return Collections.singletonList(mapping);
+ }
+
+ @Override
+ public boolean isValid() {
+ return mapping != null && mapping.isValid();
+ }
+
+ public static final String RPC_COMMAND_KEY_NAME = "rpcCommand";
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcResponseQueryingSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcResponseQueryingSnmpCommunicationConfig.java
new file mode 100644
index 0000000000..18bce94980
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcResponseQueryingSnmpCommunicationConfig.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.common.data.transport.snmp.config.impl;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.thingsboard.server.common.data.kv.DataType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
+import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
+
+import java.util.Collections;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class ToDeviceRpcResponseQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
+ private SnmpMapping mapping;
+
+ @Override
+ public SnmpCommunicationSpec getSpec() {
+ return SnmpCommunicationSpec.TO_DEVICE_RPC_RESPONSE_QUERYING;
+ }
+
+ @Override
+ public SnmpMethod getMethod() {
+ return SnmpMethod.GET;
+ }
+
+ public void setMapping(SnmpMapping mapping) {
+ this.mapping = mapping != null ? new SnmpMapping(mapping.getOid(), RPC_RESPONSE_KEY_NAME, DataType.STRING) : null;
+ }
+
+ @Override
+ public List getAllMappings() {
+ return Collections.singletonList(mapping);
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ public static final String RPC_RESPONSE_KEY_NAME = "rpcResponse";
+
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
index d7441c7c1d..d66d09ab0b 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
@@ -42,6 +42,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService;
+import org.thingsboard.server.transport.snmp.service.SnmpAuthService;
import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
@@ -116,7 +117,7 @@ public class SnmpTransportContext extends TransportContext {
);
registerSessionMsgListener(deviceSessionContext);
} catch (Exception e) {
- log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.getMessage());
+ log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.toString());
return;
}
sessions.put(device.getId(), deviceSessionContext);
@@ -177,7 +178,9 @@ public class SnmpTransportContext extends TransportContext {
);
transportService.registerAsyncSession(sessionInfo, deviceSessionContext);
- transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
+ transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
+ transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
+
deviceSessionContext.setSessionInfo(sessionInfo);
deviceSessionContext.setDeviceInfo(msg.getDeviceInfo());
} else {
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduMapper.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduMapper.java
new file mode 100644
index 0000000000..a3b5f77dfb
--- /dev/null
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduMapper.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * 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.thingsboard.server.transport.snmp.service;
+
+import com.google.gson.JsonObject;
+import lombok.extern.slf4j.Slf4j;
+import org.snmp4j.PDU;
+import org.snmp4j.ScopedPDU;
+import org.snmp4j.smi.Integer32;
+import org.snmp4j.smi.Null;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.Variable;
+import org.snmp4j.smi.VariableBinding;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
+import org.thingsboard.server.common.data.kv.DataType;
+import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
+import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
+import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
+import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
+import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+@TbSnmpTransportComponent
+@Service
+@Slf4j
+public class PduMapper {
+ public PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) {
+ PDU pdu;
+ SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
+ SnmpProtocolVersion snmpVersion = deviceTransportConfiguration.getProtocolVersion();
+ switch (snmpVersion) {
+ case V1:
+ case V2C:
+ pdu = new PDU();
+ break;
+ case V3:
+ ScopedPDU scopedPdu = new ScopedPDU();
+ scopedPdu.setContextName(new OctetString(deviceTransportConfiguration.getContextName()));
+ scopedPdu.setContextEngineID(new OctetString(deviceTransportConfiguration.getEngineId()));
+ pdu = scopedPdu;
+ break;
+ default:
+ throw new UnsupportedOperationException("SNMP version " + snmpVersion + " is not supported");
+ }
+
+ pdu.setType(communicationConfig.getMethod().getCode());
+ pdu.addAll(communicationConfig.getAllMappings().stream()
+ .filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey()))
+ .map(mapping -> Optional.ofNullable(values.get(mapping.getKey()))
+ .map(value -> {
+ Variable variable = toSnmpVariable(mapping, value);
+ return new VariableBinding(new OID(mapping.getOid()), variable);
+ })
+ .orElseGet(() -> new VariableBinding(new OID(mapping.getOid()))))
+ .collect(Collectors.toList()));
+
+ return pdu;
+ }
+
+ private Variable toSnmpVariable(SnmpMapping mapping, String value) {
+ Variable variable;
+ switch (mapping.getDataType()) {
+ case LONG:
+ try {
+ variable = new Integer32(Integer.parseInt(value));
+ break;
+ } catch (NumberFormatException ignored) {
+ }
+ case DOUBLE:
+ case BOOLEAN:
+ case STRING:
+ case JSON:
+ default:
+ variable = new OctetString(value);
+ }
+ return variable;
+ }
+
+
+ public JsonObject processPdu(PDU pdu, DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) {
+ List variablesBindings = IntStream.range(0, pdu.size())
+ .mapToObj(pdu::get)
+ .filter(Objects::nonNull)
+ .filter(variableBinding -> !(variableBinding.getVariable() instanceof Null))
+ .collect(Collectors.toList());
+ JsonObject data = new JsonObject();
+
+ Map mappings = new HashMap<>();
+ for (SnmpMapping mapping : communicationConfig.getAllMappings()) {
+ OID oid = new OID(mapping.getOid());
+ mappings.put(oid, mapping);
+ }
+
+ variablesBindings.forEach(variableBinding -> {
+ log.trace("Processing variable binding: {}", variableBinding);
+
+ OID oid = variableBinding.getOid();
+ SnmpMapping mapping = mappings.get(oid);
+ if (mapping == null) {
+ log.debug("No SNMP mapping for oid {}", oid);
+ return;
+ }
+
+ processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), data);
+ });
+
+ return data;
+ }
+
+
+ private void processValue(String key, DataType dataType, String value, JsonObject result) {
+ switch (dataType) {
+ case LONG:
+ result.addProperty(key, Long.parseLong(value));
+ break;
+ case BOOLEAN:
+ result.addProperty(key, Boolean.parseBoolean(value));
+ break;
+ case DOUBLE:
+ result.addProperty(key, Double.parseDouble(value));
+ break;
+ case STRING:
+ case JSON:
+ default:
+ result.addProperty(key, value);
+ }
+ }
+}
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpAuthService.java
similarity index 94%
rename from common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java
rename to common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpAuthService.java
index 81187fb33e..c3ed188bb0 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpAuthService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpAuthService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.transport.snmp;
+package org.thingsboard.server.transport.snmp.service;
import lombok.RequiredArgsConstructor;
import org.snmp4j.AbstractTarget;
@@ -24,6 +24,7 @@ import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
+import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
@@ -36,6 +37,8 @@ import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
+import java.util.Optional;
+
@Service
@TbSnmpTransportComponent
@RequiredArgsConstructor
@@ -97,7 +100,8 @@ public class SnmpAuthService {
throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
}
- target.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getHost() + "/" + deviceTransportConfig.getPort()));
+ Address address = GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getHost() + "/" + deviceTransportConfig.getPort());
+ target.setAddress(Optional.ofNullable(address).orElseThrow(() -> new IllegalArgumentException("Address of the SNMP device is invalid")));
target.setTimeout(profileTransportConfig.getTimeoutMs());
target.setRetries(profileTransportConfig.getRetries());
target.setVersion(protocolVersion.getCode());
diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
index 94a18f3d54..f672e36190 100644
--- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
+++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
@@ -16,11 +16,11 @@
package org.thingsboard.server.transport.snmp.service;
import com.google.gson.JsonObject;
+import lombok.Data;
import lombok.Getter;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
import org.snmp4j.PDU;
-import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
@@ -28,26 +28,18 @@ import org.snmp4j.mp.MPv3;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
-import org.snmp4j.smi.Integer32;
-import org.snmp4j.smi.Null;
-import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
-import org.snmp4j.smi.Variable;
-import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.TbTransportService;
-import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceProfileId;
-import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
-import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
-import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
-import org.thingsboard.server.common.data.transport.snmp.configs.RepeatingQueryingSnmpCommunicationConfig;
-import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig;
+import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
+import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
+import org.thingsboard.server.common.data.transport.snmp.config.impl.ToDeviceRpcResponseQueryingSnmpCommunicationConfig;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
@@ -60,44 +52,37 @@ import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.Collections;
import java.util.EnumMap;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
-import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@TbSnmpTransportComponent
@Service
@Slf4j
+@RequiredArgsConstructor
public class SnmpTransportService implements TbTransportService {
private final TransportService transportService;
+ private final PduMapper pduMapper;
@Getter
private Snmp snmp;
private ScheduledExecutorService queryingExecutor;
private ExecutorService responseProcessingExecutor;
- private final Map> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class);
+ private final Map responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class);
@Value("${transport.snmp.response_processing.parallelism_level}")
private Integer responseProcessingParallelismLevel;
@Value("${transport.snmp.underlying_protocol}")
private String snmpUnderlyingProtocol;
- public SnmpTransportService(TransportService transportService) {
- this.transportService = transportService;
- }
-
@PostConstruct
private void init() throws IOException {
- log.info("Initializing SNMP transport service");
-
queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying"));
responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
@@ -128,26 +113,23 @@ public class SnmpTransportService implements TbTransportService {
public void createQueryingTasks(DeviceSessionContext sessionContext) {
List> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
- .filter(config -> config.getSpec().isRepeatingQuerying())
+ .filter(communicationConfig -> communicationConfig instanceof RepeatingQueryingSnmpCommunicationConfig)
.map(config -> {
RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config;
- return createQueryingTaskForConfig(sessionContext, repeatingCommunicationConfig);
+ Long queryingFrequency = repeatingCommunicationConfig.getQueryingFrequencyMs();
+
+ return queryingExecutor.scheduleWithFixedDelay(() -> {
+ try {
+ if (sessionContext.isActive()) {
+ sendRequest(sessionContext, repeatingCommunicationConfig);
+ }
+ } catch (Exception e) {
+ log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString());
+ }
+ }, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
})
.collect(Collectors.toList());
- sessionContext.setQueryingTasks(queryingTasks);
- }
-
- private ScheduledFuture> createQueryingTaskForConfig(DeviceSessionContext sessionContext, RepeatingQueryingSnmpCommunicationConfig communicationConfig) {
- Long queryingFrequency = communicationConfig.getQueryingFrequencyMs();
- return queryingExecutor.scheduleWithFixedDelay(() -> {
- try {
- if (sessionContext.isActive()) {
- sendRequest(sessionContext, communicationConfig);
- }
- } catch (Exception e) {
- log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString());
- }
- }, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
+ sessionContext.getQueryingTasks().addAll(queryingTasks);
}
public void cancelQueryingTasks(DeviceSessionContext sessionContext) {
@@ -155,67 +137,23 @@ public class SnmpTransportService implements TbTransportService {
sessionContext.getQueryingTasks().clear();
}
- public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) throws IOException {
+
+ public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) {
sendRequest(sessionContext, communicationConfig, Collections.emptyMap());
}
- public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) throws IOException {
- PDU request = createPdu(sessionContext, communicationConfig, values);
- executeRequest(sessionContext, request);
- }
+ public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) {
+ PDU request = pduMapper.createPdu(sessionContext, communicationConfig, values);
- private void executeRequest(DeviceSessionContext sessionContext, PDU request) throws IOException {
if (request.size() > 0) {
log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings());
- snmp.send(request, sessionContext.getTarget(), sessionContext.getDeviceProfile().getId(), sessionContext);
- }
- }
-
- private PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map