Commit e3292e89c15e604211fcb33a7ab45229462dd2ff

Authored by Andrew Shvayka
Committed by GitHub
2 parents 5976d517 79eb1183

Merge pull request #4301 from thingsboard/develop/snmp

SNMP Transport
Showing 100 changed files with 3701 additions and 99 deletions

Too many changes to show.

To preserve performance only 100 of 125 files are displayed.

... ... @@ -90,6 +90,10 @@
90 90 <artifactId>lwm2m</artifactId>
91 91 </dependency>
92 92 <dependency>
  93 + <groupId>org.thingsboard.common.transport</groupId>
  94 + <artifactId>snmp</artifactId>
  95 + </dependency>
  96 + <dependency>
93 97 <groupId>org.thingsboard</groupId>
94 98 <artifactId>dao</artifactId>
95 99 </dependency>
... ...
... ... @@ -25,7 +25,6 @@ import org.springframework.stereotype.Service;
25 25 import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 26 import org.thingsboard.server.actors.ActorSystemContext;
27 27 import org.thingsboard.server.actors.DefaultTbActorSystem;
28   -import org.thingsboard.server.actors.TbActorId;
29 28 import org.thingsboard.server.actors.TbActorRef;
30 29 import org.thingsboard.server.actors.TbActorSystem;
31 30 import org.thingsboard.server.actors.TbActorSystemSettings;
... ... @@ -33,14 +32,13 @@ import org.thingsboard.server.actors.app.AppActor;
33 32 import org.thingsboard.server.actors.app.AppInitMsg;
34 33 import org.thingsboard.server.actors.stats.StatsActor;
35 34 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
36   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
37 35 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
  36 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
38 37
39 38 import javax.annotation.PostConstruct;
40 39 import javax.annotation.PreDestroy;
41 40 import java.util.concurrent.ExecutorService;
42 41 import java.util.concurrent.Executors;
43   -import java.util.concurrent.ScheduledExecutorService;
44 42
45 43 @Service
46 44 @Slf4j
... ...
... ... @@ -56,7 +56,7 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
56 56 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
57 57 import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
58 58 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
59   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  59 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
60 60 import org.thingsboard.server.queue.discovery.PartitionService;
61 61 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
62 62 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
... ...
... ... @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId;
23 23 import org.thingsboard.server.common.msg.queue.TbCallback;
24 24 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
25 25 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
26   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  26 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
27 27
28 28 public interface TbApiUsageStateService extends ApplicationListener<PartitionChangeEvent> {
29 29
... ...
... ... @@ -55,7 +55,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM
55 55 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
56 56 import org.thingsboard.server.queue.TbQueueConsumer;
57 57 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
58   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  58 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
59 59 import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
60 60 import org.thingsboard.server.queue.util.TbCoreComponent;
61 61 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
... ...
... ... @@ -38,7 +38,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
38 38 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
39 39 import org.thingsboard.server.queue.TbQueueConsumer;
40 40 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
41   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  41 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
42 42 import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory;
43 43 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
44 44 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
... ...
... ... @@ -16,7 +16,7 @@
16 16 package org.thingsboard.server.service.queue;
17 17
18 18 import org.springframework.context.ApplicationListener;
19   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  19 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
20 20
21 21 public interface TbCoreConsumerService extends ApplicationListener<PartitionChangeEvent> {
22 22
... ...
... ... @@ -16,7 +16,7 @@
16 16 package org.thingsboard.server.service.queue;
17 17
18 18 import org.springframework.context.ApplicationListener;
19   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  19 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
20 20
21 21 public interface TbRuleEngineConsumerService extends ApplicationListener<PartitionChangeEvent> {
22 22
... ...
... ... @@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
35 35 import org.thingsboard.server.common.msg.queue.TbCallback;
36 36 import org.thingsboard.server.queue.TbQueueConsumer;
37 37 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
38   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  38 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
39 39 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
40 40 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
41 41 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
... ...
... ... @@ -54,7 +54,7 @@ import org.thingsboard.server.dao.tenant.TenantService;
54 54 import org.thingsboard.server.dao.timeseries.TimeseriesService;
55 55 import org.thingsboard.common.util.JacksonUtil;
56 56 import org.thingsboard.server.gen.transport.TransportProtos;
57   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  57 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
58 58 import org.thingsboard.server.queue.discovery.PartitionService;
59 59 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
60 60 import org.thingsboard.server.queue.util.TbCoreComponent;
... ...
... ... @@ -18,7 +18,7 @@ package org.thingsboard.server.service.state;
18 18 import org.springframework.context.ApplicationListener;
19 19 import org.thingsboard.server.common.data.Device;
20 20 import org.thingsboard.server.common.data.id.DeviceId;
21   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  21 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
22 22 import org.thingsboard.server.gen.transport.TransportProtos;
23 23 import org.thingsboard.server.common.msg.queue.TbCallback;
24 24
... ...
... ... @@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdate
46 46 import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateValueListProto;
47 47 import org.thingsboard.server.queue.TbQueueProducer;
48 48 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
49   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  49 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
50 50 import org.thingsboard.server.queue.discovery.PartitionService;
51 51 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
52 52 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
... ...
... ... @@ -20,10 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired;
20 20 import org.springframework.context.annotation.Lazy;
21 21 import org.springframework.context.event.EventListener;
22 22 import org.springframework.stereotype.Service;
23   -import org.thingsboard.common.util.ThingsBoardThreadFactory;
24 23 import org.thingsboard.server.gen.transport.TransportProtos;
25   -import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent;
26   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  24 +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
  25 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
27 26 import org.thingsboard.server.queue.discovery.PartitionService;
28 27 import org.thingsboard.server.common.msg.queue.ServiceType;
29 28 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
... ...
... ... @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.TenantId;
22 22 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
23 23 import org.thingsboard.server.common.data.kv.TsKvEntry;
24 24 import org.thingsboard.server.common.msg.queue.TbCallback;
25   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  25 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
26 26
27 27 import java.util.List;
28 28
... ...
... ... @@ -15,8 +15,8 @@
15 15 */
16 16 package org.thingsboard.server.service.subscription;
17 17
18   -import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent;
19   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  18 +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
  19 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
20 20 import org.thingsboard.server.common.msg.queue.TbCallback;
21 21 import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
22 22 import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
... ...
... ... @@ -22,35 +22,18 @@ import lombok.extern.slf4j.Slf4j;
22 22 import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.context.ApplicationListener;
24 24 import org.springframework.context.event.EventListener;
25   -import org.springframework.stereotype.Service;
26 25 import org.thingsboard.common.util.ThingsBoardThreadFactory;
27   -import org.thingsboard.server.common.data.id.EntityId;
28   -import org.thingsboard.server.common.data.id.TenantId;
29   -import org.thingsboard.server.common.data.kv.AttributeKvEntry;
30   -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
31   -import org.thingsboard.server.common.data.kv.BooleanDataEntry;
32   -import org.thingsboard.server.common.data.kv.DoubleDataEntry;
33   -import org.thingsboard.server.common.data.kv.LongDataEntry;
34   -import org.thingsboard.server.common.data.kv.StringDataEntry;
35   -import org.thingsboard.server.common.data.kv.TsKvEntry;
36 26 import org.thingsboard.server.common.msg.queue.ServiceType;
37   -import org.thingsboard.server.common.msg.queue.TbCallback;
38 27 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
39   -import org.thingsboard.server.dao.attributes.AttributesService;
40   -import org.thingsboard.server.dao.timeseries.TimeseriesService;
41   -import org.thingsboard.server.gen.transport.TransportProtos;
42   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  28 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
43 29 import org.thingsboard.server.queue.discovery.PartitionService;
44 30 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
45 31 import org.thingsboard.server.service.queue.TbClusterService;
46 32 import org.thingsboard.server.service.subscription.SubscriptionManagerService;
47   -import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
48 33
49 34 import javax.annotation.Nullable;
50 35 import javax.annotation.PostConstruct;
51 36 import javax.annotation.PreDestroy;
52   -import java.util.Collections;
53   -import java.util.List;
54 37 import java.util.Optional;
55 38 import java.util.Set;
56 39 import java.util.concurrent.ConcurrentHashMap;
... ...
... ... @@ -17,8 +17,7 @@ package org.thingsboard.server.service.telemetry;
17 17
18 18 import org.springframework.context.ApplicationListener;
19 19 import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
20   -import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
21   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  20 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
22 21
23 22 /**
24 23 * Created by ashvayka on 27.03.18.
... ...
... ... @@ -16,8 +16,7 @@
16 16 package org.thingsboard.server.service.telemetry;
17 17
18 18 import org.springframework.context.ApplicationListener;
19   -import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
20   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  19 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
21 20
22 21 /**
23 22 * Created by ashvayka on 27.03.18.
... ...
... ... @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.ApiUsageState;
31 31 import org.thingsboard.server.common.data.DataConstants;
32 32 import org.thingsboard.server.common.data.Device;
33 33 import org.thingsboard.server.common.data.DeviceProfile;
  34 +import org.thingsboard.server.common.data.DeviceTransportType;
34 35 import org.thingsboard.server.common.data.EntityType;
35 36 import org.thingsboard.server.common.data.Firmware;
36 37 import org.thingsboard.server.common.data.FirmwareInfo;
... ... @@ -45,6 +46,8 @@ import org.thingsboard.server.common.data.id.DeviceId;
45 46 import org.thingsboard.server.common.data.id.DeviceProfileId;
46 47 import org.thingsboard.server.common.data.id.FirmwareId;
47 48 import org.thingsboard.server.common.data.id.TenantId;
  49 +import org.thingsboard.server.common.data.page.PageData;
  50 +import org.thingsboard.server.common.data.page.PageLink;
48 51 import org.thingsboard.server.common.data.relation.EntityRelation;
49 52 import org.thingsboard.server.common.data.security.DeviceCredentials;
50 53 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
... ... @@ -64,11 +67,15 @@ import org.thingsboard.server.dao.relation.RelationService;
64 67 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
65 68 import org.thingsboard.server.gen.transport.TransportProtos;
66 69 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
  70 +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg;
  71 +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg;
67 72 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
68 73 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
69 74 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
70 75 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
71 76 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg;
  77 +import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesRequestMsg;
  78 +import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesResponseMsg;
72 79 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
73 80 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
74 81 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
... ... @@ -91,6 +98,7 @@ import java.util.concurrent.ConcurrentHashMap;
91 98 import java.util.concurrent.ConcurrentMap;
92 99 import java.util.concurrent.locks.Lock;
93 100 import java.util.concurrent.locks.ReentrantLock;
  101 +import java.util.stream.Collectors;
94 102
95 103 /**
96 104 * Created by ashvayka on 05.10.18.
... ... @@ -144,43 +152,43 @@ public class DefaultTransportApiService implements TransportApiService {
144 152 @Override
145 153 public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) {
146 154 TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue();
  155 + ListenableFuture<TransportApiResponseMsg> result = null;
  156 +
147 157 if (transportApiRequestMsg.hasValidateTokenRequestMsg()) {
148 158 ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg();
149   - return Futures.transform(validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN),
150   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  159 + result = validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN);
151 160 } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) {
152 161 TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg();
153   - return Futures.transform(validateCredentials(msg),
154   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  162 + result = validateCredentials(msg);
155 163 } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
156 164 ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg();
157   - return Futures.transform(validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE),
158   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  165 + result = validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE);
159 166 } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) {
160   - return Futures.transform(handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()),
161   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  167 + result = handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg());
162 168 } else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) {
163   - return Futures.transform(handle(transportApiRequestMsg.getEntityProfileRequestMsg()),
164   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  169 + result = handle(transportApiRequestMsg.getEntityProfileRequestMsg());
165 170 } else if (transportApiRequestMsg.hasLwM2MRequestMsg()) {
166   - return Futures.transform(handle(transportApiRequestMsg.getLwM2MRequestMsg()),
167   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  171 + result = handle(transportApiRequestMsg.getLwM2MRequestMsg());
168 172 } else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) {
169 173 ValidateDeviceLwM2MCredentialsRequestMsg msg = transportApiRequestMsg.getValidateDeviceLwM2MCredentialsRequestMsg();
170   - return Futures.transform(validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS),
171   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  174 + result = validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS);
172 175 } else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) {
173   - return Futures.transform(handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()),
174   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  176 + result = handle(transportApiRequestMsg.getProvisionDeviceRequestMsg());
175 177 } else if (transportApiRequestMsg.hasResourceRequestMsg()) {
176   - return Futures.transform(handle(transportApiRequestMsg.getResourceRequestMsg()),
177   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  178 + result = handle(transportApiRequestMsg.getResourceRequestMsg());
  179 + } else if (transportApiRequestMsg.hasSnmpDevicesRequestMsg()) {
  180 + result = handle(transportApiRequestMsg.getSnmpDevicesRequestMsg());
  181 + } else if (transportApiRequestMsg.hasDeviceRequestMsg()) {
  182 + result = handle(transportApiRequestMsg.getDeviceRequestMsg());
  183 + } else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) {
  184 + result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg());
178 185 } else if (transportApiRequestMsg.hasFirmwareRequestMsg()) {
179   - return Futures.transform(handle(transportApiRequestMsg.getFirmwareRequestMsg()),
180   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  186 + result = handle(transportApiRequestMsg.getFirmwareRequestMsg());
181 187 }
182   - return Futures.transform(getEmptyTransportApiResponseFuture(),
183   - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  188 +
  189 + return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture),
  190 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()),
  191 + MoreExecutors.directExecutor());
184 192 }
185 193
186 194 private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) {
... ... @@ -374,6 +382,39 @@ public class DefaultTransportApiService implements TransportApiService {
374 382 return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build());
375 383 }
376 384
  385 + private ListenableFuture<TransportApiResponseMsg> handle(GetDeviceRequestMsg requestMsg) {
  386 + DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
  387 + Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, deviceId);
  388 +
  389 + TransportApiResponseMsg responseMsg;
  390 + if (device != null) {
  391 + UUID deviceProfileId = device.getDeviceProfileId().getId();
  392 + responseMsg = TransportApiResponseMsg.newBuilder()
  393 + .setDeviceResponseMsg(TransportProtos.GetDeviceResponseMsg.newBuilder()
  394 + .setDeviceProfileIdMSB(deviceProfileId.getMostSignificantBits())
  395 + .setDeviceProfileIdLSB(deviceProfileId.getLeastSignificantBits())
  396 + .setDeviceTransportConfiguration(ByteString.copyFrom(
  397 + dataDecodingEncodingService.encode(device.getDeviceData().getTransportConfiguration())
  398 + )))
  399 + .build();
  400 + } else {
  401 + responseMsg = TransportApiResponseMsg.getDefaultInstance();
  402 + }
  403 +
  404 + return Futures.immediateFuture(responseMsg);
  405 + }
  406 +
  407 + private ListenableFuture<TransportApiResponseMsg> handle(GetDeviceCredentialsRequestMsg requestMsg) {
  408 + DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
  409 + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, deviceId);
  410 +
  411 + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder()
  412 + .setDeviceCredentialsResponseMsg(TransportProtos.GetDeviceCredentialsResponseMsg.newBuilder()
  413 + .setDeviceCredentialsData(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceCredentials))))
  414 + .build());
  415 + }
  416 +
  417 +
377 418 private ListenableFuture<TransportApiResponseMsg> handle(GetResourceRequestMsg requestMsg) {
378 419 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
379 420 ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType());
... ... @@ -392,6 +433,22 @@ public class DefaultTransportApiService implements TransportApiService {
392 433 return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setResourceResponseMsg(builder).build());
393 434 }
394 435
  436 + private ListenableFuture<TransportApiResponseMsg> handle(GetSnmpDevicesRequestMsg requestMsg) {
  437 + PageLink pageLink = new PageLink(requestMsg.getPageSize(), requestMsg.getPage());
  438 + PageData<UUID> result = deviceService.findDevicesIdsByDeviceProfileTransportType(DeviceTransportType.SNMP, pageLink);
  439 +
  440 + GetSnmpDevicesResponseMsg responseMsg = GetSnmpDevicesResponseMsg.newBuilder()
  441 + .addAllIds(result.getData().stream()
  442 + .map(UUID::toString)
  443 + .collect(Collectors.toList()))
  444 + .setHasNextPage(result.hasNext())
  445 + .build();
  446 +
  447 + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder()
  448 + .setSnmpDevicesResponseMsg(responseMsg)
  449 + .build());
  450 + }
  451 +
395 452 private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) {
396 453 return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> {
397 454 if (device == null) {
... ...
... ... @@ -26,6 +26,7 @@
26 26 </appender>
27 27
28 28 <logger name="org.thingsboard.server" level="INFO" />
  29 + <logger name="org.thingsboard.server.transport.snmp" level="TRACE" />
29 30
30 31 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />-->
31 32 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />-->
... ...
... ... @@ -686,6 +686,13 @@ transport:
686 686 alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}"
687 687 # Use redis for Security and Registration stores
688 688 redis.enabled: "${LWM2M_REDIS_ENABLED:false}"
  689 + snmp:
  690 + enabled: "${SNMP_ENABLED:true}"
  691 + response_processing:
  692 + # parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices
  693 + parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}"
  694 + # to configure SNMP to work over UDP or TCP
  695 + underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}"
689 696
690 697 # Edges parameters
691 698 edges:
... ...
... ... @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.thingsboard.server.common.data.Device;
20 20 import org.thingsboard.server.common.data.DeviceInfo;
21 21 import org.thingsboard.server.common.data.DeviceProfile;
  22 +import org.thingsboard.server.common.data.DeviceTransportType;
22 23 import org.thingsboard.server.common.data.EntitySubtype;
23 24 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
24 25 import org.thingsboard.server.common.data.id.CustomerId;
... ... @@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
32 33 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
33 34
34 35 import java.util.List;
  36 +import java.util.UUID;
35 37
36 38 public interface DeviceService {
37 39
... ... @@ -93,6 +95,8 @@ public interface DeviceService {
93 95
94 96 Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
95 97
  98 + PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink);
  99 +
96 100 Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
97 101
98 102 Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
... ...
... ... @@ -87,6 +87,10 @@
87 87 <groupId>org.thingsboard</groupId>
88 88 <artifactId>protobuf-dynamic</artifactId>
89 89 </dependency>
  90 + <dependency>
  91 + <groupId>org.apache.commons</groupId>
  92 + <artifactId>commons-lang3</artifactId>
  93 + </dependency>
90 94 </dependencies>
91 95
92 96 <build>
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data;
18 18 public enum DeviceTransportType {
19 19 DEFAULT,
20 20 MQTT,
  21 + COAP,
21 22 LWM2M,
22   - COAP
  23 + SNMP
23 24 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data;
  17 +
  18 +public interface TbTransportService {
  19 + String getName();
  20 +}
... ...
... ... @@ -21,6 +21,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
21 21 import com.fasterxml.jackson.annotation.JsonTypeInfo;
22 22 import org.thingsboard.server.common.data.DeviceTransportType;
23 23
  24 +import java.io.Serializable;
  25 +
24 26 @JsonIgnoreProperties(ignoreUnknown = true)
25 27 @JsonTypeInfo(
26 28 use = JsonTypeInfo.Id.NAME,
... ... @@ -29,11 +31,14 @@ import org.thingsboard.server.common.data.DeviceTransportType;
29 31 @JsonSubTypes({
30 32 @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"),
31 33 @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"),
  34 + @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"),
32 35 @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"),
33   - @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP")})
34   -public interface DeviceTransportConfiguration {
35   -
  36 + @JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")})
  37 +public interface DeviceTransportConfiguration extends Serializable {
36 38 @JsonIgnore
37 39 DeviceTransportType getType();
38 40
  41 + default void validate() {
  42 + }
  43 +
39 44 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.data;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.Data;
  20 +import lombok.ToString;
  21 +import org.apache.commons.lang3.ObjectUtils;
  22 +import org.apache.commons.lang3.StringUtils;
  23 +import org.thingsboard.server.common.data.DeviceTransportType;
  24 +import org.thingsboard.server.common.data.transport.snmp.AuthenticationProtocol;
  25 +import org.thingsboard.server.common.data.transport.snmp.PrivacyProtocol;
  26 +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
  27 +
  28 +import java.util.Objects;
  29 +
  30 +@Data
  31 +@ToString(of = {"host", "port", "protocolVersion"})
  32 +public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
  33 + private String host;
  34 + private Integer port;
  35 + private SnmpProtocolVersion protocolVersion;
  36 +
  37 + /*
  38 + * For SNMP v1 and v2c
  39 + * */
  40 + private String community;
  41 +
  42 + /*
  43 + * For SNMP v3
  44 + * */
  45 + private String username;
  46 + private String securityName;
  47 + private String contextName;
  48 + private AuthenticationProtocol authenticationProtocol;
  49 + private String authenticationPassphrase;
  50 + private PrivacyProtocol privacyProtocol;
  51 + private String privacyPassphrase;
  52 + private String engineId;
  53 +
  54 + @Override
  55 + public DeviceTransportType getType() {
  56 + return DeviceTransportType.SNMP;
  57 + }
  58 +
  59 + @Override
  60 + public void validate() {
  61 + if (!isValid()) {
  62 + throw new IllegalArgumentException("Transport configuration is not valid");
  63 + }
  64 + }
  65 +
  66 + @JsonIgnore
  67 + private boolean isValid() {
  68 + boolean isValid = StringUtils.isNotBlank(host) && port != null && protocolVersion != null;
  69 + if (isValid) {
  70 + switch (protocolVersion) {
  71 + case V1:
  72 + case V2C:
  73 + isValid = StringUtils.isNotEmpty(community);
  74 + break;
  75 + case V3:
  76 + isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName)
  77 + && contextName != null && authenticationProtocol != null
  78 + && StringUtils.isNotBlank(authenticationPassphrase)
  79 + && privacyProtocol != null && privacyPassphrase != null && engineId != null;
  80 + break;
  81 + }
  82 + }
  83 + return isValid;
  84 + }
  85 +}
... ...
... ... @@ -29,13 +29,18 @@ import java.io.Serializable;
29 29 include = JsonTypeInfo.As.PROPERTY,
30 30 property = "type")
31 31 @JsonSubTypes({
32   - @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
33   - @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
34   - @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"),
35   - @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP")})
  32 + @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
  33 + @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
  34 + @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"),
  35 + @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP"),
  36 + @JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP")
  37 +})
36 38 public interface DeviceProfileTransportConfiguration extends Serializable {
37 39
38 40 @JsonIgnore
39 41 DeviceTransportType getType();
40 42
  43 + default void validate() {
  44 + }
  45 +
41 46 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.profile;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.DeviceTransportType;
  21 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  22 +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
  23 +
  24 +import java.util.List;
  25 +
  26 +@Data
  27 +public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
  28 + private Integer timeoutMs;
  29 + private Integer retries;
  30 + private List<SnmpCommunicationConfig> communicationConfigs;
  31 +
  32 + @Override
  33 + public DeviceTransportType getType() {
  34 + return DeviceTransportType.SNMP;
  35 + }
  36 +
  37 + @Override
  38 + public void validate() {
  39 + if (!isValid()) {
  40 + throw new IllegalArgumentException("SNMP transport configuration is not valid");
  41 + }
  42 + }
  43 +
  44 + @JsonIgnore
  45 + private boolean isValid() {
  46 + return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0
  47 + && communicationConfigs != null
  48 + && communicationConfigs.stream().allMatch(config -> config != null && config.isValid())
  49 + && communicationConfigs.stream().flatMap(config -> config.getAllMappings().stream()).map(SnmpMapping::getOid)
  50 + .distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getAllMappings().size()).sum();
  51 + }
  52 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +import java.util.Arrays;
  19 +import java.util.Optional;
  20 +
  21 +public enum AuthenticationProtocol {
  22 + SHA_1("1.3.6.1.6.3.10.1.1.3"),
  23 + SHA_224("1.3.6.1.6.3.10.1.1.4"),
  24 + SHA_256("1.3.6.1.6.3.10.1.1.5"),
  25 + SHA_384("1.3.6.1.6.3.10.1.1.6"),
  26 + SHA_512("1.3.6.1.6.3.10.1.1.7"),
  27 + MD5("1.3.6.1.6.3.10.1.1.2");
  28 +
  29 + // oids taken from org.snmp4j.security.SecurityProtocol implementations
  30 + private final String oid;
  31 +
  32 + AuthenticationProtocol(String oid) {
  33 + this.oid = oid;
  34 + }
  35 +
  36 + public String getOid() {
  37 + return oid;
  38 + }
  39 +
  40 + public static Optional<AuthenticationProtocol> forName(String name) {
  41 + return Arrays.stream(values())
  42 + .filter(protocol -> protocol.name().equalsIgnoreCase(name))
  43 + .findFirst();
  44 + }
  45 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +import java.util.Arrays;
  19 +import java.util.Optional;
  20 +
  21 +public enum PrivacyProtocol {
  22 + DES("1.3.6.1.6.3.10.1.2.2"),
  23 + AES_128("1.3.6.1.6.3.10.1.2.4"),
  24 + AES_192("1.3.6.1.4.1.4976.2.2.1.1.1"),
  25 + AES_256("1.3.6.1.4.1.4976.2.2.1.1.2");
  26 +
  27 + // oids taken from org.snmp4j.security.SecurityProtocol implementations
  28 + private final String oid;
  29 +
  30 + PrivacyProtocol(String oid) {
  31 + this.oid = oid;
  32 + }
  33 +
  34 + public String getOid() {
  35 + return oid;
  36 + }
  37 +
  38 + public static Optional<PrivacyProtocol> forName(String name) {
  39 + return Arrays.stream(values())
  40 + .filter(protocol -> protocol.name().equalsIgnoreCase(name))
  41 + .findFirst();
  42 + }
  43 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +public enum SnmpCommunicationSpec {
  19 + TELEMETRY_QUERYING,
  20 +
  21 + CLIENT_ATTRIBUTES_QUERYING,
  22 + SHARED_ATTRIBUTES_SETTING,
  23 +
  24 + TO_DEVICE_RPC_REQUEST,
  25 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.AllArgsConstructor;
  20 +import lombok.Data;
  21 +import lombok.NoArgsConstructor;
  22 +import org.apache.commons.lang3.StringUtils;
  23 +import org.thingsboard.server.common.data.kv.DataType;
  24 +
  25 +import java.util.regex.Pattern;
  26 +
  27 +@Data
  28 +@AllArgsConstructor
  29 +@NoArgsConstructor
  30 +public class SnmpMapping {
  31 + private String oid;
  32 + private String key;
  33 + private DataType dataType;
  34 +
  35 + private static final Pattern OID_PATTERN = Pattern.compile("^\\.?([0-2])((\\.0)|(\\.[1-9][0-9]*))*$");
  36 +
  37 + @JsonIgnore
  38 + public boolean isValid() {
  39 + return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && StringUtils.isNotBlank(key);
  40 + }
  41 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +public enum SnmpMethod {
  19 + GET(-96),
  20 + SET(-93);
  21 +
  22 + // codes taken from org.snmp4j.PDU class
  23 + private final int code;
  24 +
  25 + SnmpMethod(int code) {
  26 + this.code = code;
  27 + }
  28 +
  29 + public int getCode() {
  30 + return code;
  31 + }
  32 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +public enum SnmpProtocolVersion {
  19 + V1(0),
  20 + V2C(1),
  21 + V3(3);
  22 +
  23 + private final int code;
  24 +
  25 + SnmpProtocolVersion(int code) {
  26 + this.code = code;
  27 + }
  28 +
  29 + public int getCode() {
  30 + return code;
  31 + }
  32 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  20 +
  21 +import java.util.List;
  22 +
  23 +@Data
  24 +public abstract class MultipleMappingsSnmpCommunicationConfig implements SnmpCommunicationConfig {
  25 + protected List<SnmpMapping> mappings;
  26 +
  27 + @Override
  28 + public boolean isValid() {
  29 + return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid());
  30 + }
  31 +
  32 + @Override
  33 + public List<SnmpMapping> getAllMappings() {
  34 + return mappings;
  35 + }
  36 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config;
  17 +
  18 +import lombok.Data;
  19 +import lombok.EqualsAndHashCode;
  20 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  21 +
  22 +@EqualsAndHashCode(callSuper = true)
  23 +@Data
  24 +public abstract class RepeatingQueryingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
  25 + private Long queryingFrequencyMs;
  26 +
  27 + @Override
  28 + public SnmpMethod getMethod() {
  29 + return SnmpMethod.GET;
  30 + }
  31 +
  32 + @Override
  33 + public boolean isValid() {
  34 + return queryingFrequencyMs != null && queryingFrequencyMs > 0 && super.isValid();
  35 + }
  36 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  20 +import com.fasterxml.jackson.annotation.JsonSubTypes;
  21 +import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
  22 +import com.fasterxml.jackson.annotation.JsonTypeInfo;
  23 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  24 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  25 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  26 +import org.thingsboard.server.common.data.transport.snmp.config.impl.ClientAttributesQueryingSnmpCommunicationConfig;
  27 +import org.thingsboard.server.common.data.transport.snmp.config.impl.SharedAttributesSettingSnmpCommunicationConfig;
  28 +import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig;
  29 +
  30 +import java.util.List;
  31 +
  32 +@JsonIgnoreProperties(ignoreUnknown = true)
  33 +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "spec")
  34 +@JsonSubTypes({
  35 + @Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"),
  36 + @Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"),
  37 + @Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING")
  38 +})
  39 +public interface SnmpCommunicationConfig {
  40 +
  41 + SnmpCommunicationSpec getSpec();
  42 +
  43 + @JsonIgnore
  44 + default SnmpMethod getMethod() {
  45 + return null;
  46 + }
  47 +
  48 + @JsonIgnore
  49 + List<SnmpMapping> getAllMappings();
  50 +
  51 + @JsonIgnore
  52 + boolean isValid();
  53 +
  54 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
  17 +
  18 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  19 +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
  20 +
  21 +public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
  22 +
  23 + @Override
  24 + public SnmpCommunicationSpec getSpec() {
  25 + return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING;
  26 + }
  27 +
  28 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
  17 +
  18 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  19 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  20 +import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig;
  21 +
  22 +public class SharedAttributesSettingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
  23 +
  24 + @Override
  25 + public SnmpCommunicationSpec getSpec() {
  26 + return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING;
  27 + }
  28 +
  29 + @Override
  30 + public SnmpMethod getMethod() {
  31 + return SnmpMethod.SET;
  32 + }
  33 +
  34 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
  17 +
  18 +import lombok.Data;
  19 +import lombok.EqualsAndHashCode;
  20 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  21 +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
  22 +
  23 +@EqualsAndHashCode(callSuper = true)
  24 +@Data
  25 +public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
  26 +
  27 + @Override
  28 + public SnmpCommunicationSpec getSpec() {
  29 + return SnmpCommunicationSpec.TELEMETRY_QUERYING;
  30 + }
  31 +
  32 +}
... ...
... ... @@ -19,19 +19,23 @@ import lombok.Getter;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.springframework.beans.factory.annotation.Autowired;
21 21 import org.springframework.beans.factory.annotation.Value;
  22 +import org.springframework.context.ApplicationContext;
22 23 import org.springframework.stereotype.Component;
23 24 import org.springframework.util.StringUtils;
  25 +import org.thingsboard.server.common.data.TbTransportService;
24 26 import org.thingsboard.server.common.data.id.TenantId;
25 27 import org.thingsboard.server.common.msg.queue.ServiceType;
26 28 import org.thingsboard.server.gen.transport.TransportProtos;
27 29 import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
28 30 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
29 31 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
  32 +import org.thingsboard.server.queue.util.AfterContextReady;
30 33
31 34 import javax.annotation.PostConstruct;
32 35 import java.net.InetAddress;
33 36 import java.net.UnknownHostException;
34 37 import java.util.Arrays;
  38 +import java.util.Collection;
35 39 import java.util.Collections;
36 40 import java.util.List;
37 41 import java.util.Optional;
... ... @@ -56,6 +60,8 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
56 60
57 61 @Autowired(required = false)
58 62 private TbQueueRuleEngineSettings ruleEngineSettings;
  63 + @Autowired
  64 + private ApplicationContext applicationContext;
59 65
60 66 private List<ServiceType> serviceTypes;
61 67 private ServiceInfo serviceInfo;
... ... @@ -102,6 +108,19 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
102 108 serviceInfo = builder.build();
103 109 }
104 110
  111 + @AfterContextReady
  112 + public void setTransports() {
  113 + serviceInfo = ServiceInfo.newBuilder(serviceInfo)
  114 + .addAllTransports(getTransportServices().stream()
  115 + .map(TbTransportService::getName)
  116 + .collect(Collectors.toSet()))
  117 + .build();
  118 + }
  119 +
  120 + private Collection<TbTransportService> getTransportServices() {
  121 + return applicationContext.getBeansOfType(TbTransportService.class).values();
  122 + }
  123 +
105 124 @Override
106 125 public ServiceInfo getServiceInfo() {
107 126 return serviceInfo;
... ...
... ... @@ -15,26 +15,27 @@
15 15 */
16 16 package org.thingsboard.server.queue.discovery;
17 17
18   -import com.google.common.hash.HashCode;
19 18 import com.google.common.hash.HashFunction;
  19 +import com.google.common.hash.Hasher;
20 20 import com.google.common.hash.Hashing;
21   -import lombok.Getter;
22 21 import lombok.extern.slf4j.Slf4j;
23 22 import org.springframework.beans.factory.annotation.Value;
24 23 import org.springframework.context.ApplicationEventPublisher;
25 24 import org.springframework.stereotype.Service;
26 25 import org.thingsboard.server.common.data.id.EntityId;
27 26 import org.thingsboard.server.common.data.id.TenantId;
28   -import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
29 27 import org.thingsboard.server.common.msg.queue.ServiceQueue;
  28 +import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
30 29 import org.thingsboard.server.common.msg.queue.ServiceType;
31 30 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
32 31 import org.thingsboard.server.gen.transport.TransportProtos;
33 32 import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
  33 +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
  34 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
  35 +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
34 36 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
35 37
36 38 import javax.annotation.PostConstruct;
37   -import java.nio.charset.StandardCharsets;
38 39 import java.util.ArrayList;
39 40 import java.util.Collections;
40 41 import java.util.Comparator;
... ... @@ -46,7 +47,6 @@ import java.util.Set;
46 47 import java.util.UUID;
47 48 import java.util.concurrent.ConcurrentHashMap;
48 49 import java.util.concurrent.ConcurrentMap;
49   -import java.util.concurrent.ConcurrentNavigableMap;
50 50 import java.util.stream.Collectors;
51 51
52 52 @Service
... ... @@ -186,6 +186,8 @@ public class HashPartitionService implements PartitionService {
186 186 applicationEventPublisher.publishEvent(new ClusterTopologyChangeEvent(this, changes));
187 187 }
188 188 }
  189 +
  190 + applicationEventPublisher.publishEvent(new ServiceListChangedEvent(otherServices, currentService));
189 191 }
190 192
191 193 @Override
... ... @@ -219,6 +221,14 @@ public class HashPartitionService implements PartitionService {
219 221 }
220 222 }
221 223
  224 + @Override
  225 + public int resolvePartitionIndex(UUID entityId, int partitions) {
  226 + int hash = hashFunction.newHasher()
  227 + .putLong(entityId.getMostSignificantBits())
  228 + .putLong(entityId.getLeastSignificantBits()).hash().asInt();
  229 + return Math.abs(hash % partitions);
  230 + }
  231 +
222 232 private Map<ServiceQueueKey, List<ServiceInfo>> getServiceKeyListMap(List<ServiceInfo> services) {
223 233 final Map<ServiceQueueKey, List<ServiceInfo>> currentMap = new HashMap<>();
224 234 services.forEach(serviceInfo -> {
... ...
... ... @@ -20,9 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId;
20 20 import org.thingsboard.server.common.msg.queue.ServiceType;
21 21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
22 22 import org.thingsboard.server.gen.transport.TransportProtos;
  23 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
23 24
24 25 import java.util.List;
25 26 import java.util.Set;
  27 +import java.util.UUID;
26 28
27 29 /**
28 30 * Once application is ready or cluster topology changes, this Service will produce {@link PartitionChangeEvent}
... ... @@ -55,4 +57,6 @@ public interface PartitionService {
55 57 * @return
56 58 */
57 59 TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId);
  60 +
  61 + int resolvePartitionIndex(UUID entityId, int partitions);
58 62 }
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.discovery;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.context.ApplicationListener;
  20 +import org.thingsboard.server.queue.discovery.event.TbApplicationEvent;
20 21
21 22 import java.util.concurrent.locks.Lock;
22 23 import java.util.concurrent.locks.ReentrantLock;
... ...
... ... @@ -33,12 +33,14 @@ import org.apache.zookeeper.KeeperException;
33 33 import org.springframework.beans.factory.annotation.Value;
34 34 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
35 35 import org.springframework.boot.context.event.ApplicationReadyEvent;
  36 +import org.springframework.context.ApplicationEventPublisher;
36 37 import org.springframework.context.event.EventListener;
37 38 import org.springframework.core.annotation.Order;
38 39 import org.springframework.stereotype.Service;
39 40 import org.springframework.util.Assert;
40 41 import org.thingsboard.common.util.ThingsBoardThreadFactory;
41 42 import org.thingsboard.server.gen.transport.TransportProtos;
  43 +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
42 44
43 45 import javax.annotation.PostConstruct;
44 46 import javax.annotation.PreDestroy;
... ... @@ -77,7 +79,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
77 79
78 80 private volatile boolean stopped = true;
79 81
80   - public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider, PartitionService partitionService) {
  82 + public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider,
  83 + PartitionService partitionService) {
81 84 this.serviceInfoProvider = serviceInfoProvider;
82 85 this.partitionService = partitionService;
83 86 }
... ...
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ClusterTopologyChangeEvent.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/discovery/ClusterTopologyChangeEvent.java
... ... @@ -13,10 +13,9 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.queue.discovery;
  16 +package org.thingsboard.server.queue.discovery.event;
17 17
18 18 import lombok.Getter;
19   -import org.springframework.context.ApplicationEvent;
20 19 import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
21 20
22 21 import java.util.Set;
... ...
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionChangeEvent.java
... ... @@ -13,10 +13,9 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.queue.discovery;
  16 +package org.thingsboard.server.queue.discovery.event;
17 17
18 18 import lombok.Getter;
19   -import org.springframework.context.ApplicationEvent;
20 19 import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
21 20 import org.thingsboard.server.common.msg.queue.ServiceType;
22 21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.queue.discovery.event;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.ToString;
  20 +import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
  21 +
  22 +import java.util.List;
  23 +
  24 +@Getter
  25 +@ToString
  26 +public class ServiceListChangedEvent extends TbApplicationEvent {
  27 + private final List<ServiceInfo> otherServices;
  28 + private final ServiceInfo currentService;
  29 +
  30 + public ServiceListChangedEvent(List<ServiceInfo> otherServices, ServiceInfo currentService) {
  31 + super(otherServices);
  32 + this.otherServices = otherServices;
  33 + this.currentService = currentService;
  34 + }
  35 +}
... ...
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/TbApplicationEvent.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEvent.java
... ... @@ -13,7 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.queue.discovery;
  16 +package org.thingsboard.server.queue.discovery.event;
17 17
18 18 import lombok.Getter;
19 19 import org.springframework.context.ApplicationEvent;
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.queue.util;
  17 +
  18 +import org.springframework.context.event.ContextRefreshedEvent;
  19 +import org.springframework.context.event.EventListener;
  20 +import org.springframework.core.annotation.AliasFor;
  21 +import org.springframework.core.annotation.Order;
  22 +
  23 +import java.lang.annotation.ElementType;
  24 +import java.lang.annotation.Retention;
  25 +import java.lang.annotation.RetentionPolicy;
  26 +import java.lang.annotation.Target;
  27 +
  28 +@Retention(RetentionPolicy.RUNTIME)
  29 +@Target(ElementType.METHOD)
  30 +@EventListener(ContextRefreshedEvent.class)
  31 +@Order
  32 +public @interface AfterContextReady {
  33 + @AliasFor(annotation = Order.class, attribute = "value")
  34 + int order() default Integer.MAX_VALUE;
  35 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.queue.util;
  17 +
  18 +import org.springframework.boot.context.event.ApplicationReadyEvent;
  19 +import org.springframework.context.event.EventListener;
  20 +import org.springframework.core.annotation.AliasFor;
  21 +import org.springframework.core.annotation.Order;
  22 +
  23 +import java.lang.annotation.ElementType;
  24 +import java.lang.annotation.Retention;
  25 +import java.lang.annotation.RetentionPolicy;
  26 +import java.lang.annotation.Target;
  27 +
  28 +@Retention(RetentionPolicy.RUNTIME)
  29 +@Target(ElementType.METHOD)
  30 +@EventListener(ApplicationReadyEvent.class)
  31 +@Order
  32 +public @interface AfterStartUp {
  33 + @AliasFor(annotation = Order.class, attribute = "value")
  34 + int order() default Integer.MAX_VALUE;
  35 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.queue.util;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +
  20 +import java.lang.annotation.ElementType;
  21 +import java.lang.annotation.Retention;
  22 +import java.lang.annotation.RetentionPolicy;
  23 +import java.lang.annotation.Target;
  24 +
  25 +@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
  26 +@Retention(RetentionPolicy.RUNTIME)
  27 +@Target({ElementType.TYPE, ElementType.METHOD})
  28 +public @interface TbSnmpTransportComponent {
  29 +}
... ...
... ... @@ -34,6 +34,7 @@ message ServiceInfo {
34 34 int64 tenantIdMSB = 3;
35 35 int64 tenantIdLSB = 4;
36 36 repeated QueueInfo ruleEngineQueues = 5;
  37 + repeated string transports = 6;
37 38 }
38 39
39 40 /**
... ... @@ -246,6 +247,36 @@ message GetEntityProfileResponseMsg {
246 247 bytes apiState = 3;
247 248 }
248 249
  250 +message GetDeviceRequestMsg {
  251 + int64 deviceIdMSB = 1;
  252 + int64 deviceIdLSB = 2;
  253 +}
  254 +
  255 +message GetDeviceResponseMsg {
  256 + int64 deviceProfileIdMSB = 1;
  257 + int64 deviceProfileIdLSB = 2;
  258 + bytes deviceTransportConfiguration = 3;
  259 +}
  260 +
  261 +message GetDeviceCredentialsRequestMsg {
  262 + int64 deviceIdMSB = 1;
  263 + int64 deviceIdLSB = 2;
  264 +}
  265 +
  266 +message GetDeviceCredentialsResponseMsg {
  267 + bytes deviceCredentialsData = 1;
  268 +}
  269 +
  270 +message GetSnmpDevicesRequestMsg {
  271 + int32 page = 1;
  272 + int32 pageSize = 2;
  273 +}
  274 +
  275 +message GetSnmpDevicesResponseMsg {
  276 + repeated string ids = 1;
  277 + bool hasNextPage = 2;
  278 +}
  279 +
249 280 message EntityUpdateMsg {
250 281 string entityType = 1;
251 282 bytes data = 2;
... ... @@ -590,6 +621,9 @@ message TransportApiRequestMsg {
590 621 ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8;
591 622 GetResourceRequestMsg resourceRequestMsg = 9;
592 623 GetFirmwareRequestMsg firmwareRequestMsg = 10;
  624 + GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11;
  625 + GetDeviceRequestMsg deviceRequestMsg = 12;
  626 + GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13;
593 627 }
594 628
595 629 /* Response from ThingsBoard Core Service to Transport Service */
... ... @@ -598,9 +632,12 @@ message TransportApiResponseMsg {
598 632 GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2;
599 633 GetEntityProfileResponseMsg entityProfileResponseMsg = 3;
600 634 ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4;
  635 + GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5;
601 636 LwM2MResponseMsg lwM2MResponseMsg = 6;
602 637 GetResourceResponseMsg resourceResponseMsg = 7;
603 638 GetFirmwareResponseMsg firmwareResponseMsg = 8;
  639 + GetDeviceResponseMsg deviceResponseMsg = 9;
  640 + GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10;
604 641 }
605 642
606 643 /* Messages that are handled by ThingsBoard Core Service */
... ...
... ... @@ -21,6 +21,7 @@ import org.eclipse.californium.core.CoapServer;
21 21 import org.springframework.beans.factory.annotation.Autowired;
22 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
23 23 import org.springframework.stereotype.Service;
  24 +import org.thingsboard.server.common.data.TbTransportService;
24 25 import org.thingsboard.server.coapserver.CoapServerService;
25 26 import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource;
26 27
... ... @@ -31,7 +32,7 @@ import java.net.UnknownHostException;
31 32 @Service("CoapTransportService")
32 33 @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')")
33 34 @Slf4j
34   -public class CoapTransportService {
  35 +public class CoapTransportService implements TbTransportService {
35 36
36 37 private static final String V1 = "v1";
37 38 private static final String API = "api";
... ... @@ -65,4 +66,9 @@ public class CoapTransportService {
65 66 public void shutdown() {
66 67 log.info("CoAP transport stopped!");
67 68 }
  69 +
  70 + @Override
  71 + public String getName() {
  72 + return "COAP";
  73 + }
68 74 }
... ...
... ... @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam;
34 34 import org.springframework.web.bind.annotation.RestController;
35 35 import org.springframework.web.context.request.async.DeferredResult;
36 36 import org.thingsboard.server.common.data.DeviceTransportType;
  37 +import org.thingsboard.server.common.data.TbTransportService;
37 38 import org.thingsboard.server.common.data.id.DeviceId;
38 39 import org.thingsboard.server.common.transport.SessionMsgListener;
39 40 import org.thingsboard.server.common.transport.TransportContext;
... ... @@ -70,7 +71,7 @@ import java.util.function.Consumer;
70 71 @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.http.enabled}'=='true')")
71 72 @RequestMapping("/api/v1")
72 73 @Slf4j
73   -public class DeviceApiController {
  74 +public class DeviceApiController implements TbTransportService {
74 75
75 76 @Autowired
76 77 private HttpTransportContext transportContext;
... ... @@ -407,4 +408,9 @@ public class DeviceApiController {
407 408 }
408 409 }
409 410
  411 + @Override
  412 + public String getName() {
  413 + return "HTTP";
  414 + }
  415 +
410 416 }
... ...
... ... @@ -20,13 +20,14 @@ import org.eclipse.leshan.core.response.ReadResponse;
20 20 import org.eclipse.leshan.server.registration.Registration;
21 21 import org.thingsboard.server.common.data.Device;
22 22 import org.thingsboard.server.common.data.DeviceProfile;
  23 +import org.thingsboard.server.common.data.TbTransportService;
23 24 import org.thingsboard.server.gen.transport.TransportProtos;
24 25 import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
25 26
26 27 import java.util.Collection;
27 28 import java.util.Optional;
28 29
29   -public interface LwM2mTransportService {
  30 +public interface LwM2mTransportService extends TbTransportService {
30 31
31 32 void onRegistered(Registration registration, Collection<Observation> previousObsersations);
32 33
... ...
... ... @@ -40,6 +40,7 @@ import org.springframework.stereotype.Service;
40 40 import org.thingsboard.common.util.JacksonUtil;
41 41 import org.thingsboard.server.common.data.Device;
42 42 import org.thingsboard.server.common.data.DeviceProfile;
  43 +import org.thingsboard.server.common.data.DeviceTransportType;
43 44 import org.thingsboard.server.common.transport.TransportService;
44 45 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
45 46 import org.thingsboard.server.common.transport.service.DefaultTransportService;
... ... @@ -1353,4 +1354,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1353 1354 objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() :
1354 1355 objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)));
1355 1356 }
  1357 +
  1358 + @Override
  1359 + public String getName() {
  1360 + return "LWM2M";
  1361 + }
  1362 +
1356 1363 }
... ...
... ... @@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value;
28 28 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
29 29 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
30 30 import org.springframework.stereotype.Service;
  31 +import org.thingsboard.server.common.data.TbTransportService;
31 32
32 33 import javax.annotation.PostConstruct;
33 34 import javax.annotation.PreDestroy;
... ... @@ -38,7 +39,7 @@ import javax.annotation.PreDestroy;
38 39 @Service("MqttTransportService")
39 40 @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')")
40 41 @Slf4j
41   -public class MqttTransportService {
  42 +public class MqttTransportService implements TbTransportService {
42 43
43 44 @Value("${transport.mqtt.bind_address}")
44 45 private String host;
... ... @@ -90,4 +91,9 @@ public class MqttTransportService {
90 91 }
91 92 log.info("MQTT transport stopped!");
92 93 }
  94 +
  95 + @Override
  96 + public String getName() {
  97 + return "MQTT";
  98 + }
93 99 }
... ...
... ... @@ -40,6 +40,7 @@
40 40 <module>http</module>
41 41 <module>coap</module>
42 42 <module>lwm2m</module>
  43 + <module>snmp</module>
43 44 </modules>
44 45
45 46 </project>
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2021 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  20 + <modelVersion>4.0.0</modelVersion>
  21 +
  22 + <parent>
  23 + <groupId>org.thingsboard.common</groupId>
  24 + <version>3.3.0-SNAPSHOT</version>
  25 + <artifactId>transport</artifactId>
  26 + </parent>
  27 +
  28 + <groupId>org.thingsboard.common.transport</groupId>
  29 + <artifactId>snmp</artifactId>
  30 + <packaging>jar</packaging>
  31 +
  32 + <name>Thingsboard SNMP Transport Common</name>
  33 + <url>https://thingsboard.io</url>
  34 +
  35 + <properties>
  36 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  37 + <main.dir>${basedir}/../../..</main.dir>
  38 + </properties>
  39 +
  40 + <dependencies>
  41 + <dependency>
  42 + <groupId>org.thingsboard.common.transport</groupId>
  43 + <artifactId>transport-api</artifactId>
  44 + </dependency>
  45 + <dependency>
  46 + <groupId>org.springframework</groupId>
  47 + <artifactId>spring-context-support</artifactId>
  48 + </dependency>
  49 + <dependency>
  50 + <groupId>org.springframework</groupId>
  51 + <artifactId>spring-context</artifactId>
  52 + </dependency>
  53 + <dependency>
  54 + <groupId>org.slf4j</groupId>
  55 + <artifactId>slf4j-api</artifactId>
  56 + </dependency>
  57 + <dependency>
  58 + <groupId>org.snmp4j</groupId>
  59 + <artifactId>snmp4j</artifactId>
  60 + </dependency>
  61 + <dependency>
  62 + <groupId>org.snmp4j</groupId>
  63 + <artifactId>snmp4j-agent</artifactId>
  64 + <version>3.3.6</version>
  65 + <scope>test</scope>
  66 + </dependency>
  67 + </dependencies>
  68 +</project>
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.RequiredArgsConstructor;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.context.event.EventListener;
  22 +import org.springframework.stereotype.Component;
  23 +import org.thingsboard.server.common.data.Device;
  24 +import org.thingsboard.server.common.data.DeviceProfile;
  25 +import org.thingsboard.server.common.data.DeviceTransportType;
  26 +import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
  27 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
  28 +import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
  29 +import org.thingsboard.server.common.data.id.DeviceId;
  30 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  31 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  32 +import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  33 +import org.thingsboard.server.common.transport.DeviceUpdatedEvent;
  34 +import org.thingsboard.server.common.transport.TransportContext;
  35 +import org.thingsboard.server.common.transport.TransportDeviceProfileCache;
  36 +import org.thingsboard.server.common.transport.TransportService;
  37 +import org.thingsboard.server.common.transport.TransportServiceCallback;
  38 +import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
  39 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
  40 +import org.thingsboard.server.gen.transport.TransportProtos;
  41 +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
  42 +import org.thingsboard.server.queue.util.AfterStartUp;
  43 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  44 +import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService;
  45 +import org.thingsboard.server.transport.snmp.service.SnmpAuthService;
  46 +import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
  47 +import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
  48 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  49 +
  50 +import java.util.Collection;
  51 +import java.util.LinkedList;
  52 +import java.util.List;
  53 +import java.util.Map;
  54 +import java.util.Optional;
  55 +import java.util.UUID;
  56 +import java.util.concurrent.ConcurrentHashMap;
  57 +import java.util.concurrent.ConcurrentLinkedDeque;
  58 +import java.util.stream.Collectors;
  59 +
  60 +@TbSnmpTransportComponent
  61 +@Component
  62 +@Slf4j
  63 +@RequiredArgsConstructor
  64 +public class SnmpTransportContext extends TransportContext {
  65 + @Getter
  66 + private final SnmpTransportService snmpTransportService;
  67 + private final TransportDeviceProfileCache deviceProfileCache;
  68 + private final TransportService transportService;
  69 + private final ProtoTransportEntityService protoEntityService;
  70 + private final SnmpTransportBalancingService balancingService;
  71 + @Getter
  72 + private final SnmpAuthService snmpAuthService;
  73 +
  74 + private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>();
  75 + private Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
  76 +
  77 + @AfterStartUp(order = 2)
  78 + public void initDevicesSessions() {
  79 + log.info("Initializing SNMP devices sessions");
  80 + allSnmpDevicesIds = protoEntityService.getAllSnmpDevicesIds().stream()
  81 + .map(DeviceId::new)
  82 + .collect(Collectors.toList());
  83 + log.trace("Found all SNMP devices ids: {}", allSnmpDevicesIds);
  84 +
  85 + List<DeviceId> managedDevicesIds = allSnmpDevicesIds.stream()
  86 + .filter(deviceId -> balancingService.isManagedByCurrentTransport(deviceId.getId()))
  87 + .collect(Collectors.toList());
  88 + log.info("SNMP devices managed by current SNMP transport: {}", managedDevicesIds);
  89 +
  90 + managedDevicesIds.stream()
  91 + .map(protoEntityService::getDeviceById)
  92 + .collect(Collectors.toList())
  93 + .forEach(this::establishDeviceSession);
  94 + }
  95 +
  96 + private void establishDeviceSession(Device device) {
  97 + if (device == null) return;
  98 + log.info("Establishing SNMP session for device {}", device.getId());
  99 +
  100 + DeviceProfileId deviceProfileId = device.getDeviceProfileId();
  101 + DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
  102 +
  103 + DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
  104 + if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
  105 + log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
  106 + return;
  107 + }
  108 +
  109 + SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
  110 + SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
  111 +
  112 + DeviceSessionContext deviceSessionContext;
  113 + try {
  114 + deviceSessionContext = new DeviceSessionContext(
  115 + device, deviceProfile, credentials.getCredentialsId(),
  116 + profileTransportConfiguration, deviceTransportConfiguration, this
  117 + );
  118 + registerSessionMsgListener(deviceSessionContext);
  119 + } catch (Exception e) {
  120 + log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.toString());
  121 + return;
  122 + }
  123 + sessions.put(device.getId(), deviceSessionContext);
  124 + snmpTransportService.createQueryingTasks(deviceSessionContext);
  125 + log.info("Established SNMP device session for device {}", device.getId());
  126 + }
  127 +
  128 + private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) {
  129 + log.info("Updating SNMP session for device {}", device.getId());
  130 +
  131 + DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
  132 + if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
  133 + log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
  134 + destroyDeviceSession(sessionContext);
  135 + return;
  136 + }
  137 +
  138 + SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
  139 + SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
  140 +
  141 + try {
  142 + if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
  143 + sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
  144 + sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
  145 + snmpTransportService.cancelQueryingTasks(sessionContext);
  146 + snmpTransportService.createQueryingTasks(sessionContext);
  147 + } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
  148 + sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
  149 + sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
  150 + } else {
  151 + log.trace("Configuration of the device {} was not updated", device);
  152 + }
  153 + } catch (Exception e) {
  154 + log.error("Failed to update session for SNMP device {}: {}", sessionContext.getDeviceId(), e.getMessage());
  155 + destroyDeviceSession(sessionContext);
  156 + }
  157 + }
  158 +
  159 + private void destroyDeviceSession(DeviceSessionContext sessionContext) {
  160 + if (sessionContext == null) return;
  161 + log.info("Destroying SNMP device session for device {}", sessionContext.getDevice().getId());
  162 + sessionContext.close();
  163 + snmpAuthService.cleanUpSnmpAuthInfo(sessionContext);
  164 + transportService.deregisterSession(sessionContext.getSessionInfo());
  165 + snmpTransportService.cancelQueryingTasks(sessionContext);
  166 + sessions.remove(sessionContext.getDeviceId());
  167 + log.trace("Unregistered and removed session");
  168 + }
  169 +
  170 + private void registerSessionMsgListener(DeviceSessionContext deviceSessionContext) {
  171 + transportService.process(DeviceTransportType.SNMP,
  172 + TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceSessionContext.getToken()).build(),
  173 + new TransportServiceCallback<>() {
  174 + @Override
  175 + public void onSuccess(ValidateDeviceCredentialsResponse msg) {
  176 + if (msg.hasDeviceInfo()) {
  177 + SessionInfoProto sessionInfo = SessionInfoCreator.create(
  178 + msg, SnmpTransportContext.this, UUID.randomUUID()
  179 + );
  180 +
  181 + transportService.registerAsyncSession(sessionInfo, deviceSessionContext);
  182 + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
  183 + transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
  184 +
  185 + deviceSessionContext.setSessionInfo(sessionInfo);
  186 + deviceSessionContext.setDeviceInfo(msg.getDeviceInfo());
  187 + } else {
  188 + log.warn("[{}] Failed to process device auth", deviceSessionContext.getDeviceId());
  189 + }
  190 + }
  191 +
  192 + @Override
  193 + public void onError(Throwable e) {
  194 + log.warn("[{}] Failed to process device auth: {}", deviceSessionContext.getDeviceId(), e);
  195 + }
  196 + });
  197 + }
  198 +
  199 + @EventListener(DeviceUpdatedEvent.class)
  200 + public void onDeviceUpdatedOrCreated(DeviceUpdatedEvent deviceUpdatedEvent) {
  201 + Device device = deviceUpdatedEvent.getDevice();
  202 + log.trace("Got creating or updating device event for device {}", device);
  203 + DeviceTransportType transportType = Optional.ofNullable(device.getDeviceData().getTransportConfiguration())
  204 + .map(DeviceTransportConfiguration::getType)
  205 + .orElse(null);
  206 + if (!allSnmpDevicesIds.contains(device.getId())) {
  207 + if (transportType != DeviceTransportType.SNMP) {
  208 + return;
  209 + }
  210 + allSnmpDevicesIds.add(device.getId());
  211 + if (balancingService.isManagedByCurrentTransport(device.getId().getId())) {
  212 + establishDeviceSession(device);
  213 + }
  214 + } else {
  215 + if (balancingService.isManagedByCurrentTransport(device.getId().getId())) {
  216 + DeviceSessionContext sessionContext = sessions.get(device.getId());
  217 + if (transportType == DeviceTransportType.SNMP) {
  218 + if (sessionContext != null) {
  219 + updateDeviceSession(sessionContext, device, deviceProfileCache.get(device.getDeviceProfileId()));
  220 + } else {
  221 + establishDeviceSession(device);
  222 + }
  223 + } else {
  224 + log.trace("Transport type was changed to {}", transportType);
  225 + destroyDeviceSession(sessionContext);
  226 + }
  227 + }
  228 + }
  229 + }
  230 +
  231 + public void onDeviceDeleted(DeviceSessionContext sessionContext) {
  232 + destroyDeviceSession(sessionContext);
  233 + }
  234 +
  235 + public void onDeviceProfileUpdated(DeviceProfile deviceProfile, DeviceSessionContext sessionContext) {
  236 + updateDeviceSession(sessionContext, sessionContext.getDevice(), deviceProfile);
  237 + }
  238 +
  239 + public void onSnmpTransportListChanged() {
  240 + log.trace("SNMP transport list changed. Updating sessions");
  241 + List<DeviceId> deleted = new LinkedList<>();
  242 + for (DeviceId deviceId : allSnmpDevicesIds) {
  243 + if (balancingService.isManagedByCurrentTransport(deviceId.getId())) {
  244 + if (!sessions.containsKey(deviceId)) {
  245 + Device device = protoEntityService.getDeviceById(deviceId);
  246 + if (device != null) {
  247 + log.info("SNMP device {} is now managed by current transport node", deviceId);
  248 + establishDeviceSession(device);
  249 + } else {
  250 + deleted.add(deviceId);
  251 + }
  252 + }
  253 + } else {
  254 + Optional.ofNullable(sessions.get(deviceId))
  255 + .ifPresent(sessionContext -> {
  256 + log.info("SNMP session for device {} is not managed by current transport node anymore", deviceId);
  257 + destroyDeviceSession(sessionContext);
  258 + });
  259 + }
  260 + }
  261 + log.trace("Removing deleted SNMP devices: {}", deleted);
  262 + allSnmpDevicesIds.removeAll(deleted);
  263 + }
  264 +
  265 +
  266 + public Collection<DeviceSessionContext> getSessions() {
  267 + return sessions.values();
  268 + }
  269 +
  270 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.event;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.springframework.stereotype.Component;
  20 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
  21 +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
  22 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  23 +import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
  24 +
  25 +@TbSnmpTransportComponent
  26 +@Component
  27 +@RequiredArgsConstructor
  28 +public class ServiceListChangedEventListener extends TbApplicationEventListener<ServiceListChangedEvent> {
  29 + private final SnmpTransportBalancingService snmpTransportBalancingService;
  30 +
  31 + @Override
  32 + protected void onTbApplicationEvent(ServiceListChangedEvent event) {
  33 + snmpTransportBalancingService.onServiceListChanged(event);
  34 + }
  35 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.event;
  17 +
  18 +import org.thingsboard.server.queue.discovery.event.TbApplicationEvent;
  19 +
  20 +public class SnmpTransportListChangedEvent extends TbApplicationEvent {
  21 + public SnmpTransportListChangedEvent() {
  22 + super(new Object());
  23 + }
  24 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.event;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.springframework.stereotype.Component;
  20 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
  21 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  22 +import org.thingsboard.server.transport.snmp.SnmpTransportContext;
  23 +
  24 +@TbSnmpTransportComponent
  25 +@Component
  26 +@RequiredArgsConstructor
  27 +public class SnmpTransportListChangedEventListener extends TbApplicationEventListener<SnmpTransportListChangedEvent> {
  28 + private final SnmpTransportContext snmpTransportContext;
  29 +
  30 + @Override
  31 + protected void onTbApplicationEvent(SnmpTransportListChangedEvent event) {
  32 + snmpTransportContext.onSnmpTransportListChanged();
  33 + }
  34 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import com.google.gson.JsonObject;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.snmp4j.PDU;
  21 +import org.snmp4j.ScopedPDU;
  22 +import org.snmp4j.smi.Integer32;
  23 +import org.snmp4j.smi.Null;
  24 +import org.snmp4j.smi.OID;
  25 +import org.snmp4j.smi.OctetString;
  26 +import org.snmp4j.smi.Variable;
  27 +import org.snmp4j.smi.VariableBinding;
  28 +import org.springframework.stereotype.Service;
  29 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
  30 +import org.thingsboard.server.common.data.kv.DataType;
  31 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  32 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  33 +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
  34 +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
  35 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  36 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  37 +
  38 +import java.util.HashMap;
  39 +import java.util.List;
  40 +import java.util.Map;
  41 +import java.util.Objects;
  42 +import java.util.Optional;
  43 +import java.util.stream.Collectors;
  44 +import java.util.stream.IntStream;
  45 +
  46 +@TbSnmpTransportComponent
  47 +@Service
  48 +@Slf4j
  49 +public class PduService {
  50 + public PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
  51 + PDU pdu = setUpPdu(sessionContext);
  52 +
  53 + pdu.setType(communicationConfig.getMethod().getCode());
  54 + pdu.addAll(communicationConfig.getAllMappings().stream()
  55 + .filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey()))
  56 + .map(mapping -> Optional.ofNullable(values.get(mapping.getKey()))
  57 + .map(value -> {
  58 + Variable variable = toSnmpVariable(value, mapping.getDataType());
  59 + return new VariableBinding(new OID(mapping.getOid()), variable);
  60 + })
  61 + .orElseGet(() -> new VariableBinding(new OID(mapping.getOid()))))
  62 + .collect(Collectors.toList()));
  63 +
  64 + return pdu;
  65 + }
  66 +
  67 + public PDU createSingleVariablePdu(DeviceSessionContext sessionContext, SnmpMethod snmpMethod, String oid, String value, DataType dataType) {
  68 + PDU pdu = setUpPdu(sessionContext);
  69 + pdu.setType(snmpMethod.getCode());
  70 +
  71 + Variable variable = value == null ? Null.instance : toSnmpVariable(value, dataType);
  72 + pdu.add(new VariableBinding(new OID(oid), variable));
  73 +
  74 + return pdu;
  75 + }
  76 +
  77 + private Variable toSnmpVariable(String value, DataType dataType) {
  78 + dataType = dataType == null ? DataType.STRING : dataType;
  79 + Variable variable;
  80 + switch (dataType) {
  81 + case LONG:
  82 + try {
  83 + variable = new Integer32(Integer.parseInt(value));
  84 + break;
  85 + } catch (NumberFormatException ignored) {
  86 + }
  87 + case DOUBLE:
  88 + case BOOLEAN:
  89 + case STRING:
  90 + case JSON:
  91 + default:
  92 + variable = new OctetString(value);
  93 + }
  94 + return variable;
  95 + }
  96 +
  97 + private PDU setUpPdu(DeviceSessionContext sessionContext) {
  98 + PDU pdu;
  99 + SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
  100 + SnmpProtocolVersion snmpVersion = deviceTransportConfiguration.getProtocolVersion();
  101 + switch (snmpVersion) {
  102 + case V1:
  103 + case V2C:
  104 + pdu = new PDU();
  105 + break;
  106 + case V3:
  107 + ScopedPDU scopedPdu = new ScopedPDU();
  108 + scopedPdu.setContextName(new OctetString(deviceTransportConfiguration.getContextName()));
  109 + scopedPdu.setContextEngineID(new OctetString(deviceTransportConfiguration.getEngineId()));
  110 + pdu = scopedPdu;
  111 + break;
  112 + default:
  113 + throw new UnsupportedOperationException("SNMP version " + snmpVersion + " is not supported");
  114 + }
  115 + return pdu;
  116 + }
  117 +
  118 +
  119 + public JsonObject processPdu(PDU pdu, List<SnmpMapping> responseMappings) {
  120 + Map<OID, String> values = processPdu(pdu);
  121 +
  122 + Map<OID, SnmpMapping> mappings = new HashMap<>();
  123 + if (responseMappings != null) {
  124 + for (SnmpMapping mapping : responseMappings) {
  125 + OID oid = new OID(mapping.getOid());
  126 + mappings.put(oid, mapping);
  127 + }
  128 + }
  129 +
  130 + JsonObject data = new JsonObject();
  131 + values.forEach((oid, value) -> {
  132 + log.trace("Processing variable binding: {} - {}", oid, value);
  133 +
  134 + SnmpMapping mapping = mappings.get(oid);
  135 + if (mapping == null) {
  136 + log.debug("No SNMP mapping for oid {}", oid);
  137 + return;
  138 + }
  139 +
  140 + processValue(mapping.getKey(), mapping.getDataType(), value, data);
  141 + });
  142 +
  143 + return data;
  144 + }
  145 +
  146 + public Map<OID, String> processPdu(PDU pdu) {
  147 + return IntStream.range(0, pdu.size())
  148 + .mapToObj(pdu::get)
  149 + .filter(Objects::nonNull)
  150 + .filter(variableBinding -> !(variableBinding.getVariable() instanceof Null))
  151 + .collect(Collectors.toMap(VariableBinding::getOid, VariableBinding::toValueString));
  152 + }
  153 +
  154 + private void processValue(String key, DataType dataType, String value, JsonObject result) {
  155 + switch (dataType) {
  156 + case LONG:
  157 + result.addProperty(key, Long.parseLong(value));
  158 + break;
  159 + case BOOLEAN:
  160 + result.addProperty(key, Boolean.parseBoolean(value));
  161 + break;
  162 + case DOUBLE:
  163 + result.addProperty(key, Double.parseDouble(value));
  164 + break;
  165 + case STRING:
  166 + case JSON:
  167 + default:
  168 + result.addProperty(key, value);
  169 + }
  170 + }
  171 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.common.data.Device;
  21 +import org.thingsboard.server.common.data.device.data.DeviceData;
  22 +import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
  23 +import org.thingsboard.server.common.data.id.DeviceId;
  24 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  25 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  26 +import org.thingsboard.server.common.transport.TransportService;
  27 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  28 +import org.thingsboard.server.gen.transport.TransportProtos;
  29 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  30 +
  31 +import java.util.ArrayList;
  32 +import java.util.List;
  33 +import java.util.UUID;
  34 +import java.util.stream.Collectors;
  35 +
  36 +@TbSnmpTransportComponent
  37 +@Service
  38 +@RequiredArgsConstructor
  39 +public class ProtoTransportEntityService {
  40 + private final TransportService transportService;
  41 + private final DataDecodingEncodingService dataDecodingEncodingService;
  42 +
  43 + public Device getDeviceById(DeviceId id) {
  44 + TransportProtos.GetDeviceResponseMsg deviceProto = transportService.getDevice(TransportProtos.GetDeviceRequestMsg.newBuilder()
  45 + .setDeviceIdMSB(id.getId().getMostSignificantBits())
  46 + .setDeviceIdLSB(id.getId().getLeastSignificantBits())
  47 + .build());
  48 +
  49 + if (deviceProto == null) {
  50 + return null;
  51 + }
  52 +
  53 + DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(
  54 + deviceProto.getDeviceProfileIdMSB(), deviceProto.getDeviceProfileIdLSB())
  55 + );
  56 +
  57 + Device device = new Device();
  58 + device.setId(id);
  59 + device.setDeviceProfileId(deviceProfileId);
  60 +
  61 + DeviceTransportConfiguration deviceTransportConfiguration = (DeviceTransportConfiguration) dataDecodingEncodingService.decode(
  62 + deviceProto.getDeviceTransportConfiguration().toByteArray()
  63 + ).orElseThrow(() -> new IllegalStateException("Can't find device transport configuration"));
  64 +
  65 + DeviceData deviceData = new DeviceData();
  66 + deviceData.setTransportConfiguration(deviceTransportConfiguration);
  67 + device.setDeviceData(deviceData);
  68 +
  69 + return device;
  70 + }
  71 +
  72 + public DeviceCredentials getDeviceCredentialsByDeviceId(DeviceId deviceId) {
  73 + TransportProtos.GetDeviceCredentialsResponseMsg deviceCredentialsResponse = transportService.getDeviceCredentials(
  74 + TransportProtos.GetDeviceCredentialsRequestMsg.newBuilder()
  75 + .setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
  76 + .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
  77 + .build()
  78 + );
  79 +
  80 + return (DeviceCredentials) dataDecodingEncodingService.decode(deviceCredentialsResponse.getDeviceCredentialsData().toByteArray())
  81 + .orElseThrow(() -> new IllegalArgumentException("Device credentials not found"));
  82 + }
  83 +
  84 + public List<UUID> getAllSnmpDevicesIds() {
  85 + List<UUID> result = new ArrayList<>();
  86 +
  87 + int page = 0;
  88 + int pageSize = 512;
  89 + boolean hasNextPage = true;
  90 +
  91 + while (hasNextPage) {
  92 + TransportProtos.GetSnmpDevicesResponseMsg responseMsg = requestSnmpDevicesIds(page, pageSize);
  93 + result.addAll(responseMsg.getIdsList().stream()
  94 + .map(UUID::fromString)
  95 + .collect(Collectors.toList()));
  96 + hasNextPage = responseMsg.getHasNextPage();
  97 + page++;
  98 + }
  99 +
  100 + return result;
  101 + }
  102 +
  103 + private TransportProtos.GetSnmpDevicesResponseMsg requestSnmpDevicesIds(int page, int pageSize) {
  104 + TransportProtos.GetSnmpDevicesRequestMsg requestMsg = TransportProtos.GetSnmpDevicesRequestMsg.newBuilder()
  105 + .setPage(page)
  106 + .setPageSize(pageSize)
  107 + .build();
  108 + return transportService.getSnmpDevicesIds(requestMsg);
  109 + }
  110 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.snmp4j.AbstractTarget;
  20 +import org.snmp4j.CommunityTarget;
  21 +import org.snmp4j.Target;
  22 +import org.snmp4j.UserTarget;
  23 +import org.snmp4j.security.SecurityLevel;
  24 +import org.snmp4j.security.SecurityModel;
  25 +import org.snmp4j.security.SecurityProtocols;
  26 +import org.snmp4j.security.USM;
  27 +import org.snmp4j.smi.Address;
  28 +import org.snmp4j.smi.GenericAddress;
  29 +import org.snmp4j.smi.OID;
  30 +import org.snmp4j.smi.OctetString;
  31 +import org.springframework.beans.factory.annotation.Value;
  32 +import org.springframework.stereotype.Service;
  33 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
  34 +import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
  35 +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
  36 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  37 +import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
  38 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  39 +
  40 +import java.util.Optional;
  41 +
  42 +@Service
  43 +@TbSnmpTransportComponent
  44 +@RequiredArgsConstructor
  45 +public class SnmpAuthService {
  46 + private final SnmpTransportService snmpTransportService;
  47 +
  48 + @Value("${transport.snmp.underlying_protocol}")
  49 + private String snmpUnderlyingProtocol;
  50 +
  51 + public Target setUpSnmpTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
  52 + AbstractTarget target;
  53 +
  54 + SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();
  55 + switch (protocolVersion) {
  56 + case V1:
  57 + CommunityTarget communityTargetV1 = new CommunityTarget();
  58 + communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);
  59 + communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
  60 + communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
  61 + target = communityTargetV1;
  62 + break;
  63 + case V2C:
  64 + CommunityTarget communityTargetV2 = new CommunityTarget();
  65 + communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
  66 + communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
  67 + communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
  68 + target = communityTargetV2;
  69 + break;
  70 + case V3:
  71 + OctetString username = new OctetString(deviceTransportConfig.getUsername());
  72 + OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());
  73 + OctetString engineId = new OctetString(deviceTransportConfig.getEngineId());
  74 +
  75 + OID authenticationProtocol = new OID(deviceTransportConfig.getAuthenticationProtocol().getOid());
  76 + OID privacyProtocol = new OID(deviceTransportConfig.getPrivacyProtocol().getOid());
  77 + OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());
  78 + authenticationPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(authenticationProtocol, authenticationPassphrase, engineId.getValue()));
  79 + OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());
  80 + privacyPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(privacyProtocol, authenticationProtocol, privacyPassphrase, engineId.getValue()));
  81 +
  82 + USM usm = snmpTransportService.getSnmp().getUSM();
  83 + if (usm.hasUser(engineId, securityName)) {
  84 + usm.removeAllUsers(username, engineId);
  85 + }
  86 + usm.addLocalizedUser(
  87 + engineId.getValue(), username,
  88 + authenticationProtocol, authenticationPassphrase.getValue(),
  89 + privacyProtocol, privacyPassphrase.getValue()
  90 + );
  91 +
  92 + UserTarget userTarget = new UserTarget();
  93 + userTarget.setSecurityName(securityName);
  94 + userTarget.setAuthoritativeEngineID(engineId.getValue());
  95 + userTarget.setSecurityModel(SecurityModel.SECURITY_MODEL_USM);
  96 + userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
  97 + target = userTarget;
  98 + break;
  99 + default:
  100 + throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
  101 + }
  102 +
  103 + Address address = GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getHost() + "/" + deviceTransportConfig.getPort());
  104 + target.setAddress(Optional.ofNullable(address).orElseThrow(() -> new IllegalArgumentException("Address of the SNMP device is invalid")));
  105 + target.setTimeout(profileTransportConfig.getTimeoutMs());
  106 + target.setRetries(profileTransportConfig.getRetries());
  107 + target.setVersion(protocolVersion.getCode());
  108 +
  109 + return target;
  110 + }
  111 +
  112 + public void cleanUpSnmpAuthInfo(DeviceSessionContext sessionContext) {
  113 + SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
  114 + if (deviceTransportConfiguration.getProtocolVersion() == SnmpProtocolVersion.V3) {
  115 + OctetString username = new OctetString(deviceTransportConfiguration.getUsername());
  116 + OctetString engineId = new OctetString(deviceTransportConfiguration.getEngineId());
  117 + snmpTransportService.getSnmp().getUSM().removeAllUsers(username, engineId);
  118 + }
  119 + }
  120 +
  121 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.context.ApplicationEventPublisher;
  21 +import org.springframework.stereotype.Service;
  22 +import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
  23 +import org.thingsboard.server.queue.discovery.PartitionService;
  24 +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
  25 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  26 +import org.thingsboard.server.transport.snmp.event.SnmpTransportListChangedEvent;
  27 +
  28 +import java.util.Comparator;
  29 +import java.util.List;
  30 +import java.util.UUID;
  31 +import java.util.stream.Collectors;
  32 +import java.util.stream.Stream;
  33 +
  34 +@TbSnmpTransportComponent
  35 +@Service
  36 +@RequiredArgsConstructor
  37 +@Slf4j
  38 +public class SnmpTransportBalancingService {
  39 + private final PartitionService partitionService;
  40 + private final ApplicationEventPublisher eventPublisher;
  41 + private final SnmpTransportService snmpTransportService;
  42 +
  43 + private int snmpTransportsCount = 1;
  44 + private Integer currentTransportPartitionIndex = 0;
  45 +
  46 + public void onServiceListChanged(ServiceListChangedEvent event) {
  47 + log.trace("Got service list changed event: {}", event);
  48 + recalculatePartitions(event.getOtherServices(), event.getCurrentService());
  49 + }
  50 +
  51 + public boolean isManagedByCurrentTransport(UUID entityId) {
  52 + boolean isManaged = resolvePartitionIndexForEntity(entityId) == currentTransportPartitionIndex;
  53 + if (!isManaged) {
  54 + log.trace("Entity {} is not managed by current SNMP transport node", entityId);
  55 + }
  56 + return isManaged;
  57 + }
  58 +
  59 + private int resolvePartitionIndexForEntity(UUID entityId) {
  60 + return partitionService.resolvePartitionIndex(entityId, snmpTransportsCount);
  61 + }
  62 +
  63 + private void recalculatePartitions(List<ServiceInfo> otherServices, ServiceInfo currentService) {
  64 + log.info("Recalculating partitions for SNMP transports");
  65 + List<ServiceInfo> snmpTransports = Stream.concat(otherServices.stream(), Stream.of(currentService))
  66 + .filter(service -> service.getTransportsList().contains(snmpTransportService.getName()))
  67 + .sorted(Comparator.comparing(ServiceInfo::getServiceId))
  68 + .collect(Collectors.toList());
  69 + log.trace("Found SNMP transports: {}", snmpTransports);
  70 +
  71 + int previousCurrentTransportPartitionIndex = currentTransportPartitionIndex;
  72 + int previousSnmpTransportsCount = snmpTransportsCount;
  73 +
  74 + if (!snmpTransports.isEmpty()) {
  75 + for (int i = 0; i < snmpTransports.size(); i++) {
  76 + if (snmpTransports.get(i).equals(currentService)) {
  77 + currentTransportPartitionIndex = i;
  78 + break;
  79 + }
  80 + }
  81 + snmpTransportsCount = snmpTransports.size();
  82 + }
  83 +
  84 + if (snmpTransportsCount != previousSnmpTransportsCount || currentTransportPartitionIndex != previousCurrentTransportPartitionIndex) {
  85 + log.info("SNMP transports partitions have changed: transports count = {}, current transport partition index = {}", snmpTransportsCount, currentTransportPartitionIndex);
  86 + eventPublisher.publishEvent(new SnmpTransportListChangedEvent());
  87 + } else {
  88 + log.info("SNMP transports partitions have not changed");
  89 + }
  90 + }
  91 +
  92 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import com.google.gson.JsonElement;
  19 +import com.google.gson.JsonObject;
  20 +import lombok.Data;
  21 +import lombok.Getter;
  22 +import lombok.RequiredArgsConstructor;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.snmp4j.PDU;
  25 +import org.snmp4j.Snmp;
  26 +import org.snmp4j.TransportMapping;
  27 +import org.snmp4j.event.ResponseEvent;
  28 +import org.snmp4j.mp.MPv3;
  29 +import org.snmp4j.security.SecurityModels;
  30 +import org.snmp4j.security.SecurityProtocols;
  31 +import org.snmp4j.security.USM;
  32 +import org.snmp4j.smi.OctetString;
  33 +import org.snmp4j.transport.DefaultTcpTransportMapping;
  34 +import org.snmp4j.transport.DefaultUdpTransportMapping;
  35 +import org.springframework.beans.factory.annotation.Value;
  36 +import org.springframework.stereotype.Service;
  37 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
  38 +import org.thingsboard.server.common.data.TbTransportService;
  39 +import org.thingsboard.server.common.data.kv.DataType;
  40 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  41 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  42 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  43 +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
  44 +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
  45 +import org.thingsboard.server.common.transport.TransportService;
  46 +import org.thingsboard.server.common.transport.TransportServiceCallback;
  47 +import org.thingsboard.server.common.transport.adaptor.JsonConverter;
  48 +import org.thingsboard.server.gen.transport.TransportProtos;
  49 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  50 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  51 +
  52 +import javax.annotation.PostConstruct;
  53 +import javax.annotation.PreDestroy;
  54 +import java.io.IOException;
  55 +import java.util.Arrays;
  56 +import java.util.Collections;
  57 +import java.util.EnumMap;
  58 +import java.util.List;
  59 +import java.util.Map;
  60 +import java.util.Optional;
  61 +import java.util.concurrent.ExecutorService;
  62 +import java.util.concurrent.Executors;
  63 +import java.util.concurrent.ScheduledExecutorService;
  64 +import java.util.concurrent.ScheduledFuture;
  65 +import java.util.concurrent.TimeUnit;
  66 +import java.util.stream.Collectors;
  67 +
  68 +@TbSnmpTransportComponent
  69 +@Service
  70 +@Slf4j
  71 +@RequiredArgsConstructor
  72 +public class SnmpTransportService implements TbTransportService {
  73 + private final TransportService transportService;
  74 + private final PduService pduService;
  75 +
  76 + @Getter
  77 + private Snmp snmp;
  78 + private ScheduledExecutorService queryingExecutor;
  79 + private ExecutorService responseProcessingExecutor;
  80 +
  81 + private final Map<SnmpCommunicationSpec, ResponseDataMapper> responseDataMappers = new EnumMap<>(SnmpCommunicationSpec.class);
  82 + private final Map<SnmpCommunicationSpec, ResponseProcessor> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class);
  83 +
  84 + @Value("${transport.snmp.response_processing.parallelism_level}")
  85 + private Integer responseProcessingParallelismLevel;
  86 + @Value("${transport.snmp.underlying_protocol}")
  87 + private String snmpUnderlyingProtocol;
  88 +
  89 + @PostConstruct
  90 + private void init() throws IOException {
  91 + queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying"));
  92 + responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
  93 +
  94 + initializeSnmp();
  95 + configureResponseDataMappers();
  96 + configureResponseProcessors();
  97 +
  98 + log.info("SNMP transport service initialized");
  99 + }
  100 +
  101 + private void initializeSnmp() throws IOException {
  102 + TransportMapping<?> transportMapping;
  103 + switch (snmpUnderlyingProtocol) {
  104 + case "udp":
  105 + transportMapping = new DefaultUdpTransportMapping();
  106 + break;
  107 + case "tcp":
  108 + transportMapping = new DefaultTcpTransportMapping();
  109 + break;
  110 + default:
  111 + throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported");
  112 + }
  113 + snmp = new Snmp(transportMapping);
  114 + snmp.listen();
  115 +
  116 + USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
  117 + SecurityModels.getInstance().addSecurityModel(usm);
  118 + }
  119 +
  120 + public void createQueryingTasks(DeviceSessionContext sessionContext) {
  121 + List<ScheduledFuture<?>> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
  122 + .filter(communicationConfig -> communicationConfig instanceof RepeatingQueryingSnmpCommunicationConfig)
  123 + .map(config -> {
  124 + RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config;
  125 + Long queryingFrequency = repeatingCommunicationConfig.getQueryingFrequencyMs();
  126 +
  127 + return queryingExecutor.scheduleWithFixedDelay(() -> {
  128 + try {
  129 + if (sessionContext.isActive()) {
  130 + sendRequest(sessionContext, repeatingCommunicationConfig);
  131 + }
  132 + } catch (Exception e) {
  133 + log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString());
  134 + }
  135 + }, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
  136 + })
  137 + .collect(Collectors.toList());
  138 + sessionContext.getQueryingTasks().addAll(queryingTasks);
  139 + }
  140 +
  141 + public void cancelQueryingTasks(DeviceSessionContext sessionContext) {
  142 + sessionContext.getQueryingTasks().forEach(task -> task.cancel(true));
  143 + sessionContext.getQueryingTasks().clear();
  144 + }
  145 +
  146 +
  147 + private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) {
  148 + sendRequest(sessionContext, communicationConfig, Collections.emptyMap());
  149 + }
  150 +
  151 + private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
  152 + PDU request = pduService.createPdu(sessionContext, communicationConfig, values);
  153 + RequestInfo requestInfo = new RequestInfo(communicationConfig.getSpec(), communicationConfig.getAllMappings());
  154 + sendRequest(sessionContext, request, requestInfo);
  155 + }
  156 +
  157 + private void sendRequest(DeviceSessionContext sessionContext, PDU request, RequestInfo requestInfo) {
  158 + if (request.size() > 0) {
  159 + log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings());
  160 + try {
  161 + snmp.send(request, sessionContext.getTarget(), requestInfo, sessionContext);
  162 + } catch (IOException e) {
  163 + log.error("Failed to send SNMP request to device {}: {}", sessionContext.getDeviceId(), e.toString());
  164 + }
  165 + }
  166 + }
  167 +
  168 + public void onAttributeUpdate(DeviceSessionContext sessionContext, TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification) {
  169 + sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
  170 + .filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING)
  171 + .findFirst()
  172 + .ifPresent(communicationConfig -> {
  173 + Map<String, String> sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream()
  174 + .collect(Collectors.toMap(
  175 + Map.Entry::getKey,
  176 + entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString()
  177 + ));
  178 + sendRequest(sessionContext, communicationConfig, sharedAttributes);
  179 + });
  180 + }
  181 +
  182 + public void onToDeviceRpcRequest(DeviceSessionContext sessionContext, TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg) {
  183 + SnmpMethod snmpMethod = SnmpMethod.valueOf(toDeviceRpcRequestMsg.getMethodName());
  184 + JsonObject params = JsonConverter.parse(toDeviceRpcRequestMsg.getParams()).getAsJsonObject();
  185 +
  186 + String oid = Optional.ofNullable(params.get("oid")).map(JsonElement::getAsString).orElse(null);
  187 + String value = Optional.ofNullable(params.get("value")).map(JsonElement::getAsString).orElse(null);
  188 + DataType dataType = Optional.ofNullable(params.get("dataType")).map(e -> DataType.valueOf(e.getAsString())).orElse(DataType.STRING);
  189 +
  190 + if (oid == null || oid.isEmpty()) {
  191 + throw new IllegalArgumentException("OID in to-device RPC request is not specified");
  192 + }
  193 + if (value == null && snmpMethod == SnmpMethod.SET) {
  194 + throw new IllegalArgumentException("Value must be specified for SNMP method 'SET'");
  195 + }
  196 +
  197 + PDU request = pduService.createSingleVariablePdu(sessionContext, snmpMethod, oid, value, dataType);
  198 + sendRequest(sessionContext, request, new RequestInfo(toDeviceRpcRequestMsg.getRequestId(), SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST));
  199 + }
  200 +
  201 +
  202 + public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) {
  203 + ((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext);
  204 +
  205 + if (event.getError() != null) {
  206 + log.warn("SNMP response error: {}", event.getError().toString());
  207 + return;
  208 + }
  209 +
  210 + PDU response = event.getResponse();
  211 + if (response == null) {
  212 + log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
  213 + return;
  214 + }
  215 +
  216 + RequestInfo requestInfo = (RequestInfo) event.getUserObject();
  217 + responseProcessingExecutor.execute(() -> {
  218 + processResponse(sessionContext, response, requestInfo);
  219 + });
  220 + }
  221 +
  222 + private void processResponse(DeviceSessionContext sessionContext, PDU response, RequestInfo requestInfo) {
  223 + ResponseProcessor responseProcessor = responseProcessors.get(requestInfo.getCommunicationSpec());
  224 + if (responseProcessor == null) return;
  225 +
  226 + JsonObject responseData = responseDataMappers.get(requestInfo.getCommunicationSpec()).map(response, requestInfo);
  227 +
  228 + if (responseData.entrySet().isEmpty()) {
  229 + log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), response.getRequestID());
  230 + return;
  231 + }
  232 +
  233 + responseProcessor.process(responseData, requestInfo, sessionContext);
  234 + reportActivity(sessionContext.getSessionInfo());
  235 + }
  236 +
  237 + private void configureResponseDataMappers() {
  238 + responseDataMappers.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (pdu, requestInfo) -> {
  239 + JsonObject responseData = new JsonObject();
  240 + pduService.processPdu(pdu).forEach((oid, value) -> {
  241 + responseData.addProperty(oid.toDottedString(), value);
  242 + });
  243 + return responseData;
  244 + });
  245 +
  246 + ResponseDataMapper defaultResponseDataMapper = (pdu, requestInfo) -> {
  247 + return pduService.processPdu(pdu, requestInfo.getResponseMappings());
  248 + };
  249 + Arrays.stream(SnmpCommunicationSpec.values())
  250 + .forEach(communicationSpec -> {
  251 + responseDataMappers.putIfAbsent(communicationSpec, defaultResponseDataMapper);
  252 + });
  253 + }
  254 +
  255 + private void configureResponseProcessors() {
  256 + responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (responseData, requestInfo, sessionContext) -> {
  257 + TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(responseData);
  258 + transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, null);
  259 + log.debug("Posted telemetry for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
  260 + });
  261 +
  262 + responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (responseData, requestInfo, sessionContext) -> {
  263 + TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(responseData);
  264 + transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, null);
  265 + log.debug("Posted attributes for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
  266 + });
  267 +
  268 + responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (responseData, requestInfo, sessionContext) -> {
  269 + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
  270 + .setRequestId(requestInfo.getRequestId())
  271 + .setPayload(JsonConverter.toJson(responseData))
  272 + .build();
  273 + transportService.process(sessionContext.getSessionInfo(), rpcResponseMsg, null);
  274 + log.debug("Posted RPC response {} for device {}", responseData, sessionContext.getDeviceId());
  275 + });
  276 + }
  277 +
  278 + private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
  279 + transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
  280 + .setAttributeSubscription(true)
  281 + .setRpcSubscription(true)
  282 + .setLastActivityTime(System.currentTimeMillis())
  283 + .build(), TransportServiceCallback.EMPTY);
  284 + }
  285 +
  286 +
  287 + @Override
  288 + public String getName() {
  289 + return "SNMP";
  290 + }
  291 +
  292 + @PreDestroy
  293 + public void shutdown() {
  294 + log.info("Stopping SNMP transport!");
  295 + if (queryingExecutor != null) {
  296 + queryingExecutor.shutdownNow();
  297 + }
  298 + if (responseProcessingExecutor != null) {
  299 + responseProcessingExecutor.shutdownNow();
  300 + }
  301 + if (snmp != null) {
  302 + try {
  303 + snmp.close();
  304 + } catch (IOException e) {
  305 + log.error(e.getMessage(), e);
  306 + }
  307 + }
  308 + log.info("SNMP transport stopped!");
  309 + }
  310 +
  311 + @Data
  312 + private static class RequestInfo {
  313 + private Integer requestId;
  314 + private SnmpCommunicationSpec communicationSpec;
  315 + private List<SnmpMapping> responseMappings;
  316 +
  317 + public RequestInfo(Integer requestId, SnmpCommunicationSpec communicationSpec) {
  318 + this.requestId = requestId;
  319 + this.communicationSpec = communicationSpec;
  320 + }
  321 +
  322 + public RequestInfo(SnmpCommunicationSpec communicationSpec) {
  323 + this.communicationSpec = communicationSpec;
  324 + }
  325 +
  326 + public RequestInfo(SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) {
  327 + this.communicationSpec = communicationSpec;
  328 + this.responseMappings = responseMappings;
  329 + }
  330 + }
  331 +
  332 + private interface ResponseDataMapper {
  333 + JsonObject map(PDU pdu, RequestInfo requestInfo);
  334 + }
  335 +
  336 + private interface ResponseProcessor {
  337 + void process(JsonObject responseData, RequestInfo requestInfo, DeviceSessionContext sessionContext);
  338 + }
  339 +
  340 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.session;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.Setter;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.snmp4j.Target;
  22 +import org.snmp4j.event.ResponseEvent;
  23 +import org.snmp4j.event.ResponseListener;
  24 +import org.thingsboard.server.common.data.Device;
  25 +import org.thingsboard.server.common.data.DeviceProfile;
  26 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
  27 +import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
  28 +import org.thingsboard.server.common.data.id.DeviceId;
  29 +import org.thingsboard.server.common.transport.SessionMsgListener;
  30 +import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
  31 +import org.thingsboard.server.gen.transport.TransportProtos;
  32 +import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
  33 +import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
  34 +import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
  35 +import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
  36 +import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
  37 +import org.thingsboard.server.transport.snmp.SnmpTransportContext;
  38 +
  39 +import java.util.LinkedList;
  40 +import java.util.List;
  41 +import java.util.UUID;
  42 +import java.util.concurrent.ScheduledFuture;
  43 +import java.util.concurrent.atomic.AtomicInteger;
  44 +
  45 +@Slf4j
  46 +public class DeviceSessionContext extends DeviceAwareSessionContext implements SessionMsgListener, ResponseListener {
  47 + @Getter
  48 + private Target target;
  49 + private final String token;
  50 + @Getter
  51 + @Setter
  52 + private SnmpDeviceProfileTransportConfiguration profileTransportConfiguration;
  53 + @Getter
  54 + @Setter
  55 + private SnmpDeviceTransportConfiguration deviceTransportConfiguration;
  56 + @Getter
  57 + private final Device device;
  58 +
  59 + private final SnmpTransportContext snmpTransportContext;
  60 +
  61 + private final AtomicInteger msgIdSeq = new AtomicInteger(0);
  62 + @Getter
  63 + private boolean isActive = true;
  64 +
  65 + @Getter
  66 + private final List<ScheduledFuture<?>> queryingTasks = new LinkedList<>();
  67 +
  68 + public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token,
  69 + SnmpDeviceProfileTransportConfiguration profileTransportConfiguration,
  70 + SnmpDeviceTransportConfiguration deviceTransportConfiguration,
  71 + SnmpTransportContext snmpTransportContext) throws Exception {
  72 + super(UUID.randomUUID());
  73 + super.setDeviceId(device.getId());
  74 + super.setDeviceProfile(deviceProfile);
  75 + this.device = device;
  76 +
  77 + this.token = token;
  78 + this.snmpTransportContext = snmpTransportContext;
  79 +
  80 + this.profileTransportConfiguration = profileTransportConfiguration;
  81 + this.deviceTransportConfiguration = deviceTransportConfiguration;
  82 +
  83 + initializeTarget(profileTransportConfiguration, deviceTransportConfiguration);
  84 + }
  85 +
  86 + @Override
  87 + public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto newSessionInfo, DeviceProfile deviceProfile) {
  88 + super.onDeviceProfileUpdate(newSessionInfo, deviceProfile);
  89 + if (isActive) {
  90 + snmpTransportContext.onDeviceProfileUpdated(deviceProfile, this);
  91 + }
  92 + }
  93 +
  94 + @Override
  95 + public void onDeviceDeleted(DeviceId deviceId) {
  96 + snmpTransportContext.onDeviceDeleted(this);
  97 + }
  98 +
  99 + @Override
  100 + public void onResponse(ResponseEvent event) {
  101 + if (isActive) {
  102 + snmpTransportContext.getSnmpTransportService().processResponseEvent(this, event);
  103 + }
  104 + }
  105 +
  106 + public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) throws Exception {
  107 + log.trace("Initializing target for SNMP session of device {}", device);
  108 + this.target = snmpTransportContext.getSnmpAuthService().setUpSnmpTarget(profileTransportConfig, deviceTransportConfig);
  109 + log.debug("SNMP target initialized: {}", target);
  110 + }
  111 +
  112 + public void close() {
  113 + isActive = false;
  114 + }
  115 +
  116 + public String getToken() {
  117 + return token;
  118 + }
  119 +
  120 + @Override
  121 + public int nextMsgId() {
  122 + return msgIdSeq.incrementAndGet();
  123 + }
  124 +
  125 + @Override
  126 + public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
  127 + }
  128 +
  129 + @Override
  130 + public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
  131 + snmpTransportContext.getSnmpTransportService().onAttributeUpdate(this, attributeUpdateNotification);
  132 + }
  133 +
  134 + @Override
  135 + public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
  136 + }
  137 +
  138 + @Override
  139 + public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
  140 + snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest);
  141 + }
  142 +
  143 + @Override
  144 + public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) {
  145 + }
  146 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp;
  17 +
  18 +import org.snmp4j.CommandResponderEvent;
  19 +import org.snmp4j.CommunityTarget;
  20 +import org.snmp4j.PDU;
  21 +import org.snmp4j.Snmp;
  22 +import org.snmp4j.Target;
  23 +import org.snmp4j.TransportMapping;
  24 +import org.snmp4j.agent.BaseAgent;
  25 +import org.snmp4j.agent.CommandProcessor;
  26 +import org.snmp4j.agent.DuplicateRegistrationException;
  27 +import org.snmp4j.agent.MOGroup;
  28 +import org.snmp4j.agent.ManagedObject;
  29 +import org.snmp4j.agent.mo.MOAccessImpl;
  30 +import org.snmp4j.agent.mo.MOScalar;
  31 +import org.snmp4j.agent.mo.snmp.RowStatus;
  32 +import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
  33 +import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
  34 +import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
  35 +import org.snmp4j.agent.mo.snmp.StorageType;
  36 +import org.snmp4j.agent.mo.snmp.VacmMIB;
  37 +import org.snmp4j.agent.security.MutableVACM;
  38 +import org.snmp4j.mp.MPv3;
  39 +import org.snmp4j.mp.SnmpConstants;
  40 +import org.snmp4j.security.SecurityLevel;
  41 +import org.snmp4j.security.SecurityModel;
  42 +import org.snmp4j.security.USM;
  43 +import org.snmp4j.smi.Address;
  44 +import org.snmp4j.smi.GenericAddress;
  45 +import org.snmp4j.smi.Integer32;
  46 +import org.snmp4j.smi.OID;
  47 +import org.snmp4j.smi.OctetString;
  48 +import org.snmp4j.smi.UdpAddress;
  49 +import org.snmp4j.smi.Variable;
  50 +import org.snmp4j.smi.VariableBinding;
  51 +import org.snmp4j.transport.TransportMappings;
  52 +
  53 +import java.io.File;
  54 +import java.io.IOException;
  55 +import java.util.Map;
  56 +import java.util.Scanner;
  57 +import java.util.function.Consumer;
  58 +import java.util.stream.Collectors;
  59 +
  60 +public class SnmpDeviceSimulatorV2 extends BaseAgent {
  61 +
  62 + public static class RequestProcessor extends CommandProcessor {
  63 + private final Consumer<CommandResponderEvent> processor;
  64 +
  65 + public RequestProcessor(Consumer<CommandResponderEvent> processor) {
  66 + super(new OctetString(MPv3.createLocalEngineID()));
  67 + this.processor = processor;
  68 + }
  69 +
  70 + @Override
  71 + public void processPdu(CommandResponderEvent event) {
  72 + processor.accept(event);
  73 + }
  74 + }
  75 +
  76 +
  77 + private final Target target;
  78 + private final Address address;
  79 + private Snmp snmp;
  80 +
  81 + private final String password;
  82 +
  83 + public SnmpDeviceSimulatorV2(int port, String password) throws IOException {
  84 + super(new File("conf.agent"), new File("bootCounter.agent"), new RequestProcessor(event -> {
  85 + System.out.println("aboba");
  86 + ((Snmp) event.getSource()).cancel(event.getPDU(), event1 -> System.out.println("canceled"));
  87 + }));
  88 + CommunityTarget target = new CommunityTarget();
  89 + target.setCommunity(new OctetString(password));
  90 + this.address = GenericAddress.parse("udp:0.0.0.0/" + port);
  91 + target.setAddress(address);
  92 + target.setRetries(2);
  93 + target.setTimeout(1500);
  94 + target.setVersion(SnmpConstants.version2c);
  95 + this.target = target;
  96 + this.password = password;
  97 + }
  98 +
  99 + public void start() throws IOException {
  100 + init();
  101 + addShutdownHook();
  102 + getServer().addContext(new OctetString("public"));
  103 + finishInit();
  104 + run();
  105 + sendColdStartNotification();
  106 + snmp = new Snmp(transportMappings[0]);
  107 + }
  108 +
  109 + public void setUpMappings(Map<String, String> oidToResponseMappings) {
  110 + unregisterManagedObject(getSnmpv2MIB());
  111 + oidToResponseMappings.forEach((oid, response) -> {
  112 + registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response)));
  113 + });
  114 + }
  115 +
  116 + public void sendTrap(String host, int port, Map<String, String> values) throws IOException {
  117 + PDU pdu = new PDU();
  118 + pdu.addAll(values.entrySet().stream()
  119 + .map(entry -> new VariableBinding(new OID(entry.getKey()), new OctetString(entry.getValue())))
  120 + .collect(Collectors.toList()));
  121 + pdu.setType(PDU.TRAP);
  122 +
  123 + CommunityTarget remoteTarget = (CommunityTarget) getTarget().clone();
  124 + remoteTarget.setAddress(new UdpAddress(host + "/" + port));
  125 +
  126 + snmp.send(pdu, remoteTarget);
  127 + }
  128 +
  129 + @Override
  130 + protected void registerManagedObjects() {
  131 + }
  132 +
  133 + protected void registerManagedObject(ManagedObject mo) {
  134 + try {
  135 + server.register(mo, null);
  136 + } catch (DuplicateRegistrationException ex) {
  137 + throw new RuntimeException(ex);
  138 + }
  139 + }
  140 +
  141 + protected void unregisterManagedObject(MOGroup moGroup) {
  142 + moGroup.unregisterMOs(server, getContext(moGroup));
  143 + }
  144 +
  145 + @Override
  146 + protected void addNotificationTargets(SnmpTargetMIB targetMIB,
  147 + SnmpNotificationMIB notificationMIB) {
  148 + }
  149 +
  150 + @Override
  151 + protected void addViews(VacmMIB vacm) {
  152 + vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c, new OctetString(
  153 + "cpublic"), new OctetString("v1v2group"),
  154 + StorageType.nonVolatile);
  155 +
  156 + vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"),
  157 + SecurityModel.SECURITY_MODEL_ANY, SecurityLevel.NOAUTH_NOPRIV,
  158 + MutableVACM.VACM_MATCH_EXACT, new OctetString("fullReadView"),
  159 + new OctetString("fullWriteView"), new OctetString(
  160 + "fullNotifyView"), StorageType.nonVolatile);
  161 +
  162 + vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"),
  163 + new OctetString(), VacmMIB.vacmViewIncluded,
  164 + StorageType.nonVolatile);
  165 + }
  166 +
  167 + protected void addUsmUser(USM usm) {
  168 + }
  169 +
  170 + protected void initTransportMappings() {
  171 + transportMappings = new TransportMapping[]{TransportMappings.getInstance().createTransportMapping(address)};
  172 + }
  173 +
  174 + protected void unregisterManagedObjects() {
  175 + }
  176 +
  177 + protected void addCommunities(SnmpCommunityMIB communityMIB) {
  178 + Variable[] com2sec = new Variable[]{
  179 + new OctetString("public"),
  180 + new OctetString("cpublic"),
  181 + getAgent().getContextEngineID(),
  182 + new OctetString("public"),
  183 + new OctetString(),
  184 + new Integer32(StorageType.nonVolatile),
  185 + new Integer32(RowStatus.active)
  186 + };
  187 + SnmpCommunityMIB.SnmpCommunityEntryRow row = communityMIB.getSnmpCommunityEntry().createRow(
  188 + new OctetString("public2public").toSubIndex(true), com2sec);
  189 + communityMIB.getSnmpCommunityEntry().addRow(row);
  190 + }
  191 +
  192 + public Target getTarget() {
  193 + return target;
  194 + }
  195 +
  196 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp;
  17 +
  18 +import org.snmp4j.MessageDispatcherImpl;
  19 +import org.snmp4j.TransportMapping;
  20 +import org.snmp4j.agent.BaseAgent;
  21 +import org.snmp4j.agent.CommandProcessor;
  22 +import org.snmp4j.agent.DuplicateRegistrationException;
  23 +import org.snmp4j.agent.MOGroup;
  24 +import org.snmp4j.agent.ManagedObject;
  25 +import org.snmp4j.agent.mo.DefaultMOMutableRow2PC;
  26 +import org.snmp4j.agent.mo.DefaultMOTable;
  27 +import org.snmp4j.agent.mo.MOAccessImpl;
  28 +import org.snmp4j.agent.mo.MOColumn;
  29 +import org.snmp4j.agent.mo.MOMutableColumn;
  30 +import org.snmp4j.agent.mo.MOMutableTableModel;
  31 +import org.snmp4j.agent.mo.MOScalar;
  32 +import org.snmp4j.agent.mo.MOTableIndex;
  33 +import org.snmp4j.agent.mo.MOTableRow;
  34 +import org.snmp4j.agent.mo.MOTableSubIndex;
  35 +import org.snmp4j.agent.mo.ext.AgentppSimulationMib;
  36 +import org.snmp4j.agent.mo.snmp.RowStatus;
  37 +import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
  38 +import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
  39 +import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
  40 +import org.snmp4j.agent.mo.snmp.StorageType;
  41 +import org.snmp4j.agent.mo.snmp.TransportDomains;
  42 +import org.snmp4j.agent.mo.snmp.VacmMIB;
  43 +import org.snmp4j.agent.mo.snmp4j.example.Snmp4jHeartbeatMib;
  44 +import org.snmp4j.agent.security.MutableVACM;
  45 +import org.snmp4j.mp.MPv1;
  46 +import org.snmp4j.mp.MPv2c;
  47 +import org.snmp4j.mp.MPv3;
  48 +import org.snmp4j.mp.MessageProcessingModel;
  49 +import org.snmp4j.security.AuthHMAC192SHA256;
  50 +import org.snmp4j.security.AuthMD5;
  51 +import org.snmp4j.security.AuthSHA;
  52 +import org.snmp4j.security.PrivAES128;
  53 +import org.snmp4j.security.PrivAES192;
  54 +import org.snmp4j.security.PrivAES256;
  55 +import org.snmp4j.security.PrivDES;
  56 +import org.snmp4j.security.SecurityLevel;
  57 +import org.snmp4j.security.SecurityModel;
  58 +import org.snmp4j.security.SecurityModels;
  59 +import org.snmp4j.security.SecurityProtocols;
  60 +import org.snmp4j.security.USM;
  61 +import org.snmp4j.security.UsmUser;
  62 +import org.snmp4j.smi.Address;
  63 +import org.snmp4j.smi.Gauge32;
  64 +import org.snmp4j.smi.GenericAddress;
  65 +import org.snmp4j.smi.Integer32;
  66 +import org.snmp4j.smi.OID;
  67 +import org.snmp4j.smi.OctetString;
  68 +import org.snmp4j.smi.SMIConstants;
  69 +import org.snmp4j.smi.TcpAddress;
  70 +import org.snmp4j.smi.TimeTicks;
  71 +import org.snmp4j.smi.UdpAddress;
  72 +import org.snmp4j.smi.Variable;
  73 +import org.snmp4j.transport.DefaultTcpTransportMapping;
  74 +import org.snmp4j.transport.TransportMappings;
  75 +import org.snmp4j.util.ThreadPool;
  76 +
  77 +import java.io.File;
  78 +import java.io.IOException;
  79 +import java.util.Map;
  80 +
  81 +/**
  82 + * The TestAgent is a sample SNMP agent implementation of all
  83 + * features (MIB implementations) provided by the SNMP4J-Agent framework.
  84 + *
  85 + * Note, for snmp4s, this code is mostly a copy from snmp4j.
  86 + * And don't remove snmp users
  87 + *
  88 + */
  89 +public class SnmpDeviceSimulatorV3 extends BaseAgent {
  90 + protected String address;
  91 + private Snmp4jHeartbeatMib heartbeatMIB;
  92 + private AgentppSimulationMib agentppSimulationMIB;
  93 +
  94 + public SnmpDeviceSimulatorV3(CommandProcessor processor) throws IOException {
  95 + super(new File("SNMP4JTestAgentBC.cfg"), new File("SNMP4JTestAgentConfig.cfg"),
  96 + processor);
  97 + agent.setWorkerPool(ThreadPool.create("RequestPool", 4));
  98 + }
  99 +
  100 + public void setUpMappings(Map<String, String> oidToResponseMappings) {
  101 + unregisterManagedObject(getSnmpv2MIB());
  102 + oidToResponseMappings.forEach((oid, response) -> {
  103 + registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response)));
  104 + });
  105 + }
  106 + protected void registerManagedObject(ManagedObject mo) {
  107 + try {
  108 + server.register(mo, null);
  109 + } catch (DuplicateRegistrationException ex) {
  110 + throw new RuntimeException(ex);
  111 + }
  112 + }
  113 +
  114 + protected void unregisterManagedObject(MOGroup moGroup) {
  115 + moGroup.unregisterMOs(server, getContext(moGroup));
  116 + }
  117 +
  118 + protected void registerManagedObjects() {
  119 + try {
  120 + server.register(createStaticIfTable(), null);
  121 + server.register(createStaticIfXTable(), null);
  122 + agentppSimulationMIB.registerMOs(server, null);
  123 + heartbeatMIB.registerMOs(server, null);
  124 + } catch (DuplicateRegistrationException ex) {
  125 + ex.printStackTrace();
  126 + }
  127 + }
  128 +
  129 + protected void addNotificationTargets(SnmpTargetMIB targetMIB,
  130 + SnmpNotificationMIB notificationMIB) {
  131 + targetMIB.addDefaultTDomains();
  132 +
  133 + targetMIB.addTargetAddress(new OctetString("notificationV2c"),
  134 + TransportDomains.transportDomainUdpIpv4,
  135 + new OctetString(new UdpAddress("127.0.0.1/162").getValue()),
  136 + 200, 1,
  137 + new OctetString("notify"),
  138 + new OctetString("v2c"),
  139 + StorageType.permanent);
  140 + targetMIB.addTargetAddress(new OctetString("notificationV3"),
  141 + TransportDomains.transportDomainUdpIpv4,
  142 + new OctetString(new UdpAddress("127.0.0.1/1162").getValue()),
  143 + 200, 1,
  144 + new OctetString("notify"),
  145 + new OctetString("v3notify"),
  146 + StorageType.permanent);
  147 + targetMIB.addTargetParams(new OctetString("v2c"),
  148 + MessageProcessingModel.MPv2c,
  149 + SecurityModel.SECURITY_MODEL_SNMPv2c,
  150 + new OctetString("cpublic"),
  151 + SecurityLevel.AUTH_PRIV,
  152 + StorageType.permanent);
  153 + targetMIB.addTargetParams(new OctetString("v3notify"),
  154 + MessageProcessingModel.MPv3,
  155 + SecurityModel.SECURITY_MODEL_USM,
  156 + new OctetString("v3notify"),
  157 + SecurityLevel.NOAUTH_NOPRIV,
  158 + StorageType.permanent);
  159 + notificationMIB.addNotifyEntry(new OctetString("default"),
  160 + new OctetString("notify"),
  161 + SnmpNotificationMIB.SnmpNotifyTypeEnum.inform,
  162 + StorageType.permanent);
  163 + }
  164 + protected void addViews(VacmMIB vacm) {
  165 + vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv1,
  166 + new OctetString("cpublic"),
  167 + new OctetString("v1v2group"),
  168 + StorageType.nonVolatile);
  169 + vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c,
  170 + new OctetString("cpublic"),
  171 + new OctetString("v1v2group"),
  172 + StorageType.nonVolatile);
  173 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  174 + new OctetString("SHADES"),
  175 + new OctetString("v3group"),
  176 + StorageType.nonVolatile);
  177 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  178 + new OctetString("MD5DES"),
  179 + new OctetString("v3group"),
  180 + StorageType.nonVolatile);
  181 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  182 + new OctetString("TEST"),
  183 + new OctetString("v3test"),
  184 + StorageType.nonVolatile);
  185 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  186 + new OctetString("SHA"),
  187 + new OctetString("v3restricted"),
  188 + StorageType.nonVolatile);
  189 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  190 + new OctetString("SHAAES128"),
  191 + new OctetString("v3group"),
  192 + StorageType.nonVolatile);
  193 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  194 + new OctetString("SHAAES192"),
  195 + new OctetString("v3group"),
  196 + StorageType.nonVolatile);
  197 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  198 + new OctetString("SHAAES256"),
  199 + new OctetString("v3group"),
  200 + StorageType.nonVolatile);
  201 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  202 + new OctetString("MD5AES128"),
  203 + new OctetString("v3group"),
  204 + StorageType.nonVolatile);
  205 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  206 + new OctetString("MD5AES192"),
  207 + new OctetString("v3group"),
  208 + StorageType.nonVolatile);
  209 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  210 + new OctetString("MD5AES256"),
  211 + new OctetString("v3group"),
  212 + StorageType.nonVolatile);
  213 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  214 + new OctetString("aboba"),
  215 + new OctetString("v3group"),
  216 + StorageType.nonVolatile);
  217 + //============================================//
  218 + // agent5-auth-priv
  219 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  220 + new OctetString("agent5"),
  221 + new OctetString("v3group"),
  222 + StorageType.nonVolatile);
  223 + //===========================================//
  224 + // agent002
  225 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  226 + new OctetString("agent002"),
  227 + new OctetString("v3group"),
  228 + StorageType.nonVolatile);
  229 + //===========================================//
  230 + // user001-auth-no-priv
  231 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  232 + new OctetString("user001"),
  233 + new OctetString("group001"),
  234 + StorageType.nonVolatile);
  235 + //===========================================//
  236 +
  237 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  238 + new OctetString("v3notify"),
  239 + new OctetString("v3group"),
  240 + StorageType.nonVolatile);
  241 +
  242 + //===========================================//
  243 + // group auth no priv
  244 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  245 + new OctetString("v3notify-auth"),
  246 + new OctetString("group001"),
  247 + StorageType.nonVolatile);
  248 + //===========================================//
  249 +
  250 +
  251 +
  252 + // my conf
  253 + vacm.addAccess(new OctetString("group001"), new OctetString("public"),
  254 + SecurityModel.SECURITY_MODEL_USM,
  255 + SecurityLevel.AUTH_NOPRIV,
  256 + MutableVACM.VACM_MATCH_EXACT,
  257 + new OctetString("fullReadView"),
  258 + new OctetString("fullWriteView"),
  259 + new OctetString("fullNotifyView"),
  260 + StorageType.nonVolatile);
  261 +
  262 + vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"),
  263 + SecurityModel.SECURITY_MODEL_ANY,
  264 + SecurityLevel.NOAUTH_NOPRIV,
  265 + MutableVACM.VACM_MATCH_EXACT,
  266 + new OctetString("fullReadView"),
  267 + new OctetString("fullWriteView"),
  268 + new OctetString("fullNotifyView"),
  269 + StorageType.nonVolatile);
  270 + vacm.addAccess(new OctetString("v3group"), new OctetString(),
  271 + SecurityModel.SECURITY_MODEL_USM,
  272 + SecurityLevel.AUTH_PRIV,
  273 + MutableVACM.VACM_MATCH_EXACT,
  274 + new OctetString("fullReadView"),
  275 + new OctetString("fullWriteView"),
  276 + new OctetString("fullNotifyView"),
  277 + StorageType.nonVolatile);
  278 + vacm.addAccess(new OctetString("v3restricted"), new OctetString(),
  279 + SecurityModel.SECURITY_MODEL_USM,
  280 + SecurityLevel.NOAUTH_NOPRIV,
  281 + MutableVACM.VACM_MATCH_EXACT,
  282 + new OctetString("restrictedReadView"),
  283 + new OctetString("restrictedWriteView"),
  284 + new OctetString("restrictedNotifyView"),
  285 + StorageType.nonVolatile);
  286 + vacm.addAccess(new OctetString("v3test"), new OctetString(),
  287 + SecurityModel.SECURITY_MODEL_USM,
  288 + SecurityLevel.AUTH_PRIV,
  289 + MutableVACM.VACM_MATCH_EXACT,
  290 + new OctetString("testReadView"),
  291 + new OctetString("testWriteView"),
  292 + new OctetString("testNotifyView"),
  293 + StorageType.nonVolatile);
  294 +
  295 + vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"),
  296 + new OctetString(), VacmMIB.vacmViewIncluded,
  297 + StorageType.nonVolatile);
  298 + vacm.addViewTreeFamily(new OctetString("fullWriteView"), new OID("1.3"),
  299 + new OctetString(), VacmMIB.vacmViewIncluded,
  300 + StorageType.nonVolatile);
  301 + vacm.addViewTreeFamily(new OctetString("fullNotifyView"), new OID("1.3"),
  302 + new OctetString(), VacmMIB.vacmViewIncluded,
  303 + StorageType.nonVolatile);
  304 +
  305 + vacm.addViewTreeFamily(new OctetString("restrictedReadView"),
  306 + new OID("1.3.6.1.2"),
  307 + new OctetString(), VacmMIB.vacmViewIncluded,
  308 + StorageType.nonVolatile);
  309 + vacm.addViewTreeFamily(new OctetString("restrictedWriteView"),
  310 + new OID("1.3.6.1.2.1"),
  311 + new OctetString(),
  312 + VacmMIB.vacmViewIncluded,
  313 + StorageType.nonVolatile);
  314 + vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
  315 + new OID("1.3.6.1.2"),
  316 + new OctetString(), VacmMIB.vacmViewIncluded,
  317 + StorageType.nonVolatile);
  318 + vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
  319 + new OID("1.3.6.1.6.3.1"),
  320 + new OctetString(), VacmMIB.vacmViewIncluded,
  321 + StorageType.nonVolatile);
  322 +
  323 + vacm.addViewTreeFamily(new OctetString("testReadView"),
  324 + new OID("1.3.6.1.2"),
  325 + new OctetString(), VacmMIB.vacmViewIncluded,
  326 + StorageType.nonVolatile);
  327 + vacm.addViewTreeFamily(new OctetString("testReadView"),
  328 + new OID("1.3.6.1.2.1.1"),
  329 + new OctetString(), VacmMIB.vacmViewExcluded,
  330 + StorageType.nonVolatile);
  331 + vacm.addViewTreeFamily(new OctetString("testWriteView"),
  332 + new OID("1.3.6.1.2.1"),
  333 + new OctetString(),
  334 + VacmMIB.vacmViewIncluded,
  335 + StorageType.nonVolatile);
  336 + vacm.addViewTreeFamily(new OctetString("testNotifyView"),
  337 + new OID("1.3.6.1.2"),
  338 + new OctetString(), VacmMIB.vacmViewIncluded,
  339 + StorageType.nonVolatile);
  340 +
  341 + }
  342 +
  343 + protected void addUsmUser(USM usm) {
  344 + UsmUser user = new UsmUser(new OctetString("SHADES"),
  345 + AuthSHA.ID,
  346 + new OctetString("SHADESAuthPassword"),
  347 + PrivDES.ID,
  348 + new OctetString("SHADESPrivPassword"));
  349 +// usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  350 + usm.addUser(user.getSecurityName(), null, user);
  351 + user = new UsmUser(new OctetString("TEST"),
  352 + AuthSHA.ID,
  353 + new OctetString("maplesyrup"),
  354 + PrivDES.ID,
  355 + new OctetString("maplesyrup"));
  356 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  357 + user = new UsmUser(new OctetString("SHA"),
  358 + AuthSHA.ID,
  359 + new OctetString("SHAAuthPassword"),
  360 + null,
  361 + null);
  362 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  363 + user = new UsmUser(new OctetString("SHADES"),
  364 + AuthSHA.ID,
  365 + new OctetString("SHADESAuthPassword"),
  366 + PrivDES.ID,
  367 + new OctetString("SHADESPrivPassword"));
  368 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  369 + user = new UsmUser(new OctetString("MD5DES"),
  370 + AuthMD5.ID,
  371 + new OctetString("MD5DESAuthPassword"),
  372 + PrivDES.ID,
  373 + new OctetString("MD5DESPrivPassword"));
  374 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  375 + user = new UsmUser(new OctetString("SHAAES128"),
  376 + AuthSHA.ID,
  377 + new OctetString("SHAAES128AuthPassword"),
  378 + PrivAES128.ID,
  379 + new OctetString("SHAAES128PrivPassword"));
  380 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  381 + user = new UsmUser(new OctetString("SHAAES192"),
  382 + AuthSHA.ID,
  383 + new OctetString("SHAAES192AuthPassword"),
  384 + PrivAES192.ID,
  385 + new OctetString("SHAAES192PrivPassword"));
  386 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  387 + user = new UsmUser(new OctetString("SHAAES256"),
  388 + AuthSHA.ID,
  389 + new OctetString("SHAAES256AuthPassword"),
  390 + PrivAES256.ID,
  391 + new OctetString("SHAAES256PrivPassword"));
  392 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  393 +
  394 + user = new UsmUser(new OctetString("MD5AES128"),
  395 + AuthMD5.ID,
  396 + new OctetString("MD5AES128AuthPassword"),
  397 + PrivAES128.ID,
  398 + new OctetString("MD5AES128PrivPassword"));
  399 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  400 + user = new UsmUser(new OctetString("MD5AES192"),
  401 + AuthHMAC192SHA256.ID,
  402 + new OctetString("MD5AES192AuthPassword"),
  403 + PrivAES192.ID,
  404 + new OctetString("MD5AES192PrivPassword"));
  405 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  406 + //==============================================================
  407 + user = new UsmUser(new OctetString("MD5AES256"),
  408 + AuthMD5.ID,
  409 + new OctetString("MD5AES256AuthPassword"),
  410 + PrivAES256.ID,
  411 + new OctetString("MD5AES256PrivPassword"));
  412 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  413 + user = new UsmUser(new OctetString("MD5AES256"),
  414 + AuthMD5.ID,
  415 + new OctetString("MD5AES256AuthPassword"),
  416 + PrivAES256.ID,
  417 + new OctetString("MD5AES256PrivPassword"));
  418 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  419 +
  420 + OctetString securityName = new OctetString("aboba");
  421 + OctetString authenticationPassphrase = new OctetString("abobaaboba");
  422 + OctetString privacyPassphrase = new OctetString("abobaaboba");
  423 + OID authenticationProtocol = AuthSHA.ID;
  424 + OID privacyProtocol = PrivDES.ID; // FIXME: to config
  425 + user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase);
  426 + usm.addUser(user);
  427 +
  428 + //===============================================================//
  429 + user = new UsmUser(new OctetString("agent5"),
  430 + AuthSHA.ID,
  431 + new OctetString("authpass"),
  432 + PrivDES.ID,
  433 + new OctetString("privpass"));
  434 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  435 + //===============================================================//
  436 + // user001
  437 + user = new UsmUser(new OctetString("user001"),
  438 + AuthSHA.ID,
  439 + new OctetString("authpass"),
  440 + null, null);
  441 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  442 + //===============================================================//
  443 + // user002
  444 + user = new UsmUser(new OctetString("user001"),
  445 + null,
  446 + null,
  447 + null, null);
  448 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  449 + //===============================================================//
  450 +
  451 + user = new UsmUser(new OctetString("v3notify"),
  452 + null,
  453 + null,
  454 + null,
  455 + null);
  456 + usm.addUser(user.getSecurityName(), null, user);
  457 +
  458 + this.usm = usm;
  459 + }
  460 +
  461 + private static DefaultMOTable createStaticIfXTable() {
  462 + MOTableSubIndex[] subIndexes =
  463 + new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
  464 + MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
  465 + MOColumn[] columns = new MOColumn[19];
  466 + int c = 0;
  467 + columns[c++] =
  468 + new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
  469 + MOAccessImpl.ACCESS_READ_ONLY); // ifName
  470 + columns[c++] =
  471 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  472 + MOAccessImpl.ACCESS_READ_ONLY); // ifInMulticastPkts
  473 + columns[c++] =
  474 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  475 + MOAccessImpl.ACCESS_READ_ONLY); // ifInBroadcastPkts
  476 + columns[c++] =
  477 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  478 + MOAccessImpl.ACCESS_READ_ONLY); // ifOutMulticastPkts
  479 + columns[c++] =
  480 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  481 + MOAccessImpl.ACCESS_READ_ONLY); // ifOutBroadcastPkts
  482 + columns[c++] =
  483 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  484 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCInOctets
  485 + columns[c++] =
  486 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  487 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCInUcastPkts
  488 + columns[c++] =
  489 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  490 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCInMulticastPkts
  491 + columns[c++] =
  492 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  493 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCInBroadcastPkts
  494 + columns[c++] =
  495 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  496 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutOctets
  497 + columns[c++] =
  498 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  499 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutUcastPkts
  500 + columns[c++] =
  501 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  502 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutMulticastPkts
  503 + columns[c++] =
  504 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  505 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutBroadcastPkts
  506 + columns[c++] =
  507 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  508 + MOAccessImpl.ACCESS_READ_WRITE); // ifLinkUpDownTrapEnable
  509 + columns[c++] =
  510 + new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
  511 + MOAccessImpl.ACCESS_READ_ONLY); // ifHighSpeed
  512 + columns[c++] =
  513 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  514 + MOAccessImpl.ACCESS_READ_WRITE); // ifPromiscuousMode
  515 + columns[c++] =
  516 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  517 + MOAccessImpl.ACCESS_READ_ONLY); // ifConnectorPresent
  518 + columns[c++] =
  519 + new MOMutableColumn(c, SMIConstants.SYNTAX_OCTET_STRING, // ifAlias
  520 + MOAccessImpl.ACCESS_READ_WRITE, null);
  521 + columns[c++] =
  522 + new MOColumn(c, SMIConstants.SYNTAX_TIMETICKS,
  523 + MOAccessImpl.ACCESS_READ_ONLY); // ifCounterDiscontinuityTime
  524 +
  525 + DefaultMOTable ifXTable =
  526 + new DefaultMOTable(new OID("1.3.6.1.2.1.31.1.1.1"), indexDef, columns);
  527 + MOMutableTableModel model = (MOMutableTableModel) ifXTable.getModel();
  528 + Variable[] rowValues1 = new Variable[] {
  529 + new OctetString("Ethernet-0"),
  530 + new Integer32(1),
  531 + new Integer32(2),
  532 + new Integer32(3),
  533 + new Integer32(4),
  534 + new Integer32(5),
  535 + new Integer32(6),
  536 + new Integer32(7),
  537 + new Integer32(8),
  538 + new Integer32(9),
  539 + new Integer32(10),
  540 + new Integer32(11),
  541 + new Integer32(12),
  542 + new Integer32(13),
  543 + new Integer32(14),
  544 + new Integer32(15),
  545 + new Integer32(16),
  546 + new OctetString("My eth"),
  547 + new TimeTicks(1000)
  548 + };
  549 + Variable[] rowValues2 = new Variable[] {
  550 + new OctetString("Loopback"),
  551 + new Integer32(21),
  552 + new Integer32(22),
  553 + new Integer32(23),
  554 + new Integer32(24),
  555 + new Integer32(25),
  556 + new Integer32(26),
  557 + new Integer32(27),
  558 + new Integer32(28),
  559 + new Integer32(29),
  560 + new Integer32(30),
  561 + new Integer32(31),
  562 + new Integer32(32),
  563 + new Integer32(33),
  564 + new Integer32(34),
  565 + new Integer32(35),
  566 + new Integer32(36),
  567 + new OctetString("My loop"),
  568 + new TimeTicks(2000)
  569 + };
  570 + model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
  571 + model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
  572 + ifXTable.setVolatile(true);
  573 + return ifXTable;
  574 + }
  575 +
  576 + private static DefaultMOTable createStaticIfTable() {
  577 + MOTableSubIndex[] subIndexes =
  578 + new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
  579 + MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
  580 + MOColumn[] columns = new MOColumn[8];
  581 + int c = 0;
  582 + columns[c++] =
  583 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  584 + MOAccessImpl.ACCESS_READ_ONLY); // ifIndex
  585 + columns[c++] =
  586 + new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
  587 + MOAccessImpl.ACCESS_READ_ONLY); // ifDescr
  588 + columns[c++] =
  589 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  590 + MOAccessImpl.ACCESS_READ_ONLY); // ifType
  591 + columns[c++] =
  592 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  593 + MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
  594 + columns[c++] =
  595 + new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
  596 + MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
  597 + columns[c++] =
  598 + new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
  599 + MOAccessImpl.ACCESS_READ_ONLY); // ifPhysAddress
  600 + columns[c++] =
  601 + new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, // ifAdminStatus
  602 + MOAccessImpl.ACCESS_READ_WRITE, null);
  603 + columns[c++] =
  604 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  605 + MOAccessImpl.ACCESS_READ_ONLY); // ifOperStatus
  606 +
  607 + DefaultMOTable ifTable =
  608 + new DefaultMOTable(new OID("1.3.6.1.2.1.2.2.1"), indexDef, columns);
  609 + MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
  610 + Variable[] rowValues1 = new Variable[] {
  611 + new Integer32(1),
  612 + new OctetString("eth0"),
  613 + new Integer32(6),
  614 + new Integer32(1500),
  615 + new Gauge32(100000000),
  616 + new OctetString("00:00:00:00:01"),
  617 + new Integer32(1),
  618 + new Integer32(1)
  619 + };
  620 + Variable[] rowValues2 = new Variable[] {
  621 + new Integer32(2),
  622 + new OctetString("loopback"),
  623 + new Integer32(24),
  624 + new Integer32(1500),
  625 + new Gauge32(10000000),
  626 + new OctetString("00:00:00:00:02"),
  627 + new Integer32(1),
  628 + new Integer32(1)
  629 + };
  630 + model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
  631 + model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
  632 + ifTable.setVolatile(true);
  633 + return ifTable;
  634 + }
  635 +
  636 + private static DefaultMOTable createStaticSnmp4sTable() {
  637 + MOTableSubIndex[] subIndexes =
  638 + new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
  639 + MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
  640 + MOColumn[] columns = new MOColumn[8];
  641 + int c = 0;
  642 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_NULL, MOAccessImpl.ACCESS_READ_ONLY); // testNull
  643 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // testBoolean
  644 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifType
  645 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
  646 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_GAUGE32, MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
  647 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING, MOAccessImpl.ACCESS_READ_ONLY); //ifPhysAddress
  648 + columns[c++] = new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_WRITE,
  649 + null);
  650 + // ifAdminStatus
  651 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY);
  652 + // ifOperStatus
  653 +
  654 + DefaultMOTable ifTable =
  655 + new DefaultMOTable(new OID("1.3.6.1.4.1.50000.1.1"), indexDef, columns);
  656 + MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
  657 + Variable[] rowValues1 = new Variable[] {
  658 + new Integer32(1),
  659 + new OctetString("eth0"),
  660 + new Integer32(6),
  661 + new Integer32(1500),
  662 + new Gauge32(100000000),
  663 + new OctetString("00:00:00:00:01"),
  664 + new Integer32(1),
  665 + new Integer32(1)
  666 + };
  667 + Variable[] rowValues2 = new Variable[] {
  668 + new Integer32(2),
  669 + new OctetString("loopback"),
  670 + new Integer32(24),
  671 + new Integer32(1500),
  672 + new Gauge32(10000000),
  673 + new OctetString("00:00:00:00:02"),
  674 + new Integer32(1),
  675 + new Integer32(1)
  676 + };
  677 + model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
  678 + model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
  679 + ifTable.setVolatile(true);
  680 + return ifTable;
  681 + }
  682 +
  683 + protected void initTransportMappings() throws IOException {
  684 + transportMappings = new TransportMapping[2];
  685 + Address addr = GenericAddress.parse(address);
  686 + TransportMapping tm =
  687 + TransportMappings.getInstance().createTransportMapping(addr);
  688 + transportMappings[0] = tm;
  689 + transportMappings[1] = new DefaultTcpTransportMapping(new TcpAddress(address));
  690 + }
  691 +
  692 + public void start(String ip, String port) throws IOException {
  693 + address = ip + "/" + port;
  694 + //BasicConfigurator.configure();
  695 + init();
  696 + addShutdownHook();
  697 +// loadConfig(ImportModes.REPLACE_CREATE);
  698 + getServer().addContext(new OctetString("public"));
  699 + finishInit();
  700 + run();
  701 + sendColdStartNotification();
  702 + }
  703 +
  704 + protected void unregisterManagedObjects() {
  705 + // here we should unregister those objects previously registered...
  706 + }
  707 +
  708 + protected void addCommunities(SnmpCommunityMIB communityMIB) {
  709 + Variable[] com2sec = new Variable[] {
  710 + new OctetString("public"), // community name
  711 + new OctetString("cpublic"), // security name
  712 + getAgent().getContextEngineID(), // local engine ID
  713 + new OctetString("public"), // default context name
  714 + new OctetString(), // transport tag
  715 + new Integer32(StorageType.nonVolatile), // storage type
  716 + new Integer32(RowStatus.active) // row status
  717 + };
  718 + MOTableRow row =
  719 + communityMIB.getSnmpCommunityEntry().createRow(
  720 + new OctetString("public2public").toSubIndex(true), com2sec);
  721 + communityMIB.getSnmpCommunityEntry().addRow((SnmpCommunityMIB.SnmpCommunityEntryRow) row);
  722 +// snmpCommunityMIB.setSourceAddressFiltering(true);
  723 + }
  724 +
  725 + protected void registerSnmpMIBs() {
  726 + heartbeatMIB = new Snmp4jHeartbeatMib(super.getNotificationOriginator(),
  727 + new OctetString(),
  728 + super.snmpv2MIB.getSysUpTime());
  729 + agentppSimulationMIB = new AgentppSimulationMib();
  730 + super.registerSnmpMIBs();
  731 + }
  732 +
  733 + protected void initMessageDispatcher() {
  734 + this.dispatcher = new MessageDispatcherImpl();
  735 + this.mpv3 = new MPv3(this.agent.getContextEngineID().getValue());
  736 + this.usm = new USM(SecurityProtocols.getInstance(), this.agent.getContextEngineID(), this.updateEngineBoots());
  737 + SecurityModels.getInstance().addSecurityModel(this.usm);
  738 + SecurityProtocols.getInstance().addDefaultProtocols();
  739 + this.dispatcher.addMessageProcessingModel(new MPv1());
  740 + this.dispatcher.addMessageProcessingModel(new MPv2c());
  741 + this.dispatcher.addMessageProcessingModel(this.mpv3);
  742 + this.initSnmpSession();
  743 + }
  744 +
  745 +}
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp;
  17 +
  18 +import java.io.IOException;
  19 +import java.util.Map;
  20 +import java.util.Scanner;
  21 +
  22 +public class SnmpTestV2 {
  23 + public static void main(String[] args) throws IOException {
  24 + SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");
  25 +
  26 + device.start();
  27 + device.setUpMappings(Map.of(
  28 + ".1.3.6.1.2.1.1.1.50", "12",
  29 + ".1.3.6.1.2.1.2.1.52", "56",
  30 + ".1.3.6.1.2.1.3.1.54", "yes",
  31 + ".1.3.6.1.2.1.7.1.58", ""
  32 + ));
  33 +
  34 +
  35 +// while (true) {
  36 +// new Scanner(System.in).nextLine();
  37 +// device.sendTrap("127.0.0.1", 1062, Map.of(".1.3.6.1.2.87.1.56", "12"));
  38 +// System.out.println("sent");
  39 +// }
  40 +
  41 +// Snmp snmp = new Snmp(device.transportMappings[0]);
  42 +// device.snmp.addCommandResponder(event -> {
  43 +// System.out.println(event);
  44 +// });
  45 +
  46 + new Scanner(System.in).nextLine();
  47 + }
  48 +
  49 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp;
  17 +
  18 +import org.snmp4j.CommandResponderEvent;
  19 +import org.snmp4j.agent.CommandProcessor;
  20 +import org.snmp4j.mp.MPv3;
  21 +import org.snmp4j.smi.OctetString;
  22 +
  23 +import java.io.IOException;
  24 +import java.util.Map;
  25 +import java.util.Scanner;
  26 +
  27 +public class SnmpTestV3 {
  28 + public static void main(String[] args) throws IOException {
  29 + SnmpDeviceSimulatorV3 device = new SnmpDeviceSimulatorV3(new CommandProcessor(new OctetString(MPv3.createLocalEngineID())) {
  30 + @Override
  31 + public void processPdu(CommandResponderEvent event) {
  32 + System.out.println("event: " + event);
  33 + }
  34 + });
  35 + device.start("0.0.0.0", "1610");
  36 +
  37 + device.setUpMappings(Map.of(
  38 + ".1.3.6.1.2.1.1.1.50", "12",
  39 + ".1.3.6.1.2.1.2.1.52", "56",
  40 + ".1.3.6.1.2.1.3.1.54", "yes",
  41 + ".1.3.6.1.2.1.7.1.58", ""
  42 + ));
  43 +
  44 + new Scanner(System.in).nextLine();
  45 + }
  46 +}
... ...
  1 +{
  2 + "timeoutMs": 500,
  3 + "retries": 0,
  4 + "communicationConfigs": [
  5 + {
  6 + "spec": "TELEMETRY_QUERYING",
  7 + "queryingFrequencyMs": 3000,
  8 + "mappings": [
  9 + {
  10 + "oid": ".1.3.6.1.2.1.1.1.50",
  11 + "key": "temperature",
  12 + "dataType": "LONG"
  13 + },
  14 + {
  15 + "oid": ".1.3.6.1.2.1.2.1.52",
  16 + "key": "humidity",
  17 + "dataType": "DOUBLE"
  18 + }
  19 + ]
  20 + },
  21 + {
  22 + "spec": "CLIENT_ATTRIBUTES_QUERYING",
  23 + "queryingFrequencyMs": 5000,
  24 + "mappings": [
  25 + {
  26 + "oid": ".1.3.6.1.2.1.3.1.54",
  27 + "key": "isCool",
  28 + "dataType": "STRING"
  29 + }
  30 + ]
  31 + },
  32 + {
  33 + "spec": "SHARED_ATTRIBUTES_SETTING",
  34 + "mappings": [
  35 + {
  36 + "oid": ".1.3.6.1.2.1.7.1.58",
  37 + "key": "shared",
  38 + "dataType": "STRING"
  39 + }
  40 + ]
  41 + }
  42 + ]
  43 +}
... ...
  1 +{
  2 + "address": "192.168.3.23",
  3 + "port": 1610,
  4 + "protocolVersion": "V3",
  5 +
  6 + "username": "tb-user",
  7 + "engineId": "qwertyuioa",
  8 + "securityName": "tb-user",
  9 + "authenticationProtocol": "SHA_512",
  10 + "authenticationPassphrase": "sdfghjkloifgh",
  11 + "privacyProtocol": "DES",
  12 + "privacyPassphrase": "rtytguijokod"
  13 +}
\ No newline at end of file
... ...
  1 +{
  2 + "address": "127.0.0.1",
  3 + "port": 1610,
  4 + "community": "public",
  5 + "protocolVersion": "V2C"
  6 +}
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.transport;
  17 +
  18 +import lombok.Getter;
  19 +import org.thingsboard.server.common.data.Device;
  20 +
  21 +@Getter
  22 +public class DeviceUpdatedEvent {
  23 + private final Device device;
  24 +
  25 + public DeviceUpdatedEvent(Device device) {
  26 + this.device = device;
  27 + }
  28 +}
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.common.transport;
17 17
18 18 import org.thingsboard.server.common.data.Device;
19 19 import org.thingsboard.server.common.data.DeviceProfile;
  20 +import org.thingsboard.server.common.data.id.DeviceId;
20 21 import org.thingsboard.server.gen.transport.TransportProtos;
21 22 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
22 23 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
... ... @@ -49,6 +50,8 @@ public interface SessionMsgListener {
49 50 default void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device,
50 51 Optional<DeviceProfile> deviceProfileOpt) {}
51 52
  53 + default void onDeviceDeleted(DeviceId deviceId) {}
  54 +
52 55 default void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) {}
53 56
54 57 default void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceUpdateMsgOpt) {}
... ...
... ... @@ -22,6 +22,10 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
22 22 import org.thingsboard.server.common.transport.service.SessionMetaData;
23 23 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
24 24 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
  25 +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsResponseMsg;
  27 +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg;
  28 +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceResponseMsg;
25 29 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
26 30 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
27 31 import org.thingsboard.server.gen.transport.TransportProtos.GetFirmwareRequestMsg;
... ... @@ -29,6 +33,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetFirmwareResponseM
29 33 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
30 34 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg;
31 35 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceResponseMsg;
  36 +import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesRequestMsg;
  37 +import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesResponseMsg;
32 38 import org.thingsboard.server.gen.transport.TransportProtos.LwM2MRequestMsg;
33 39 import org.thingsboard.server.gen.transport.TransportProtos.LwM2MResponseMsg;
34 40 import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
... ... @@ -57,6 +63,12 @@ public interface TransportService {
57 63
58 64 GetResourceResponseMsg getResource(GetResourceRequestMsg msg);
59 65
  66 + GetSnmpDevicesResponseMsg getSnmpDevicesIds(GetSnmpDevicesRequestMsg requestMsg);
  67 +
  68 + GetDeviceResponseMsg getDevice(GetDeviceRequestMsg requestMsg);
  69 +
  70 + GetDeviceCredentialsResponseMsg getDeviceCredentials(GetDeviceCredentialsRequestMsg requestMsg);
  71 +
60 72 void process(DeviceTransportType transportType, ValidateDeviceTokenRequestMsg msg,
61 73 TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);
62 74
... ...
... ... @@ -289,7 +289,7 @@ public class JsonConverter {
289 289 return result;
290 290 }
291 291
292   - public static JsonElement toJson(AttributeUpdateNotificationMsg payload) {
  292 + public static JsonObject toJson(AttributeUpdateNotificationMsg payload) {
293 293 JsonObject result = new JsonObject();
294 294 if (payload.getSharedUpdatedCount() > 0) {
295 295 payload.getSharedUpdatedList().forEach(addToObjectFromProto(result));
... ... @@ -558,6 +558,14 @@ public class JsonConverter {
558 558 }
559 559 }
560 560
  561 + public static JsonElement parse(String json) {
  562 + return JSON_PARSER.parse(json);
  563 + }
  564 +
  565 + public static String toJson(JsonElement element) {
  566 + return GSON.toJson(element);
  567 + }
  568 +
561 569 public static void setTypeCastEnabled(boolean enabled) {
562 570 isTypeCastEnabled = enabled;
563 571 }
... ... @@ -599,8 +607,7 @@ public class JsonConverter {
599 607 .build();
600 608 }
601 609
602   - private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String
603   - provisionKey, String provisionSecret) {
  610 + private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String provisionKey, String provisionSecret) {
604 611 return TransportProtos.ProvisionDeviceCredentialsMsg.newBuilder()
605 612 .setProvisionDeviceKey(provisionKey)
606 613 .setProvisionDeviceSecret(provisionSecret)
... ...
... ... @@ -112,8 +112,8 @@ public class DefaultTransportDeviceProfileCache implements TransportDeviceProfil
112 112 profile = profileOpt.get();
113 113 this.put(profile);
114 114 } else {
115   - log.warn("[{}] Can't device profile: {}", id, entityProfileMsg.getData());
116   - throw new RuntimeException("Can't device profile!");
  115 + log.warn("[{}] Can't find device profile: {}", id, entityProfileMsg.getData());
  116 + throw new RuntimeException("Can't find device profile!");
117 117 }
118 118 } finally {
119 119 deviceProfileFetchLock.unlock();
... ...
... ... @@ -23,6 +23,7 @@ import com.google.gson.JsonObject;
23 23 import com.google.protobuf.ByteString;
24 24 import lombok.extern.slf4j.Slf4j;
25 25 import org.springframework.beans.factory.annotation.Value;
  26 +import org.springframework.context.ApplicationEventPublisher;
26 27 import org.springframework.stereotype.Service;
27 28 import org.thingsboard.common.util.ThingsBoardThreadFactory;
28 29 import org.thingsboard.server.common.data.ApiUsageRecordKey;
... ... @@ -50,6 +51,7 @@ import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
50 51 import org.thingsboard.server.common.stats.MessagesStats;
51 52 import org.thingsboard.server.common.stats.StatsFactory;
52 53 import org.thingsboard.server.common.stats.StatsType;
  54 +import org.thingsboard.server.common.transport.DeviceUpdatedEvent;
53 55 import org.thingsboard.server.common.transport.SessionMsgListener;
54 56 import org.thingsboard.server.common.transport.TransportDeviceProfileCache;
55 57 import org.thingsboard.server.common.transport.TransportResourceCache;
... ... @@ -134,6 +136,7 @@ public class DefaultTransportService implements TransportService {
134 136 private final TransportRateLimitService rateLimitService;
135 137 private final DataDecodingEncodingService dataDecodingEncodingService;
136 138 private final SchedulerComponent scheduler;
  139 + private final ApplicationEventPublisher eventPublisher;
137 140 private final TransportResourceCache transportResourceCache;
138 141
139 142 protected TbQueueRequestTemplate<TbProtoQueueMsg<TransportApiRequestMsg>, TbProtoQueueMsg<TransportApiResponseMsg>> transportApiRequestTemplate;
... ... @@ -161,7 +164,8 @@ public class DefaultTransportService implements TransportService {
161 164 TransportDeviceProfileCache deviceProfileCache,
162 165 TransportTenantProfileCache tenantProfileCache,
163 166 TbApiUsageClient apiUsageClient, TransportRateLimitService rateLimitService,
164   - DataDecodingEncodingService dataDecodingEncodingService, SchedulerComponent scheduler, TransportResourceCache transportResourceCache) {
  167 + DataDecodingEncodingService dataDecodingEncodingService, SchedulerComponent scheduler, TransportResourceCache transportResourceCache,
  168 + ApplicationEventPublisher eventPublisher) {
165 169 this.serviceInfoProvider = serviceInfoProvider;
166 170 this.queueProvider = queueProvider;
167 171 this.producerProvider = producerProvider;
... ... @@ -174,6 +178,7 @@ public class DefaultTransportService implements TransportService {
174 178 this.dataDecodingEncodingService = dataDecodingEncodingService;
175 179 this.scheduler = scheduler;
176 180 this.transportResourceCache = transportResourceCache;
  181 + this.eventPublisher = eventPublisher;
177 182 }
178 183
179 184 @PostConstruct
... ... @@ -272,6 +277,58 @@ public class DefaultTransportService implements TransportService {
272 277 }
273 278
274 279 @Override
  280 + public TransportProtos.GetSnmpDevicesResponseMsg getSnmpDevicesIds(TransportProtos.GetSnmpDevicesRequestMsg requestMsg) {
  281 + TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(
  282 + UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder()
  283 + .setSnmpDevicesRequestMsg(requestMsg)
  284 + .build()
  285 + );
  286 +
  287 + try {
  288 + TbProtoQueueMsg<TransportApiResponseMsg> response = transportApiRequestTemplate.send(protoMsg).get();
  289 + return response.getValue().getSnmpDevicesResponseMsg();
  290 + } catch (InterruptedException | ExecutionException e) {
  291 + throw new RuntimeException(e);
  292 + }
  293 + }
  294 +
  295 + @Override
  296 + public TransportProtos.GetDeviceResponseMsg getDevice(TransportProtos.GetDeviceRequestMsg requestMsg) {
  297 + TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(
  298 + UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder()
  299 + .setDeviceRequestMsg(requestMsg)
  300 + .build()
  301 + );
  302 +
  303 + try {
  304 + TransportApiResponseMsg response = transportApiRequestTemplate.send(protoMsg).get().getValue();
  305 + if (response.hasDeviceResponseMsg()) {
  306 + return response.getDeviceResponseMsg();
  307 + } else {
  308 + return null;
  309 + }
  310 + } catch (InterruptedException | ExecutionException e) {
  311 + throw new RuntimeException(e);
  312 + }
  313 + }
  314 +
  315 + @Override
  316 + public TransportProtos.GetDeviceCredentialsResponseMsg getDeviceCredentials(TransportProtos.GetDeviceCredentialsRequestMsg requestMsg) {
  317 + TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(
  318 + UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder()
  319 + .setDeviceCredentialsRequestMsg(requestMsg)
  320 + .build()
  321 + );
  322 +
  323 + try {
  324 + TbProtoQueueMsg<TransportApiResponseMsg> response = transportApiRequestTemplate.send(protoMsg).get();
  325 + return response.getValue().getDeviceCredentialsResponseMsg();
  326 + } catch (InterruptedException | ExecutionException e) {
  327 + throw new RuntimeException(e);
  328 + }
  329 + }
  330 +
  331 + @Override
275 332 public void process(DeviceTransportType transportType, TransportProtos.ValidateDeviceTokenRequestMsg msg,
276 333 TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
277 334 log.trace("Processing msg: {}", msg);
... ... @@ -704,7 +761,10 @@ public class DefaultTransportService implements TransportService {
704 761 }
705 762 } else if (EntityType.DEVICE.equals(entityType)) {
706 763 Optional<Device> deviceOpt = dataDecodingEncodingService.decode(msg.getData().toByteArray());
707   - deviceOpt.ifPresent(this::onDeviceUpdate);
  764 + deviceOpt.ifPresent(device -> {
  765 + onDeviceUpdate(device);
  766 + eventPublisher.publishEvent(new DeviceUpdatedEvent(device));
  767 + });
708 768 }
709 769 } else if (toSessionMsg.hasEntityDeleteMsg()) {
710 770 TransportProtos.EntityDeleteMsg msg = toSessionMsg.getEntityDeleteMsg();
... ... @@ -718,6 +778,7 @@ public class DefaultTransportService implements TransportService {
718 778 rateLimitService.remove(new TenantId(entityUuid));
719 779 } else if (EntityType.DEVICE.equals(entityType)) {
720 780 rateLimitService.remove(new DeviceId(entityUuid));
  781 + onDeviceDeleted(new DeviceId(entityUuid));
721 782 }
722 783 } else if (toSessionMsg.hasResourceUpdateMsg()) {
723 784 TransportProtos.ResourceUpdateMsg msg = toSessionMsg.getResourceUpdateMsg();
... ... @@ -800,6 +861,17 @@ public class DefaultTransportService implements TransportService {
800 861 });
801 862 }
802 863
  864 + private void onDeviceDeleted(DeviceId deviceId) {
  865 + sessions.forEach((id, md) -> {
  866 + DeviceId sessionDeviceId = new DeviceId(new UUID(md.getSessionInfo().getDeviceIdMSB(), md.getSessionInfo().getDeviceIdLSB()));
  867 + if (sessionDeviceId.equals(deviceId)) {
  868 + transportCallbackExecutor.submit(() -> {
  869 + md.getListener().onDeviceDeleted(deviceId);
  870 + });
  871 + }
  872 + });
  873 + }
  874 +
803 875 protected UUID toSessionId(TransportProtos.SessionInfoProto sessionInfo) {
804 876 return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
805 877 }
... ...
... ... @@ -18,11 +18,11 @@ package org.thingsboard.server.dao.device;
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.thingsboard.server.common.data.Device;
20 20 import org.thingsboard.server.common.data.DeviceInfo;
  21 +import org.thingsboard.server.common.data.DeviceTransportType;
21 22 import org.thingsboard.server.common.data.EntitySubtype;
22 23 import org.thingsboard.server.common.data.id.TenantId;
23 24 import org.thingsboard.server.common.data.page.PageData;
24 25 import org.thingsboard.server.common.data.page.PageLink;
25   -import org.thingsboard.server.common.data.page.TimePageLink;
26 26 import org.thingsboard.server.dao.Dao;
27 27 import org.thingsboard.server.dao.TenantEntityDao;
28 28
... ... @@ -219,6 +219,7 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao {
219 219 */
220 220 PageData<Device> findDevicesByTenantIdAndProfileId(UUID tenantId, UUID profileId, PageLink pageLink);
221 221
  222 + PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink);
222 223
223 224 /**
224 225 * Find devices by tenantId, edgeId and page link.
... ...
... ... @@ -363,6 +363,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
363 363 }
364 364
365 365 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  366 + transportConfiguration.validate();
366 367 if (transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) {
367 368 MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
368 369 if (mqttTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) {
... ...
... ... @@ -36,20 +36,22 @@ import org.thingsboard.server.common.data.Customer;
36 36 import org.thingsboard.server.common.data.Device;
37 37 import org.thingsboard.server.common.data.DeviceInfo;
38 38 import org.thingsboard.server.common.data.DeviceProfile;
  39 +import org.thingsboard.server.common.data.DeviceTransportType;
39 40 import org.thingsboard.server.common.data.EntitySubtype;
40 41 import org.thingsboard.server.common.data.EntityType;
41 42 import org.thingsboard.server.common.data.EntityView;
42 43 import org.thingsboard.server.common.data.Firmware;
43 44 import org.thingsboard.server.common.data.Tenant;
44   -import org.thingsboard.server.common.data.asset.Asset;
45 45 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
46 46 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
47 47 import org.thingsboard.server.common.data.device.data.CoapDeviceTransportConfiguration;
48 48 import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration;
49 49 import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration;
50 50 import org.thingsboard.server.common.data.device.data.DeviceData;
  51 +import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
51 52 import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
52 53 import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
  54 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
53 55 import org.thingsboard.server.common.data.edge.Edge;
54 56 import org.thingsboard.server.common.data.id.CustomerId;
55 57 import org.thingsboard.server.common.data.id.DeviceId;
... ... @@ -59,7 +61,6 @@ import org.thingsboard.server.common.data.id.EntityId;
59 61 import org.thingsboard.server.common.data.id.TenantId;
60 62 import org.thingsboard.server.common.data.page.PageData;
61 63 import org.thingsboard.server.common.data.page.PageLink;
62   -import org.thingsboard.server.common.data.page.TimePageLink;
63 64 import org.thingsboard.server.common.data.relation.EntityRelation;
64 65 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
65 66 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
... ... @@ -86,6 +87,7 @@ import java.util.Collections;
86 87 import java.util.Comparator;
87 88 import java.util.List;
88 89 import java.util.Optional;
  90 +import java.util.UUID;
89 91 import java.util.concurrent.ExecutionException;
90 92 import java.util.stream.Collectors;
91 93
... ... @@ -269,11 +271,14 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
269 271 case MQTT:
270 272 deviceData.setTransportConfiguration(new MqttDeviceTransportConfiguration());
271 273 break;
  274 + case COAP:
  275 + deviceData.setTransportConfiguration(new CoapDeviceTransportConfiguration());
  276 + break;
272 277 case LWM2M:
273 278 deviceData.setTransportConfiguration(new Lwm2mDeviceTransportConfiguration());
274 279 break;
275   - case COAP:
276   - deviceData.setTransportConfiguration(new CoapDeviceTransportConfiguration());
  280 + case SNMP:
  281 + deviceData.setTransportConfiguration(new SnmpDeviceTransportConfiguration());
277 282 break;
278 283 }
279 284 }
... ... @@ -573,6 +578,11 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
573 578 }
574 579
575 580 @Override
  581 + public PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink) {
  582 + return deviceDao.findDevicesIdsByDeviceProfileTransportType(transportType, pageLink);
  583 + }
  584 +
  585 + @Override
576 586 public Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId) {
577 587 Device device = findDeviceById(tenantId, deviceId);
578 588 Edge edge = edgeService.findEdgeById(tenantId, edgeId);
... ... @@ -677,6 +687,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
677 687 throw new DataValidationException("Can't assign device to customer from different tenant!");
678 688 }
679 689 }
  690 + Optional.ofNullable(device.getDeviceData())
  691 + .flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration()))
  692 + .ifPresent(DeviceTransportConfiguration::validate);
680 693
681 694 if (device.getFirmwareId() != null) {
682 695 Firmware firmware = firmwareService.findFirmwareById(tenantId, device.getFirmwareId());
... ...
... ... @@ -20,6 +20,7 @@ import org.springframework.data.domain.Pageable;
20 20 import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
  23 +import org.thingsboard.server.common.data.DeviceTransportType;
23 24 import org.thingsboard.server.dao.model.sql.DeviceEntity;
24 25 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity;
25 26
... ... @@ -210,4 +211,9 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
210 211 * */
211 212 @Query("SELECT count(*) FROM DeviceEntity d WHERE d.tenantId = :tenantId")
212 213 Long countByTenantId(@Param("tenantId") UUID tenantId);
  214 +
  215 + @Query("SELECT d.id FROM DeviceEntity d " +
  216 + "INNER JOIN DeviceProfileEntity p ON d.deviceProfileId = p.id " +
  217 + "WHERE p.transportType = :transportType")
  218 + Page<UUID> findIdsByDeviceProfileTransportType(@Param("transportType") DeviceTransportType transportType, Pageable pageable);
213 219 }
... ...
... ... @@ -23,12 +23,12 @@ import org.springframework.stereotype.Component;
23 23 import org.springframework.util.StringUtils;
24 24 import org.thingsboard.server.common.data.Device;
25 25 import org.thingsboard.server.common.data.DeviceInfo;
  26 +import org.thingsboard.server.common.data.DeviceTransportType;
26 27 import org.thingsboard.server.common.data.EntitySubtype;
27 28 import org.thingsboard.server.common.data.EntityType;
28 29 import org.thingsboard.server.common.data.id.TenantId;
29 30 import org.thingsboard.server.common.data.page.PageData;
30 31 import org.thingsboard.server.common.data.page.PageLink;
31   -import org.thingsboard.server.common.data.page.TimePageLink;
32 32 import org.thingsboard.server.dao.DaoUtil;
33 33 import org.thingsboard.server.dao.device.DeviceDao;
34 34 import org.thingsboard.server.dao.model.sql.DeviceEntity;
... ... @@ -118,6 +118,11 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
118 118 }
119 119
120 120 @Override
  121 + public PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink) {
  122 + return DaoUtil.pageToPageData(deviceRepository.findIdsByDeviceProfileTransportType(transportType, DaoUtil.toPageable(pageLink)));
  123 + }
  124 +
  125 + @Override
121 126 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndCustomerId(UUID tenantId, UUID customerId, PageLink pageLink) {
122 127 return DaoUtil.toPageData(
123 128 deviceRepository.findDeviceInfosByTenantIdAndCustomerId(
... ...
... ... @@ -9,6 +9,7 @@ MQTT_TRANSPORT_DOCKER_NAME=tb-mqtt-transport
9 9 HTTP_TRANSPORT_DOCKER_NAME=tb-http-transport
10 10 COAP_TRANSPORT_DOCKER_NAME=tb-coap-transport
11 11 LWM2M_TRANSPORT_DOCKER_NAME=tb-lwm2m-transport
  12 +SNMP_TRANSPORT_DOCKER_NAME=tb-snmp-transport
12 13
13 14 TB_VERSION=latest
14 15
... ...
... ... @@ -74,3 +74,8 @@ services:
74 74 - queue-aws-sqs.env
75 75 depends_on:
76 76 - zookeeper
  77 + tb-snmp-transport:
  78 + env_file:
  79 + - queue-aws-sqs.env
  80 + depends_on:
  81 + - zookeeper
... ...
... ... @@ -58,3 +58,6 @@ services:
58 58 tb-lwm2m-transport:
59 59 env_file:
60 60 - queue-confluent.env
  61 + tb-snmp-transport:
  62 + env_file:
  63 + - queue-confluent.env
... ...
... ... @@ -85,3 +85,8 @@ services:
85 85 - queue-kafka.env
86 86 depends_on:
87 87 - kafka
  88 + tb-snmp-transport:
  89 + env_file:
  90 + - queue-kafka.env
  91 + depends_on:
  92 + - kafka
... ...
... ... @@ -50,6 +50,9 @@ services:
50 50 tb-mqtt-transport2:
51 51 volumes:
52 52 - tb-mqtt-transport-log-volume:/var/log/tb-mqtt-transport
  53 + tb-snmp-transport:
  54 + volumes:
  55 + - tb-snmp-transport-log-volume:/var/log/tb-snmp-transport
53 56
54 57 volumes:
55 58 postgres-db-volume:
... ... @@ -70,3 +73,6 @@ volumes:
70 73 tb-mqtt-transport-log-volume:
71 74 external: true
72 75 name: ${TB_MQTT_TRANSPORT_LOG_VOLUME}
  76 + tb-snmp-transport-log-volume:
  77 + external: true
  78 + name: ${TB_SNMP_TRANSPORT_LOG_VOLUME}
... ...
... ... @@ -74,3 +74,8 @@ services:
74 74 - queue-pubsub.env
75 75 depends_on:
76 76 - zookeeper
  77 + tb-snmp-transport:
  78 + env_file:
  79 + - queue-pubsub.env
  80 + depends_on:
  81 + - zookeeper
... ...
... ... @@ -74,3 +74,8 @@ services:
74 74 - queue-rabbitmq.env
75 75 depends_on:
76 76 - zookeeper
  77 + tb-snmp-transport:
  78 + env_file:
  79 + - queue-rabbitmq.env
  80 + depends_on:
  81 + - zookeeper
... ...
... ... @@ -72,3 +72,8 @@ services:
72 72 - queue-service-bus.env
73 73 depends_on:
74 74 - zookeeper
  75 + tb-snmp-transport:
  76 + env_file:
  77 + - queue-service-bus.env
  78 + depends_on:
  79 + - zookeeper
... ...
... ... @@ -217,6 +217,18 @@ services:
217 217 - ./tb-transports/lwm2m/log:/var/log/tb-lwm2m-transport
218 218 depends_on:
219 219 - zookeeper
  220 + tb-snmp-transport:
  221 + restart: always
  222 + image: "${DOCKER_REPO}/${SNMP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
  223 + environment:
  224 + TB_SERVICE_ID: tb-snmp-transport
  225 + env_file:
  226 + - tb-snmp-transport.env
  227 + volumes:
  228 + - ./tb-transports/snmp/conf:/config
  229 + - ./tb-transports/snmp/log:/var/log/tb-snmp-transport
  230 + depends_on:
  231 + - zookeeper
220 232 tb-web-ui1:
221 233 restart: always
222 234 image: "${DOCKER_REPO}/${WEB_UI_DOCKER_NAME}:${TB_VERSION}"
... ...
... ... @@ -24,3 +24,5 @@ mkdir -p tb-transports/lwm2m/log && sudo chown -R 799:799 tb-transports/lwm2m/lo
24 24 mkdir -p tb-transports/http/log && sudo chown -R 799:799 tb-transports/http/log
25 25
26 26 mkdir -p tb-transports/mqtt/log && sudo chown -R 799:799 tb-transports/mqtt/log
  27 +
  28 +mkdir -p tb-transports/snmp/log && sudo chown -R 799:799 tb-transports/snmp/log
... ...
  1 +ZOOKEEPER_ENABLED=true
  2 +ZOOKEEPER_URL=zookeeper:2181
... ...