223 changed files with 10783 additions and 1597 deletions
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.coapserver; |
|||
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
|||
|
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
|
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") |
|||
public @interface TbCoapServerComponent { |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data; |
|||
|
|||
public interface TbTransportService { |
|||
String getName(); |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.device.data; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import lombok.Data; |
|||
import lombok.ToString; |
|||
import org.apache.commons.lang3.ObjectUtils; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.thingsboard.server.common.data.DeviceTransportType; |
|||
import org.thingsboard.server.common.data.transport.snmp.AuthenticationProtocol; |
|||
import org.thingsboard.server.common.data.transport.snmp.PrivacyProtocol; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion; |
|||
|
|||
import java.util.Objects; |
|||
|
|||
@Data |
|||
@ToString(of = {"host", "port", "protocolVersion"}) |
|||
public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration { |
|||
private String host; |
|||
private Integer port; |
|||
private SnmpProtocolVersion protocolVersion; |
|||
|
|||
/* |
|||
* For SNMP v1 and v2c |
|||
* */ |
|||
private String community; |
|||
|
|||
/* |
|||
* For SNMP v3 |
|||
* */ |
|||
private String username; |
|||
private String securityName; |
|||
private String contextName; |
|||
private AuthenticationProtocol authenticationProtocol; |
|||
private String authenticationPassphrase; |
|||
private PrivacyProtocol privacyProtocol; |
|||
private String privacyPassphrase; |
|||
private String engineId; |
|||
|
|||
@Override |
|||
public DeviceTransportType getType() { |
|||
return DeviceTransportType.SNMP; |
|||
} |
|||
|
|||
@Override |
|||
public void validate() { |
|||
if (!isValid()) { |
|||
throw new IllegalArgumentException("Transport configuration is not valid"); |
|||
} |
|||
} |
|||
|
|||
@JsonIgnore |
|||
private boolean isValid() { |
|||
boolean isValid = StringUtils.isNotBlank(host) && port != null && protocolVersion != null; |
|||
if (isValid) { |
|||
switch (protocolVersion) { |
|||
case V1: |
|||
case V2C: |
|||
isValid = StringUtils.isNotEmpty(community); |
|||
break; |
|||
case V3: |
|||
isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName) |
|||
&& contextName != null && authenticationProtocol != null |
|||
&& StringUtils.isNotBlank(authenticationPassphrase) |
|||
&& privacyProtocol != null && privacyPassphrase != null && engineId != null; |
|||
break; |
|||
} |
|||
} |
|||
return isValid; |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.device.profile; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.DeviceTransportType; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { |
|||
private Integer timeoutMs; |
|||
private Integer retries; |
|||
private List<SnmpCommunicationConfig> communicationConfigs; |
|||
|
|||
@Override |
|||
public DeviceTransportType getType() { |
|||
return DeviceTransportType.SNMP; |
|||
} |
|||
|
|||
@Override |
|||
public void validate() { |
|||
if (!isValid()) { |
|||
throw new IllegalArgumentException("SNMP transport configuration is not valid"); |
|||
} |
|||
} |
|||
|
|||
@JsonIgnore |
|||
private boolean isValid() { |
|||
return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0 |
|||
&& communicationConfigs != null |
|||
&& communicationConfigs.stream().allMatch(config -> config != null && config.isValid()) |
|||
&& communicationConfigs.stream().flatMap(config -> config.getAllMappings().stream()).map(SnmpMapping::getOid) |
|||
.distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getAllMappings().size()).sum(); |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.Optional; |
|||
|
|||
public enum AuthenticationProtocol { |
|||
SHA_1("1.3.6.1.6.3.10.1.1.3"), |
|||
SHA_224("1.3.6.1.6.3.10.1.1.4"), |
|||
SHA_256("1.3.6.1.6.3.10.1.1.5"), |
|||
SHA_384("1.3.6.1.6.3.10.1.1.6"), |
|||
SHA_512("1.3.6.1.6.3.10.1.1.7"), |
|||
MD5("1.3.6.1.6.3.10.1.1.2"); |
|||
|
|||
// oids taken from org.snmp4j.security.SecurityProtocol implementations
|
|||
private final String oid; |
|||
|
|||
AuthenticationProtocol(String oid) { |
|||
this.oid = oid; |
|||
} |
|||
|
|||
public String getOid() { |
|||
return oid; |
|||
} |
|||
|
|||
public static Optional<AuthenticationProtocol> forName(String name) { |
|||
return Arrays.stream(values()) |
|||
.filter(protocol -> protocol.name().equalsIgnoreCase(name)) |
|||
.findFirst(); |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.Optional; |
|||
|
|||
public enum PrivacyProtocol { |
|||
DES("1.3.6.1.6.3.10.1.2.2"), |
|||
AES_128("1.3.6.1.6.3.10.1.2.4"), |
|||
AES_192("1.3.6.1.4.1.4976.2.2.1.1.1"), |
|||
AES_256("1.3.6.1.4.1.4976.2.2.1.1.2"); |
|||
|
|||
// oids taken from org.snmp4j.security.SecurityProtocol implementations
|
|||
private final String oid; |
|||
|
|||
PrivacyProtocol(String oid) { |
|||
this.oid = oid; |
|||
} |
|||
|
|||
public String getOid() { |
|||
return oid; |
|||
} |
|||
|
|||
public static Optional<PrivacyProtocol> forName(String name) { |
|||
return Arrays.stream(values()) |
|||
.filter(protocol -> protocol.name().equalsIgnoreCase(name)) |
|||
.findFirst(); |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp; |
|||
|
|||
public enum SnmpCommunicationSpec { |
|||
TELEMETRY_QUERYING, |
|||
|
|||
CLIENT_ATTRIBUTES_QUERYING, |
|||
SHARED_ATTRIBUTES_SETTING, |
|||
|
|||
TO_DEVICE_RPC_REQUEST, |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.thingsboard.server.common.data.kv.DataType; |
|||
|
|||
import java.util.regex.Pattern; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
public class SnmpMapping { |
|||
private String oid; |
|||
private String key; |
|||
private DataType dataType; |
|||
|
|||
private static final Pattern OID_PATTERN = Pattern.compile("^\\.?([0-2])((\\.0)|(\\.[1-9][0-9]*))*$"); |
|||
|
|||
@JsonIgnore |
|||
public boolean isValid() { |
|||
return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && StringUtils.isNotBlank(key); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp; |
|||
|
|||
public enum SnmpMethod { |
|||
GET(-96), |
|||
SET(-93); |
|||
|
|||
// codes taken from org.snmp4j.PDU class
|
|||
private final int code; |
|||
|
|||
SnmpMethod(int code) { |
|||
this.code = code; |
|||
} |
|||
|
|||
public int getCode() { |
|||
return code; |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp; |
|||
|
|||
public enum SnmpProtocolVersion { |
|||
V1(0), |
|||
V2C(1), |
|||
V3(3); |
|||
|
|||
private final int code; |
|||
|
|||
SnmpProtocolVersion(int code) { |
|||
this.code = code; |
|||
} |
|||
|
|||
public int getCode() { |
|||
return code; |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp.config; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public abstract class MultipleMappingsSnmpCommunicationConfig implements SnmpCommunicationConfig { |
|||
protected List<SnmpMapping> mappings; |
|||
|
|||
@Override |
|||
public boolean isValid() { |
|||
return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid()); |
|||
} |
|||
|
|||
@Override |
|||
public List<SnmpMapping> getAllMappings() { |
|||
return mappings; |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp.config; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Data |
|||
public abstract class RepeatingQueryingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig { |
|||
private Long queryingFrequencyMs; |
|||
|
|||
@Override |
|||
public SnmpMethod getMethod() { |
|||
return SnmpMethod.GET; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isValid() { |
|||
return queryingFrequencyMs != null && queryingFrequencyMs > 0 && super.isValid(); |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp.config; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes.Type; |
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.impl.ClientAttributesQueryingSnmpCommunicationConfig; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.impl.SharedAttributesSettingSnmpCommunicationConfig; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.impl.ToDeviceRpcRequestSnmpCommunicationConfig; |
|||
|
|||
import java.util.List; |
|||
|
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "spec") |
|||
@JsonSubTypes({ |
|||
@Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"), |
|||
@Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"), |
|||
@Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING"), |
|||
@Type(value = ToDeviceRpcRequestSnmpCommunicationConfig.class, name = "TO_DEVICE_RPC_REQUEST") |
|||
}) |
|||
public interface SnmpCommunicationConfig { |
|||
|
|||
SnmpCommunicationSpec getSpec(); |
|||
|
|||
@JsonIgnore |
|||
default SnmpMethod getMethod() { |
|||
return null; |
|||
} |
|||
|
|||
@JsonIgnore |
|||
List<SnmpMapping> getAllMappings(); |
|||
|
|||
@JsonIgnore |
|||
boolean isValid(); |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp.config.impl; |
|||
|
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig; |
|||
|
|||
public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig { |
|||
|
|||
@Override |
|||
public SnmpCommunicationSpec getSpec() { |
|||
return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp.config.impl; |
|||
|
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig; |
|||
|
|||
public class SharedAttributesSettingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig { |
|||
|
|||
@Override |
|||
public SnmpCommunicationSpec getSpec() { |
|||
return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING; |
|||
} |
|||
|
|||
@Override |
|||
public SnmpMethod getMethod() { |
|||
return SnmpMethod.SET; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp.config.impl; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Data |
|||
public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig { |
|||
|
|||
@Override |
|||
public SnmpCommunicationSpec getSpec() { |
|||
return SnmpCommunicationSpec.TELEMETRY_QUERYING; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.transport.snmp.config.impl; |
|||
|
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig; |
|||
|
|||
public class ToDeviceRpcRequestSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig { |
|||
@Override |
|||
public SnmpCommunicationSpec getSpec() { |
|||
return SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST; |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.queue.discovery.event; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Getter |
|||
@ToString |
|||
public class ServiceListChangedEvent extends TbApplicationEvent { |
|||
private final List<ServiceInfo> otherServices; |
|||
private final ServiceInfo currentService; |
|||
|
|||
public ServiceListChangedEvent(List<ServiceInfo> otherServices, ServiceInfo currentService) { |
|||
super(otherServices); |
|||
this.otherServices = otherServices; |
|||
this.currentService = currentService; |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.queue.util; |
|||
|
|||
import org.springframework.context.event.ContextRefreshedEvent; |
|||
import org.springframework.context.event.EventListener; |
|||
import org.springframework.core.annotation.AliasFor; |
|||
import org.springframework.core.annotation.Order; |
|||
|
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Target(ElementType.METHOD) |
|||
@EventListener(ContextRefreshedEvent.class) |
|||
@Order |
|||
public @interface AfterContextReady { |
|||
@AliasFor(annotation = Order.class, attribute = "value") |
|||
int order() default Integer.MAX_VALUE; |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.queue.util; |
|||
|
|||
import org.springframework.boot.context.event.ApplicationReadyEvent; |
|||
import org.springframework.context.event.EventListener; |
|||
import org.springframework.core.annotation.AliasFor; |
|||
import org.springframework.core.annotation.Order; |
|||
|
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Target(ElementType.METHOD) |
|||
@EventListener(ApplicationReadyEvent.class) |
|||
@Order |
|||
public @interface AfterStartUp { |
|||
@AliasFor(annotation = Order.class, attribute = "value") |
|||
int order() default Integer.MAX_VALUE; |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.queue.util; |
|||
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
|||
|
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')") |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Target({ElementType.TYPE, ElementType.METHOD}) |
|||
public @interface TbSnmpTransportComponent { |
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.server; |
|||
|
|||
import org.eclipse.californium.core.network.config.NetworkConfig; |
|||
|
|||
public class LwM2mNetworkConfig { |
|||
|
|||
public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) { |
|||
NetworkConfig coapConfig = new NetworkConfig(); |
|||
coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec); |
|||
coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort); |
|||
/** |
|||
* Example:Property for large packet: |
|||
* #NetworkConfig config = new NetworkConfig(); |
|||
* #config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32); |
|||
* #config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32); |
|||
* #config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048); |
|||
* #config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3); |
|||
* #config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000); |
|||
*/ |
|||
|
|||
/** |
|||
* Property to indicate if the response should always include the Block2 option \ |
|||
* when client request early blockwise negociation but the response can be sent on one packet. |
|||
* - value of false indicate that the server will respond without block2 option if no further blocks are required. |
|||
* - value of true indicate that the server will response with block2 option event if no further blocks are required. |
|||
* CoAP client will try to use block mode |
|||
* or adapt the block size when receiving a 4.13 Entity too large response code |
|||
*/ |
|||
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); |
|||
/*** |
|||
* Property to indicate if the response should always include the Block2 option \ |
|||
* when client request early blockwise negociation but the response can be sent on one packet. |
|||
* - value of false indicate that the server will respond without block2 option if no further blocks are required. |
|||
* - value of true indicate that the server will response with block2 option event if no further blocks are required. |
|||
*/ |
|||
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); |
|||
|
|||
coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000); |
|||
/** |
|||
* !!! REQUEST_ENTITY_TOO_LARGE CODE=4.13 |
|||
* The maximum size of a resource body (in bytes) that will be accepted |
|||
* as the payload of a POST/PUT or the response to a GET request in a |
|||
* transparent> blockwise transfer. |
|||
* This option serves as a safeguard against excessive memory |
|||
* consumption when many resources contain large bodies that cannot be |
|||
* transferred in a single CoAP message. This option has no impact on |
|||
* *manually* managed blockwise transfers in which the blocks are handled individually. |
|||
* Note that this option does not prevent local clients or resource |
|||
* implementations from sending large bodies as part of a request or response to a peer. |
|||
* The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192 |
|||
* A value of {@code 0} turns off transparent handling of blockwise transfers altogether. |
|||
*/ |
|||
// coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 8192);
|
|||
coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 16384); |
|||
/** |
|||
* The default DTLS response matcher. |
|||
* Supported values are STRICT, RELAXED, or PRINCIPAL. |
|||
* The default value is STRICT. |
|||
* Create new instance of udp endpoint context matcher. |
|||
* Params: |
|||
* checkAddress |
|||
* – true with address check, (STRICT, UDP) |
|||
* - false, without |
|||
*/ |
|||
coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT"); |
|||
/** |
|||
* https://tools.ietf.org/html/rfc7959#section-2.9.3
|
|||
* The block size (number of bytes) to use when doing a blockwise transfer. \ |
|||
* This value serves as the upper limit for block size in blockwise transfers |
|||
*/ |
|||
coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 512); |
|||
/** |
|||
* The maximum payload size (in bytes) that can be transferred in a |
|||
* single message, i.e. without requiring a blockwise transfer. |
|||
* NB: this value MUST be adapted to the maximum message size supported by the transport layer. |
|||
* In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol |
|||
* DEFAULT_VALUE = 1024 |
|||
*/ |
|||
coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 512); |
|||
|
|||
coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); |
|||
|
|||
return coapConfig; |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.server.client; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
public class LwM2mFirmwareUpdate { |
|||
private volatile String clientFwVersion; |
|||
private volatile String currentFwVersion; |
|||
private volatile UUID currentFwId; |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2021 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<parent> |
|||
<groupId>org.thingsboard.common</groupId> |
|||
<version>3.3.0-SNAPSHOT</version> |
|||
<artifactId>transport</artifactId> |
|||
</parent> |
|||
|
|||
<groupId>org.thingsboard.common.transport</groupId> |
|||
<artifactId>snmp</artifactId> |
|||
<packaging>jar</packaging> |
|||
|
|||
<name>Thingsboard SNMP Transport Common</name> |
|||
<url>https://thingsboard.io</url> |
|||
|
|||
<properties> |
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
|||
<main.dir>${basedir}/../../..</main.dir> |
|||
</properties> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.thingsboard.common.transport</groupId> |
|||
<artifactId>transport-api</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-context-support</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-context</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.slf4j</groupId> |
|||
<artifactId>slf4j-api</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.snmp4j</groupId> |
|||
<artifactId>snmp4j</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.snmp4j</groupId> |
|||
<artifactId>snmp4j-agent</artifactId> |
|||
<version>3.3.6</version> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
</dependencies> |
|||
</project> |
|||
@ -0,0 +1,274 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.context.event.EventListener; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.DeviceTransportType; |
|||
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration; |
|||
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.DeviceProfileId; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
|||
import org.thingsboard.server.common.transport.DeviceUpdatedEvent; |
|||
import org.thingsboard.server.common.transport.TransportContext; |
|||
import org.thingsboard.server.common.transport.TransportDeviceProfileCache; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.common.transport.TransportServiceCallback; |
|||
import org.thingsboard.server.common.transport.auth.SessionInfoCreator; |
|||
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
|||
import org.thingsboard.server.queue.util.AfterStartUp; |
|||
import org.thingsboard.server.queue.util.TbSnmpTransportComponent; |
|||
import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService; |
|||
import org.thingsboard.server.transport.snmp.service.SnmpAuthService; |
|||
import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService; |
|||
import org.thingsboard.server.transport.snmp.service.SnmpTransportService; |
|||
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Optional; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentLinkedDeque; |
|||
|
|||
@TbSnmpTransportComponent |
|||
@Component |
|||
@Slf4j |
|||
@RequiredArgsConstructor |
|||
public class SnmpTransportContext extends TransportContext { |
|||
@Getter |
|||
private final SnmpTransportService snmpTransportService; |
|||
private final TransportDeviceProfileCache deviceProfileCache; |
|||
private final TransportService transportService; |
|||
private final ProtoTransportEntityService protoEntityService; |
|||
private final SnmpTransportBalancingService balancingService; |
|||
@Getter |
|||
private final SnmpAuthService snmpAuthService; |
|||
|
|||
private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>(); |
|||
private final Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>(); |
|||
|
|||
@AfterStartUp(order = 2) |
|||
public void fetchDevicesAndEstablishSessions() { |
|||
log.info("Initializing SNMP devices sessions"); |
|||
|
|||
int batchIndex = 0; |
|||
int batchSize = 512; |
|||
boolean nextBatchExists = true; |
|||
|
|||
while (nextBatchExists) { |
|||
TransportProtos.GetSnmpDevicesResponseMsg snmpDevicesResponse = protoEntityService.getSnmpDevicesIds(batchIndex, batchSize); |
|||
snmpDevicesResponse.getIdsList().stream() |
|||
.map(id -> new DeviceId(UUID.fromString(id))) |
|||
.peek(allSnmpDevicesIds::add) |
|||
.filter(deviceId -> balancingService.isManagedByCurrentTransport(deviceId.getId())) |
|||
.map(protoEntityService::getDeviceById) |
|||
.forEach(device -> getExecutor().execute(() -> establishDeviceSession(device))); |
|||
|
|||
nextBatchExists = snmpDevicesResponse.getHasNextPage(); |
|||
batchIndex++; |
|||
} |
|||
|
|||
log.debug("Found all SNMP devices ids: {}", allSnmpDevicesIds); |
|||
} |
|||
|
|||
private void establishDeviceSession(Device device) { |
|||
if (device == null) return; |
|||
log.info("Establishing SNMP session for device {}", device.getId()); |
|||
|
|||
DeviceProfileId deviceProfileId = device.getDeviceProfileId(); |
|||
DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId); |
|||
|
|||
DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId()); |
|||
if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) { |
|||
log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType()); |
|||
return; |
|||
} |
|||
|
|||
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration(); |
|||
SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); |
|||
|
|||
DeviceSessionContext deviceSessionContext; |
|||
try { |
|||
deviceSessionContext = new DeviceSessionContext( |
|||
device, deviceProfile, credentials.getCredentialsId(), |
|||
profileTransportConfiguration, deviceTransportConfiguration, this |
|||
); |
|||
registerSessionMsgListener(deviceSessionContext); |
|||
} catch (Exception e) { |
|||
log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.toString()); |
|||
return; |
|||
} |
|||
sessions.put(device.getId(), deviceSessionContext); |
|||
snmpTransportService.createQueryingTasks(deviceSessionContext); |
|||
log.info("Established SNMP device session for device {}", device.getId()); |
|||
} |
|||
|
|||
private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) { |
|||
log.info("Updating SNMP session for device {}", device.getId()); |
|||
|
|||
DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId()); |
|||
if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) { |
|||
log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType()); |
|||
destroyDeviceSession(sessionContext); |
|||
return; |
|||
} |
|||
|
|||
SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration(); |
|||
SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); |
|||
|
|||
try { |
|||
if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) { |
|||
sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration); |
|||
sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration); |
|||
snmpTransportService.cancelQueryingTasks(sessionContext); |
|||
snmpTransportService.createQueryingTasks(sessionContext); |
|||
} else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) { |
|||
sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration); |
|||
sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration); |
|||
} else { |
|||
log.trace("Configuration of the device {} was not updated", device); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("Failed to update session for SNMP device {}: {}", sessionContext.getDeviceId(), e.getMessage()); |
|||
destroyDeviceSession(sessionContext); |
|||
} |
|||
} |
|||
|
|||
private void destroyDeviceSession(DeviceSessionContext sessionContext) { |
|||
if (sessionContext == null) return; |
|||
log.info("Destroying SNMP device session for device {}", sessionContext.getDevice().getId()); |
|||
sessionContext.close(); |
|||
snmpAuthService.cleanUpSnmpAuthInfo(sessionContext); |
|||
transportService.deregisterSession(sessionContext.getSessionInfo()); |
|||
snmpTransportService.cancelQueryingTasks(sessionContext); |
|||
sessions.remove(sessionContext.getDeviceId()); |
|||
log.trace("Unregistered and removed session"); |
|||
} |
|||
|
|||
private void registerSessionMsgListener(DeviceSessionContext deviceSessionContext) { |
|||
transportService.process(DeviceTransportType.SNMP, |
|||
TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceSessionContext.getToken()).build(), |
|||
new TransportServiceCallback<>() { |
|||
@Override |
|||
public void onSuccess(ValidateDeviceCredentialsResponse msg) { |
|||
if (msg.hasDeviceInfo()) { |
|||
SessionInfoProto sessionInfo = SessionInfoCreator.create( |
|||
msg, SnmpTransportContext.this, UUID.randomUUID() |
|||
); |
|||
|
|||
transportService.registerAsyncSession(sessionInfo, deviceSessionContext); |
|||
transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), TransportServiceCallback.EMPTY); |
|||
transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), TransportServiceCallback.EMPTY); |
|||
|
|||
deviceSessionContext.setSessionInfo(sessionInfo); |
|||
deviceSessionContext.setDeviceInfo(msg.getDeviceInfo()); |
|||
} else { |
|||
log.warn("[{}] Failed to process device auth", deviceSessionContext.getDeviceId()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onError(Throwable e) { |
|||
log.warn("[{}] Failed to process device auth: {}", deviceSessionContext.getDeviceId(), e); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
@EventListener(DeviceUpdatedEvent.class) |
|||
public void onDeviceUpdatedOrCreated(DeviceUpdatedEvent deviceUpdatedEvent) { |
|||
Device device = deviceUpdatedEvent.getDevice(); |
|||
log.trace("Got creating or updating device event for device {}", device); |
|||
DeviceTransportType transportType = Optional.ofNullable(device.getDeviceData().getTransportConfiguration()) |
|||
.map(DeviceTransportConfiguration::getType) |
|||
.orElse(null); |
|||
if (!allSnmpDevicesIds.contains(device.getId())) { |
|||
if (transportType != DeviceTransportType.SNMP) { |
|||
return; |
|||
} |
|||
allSnmpDevicesIds.add(device.getId()); |
|||
if (balancingService.isManagedByCurrentTransport(device.getId().getId())) { |
|||
establishDeviceSession(device); |
|||
} |
|||
} else { |
|||
if (balancingService.isManagedByCurrentTransport(device.getId().getId())) { |
|||
DeviceSessionContext sessionContext = sessions.get(device.getId()); |
|||
if (transportType == DeviceTransportType.SNMP) { |
|||
if (sessionContext != null) { |
|||
updateDeviceSession(sessionContext, device, deviceProfileCache.get(device.getDeviceProfileId())); |
|||
} else { |
|||
establishDeviceSession(device); |
|||
} |
|||
} else { |
|||
log.trace("Transport type was changed to {}", transportType); |
|||
destroyDeviceSession(sessionContext); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void onDeviceDeleted(DeviceSessionContext sessionContext) { |
|||
destroyDeviceSession(sessionContext); |
|||
} |
|||
|
|||
public void onDeviceProfileUpdated(DeviceProfile deviceProfile, DeviceSessionContext sessionContext) { |
|||
updateDeviceSession(sessionContext, sessionContext.getDevice(), deviceProfile); |
|||
} |
|||
|
|||
public void onSnmpTransportListChanged() { |
|||
log.trace("SNMP transport list changed. Updating sessions"); |
|||
List<DeviceId> deleted = new LinkedList<>(); |
|||
for (DeviceId deviceId : allSnmpDevicesIds) { |
|||
if (balancingService.isManagedByCurrentTransport(deviceId.getId())) { |
|||
if (!sessions.containsKey(deviceId)) { |
|||
Device device = protoEntityService.getDeviceById(deviceId); |
|||
if (device != null) { |
|||
log.info("SNMP device {} is now managed by current transport node", deviceId); |
|||
establishDeviceSession(device); |
|||
} else { |
|||
deleted.add(deviceId); |
|||
} |
|||
} |
|||
} else { |
|||
Optional.ofNullable(sessions.get(deviceId)) |
|||
.ifPresent(sessionContext -> { |
|||
log.info("SNMP session for device {} is not managed by current transport node anymore", deviceId); |
|||
destroyDeviceSession(sessionContext); |
|||
}); |
|||
} |
|||
} |
|||
log.trace("Removing deleted SNMP devices: {}", deleted); |
|||
allSnmpDevicesIds.removeAll(deleted); |
|||
} |
|||
|
|||
|
|||
public Collection<DeviceSessionContext> getSessions() { |
|||
return sessions.values(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp.event; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; |
|||
import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent; |
|||
import org.thingsboard.server.queue.util.TbSnmpTransportComponent; |
|||
import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService; |
|||
|
|||
@TbSnmpTransportComponent |
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class ServiceListChangedEventListener extends TbApplicationEventListener<ServiceListChangedEvent> { |
|||
private final SnmpTransportBalancingService snmpTransportBalancingService; |
|||
|
|||
@Override |
|||
protected void onTbApplicationEvent(ServiceListChangedEvent event) { |
|||
snmpTransportBalancingService.onServiceListChanged(event); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp.event; |
|||
|
|||
import org.thingsboard.server.queue.discovery.event.TbApplicationEvent; |
|||
|
|||
public class SnmpTransportListChangedEvent extends TbApplicationEvent { |
|||
public SnmpTransportListChangedEvent() { |
|||
super(new Object()); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp.event; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; |
|||
import org.thingsboard.server.queue.util.TbSnmpTransportComponent; |
|||
import org.thingsboard.server.transport.snmp.SnmpTransportContext; |
|||
|
|||
@TbSnmpTransportComponent |
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class SnmpTransportListChangedEventListener extends TbApplicationEventListener<SnmpTransportListChangedEvent> { |
|||
private final SnmpTransportContext snmpTransportContext; |
|||
|
|||
@Override |
|||
protected void onTbApplicationEvent(SnmpTransportListChangedEvent event) { |
|||
snmpTransportContext.onSnmpTransportListChanged(); |
|||
} |
|||
} |
|||
@ -0,0 +1,171 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp.service; |
|||
|
|||
import com.google.gson.JsonObject; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.snmp4j.PDU; |
|||
import org.snmp4j.ScopedPDU; |
|||
import org.snmp4j.smi.Integer32; |
|||
import org.snmp4j.smi.Null; |
|||
import org.snmp4j.smi.OID; |
|||
import org.snmp4j.smi.OctetString; |
|||
import org.snmp4j.smi.Variable; |
|||
import org.snmp4j.smi.VariableBinding; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; |
|||
import org.thingsboard.server.common.data.kv.DataType; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; |
|||
import org.thingsboard.server.queue.util.TbSnmpTransportComponent; |
|||
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
import java.util.Optional; |
|||
import java.util.stream.Collectors; |
|||
import java.util.stream.IntStream; |
|||
|
|||
@TbSnmpTransportComponent |
|||
@Service |
|||
@Slf4j |
|||
public class PduService { |
|||
public PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) { |
|||
PDU pdu = setUpPdu(sessionContext); |
|||
|
|||
pdu.setType(communicationConfig.getMethod().getCode()); |
|||
pdu.addAll(communicationConfig.getAllMappings().stream() |
|||
.filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey())) |
|||
.map(mapping -> Optional.ofNullable(values.get(mapping.getKey())) |
|||
.map(value -> { |
|||
Variable variable = toSnmpVariable(value, mapping.getDataType()); |
|||
return new VariableBinding(new OID(mapping.getOid()), variable); |
|||
}) |
|||
.orElseGet(() -> new VariableBinding(new OID(mapping.getOid())))) |
|||
.collect(Collectors.toList())); |
|||
|
|||
return pdu; |
|||
} |
|||
|
|||
public PDU createSingleVariablePdu(DeviceSessionContext sessionContext, SnmpMethod snmpMethod, String oid, String value, DataType dataType) { |
|||
PDU pdu = setUpPdu(sessionContext); |
|||
pdu.setType(snmpMethod.getCode()); |
|||
|
|||
Variable variable = value == null ? Null.instance : toSnmpVariable(value, dataType); |
|||
pdu.add(new VariableBinding(new OID(oid), variable)); |
|||
|
|||
return pdu; |
|||
} |
|||
|
|||
private Variable toSnmpVariable(String value, DataType dataType) { |
|||
dataType = dataType == null ? DataType.STRING : dataType; |
|||
Variable variable; |
|||
switch (dataType) { |
|||
case LONG: |
|||
try { |
|||
variable = new Integer32(Integer.parseInt(value)); |
|||
break; |
|||
} catch (NumberFormatException ignored) { |
|||
} |
|||
case DOUBLE: |
|||
case BOOLEAN: |
|||
case STRING: |
|||
case JSON: |
|||
default: |
|||
variable = new OctetString(value); |
|||
} |
|||
return variable; |
|||
} |
|||
|
|||
private PDU setUpPdu(DeviceSessionContext sessionContext) { |
|||
PDU pdu; |
|||
SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration(); |
|||
SnmpProtocolVersion snmpVersion = deviceTransportConfiguration.getProtocolVersion(); |
|||
switch (snmpVersion) { |
|||
case V1: |
|||
case V2C: |
|||
pdu = new PDU(); |
|||
break; |
|||
case V3: |
|||
ScopedPDU scopedPdu = new ScopedPDU(); |
|||
scopedPdu.setContextName(new OctetString(deviceTransportConfiguration.getContextName())); |
|||
scopedPdu.setContextEngineID(new OctetString(deviceTransportConfiguration.getEngineId())); |
|||
pdu = scopedPdu; |
|||
break; |
|||
default: |
|||
throw new UnsupportedOperationException("SNMP version " + snmpVersion + " is not supported"); |
|||
} |
|||
return pdu; |
|||
} |
|||
|
|||
|
|||
public JsonObject processPdu(PDU pdu, List<SnmpMapping> responseMappings) { |
|||
Map<OID, String> values = processPdu(pdu); |
|||
|
|||
Map<OID, SnmpMapping> mappings = new HashMap<>(); |
|||
if (responseMappings != null) { |
|||
for (SnmpMapping mapping : responseMappings) { |
|||
OID oid = new OID(mapping.getOid()); |
|||
mappings.put(oid, mapping); |
|||
} |
|||
} |
|||
|
|||
JsonObject data = new JsonObject(); |
|||
values.forEach((oid, value) -> { |
|||
log.trace("Processing variable binding: {} - {}", oid, value); |
|||
|
|||
SnmpMapping mapping = mappings.get(oid); |
|||
if (mapping == null) { |
|||
log.debug("No SNMP mapping for oid {}", oid); |
|||
return; |
|||
} |
|||
|
|||
processValue(mapping.getKey(), mapping.getDataType(), value, data); |
|||
}); |
|||
|
|||
return data; |
|||
} |
|||
|
|||
public Map<OID, String> processPdu(PDU pdu) { |
|||
return IntStream.range(0, pdu.size()) |
|||
.mapToObj(pdu::get) |
|||
.filter(Objects::nonNull) |
|||
.filter(variableBinding -> !(variableBinding.getVariable() instanceof Null)) |
|||
.collect(Collectors.toMap(VariableBinding::getOid, VariableBinding::toValueString)); |
|||
} |
|||
|
|||
public void processValue(String key, DataType dataType, String value, JsonObject result) { |
|||
switch (dataType) { |
|||
case LONG: |
|||
result.addProperty(key, Long.parseLong(value)); |
|||
break; |
|||
case BOOLEAN: |
|||
result.addProperty(key, Boolean.parseBoolean(value)); |
|||
break; |
|||
case DOUBLE: |
|||
result.addProperty(key, Double.parseDouble(value)); |
|||
break; |
|||
case STRING: |
|||
case JSON: |
|||
default: |
|||
result.addProperty(key, value); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp.service; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.device.data.DeviceData; |
|||
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.DeviceProfileId; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.queue.util.TbSnmpTransportComponent; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@TbSnmpTransportComponent |
|||
@Service |
|||
@RequiredArgsConstructor |
|||
public class ProtoTransportEntityService { |
|||
private final TransportService transportService; |
|||
private final DataDecodingEncodingService dataDecodingEncodingService; |
|||
|
|||
public Device getDeviceById(DeviceId id) { |
|||
TransportProtos.GetDeviceResponseMsg deviceProto = transportService.getDevice(TransportProtos.GetDeviceRequestMsg.newBuilder() |
|||
.setDeviceIdMSB(id.getId().getMostSignificantBits()) |
|||
.setDeviceIdLSB(id.getId().getLeastSignificantBits()) |
|||
.build()); |
|||
|
|||
if (deviceProto == null) { |
|||
return null; |
|||
} |
|||
|
|||
DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID( |
|||
deviceProto.getDeviceProfileIdMSB(), deviceProto.getDeviceProfileIdLSB()) |
|||
); |
|||
|
|||
Device device = new Device(); |
|||
device.setId(id); |
|||
device.setDeviceProfileId(deviceProfileId); |
|||
|
|||
DeviceTransportConfiguration deviceTransportConfiguration = (DeviceTransportConfiguration) dataDecodingEncodingService.decode( |
|||
deviceProto.getDeviceTransportConfiguration().toByteArray() |
|||
).orElseThrow(() -> new IllegalStateException("Can't find device transport configuration")); |
|||
|
|||
DeviceData deviceData = new DeviceData(); |
|||
deviceData.setTransportConfiguration(deviceTransportConfiguration); |
|||
device.setDeviceData(deviceData); |
|||
|
|||
return device; |
|||
} |
|||
|
|||
public DeviceCredentials getDeviceCredentialsByDeviceId(DeviceId deviceId) { |
|||
TransportProtos.GetDeviceCredentialsResponseMsg deviceCredentialsResponse = transportService.getDeviceCredentials( |
|||
TransportProtos.GetDeviceCredentialsRequestMsg.newBuilder() |
|||
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) |
|||
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) |
|||
.build() |
|||
); |
|||
|
|||
return (DeviceCredentials) dataDecodingEncodingService.decode(deviceCredentialsResponse.getDeviceCredentialsData().toByteArray()) |
|||
.orElseThrow(() -> new IllegalArgumentException("Device credentials not found")); |
|||
} |
|||
|
|||
public TransportProtos.GetSnmpDevicesResponseMsg getSnmpDevicesIds(int page, int pageSize) { |
|||
TransportProtos.GetSnmpDevicesRequestMsg requestMsg = TransportProtos.GetSnmpDevicesRequestMsg.newBuilder() |
|||
.setPage(page) |
|||
.setPageSize(pageSize) |
|||
.build(); |
|||
return transportService.getSnmpDevicesIds(requestMsg); |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp.service; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.snmp4j.AbstractTarget; |
|||
import org.snmp4j.CommunityTarget; |
|||
import org.snmp4j.Target; |
|||
import org.snmp4j.UserTarget; |
|||
import org.snmp4j.security.SecurityLevel; |
|||
import org.snmp4j.security.SecurityModel; |
|||
import org.snmp4j.security.SecurityProtocols; |
|||
import org.snmp4j.security.USM; |
|||
import org.snmp4j.smi.Address; |
|||
import org.snmp4j.smi.GenericAddress; |
|||
import org.snmp4j.smi.OID; |
|||
import org.snmp4j.smi.OctetString; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion; |
|||
import org.thingsboard.server.queue.util.TbSnmpTransportComponent; |
|||
import org.thingsboard.server.transport.snmp.service.SnmpTransportService; |
|||
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; |
|||
|
|||
import java.util.Optional; |
|||
|
|||
@Service |
|||
@TbSnmpTransportComponent |
|||
@RequiredArgsConstructor |
|||
public class SnmpAuthService { |
|||
private final SnmpTransportService snmpTransportService; |
|||
|
|||
@Value("${transport.snmp.underlying_protocol}") |
|||
private String snmpUnderlyingProtocol; |
|||
|
|||
public Target setUpSnmpTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) { |
|||
AbstractTarget target; |
|||
|
|||
SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion(); |
|||
switch (protocolVersion) { |
|||
case V1: |
|||
CommunityTarget communityTargetV1 = new CommunityTarget(); |
|||
communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1); |
|||
communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV); |
|||
communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getCommunity())); |
|||
target = communityTargetV1; |
|||
break; |
|||
case V2C: |
|||
CommunityTarget communityTargetV2 = new CommunityTarget(); |
|||
communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c); |
|||
communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV); |
|||
communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getCommunity())); |
|||
target = communityTargetV2; |
|||
break; |
|||
case V3: |
|||
OctetString username = new OctetString(deviceTransportConfig.getUsername()); |
|||
OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName()); |
|||
OctetString engineId = new OctetString(deviceTransportConfig.getEngineId()); |
|||
|
|||
OID authenticationProtocol = new OID(deviceTransportConfig.getAuthenticationProtocol().getOid()); |
|||
OID privacyProtocol = new OID(deviceTransportConfig.getPrivacyProtocol().getOid()); |
|||
OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase()); |
|||
authenticationPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(authenticationProtocol, authenticationPassphrase, engineId.getValue())); |
|||
OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase()); |
|||
privacyPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(privacyProtocol, authenticationProtocol, privacyPassphrase, engineId.getValue())); |
|||
|
|||
USM usm = snmpTransportService.getSnmp().getUSM(); |
|||
if (usm.hasUser(engineId, securityName)) { |
|||
usm.removeAllUsers(username, engineId); |
|||
} |
|||
usm.addLocalizedUser( |
|||
engineId.getValue(), username, |
|||
authenticationProtocol, authenticationPassphrase.getValue(), |
|||
privacyProtocol, privacyPassphrase.getValue() |
|||
); |
|||
|
|||
UserTarget userTarget = new UserTarget(); |
|||
userTarget.setSecurityName(securityName); |
|||
userTarget.setAuthoritativeEngineID(engineId.getValue()); |
|||
userTarget.setSecurityModel(SecurityModel.SECURITY_MODEL_USM); |
|||
userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV); |
|||
target = userTarget; |
|||
break; |
|||
default: |
|||
throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported"); |
|||
} |
|||
|
|||
Address address = GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getHost() + "/" + deviceTransportConfig.getPort()); |
|||
target.setAddress(Optional.ofNullable(address).orElseThrow(() -> new IllegalArgumentException("Address of the SNMP device is invalid"))); |
|||
target.setTimeout(profileTransportConfig.getTimeoutMs()); |
|||
target.setRetries(profileTransportConfig.getRetries()); |
|||
target.setVersion(protocolVersion.getCode()); |
|||
|
|||
return target; |
|||
} |
|||
|
|||
public void cleanUpSnmpAuthInfo(DeviceSessionContext sessionContext) { |
|||
SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration(); |
|||
if (deviceTransportConfiguration.getProtocolVersion() == SnmpProtocolVersion.V3) { |
|||
OctetString username = new OctetString(deviceTransportConfiguration.getUsername()); |
|||
OctetString engineId = new OctetString(deviceTransportConfiguration.getEngineId()); |
|||
snmpTransportService.getSnmp().getUSM().removeAllUsers(username, engineId); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp.service; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.context.ApplicationEventPublisher; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; |
|||
import org.thingsboard.server.queue.discovery.PartitionService; |
|||
import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent; |
|||
import org.thingsboard.server.queue.util.TbSnmpTransportComponent; |
|||
import org.thingsboard.server.transport.snmp.event.SnmpTransportListChangedEvent; |
|||
|
|||
import java.util.Comparator; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.stream.Collectors; |
|||
import java.util.stream.Stream; |
|||
|
|||
@TbSnmpTransportComponent |
|||
@Service |
|||
@RequiredArgsConstructor |
|||
@Slf4j |
|||
public class SnmpTransportBalancingService { |
|||
private final PartitionService partitionService; |
|||
private final ApplicationEventPublisher eventPublisher; |
|||
private final SnmpTransportService snmpTransportService; |
|||
|
|||
private int snmpTransportsCount = 1; |
|||
private Integer currentTransportPartitionIndex = 0; |
|||
|
|||
public void onServiceListChanged(ServiceListChangedEvent event) { |
|||
log.trace("Got service list changed event: {}", event); |
|||
recalculatePartitions(event.getOtherServices(), event.getCurrentService()); |
|||
} |
|||
|
|||
public boolean isManagedByCurrentTransport(UUID entityId) { |
|||
boolean isManaged = resolvePartitionIndexForEntity(entityId) == currentTransportPartitionIndex; |
|||
if (!isManaged) { |
|||
log.trace("Entity {} is not managed by current SNMP transport node", entityId); |
|||
} |
|||
return isManaged; |
|||
} |
|||
|
|||
private int resolvePartitionIndexForEntity(UUID entityId) { |
|||
return partitionService.resolvePartitionIndex(entityId, snmpTransportsCount); |
|||
} |
|||
|
|||
private void recalculatePartitions(List<ServiceInfo> otherServices, ServiceInfo currentService) { |
|||
log.info("Recalculating partitions for SNMP transports"); |
|||
List<ServiceInfo> snmpTransports = Stream.concat(otherServices.stream(), Stream.of(currentService)) |
|||
.filter(service -> service.getTransportsList().contains(snmpTransportService.getName())) |
|||
.sorted(Comparator.comparing(ServiceInfo::getServiceId)) |
|||
.collect(Collectors.toList()); |
|||
log.trace("Found SNMP transports: {}", snmpTransports); |
|||
|
|||
int previousCurrentTransportPartitionIndex = currentTransportPartitionIndex; |
|||
int previousSnmpTransportsCount = snmpTransportsCount; |
|||
|
|||
if (!snmpTransports.isEmpty()) { |
|||
for (int i = 0; i < snmpTransports.size(); i++) { |
|||
if (snmpTransports.get(i).equals(currentService)) { |
|||
currentTransportPartitionIndex = i; |
|||
break; |
|||
} |
|||
} |
|||
snmpTransportsCount = snmpTransports.size(); |
|||
} |
|||
|
|||
if (snmpTransportsCount != previousSnmpTransportsCount || currentTransportPartitionIndex != previousCurrentTransportPartitionIndex) { |
|||
log.info("SNMP transports partitions have changed: transports count = {}, current transport partition index = {}", snmpTransportsCount, currentTransportPartitionIndex); |
|||
eventPublisher.publishEvent(new SnmpTransportListChangedEvent()); |
|||
} else { |
|||
log.info("SNMP transports partitions have not changed"); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,351 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp.service; |
|||
|
|||
import com.google.gson.JsonElement; |
|||
import com.google.gson.JsonObject; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.snmp4j.PDU; |
|||
import org.snmp4j.Snmp; |
|||
import org.snmp4j.TransportMapping; |
|||
import org.snmp4j.event.ResponseEvent; |
|||
import org.snmp4j.mp.MPv3; |
|||
import org.snmp4j.security.SecurityModels; |
|||
import org.snmp4j.security.SecurityProtocols; |
|||
import org.snmp4j.security.USM; |
|||
import org.snmp4j.smi.OctetString; |
|||
import org.snmp4j.transport.DefaultTcpTransportMapping; |
|||
import org.snmp4j.transport.DefaultUdpTransportMapping; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.common.util.ThingsBoardThreadFactory; |
|||
import org.thingsboard.server.common.data.TbTransportService; |
|||
import org.thingsboard.server.common.data.kv.DataType; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; |
|||
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig; |
|||
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.common.transport.TransportServiceCallback; |
|||
import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.queue.util.TbSnmpTransportComponent; |
|||
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.annotation.PreDestroy; |
|||
import java.io.IOException; |
|||
import java.util.Arrays; |
|||
import java.util.Collections; |
|||
import java.util.EnumMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Optional; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.ScheduledFuture; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@TbSnmpTransportComponent |
|||
@Service |
|||
@Slf4j |
|||
@RequiredArgsConstructor |
|||
public class SnmpTransportService implements TbTransportService { |
|||
private final TransportService transportService; |
|||
private final PduService pduService; |
|||
|
|||
@Getter |
|||
private Snmp snmp; |
|||
private ScheduledExecutorService queryingExecutor; |
|||
private ExecutorService responseProcessingExecutor; |
|||
|
|||
private final Map<SnmpCommunicationSpec, ResponseDataMapper> responseDataMappers = new EnumMap<>(SnmpCommunicationSpec.class); |
|||
private final Map<SnmpCommunicationSpec, ResponseProcessor> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class); |
|||
|
|||
@Value("${transport.snmp.response_processing.parallelism_level}") |
|||
private Integer responseProcessingParallelismLevel; |
|||
@Value("${transport.snmp.underlying_protocol}") |
|||
private String snmpUnderlyingProtocol; |
|||
|
|||
@PostConstruct |
|||
private void init() throws IOException { |
|||
queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying")); |
|||
responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel); |
|||
|
|||
initializeSnmp(); |
|||
configureResponseDataMappers(); |
|||
configureResponseProcessors(); |
|||
|
|||
log.info("SNMP transport service initialized"); |
|||
} |
|||
|
|||
private void initializeSnmp() throws IOException { |
|||
TransportMapping<?> transportMapping; |
|||
switch (snmpUnderlyingProtocol) { |
|||
case "udp": |
|||
transportMapping = new DefaultUdpTransportMapping(); |
|||
break; |
|||
case "tcp": |
|||
transportMapping = new DefaultTcpTransportMapping(); |
|||
break; |
|||
default: |
|||
throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported"); |
|||
} |
|||
snmp = new Snmp(transportMapping); |
|||
snmp.listen(); |
|||
|
|||
USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0); |
|||
SecurityModels.getInstance().addSecurityModel(usm); |
|||
} |
|||
|
|||
public void createQueryingTasks(DeviceSessionContext sessionContext) { |
|||
List<ScheduledFuture<?>> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream() |
|||
.filter(communicationConfig -> communicationConfig instanceof RepeatingQueryingSnmpCommunicationConfig) |
|||
.map(config -> { |
|||
RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config; |
|||
Long queryingFrequency = repeatingCommunicationConfig.getQueryingFrequencyMs(); |
|||
|
|||
return queryingExecutor.scheduleWithFixedDelay(() -> { |
|||
try { |
|||
if (sessionContext.isActive()) { |
|||
sendRequest(sessionContext, repeatingCommunicationConfig); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString()); |
|||
} |
|||
}, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS); |
|||
}) |
|||
.collect(Collectors.toList()); |
|||
sessionContext.getQueryingTasks().addAll(queryingTasks); |
|||
} |
|||
|
|||
public void cancelQueryingTasks(DeviceSessionContext sessionContext) { |
|||
sessionContext.getQueryingTasks().forEach(task -> task.cancel(true)); |
|||
sessionContext.getQueryingTasks().clear(); |
|||
} |
|||
|
|||
|
|||
private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) { |
|||
sendRequest(sessionContext, communicationConfig, Collections.emptyMap()); |
|||
} |
|||
|
|||
private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) { |
|||
PDU request = pduService.createPdu(sessionContext, communicationConfig, values); |
|||
RequestInfo requestInfo = new RequestInfo(communicationConfig.getSpec(), communicationConfig.getAllMappings()); |
|||
sendRequest(sessionContext, request, requestInfo); |
|||
} |
|||
|
|||
private void sendRequest(DeviceSessionContext sessionContext, PDU request, RequestInfo requestInfo) { |
|||
if (request.size() > 0) { |
|||
log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings()); |
|||
try { |
|||
snmp.send(request, sessionContext.getTarget(), requestInfo, sessionContext); |
|||
} catch (IOException e) { |
|||
log.error("Failed to send SNMP request to device {}: {}", sessionContext.getDeviceId(), e.toString()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void onAttributeUpdate(DeviceSessionContext sessionContext, TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification) { |
|||
sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream() |
|||
.filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING) |
|||
.findFirst() |
|||
.ifPresent(communicationConfig -> { |
|||
Map<String, String> sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream() |
|||
.collect(Collectors.toMap( |
|||
Map.Entry::getKey, |
|||
entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString() |
|||
)); |
|||
sendRequest(sessionContext, communicationConfig, sharedAttributes); |
|||
}); |
|||
} |
|||
|
|||
public void onToDeviceRpcRequest(DeviceSessionContext sessionContext, TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg) { |
|||
SnmpMethod snmpMethod = SnmpMethod.valueOf(toDeviceRpcRequestMsg.getMethodName()); |
|||
JsonObject params = JsonConverter.parse(toDeviceRpcRequestMsg.getParams()).getAsJsonObject(); |
|||
|
|||
String key = Optional.ofNullable(params.get("key")).map(JsonElement::getAsString).orElse(null); |
|||
String value = Optional.ofNullable(params.get("value")).map(JsonElement::getAsString).orElse(null); |
|||
|
|||
if (value == null && snmpMethod == SnmpMethod.SET) { |
|||
throw new IllegalArgumentException("Value must be specified for SNMP method 'SET'"); |
|||
} |
|||
|
|||
SnmpCommunicationConfig communicationConfig = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream() |
|||
.filter(config -> config.getSpec() == SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST) |
|||
.findFirst() |
|||
.orElseThrow(() -> new IllegalArgumentException("No communication config found with RPC spec")); |
|||
SnmpMapping snmpMapping = communicationConfig.getAllMappings().stream() |
|||
.filter(mapping -> mapping.getKey().equals(key)) |
|||
.findFirst() |
|||
.orElseThrow(() -> new IllegalArgumentException("No SNMP mapping found in the config for specified key")); |
|||
|
|||
String oid = snmpMapping.getOid(); |
|||
DataType dataType = snmpMapping.getDataType(); |
|||
|
|||
PDU request = pduService.createSingleVariablePdu(sessionContext, snmpMethod, oid, value, dataType); |
|||
RequestInfo requestInfo = new RequestInfo(toDeviceRpcRequestMsg.getRequestId(), communicationConfig.getSpec(), communicationConfig.getAllMappings()); |
|||
sendRequest(sessionContext, request, requestInfo); |
|||
} |
|||
|
|||
|
|||
public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) { |
|||
((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext); |
|||
|
|||
if (event.getError() != null) { |
|||
log.warn("SNMP response error: {}", event.getError().toString()); |
|||
return; |
|||
} |
|||
|
|||
PDU response = event.getResponse(); |
|||
if (response == null) { |
|||
log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID()); |
|||
return; |
|||
} |
|||
|
|||
RequestInfo requestInfo = (RequestInfo) event.getUserObject(); |
|||
responseProcessingExecutor.execute(() -> { |
|||
processResponse(sessionContext, response, requestInfo); |
|||
}); |
|||
} |
|||
|
|||
private void processResponse(DeviceSessionContext sessionContext, PDU response, RequestInfo requestInfo) { |
|||
ResponseProcessor responseProcessor = responseProcessors.get(requestInfo.getCommunicationSpec()); |
|||
if (responseProcessor == null) return; |
|||
|
|||
JsonObject responseData = responseDataMappers.get(requestInfo.getCommunicationSpec()).map(response, requestInfo); |
|||
|
|||
if (responseData.entrySet().isEmpty()) { |
|||
log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), response.getRequestID()); |
|||
return; |
|||
} |
|||
|
|||
responseProcessor.process(responseData, requestInfo, sessionContext); |
|||
reportActivity(sessionContext.getSessionInfo()); |
|||
} |
|||
|
|||
private void configureResponseDataMappers() { |
|||
responseDataMappers.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (pdu, requestInfo) -> { |
|||
JsonObject responseData = new JsonObject(); |
|||
pduService.processPdu(pdu).forEach((oid, value) -> { |
|||
requestInfo.getResponseMappings().stream() |
|||
.filter(snmpMapping -> snmpMapping.getOid().equals(oid.toDottedString())) |
|||
.findFirst() |
|||
.ifPresent(snmpMapping -> { |
|||
pduService.processValue(snmpMapping.getKey(), snmpMapping.getDataType(), value, responseData); |
|||
}); |
|||
}); |
|||
return responseData; |
|||
}); |
|||
|
|||
ResponseDataMapper defaultResponseDataMapper = (pdu, requestInfo) -> { |
|||
return pduService.processPdu(pdu, requestInfo.getResponseMappings()); |
|||
}; |
|||
Arrays.stream(SnmpCommunicationSpec.values()) |
|||
.forEach(communicationSpec -> { |
|||
responseDataMappers.putIfAbsent(communicationSpec, defaultResponseDataMapper); |
|||
}); |
|||
} |
|||
|
|||
private void configureResponseProcessors() { |
|||
responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (responseData, requestInfo, sessionContext) -> { |
|||
TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(responseData); |
|||
transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, null); |
|||
log.debug("Posted telemetry for SNMP device {}: {}", sessionContext.getDeviceId(), responseData); |
|||
}); |
|||
|
|||
responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (responseData, requestInfo, sessionContext) -> { |
|||
TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(responseData); |
|||
transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, null); |
|||
log.debug("Posted attributes for SNMP device {}: {}", sessionContext.getDeviceId(), responseData); |
|||
}); |
|||
|
|||
responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (responseData, requestInfo, sessionContext) -> { |
|||
TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() |
|||
.setRequestId(requestInfo.getRequestId()) |
|||
.setPayload(JsonConverter.toJson(responseData)) |
|||
.build(); |
|||
transportService.process(sessionContext.getSessionInfo(), rpcResponseMsg, null); |
|||
log.debug("Posted RPC response {} for device {}", responseData, sessionContext.getDeviceId()); |
|||
}); |
|||
} |
|||
|
|||
private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) { |
|||
transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder() |
|||
.setAttributeSubscription(true) |
|||
.setRpcSubscription(true) |
|||
.setLastActivityTime(System.currentTimeMillis()) |
|||
.build(), TransportServiceCallback.EMPTY); |
|||
} |
|||
|
|||
|
|||
@Override |
|||
public String getName() { |
|||
return "SNMP"; |
|||
} |
|||
|
|||
@PreDestroy |
|||
public void shutdown() { |
|||
log.info("Stopping SNMP transport!"); |
|||
if (queryingExecutor != null) { |
|||
queryingExecutor.shutdownNow(); |
|||
} |
|||
if (responseProcessingExecutor != null) { |
|||
responseProcessingExecutor.shutdownNow(); |
|||
} |
|||
if (snmp != null) { |
|||
try { |
|||
snmp.close(); |
|||
} catch (IOException e) { |
|||
log.error(e.getMessage(), e); |
|||
} |
|||
} |
|||
log.info("SNMP transport stopped!"); |
|||
} |
|||
|
|||
@Data |
|||
private static class RequestInfo { |
|||
private Integer requestId; |
|||
private SnmpCommunicationSpec communicationSpec; |
|||
private List<SnmpMapping> responseMappings; |
|||
|
|||
public RequestInfo(Integer requestId, SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) { |
|||
this.requestId = requestId; |
|||
this.communicationSpec = communicationSpec; |
|||
this.responseMappings = responseMappings; |
|||
} |
|||
|
|||
public RequestInfo(SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) { |
|||
this.communicationSpec = communicationSpec; |
|||
this.responseMappings = responseMappings; |
|||
} |
|||
} |
|||
|
|||
private interface ResponseDataMapper { |
|||
JsonObject map(PDU pdu, RequestInfo requestInfo); |
|||
} |
|||
|
|||
private interface ResponseProcessor { |
|||
void process(JsonObject responseData, RequestInfo requestInfo, DeviceSessionContext sessionContext); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,146 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp.session; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.snmp4j.Target; |
|||
import org.snmp4j.event.ResponseEvent; |
|||
import org.snmp4j.event.ResponseListener; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.transport.SessionMsgListener; |
|||
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; |
|||
import org.thingsboard.server.transport.snmp.SnmpTransportContext; |
|||
|
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ScheduledFuture; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
|
|||
@Slf4j |
|||
public class DeviceSessionContext extends DeviceAwareSessionContext implements SessionMsgListener, ResponseListener { |
|||
@Getter |
|||
private Target target; |
|||
private final String token; |
|||
@Getter |
|||
@Setter |
|||
private SnmpDeviceProfileTransportConfiguration profileTransportConfiguration; |
|||
@Getter |
|||
@Setter |
|||
private SnmpDeviceTransportConfiguration deviceTransportConfiguration; |
|||
@Getter |
|||
private final Device device; |
|||
|
|||
private final SnmpTransportContext snmpTransportContext; |
|||
|
|||
private final AtomicInteger msgIdSeq = new AtomicInteger(0); |
|||
@Getter |
|||
private boolean isActive = true; |
|||
|
|||
@Getter |
|||
private final List<ScheduledFuture<?>> queryingTasks = new LinkedList<>(); |
|||
|
|||
public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token, |
|||
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration, |
|||
SnmpDeviceTransportConfiguration deviceTransportConfiguration, |
|||
SnmpTransportContext snmpTransportContext) throws Exception { |
|||
super(UUID.randomUUID()); |
|||
super.setDeviceId(device.getId()); |
|||
super.setDeviceProfile(deviceProfile); |
|||
this.device = device; |
|||
|
|||
this.token = token; |
|||
this.snmpTransportContext = snmpTransportContext; |
|||
|
|||
this.profileTransportConfiguration = profileTransportConfiguration; |
|||
this.deviceTransportConfiguration = deviceTransportConfiguration; |
|||
|
|||
initializeTarget(profileTransportConfiguration, deviceTransportConfiguration); |
|||
} |
|||
|
|||
@Override |
|||
public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto newSessionInfo, DeviceProfile deviceProfile) { |
|||
super.onDeviceProfileUpdate(newSessionInfo, deviceProfile); |
|||
if (isActive) { |
|||
snmpTransportContext.onDeviceProfileUpdated(deviceProfile, this); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onDeviceDeleted(DeviceId deviceId) { |
|||
snmpTransportContext.onDeviceDeleted(this); |
|||
} |
|||
|
|||
@Override |
|||
public void onResponse(ResponseEvent event) { |
|||
if (isActive) { |
|||
snmpTransportContext.getSnmpTransportService().processResponseEvent(this, event); |
|||
} |
|||
} |
|||
|
|||
public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) throws Exception { |
|||
log.trace("Initializing target for SNMP session of device {}", device); |
|||
this.target = snmpTransportContext.getSnmpAuthService().setUpSnmpTarget(profileTransportConfig, deviceTransportConfig); |
|||
log.debug("SNMP target initialized: {}", target); |
|||
} |
|||
|
|||
public void close() { |
|||
isActive = false; |
|||
} |
|||
|
|||
public String getToken() { |
|||
return token; |
|||
} |
|||
|
|||
@Override |
|||
public int nextMsgId() { |
|||
return msgIdSeq.incrementAndGet(); |
|||
} |
|||
|
|||
@Override |
|||
public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) { |
|||
} |
|||
|
|||
@Override |
|||
public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) { |
|||
snmpTransportContext.getSnmpTransportService().onAttributeUpdate(this, attributeUpdateNotification); |
|||
} |
|||
|
|||
@Override |
|||
public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) { |
|||
} |
|||
|
|||
@Override |
|||
public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { |
|||
snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest); |
|||
} |
|||
|
|||
@Override |
|||
public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) { |
|||
} |
|||
} |
|||
@ -0,0 +1,196 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp; |
|||
|
|||
import org.snmp4j.CommandResponderEvent; |
|||
import org.snmp4j.CommunityTarget; |
|||
import org.snmp4j.PDU; |
|||
import org.snmp4j.Snmp; |
|||
import org.snmp4j.Target; |
|||
import org.snmp4j.TransportMapping; |
|||
import org.snmp4j.agent.BaseAgent; |
|||
import org.snmp4j.agent.CommandProcessor; |
|||
import org.snmp4j.agent.DuplicateRegistrationException; |
|||
import org.snmp4j.agent.MOGroup; |
|||
import org.snmp4j.agent.ManagedObject; |
|||
import org.snmp4j.agent.mo.MOAccessImpl; |
|||
import org.snmp4j.agent.mo.MOScalar; |
|||
import org.snmp4j.agent.mo.snmp.RowStatus; |
|||
import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB; |
|||
import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB; |
|||
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB; |
|||
import org.snmp4j.agent.mo.snmp.StorageType; |
|||
import org.snmp4j.agent.mo.snmp.VacmMIB; |
|||
import org.snmp4j.agent.security.MutableVACM; |
|||
import org.snmp4j.mp.MPv3; |
|||
import org.snmp4j.mp.SnmpConstants; |
|||
import org.snmp4j.security.SecurityLevel; |
|||
import org.snmp4j.security.SecurityModel; |
|||
import org.snmp4j.security.USM; |
|||
import org.snmp4j.smi.Address; |
|||
import org.snmp4j.smi.GenericAddress; |
|||
import org.snmp4j.smi.Integer32; |
|||
import org.snmp4j.smi.OID; |
|||
import org.snmp4j.smi.OctetString; |
|||
import org.snmp4j.smi.UdpAddress; |
|||
import org.snmp4j.smi.Variable; |
|||
import org.snmp4j.smi.VariableBinding; |
|||
import org.snmp4j.transport.TransportMappings; |
|||
|
|||
import java.io.File; |
|||
import java.io.IOException; |
|||
import java.util.Map; |
|||
import java.util.Scanner; |
|||
import java.util.function.Consumer; |
|||
import java.util.stream.Collectors; |
|||
|
|||
public class SnmpDeviceSimulatorV2 extends BaseAgent { |
|||
|
|||
public static class RequestProcessor extends CommandProcessor { |
|||
private final Consumer<CommandResponderEvent> processor; |
|||
|
|||
public RequestProcessor(Consumer<CommandResponderEvent> processor) { |
|||
super(new OctetString(MPv3.createLocalEngineID())); |
|||
this.processor = processor; |
|||
} |
|||
|
|||
@Override |
|||
public void processPdu(CommandResponderEvent event) { |
|||
processor.accept(event); |
|||
} |
|||
} |
|||
|
|||
|
|||
private final Target target; |
|||
private final Address address; |
|||
private Snmp snmp; |
|||
|
|||
private final String password; |
|||
|
|||
public SnmpDeviceSimulatorV2(int port, String password) throws IOException { |
|||
super(new File("conf.agent"), new File("bootCounter.agent"), new RequestProcessor(event -> { |
|||
System.out.println("aboba"); |
|||
((Snmp) event.getSource()).cancel(event.getPDU(), event1 -> System.out.println("canceled")); |
|||
})); |
|||
CommunityTarget target = new CommunityTarget(); |
|||
target.setCommunity(new OctetString(password)); |
|||
this.address = GenericAddress.parse("udp:0.0.0.0/" + port); |
|||
target.setAddress(address); |
|||
target.setRetries(2); |
|||
target.setTimeout(1500); |
|||
target.setVersion(SnmpConstants.version2c); |
|||
this.target = target; |
|||
this.password = password; |
|||
} |
|||
|
|||
public void start() throws IOException { |
|||
init(); |
|||
addShutdownHook(); |
|||
getServer().addContext(new OctetString("public")); |
|||
finishInit(); |
|||
run(); |
|||
sendColdStartNotification(); |
|||
snmp = new Snmp(transportMappings[0]); |
|||
} |
|||
|
|||
public void setUpMappings(Map<String, String> oidToResponseMappings) { |
|||
unregisterManagedObject(getSnmpv2MIB()); |
|||
oidToResponseMappings.forEach((oid, response) -> { |
|||
registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response))); |
|||
}); |
|||
} |
|||
|
|||
public void sendTrap(String host, int port, Map<String, String> values) throws IOException { |
|||
PDU pdu = new PDU(); |
|||
pdu.addAll(values.entrySet().stream() |
|||
.map(entry -> new VariableBinding(new OID(entry.getKey()), new OctetString(entry.getValue()))) |
|||
.collect(Collectors.toList())); |
|||
pdu.setType(PDU.TRAP); |
|||
|
|||
CommunityTarget remoteTarget = (CommunityTarget) getTarget().clone(); |
|||
remoteTarget.setAddress(new UdpAddress(host + "/" + port)); |
|||
|
|||
snmp.send(pdu, remoteTarget); |
|||
} |
|||
|
|||
@Override |
|||
protected void registerManagedObjects() { |
|||
} |
|||
|
|||
protected void registerManagedObject(ManagedObject mo) { |
|||
try { |
|||
server.register(mo, null); |
|||
} catch (DuplicateRegistrationException ex) { |
|||
throw new RuntimeException(ex); |
|||
} |
|||
} |
|||
|
|||
protected void unregisterManagedObject(MOGroup moGroup) { |
|||
moGroup.unregisterMOs(server, getContext(moGroup)); |
|||
} |
|||
|
|||
@Override |
|||
protected void addNotificationTargets(SnmpTargetMIB targetMIB, |
|||
SnmpNotificationMIB notificationMIB) { |
|||
} |
|||
|
|||
@Override |
|||
protected void addViews(VacmMIB vacm) { |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c, new OctetString( |
|||
"cpublic"), new OctetString("v1v2group"), |
|||
StorageType.nonVolatile); |
|||
|
|||
vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"), |
|||
SecurityModel.SECURITY_MODEL_ANY, SecurityLevel.NOAUTH_NOPRIV, |
|||
MutableVACM.VACM_MATCH_EXACT, new OctetString("fullReadView"), |
|||
new OctetString("fullWriteView"), new OctetString( |
|||
"fullNotifyView"), StorageType.nonVolatile); |
|||
|
|||
vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"), |
|||
new OctetString(), VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
} |
|||
|
|||
protected void addUsmUser(USM usm) { |
|||
} |
|||
|
|||
protected void initTransportMappings() { |
|||
transportMappings = new TransportMapping[]{TransportMappings.getInstance().createTransportMapping(address)}; |
|||
} |
|||
|
|||
protected void unregisterManagedObjects() { |
|||
} |
|||
|
|||
protected void addCommunities(SnmpCommunityMIB communityMIB) { |
|||
Variable[] com2sec = new Variable[]{ |
|||
new OctetString("public"), |
|||
new OctetString("cpublic"), |
|||
getAgent().getContextEngineID(), |
|||
new OctetString("public"), |
|||
new OctetString(), |
|||
new Integer32(StorageType.nonVolatile), |
|||
new Integer32(RowStatus.active) |
|||
}; |
|||
SnmpCommunityMIB.SnmpCommunityEntryRow row = communityMIB.getSnmpCommunityEntry().createRow( |
|||
new OctetString("public2public").toSubIndex(true), com2sec); |
|||
communityMIB.getSnmpCommunityEntry().addRow(row); |
|||
} |
|||
|
|||
public Target getTarget() { |
|||
return target; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,745 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp; |
|||
|
|||
import org.snmp4j.MessageDispatcherImpl; |
|||
import org.snmp4j.TransportMapping; |
|||
import org.snmp4j.agent.BaseAgent; |
|||
import org.snmp4j.agent.CommandProcessor; |
|||
import org.snmp4j.agent.DuplicateRegistrationException; |
|||
import org.snmp4j.agent.MOGroup; |
|||
import org.snmp4j.agent.ManagedObject; |
|||
import org.snmp4j.agent.mo.DefaultMOMutableRow2PC; |
|||
import org.snmp4j.agent.mo.DefaultMOTable; |
|||
import org.snmp4j.agent.mo.MOAccessImpl; |
|||
import org.snmp4j.agent.mo.MOColumn; |
|||
import org.snmp4j.agent.mo.MOMutableColumn; |
|||
import org.snmp4j.agent.mo.MOMutableTableModel; |
|||
import org.snmp4j.agent.mo.MOScalar; |
|||
import org.snmp4j.agent.mo.MOTableIndex; |
|||
import org.snmp4j.agent.mo.MOTableRow; |
|||
import org.snmp4j.agent.mo.MOTableSubIndex; |
|||
import org.snmp4j.agent.mo.ext.AgentppSimulationMib; |
|||
import org.snmp4j.agent.mo.snmp.RowStatus; |
|||
import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB; |
|||
import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB; |
|||
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB; |
|||
import org.snmp4j.agent.mo.snmp.StorageType; |
|||
import org.snmp4j.agent.mo.snmp.TransportDomains; |
|||
import org.snmp4j.agent.mo.snmp.VacmMIB; |
|||
import org.snmp4j.agent.mo.snmp4j.example.Snmp4jHeartbeatMib; |
|||
import org.snmp4j.agent.security.MutableVACM; |
|||
import org.snmp4j.mp.MPv1; |
|||
import org.snmp4j.mp.MPv2c; |
|||
import org.snmp4j.mp.MPv3; |
|||
import org.snmp4j.mp.MessageProcessingModel; |
|||
import org.snmp4j.security.AuthHMAC192SHA256; |
|||
import org.snmp4j.security.AuthMD5; |
|||
import org.snmp4j.security.AuthSHA; |
|||
import org.snmp4j.security.PrivAES128; |
|||
import org.snmp4j.security.PrivAES192; |
|||
import org.snmp4j.security.PrivAES256; |
|||
import org.snmp4j.security.PrivDES; |
|||
import org.snmp4j.security.SecurityLevel; |
|||
import org.snmp4j.security.SecurityModel; |
|||
import org.snmp4j.security.SecurityModels; |
|||
import org.snmp4j.security.SecurityProtocols; |
|||
import org.snmp4j.security.USM; |
|||
import org.snmp4j.security.UsmUser; |
|||
import org.snmp4j.smi.Address; |
|||
import org.snmp4j.smi.Gauge32; |
|||
import org.snmp4j.smi.GenericAddress; |
|||
import org.snmp4j.smi.Integer32; |
|||
import org.snmp4j.smi.OID; |
|||
import org.snmp4j.smi.OctetString; |
|||
import org.snmp4j.smi.SMIConstants; |
|||
import org.snmp4j.smi.TcpAddress; |
|||
import org.snmp4j.smi.TimeTicks; |
|||
import org.snmp4j.smi.UdpAddress; |
|||
import org.snmp4j.smi.Variable; |
|||
import org.snmp4j.transport.DefaultTcpTransportMapping; |
|||
import org.snmp4j.transport.TransportMappings; |
|||
import org.snmp4j.util.ThreadPool; |
|||
|
|||
import java.io.File; |
|||
import java.io.IOException; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* The TestAgent is a sample SNMP agent implementation of all |
|||
* features (MIB implementations) provided by the SNMP4J-Agent framework. |
|||
* |
|||
* Note, for snmp4s, this code is mostly a copy from snmp4j. |
|||
* And don't remove snmp users |
|||
* |
|||
*/ |
|||
public class SnmpDeviceSimulatorV3 extends BaseAgent { |
|||
protected String address; |
|||
private Snmp4jHeartbeatMib heartbeatMIB; |
|||
private AgentppSimulationMib agentppSimulationMIB; |
|||
|
|||
public SnmpDeviceSimulatorV3(CommandProcessor processor) throws IOException { |
|||
super(new File("SNMP4JTestAgentBC.cfg"), new File("SNMP4JTestAgentConfig.cfg"), |
|||
processor); |
|||
agent.setWorkerPool(ThreadPool.create("RequestPool", 4)); |
|||
} |
|||
|
|||
public void setUpMappings(Map<String, String> oidToResponseMappings) { |
|||
unregisterManagedObject(getSnmpv2MIB()); |
|||
oidToResponseMappings.forEach((oid, response) -> { |
|||
registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response))); |
|||
}); |
|||
} |
|||
protected void registerManagedObject(ManagedObject mo) { |
|||
try { |
|||
server.register(mo, null); |
|||
} catch (DuplicateRegistrationException ex) { |
|||
throw new RuntimeException(ex); |
|||
} |
|||
} |
|||
|
|||
protected void unregisterManagedObject(MOGroup moGroup) { |
|||
moGroup.unregisterMOs(server, getContext(moGroup)); |
|||
} |
|||
|
|||
protected void registerManagedObjects() { |
|||
try { |
|||
server.register(createStaticIfTable(), null); |
|||
server.register(createStaticIfXTable(), null); |
|||
agentppSimulationMIB.registerMOs(server, null); |
|||
heartbeatMIB.registerMOs(server, null); |
|||
} catch (DuplicateRegistrationException ex) { |
|||
ex.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
protected void addNotificationTargets(SnmpTargetMIB targetMIB, |
|||
SnmpNotificationMIB notificationMIB) { |
|||
targetMIB.addDefaultTDomains(); |
|||
|
|||
targetMIB.addTargetAddress(new OctetString("notificationV2c"), |
|||
TransportDomains.transportDomainUdpIpv4, |
|||
new OctetString(new UdpAddress("127.0.0.1/162").getValue()), |
|||
200, 1, |
|||
new OctetString("notify"), |
|||
new OctetString("v2c"), |
|||
StorageType.permanent); |
|||
targetMIB.addTargetAddress(new OctetString("notificationV3"), |
|||
TransportDomains.transportDomainUdpIpv4, |
|||
new OctetString(new UdpAddress("127.0.0.1/1162").getValue()), |
|||
200, 1, |
|||
new OctetString("notify"), |
|||
new OctetString("v3notify"), |
|||
StorageType.permanent); |
|||
targetMIB.addTargetParams(new OctetString("v2c"), |
|||
MessageProcessingModel.MPv2c, |
|||
SecurityModel.SECURITY_MODEL_SNMPv2c, |
|||
new OctetString("cpublic"), |
|||
SecurityLevel.AUTH_PRIV, |
|||
StorageType.permanent); |
|||
targetMIB.addTargetParams(new OctetString("v3notify"), |
|||
MessageProcessingModel.MPv3, |
|||
SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("v3notify"), |
|||
SecurityLevel.NOAUTH_NOPRIV, |
|||
StorageType.permanent); |
|||
notificationMIB.addNotifyEntry(new OctetString("default"), |
|||
new OctetString("notify"), |
|||
SnmpNotificationMIB.SnmpNotifyTypeEnum.inform, |
|||
StorageType.permanent); |
|||
} |
|||
protected void addViews(VacmMIB vacm) { |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv1, |
|||
new OctetString("cpublic"), |
|||
new OctetString("v1v2group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c, |
|||
new OctetString("cpublic"), |
|||
new OctetString("v1v2group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("SHADES"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("MD5DES"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("TEST"), |
|||
new OctetString("v3test"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("SHA"), |
|||
new OctetString("v3restricted"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("SHAAES128"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("SHAAES192"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("SHAAES256"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("MD5AES128"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("MD5AES192"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("MD5AES256"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("aboba"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
//============================================//
|
|||
// agent5-auth-priv
|
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("agent5"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
//===========================================//
|
|||
// agent002
|
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("agent002"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
//===========================================//
|
|||
// user001-auth-no-priv
|
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("user001"), |
|||
new OctetString("group001"), |
|||
StorageType.nonVolatile); |
|||
//===========================================//
|
|||
|
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("v3notify"), |
|||
new OctetString("v3group"), |
|||
StorageType.nonVolatile); |
|||
|
|||
//===========================================//
|
|||
// group auth no priv
|
|||
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, |
|||
new OctetString("v3notify-auth"), |
|||
new OctetString("group001"), |
|||
StorageType.nonVolatile); |
|||
//===========================================//
|
|||
|
|||
|
|||
|
|||
// my conf
|
|||
vacm.addAccess(new OctetString("group001"), new OctetString("public"), |
|||
SecurityModel.SECURITY_MODEL_USM, |
|||
SecurityLevel.AUTH_NOPRIV, |
|||
MutableVACM.VACM_MATCH_EXACT, |
|||
new OctetString("fullReadView"), |
|||
new OctetString("fullWriteView"), |
|||
new OctetString("fullNotifyView"), |
|||
StorageType.nonVolatile); |
|||
|
|||
vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"), |
|||
SecurityModel.SECURITY_MODEL_ANY, |
|||
SecurityLevel.NOAUTH_NOPRIV, |
|||
MutableVACM.VACM_MATCH_EXACT, |
|||
new OctetString("fullReadView"), |
|||
new OctetString("fullWriteView"), |
|||
new OctetString("fullNotifyView"), |
|||
StorageType.nonVolatile); |
|||
vacm.addAccess(new OctetString("v3group"), new OctetString(), |
|||
SecurityModel.SECURITY_MODEL_USM, |
|||
SecurityLevel.AUTH_PRIV, |
|||
MutableVACM.VACM_MATCH_EXACT, |
|||
new OctetString("fullReadView"), |
|||
new OctetString("fullWriteView"), |
|||
new OctetString("fullNotifyView"), |
|||
StorageType.nonVolatile); |
|||
vacm.addAccess(new OctetString("v3restricted"), new OctetString(), |
|||
SecurityModel.SECURITY_MODEL_USM, |
|||
SecurityLevel.NOAUTH_NOPRIV, |
|||
MutableVACM.VACM_MATCH_EXACT, |
|||
new OctetString("restrictedReadView"), |
|||
new OctetString("restrictedWriteView"), |
|||
new OctetString("restrictedNotifyView"), |
|||
StorageType.nonVolatile); |
|||
vacm.addAccess(new OctetString("v3test"), new OctetString(), |
|||
SecurityModel.SECURITY_MODEL_USM, |
|||
SecurityLevel.AUTH_PRIV, |
|||
MutableVACM.VACM_MATCH_EXACT, |
|||
new OctetString("testReadView"), |
|||
new OctetString("testWriteView"), |
|||
new OctetString("testNotifyView"), |
|||
StorageType.nonVolatile); |
|||
|
|||
vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"), |
|||
new OctetString(), VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
vacm.addViewTreeFamily(new OctetString("fullWriteView"), new OID("1.3"), |
|||
new OctetString(), VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
vacm.addViewTreeFamily(new OctetString("fullNotifyView"), new OID("1.3"), |
|||
new OctetString(), VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
|
|||
vacm.addViewTreeFamily(new OctetString("restrictedReadView"), |
|||
new OID("1.3.6.1.2"), |
|||
new OctetString(), VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
vacm.addViewTreeFamily(new OctetString("restrictedWriteView"), |
|||
new OID("1.3.6.1.2.1"), |
|||
new OctetString(), |
|||
VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"), |
|||
new OID("1.3.6.1.2"), |
|||
new OctetString(), VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"), |
|||
new OID("1.3.6.1.6.3.1"), |
|||
new OctetString(), VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
|
|||
vacm.addViewTreeFamily(new OctetString("testReadView"), |
|||
new OID("1.3.6.1.2"), |
|||
new OctetString(), VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
vacm.addViewTreeFamily(new OctetString("testReadView"), |
|||
new OID("1.3.6.1.2.1.1"), |
|||
new OctetString(), VacmMIB.vacmViewExcluded, |
|||
StorageType.nonVolatile); |
|||
vacm.addViewTreeFamily(new OctetString("testWriteView"), |
|||
new OID("1.3.6.1.2.1"), |
|||
new OctetString(), |
|||
VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
vacm.addViewTreeFamily(new OctetString("testNotifyView"), |
|||
new OID("1.3.6.1.2"), |
|||
new OctetString(), VacmMIB.vacmViewIncluded, |
|||
StorageType.nonVolatile); |
|||
|
|||
} |
|||
|
|||
protected void addUsmUser(USM usm) { |
|||
UsmUser user = new UsmUser(new OctetString("SHADES"), |
|||
AuthSHA.ID, |
|||
new OctetString("SHADESAuthPassword"), |
|||
PrivDES.ID, |
|||
new OctetString("SHADESPrivPassword")); |
|||
// usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
|
|||
usm.addUser(user.getSecurityName(), null, user); |
|||
user = new UsmUser(new OctetString("TEST"), |
|||
AuthSHA.ID, |
|||
new OctetString("maplesyrup"), |
|||
PrivDES.ID, |
|||
new OctetString("maplesyrup")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
user = new UsmUser(new OctetString("SHA"), |
|||
AuthSHA.ID, |
|||
new OctetString("SHAAuthPassword"), |
|||
null, |
|||
null); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
user = new UsmUser(new OctetString("SHADES"), |
|||
AuthSHA.ID, |
|||
new OctetString("SHADESAuthPassword"), |
|||
PrivDES.ID, |
|||
new OctetString("SHADESPrivPassword")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
user = new UsmUser(new OctetString("MD5DES"), |
|||
AuthMD5.ID, |
|||
new OctetString("MD5DESAuthPassword"), |
|||
PrivDES.ID, |
|||
new OctetString("MD5DESPrivPassword")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
user = new UsmUser(new OctetString("SHAAES128"), |
|||
AuthSHA.ID, |
|||
new OctetString("SHAAES128AuthPassword"), |
|||
PrivAES128.ID, |
|||
new OctetString("SHAAES128PrivPassword")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
user = new UsmUser(new OctetString("SHAAES192"), |
|||
AuthSHA.ID, |
|||
new OctetString("SHAAES192AuthPassword"), |
|||
PrivAES192.ID, |
|||
new OctetString("SHAAES192PrivPassword")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
user = new UsmUser(new OctetString("SHAAES256"), |
|||
AuthSHA.ID, |
|||
new OctetString("SHAAES256AuthPassword"), |
|||
PrivAES256.ID, |
|||
new OctetString("SHAAES256PrivPassword")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
|
|||
user = new UsmUser(new OctetString("MD5AES128"), |
|||
AuthMD5.ID, |
|||
new OctetString("MD5AES128AuthPassword"), |
|||
PrivAES128.ID, |
|||
new OctetString("MD5AES128PrivPassword")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
user = new UsmUser(new OctetString("MD5AES192"), |
|||
AuthHMAC192SHA256.ID, |
|||
new OctetString("MD5AES192AuthPassword"), |
|||
PrivAES192.ID, |
|||
new OctetString("MD5AES192PrivPassword")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
//==============================================================
|
|||
user = new UsmUser(new OctetString("MD5AES256"), |
|||
AuthMD5.ID, |
|||
new OctetString("MD5AES256AuthPassword"), |
|||
PrivAES256.ID, |
|||
new OctetString("MD5AES256PrivPassword")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
user = new UsmUser(new OctetString("MD5AES256"), |
|||
AuthMD5.ID, |
|||
new OctetString("MD5AES256AuthPassword"), |
|||
PrivAES256.ID, |
|||
new OctetString("MD5AES256PrivPassword")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
|
|||
OctetString securityName = new OctetString("aboba"); |
|||
OctetString authenticationPassphrase = new OctetString("abobaaboba"); |
|||
OctetString privacyPassphrase = new OctetString("abobaaboba"); |
|||
OID authenticationProtocol = AuthSHA.ID; |
|||
OID privacyProtocol = PrivDES.ID; // FIXME: to config
|
|||
user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase); |
|||
usm.addUser(user); |
|||
|
|||
//===============================================================//
|
|||
user = new UsmUser(new OctetString("agent5"), |
|||
AuthSHA.ID, |
|||
new OctetString("authpass"), |
|||
PrivDES.ID, |
|||
new OctetString("privpass")); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
//===============================================================//
|
|||
// user001
|
|||
user = new UsmUser(new OctetString("user001"), |
|||
AuthSHA.ID, |
|||
new OctetString("authpass"), |
|||
null, null); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
//===============================================================//
|
|||
// user002
|
|||
user = new UsmUser(new OctetString("user001"), |
|||
null, |
|||
null, |
|||
null, null); |
|||
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user); |
|||
//===============================================================//
|
|||
|
|||
user = new UsmUser(new OctetString("v3notify"), |
|||
null, |
|||
null, |
|||
null, |
|||
null); |
|||
usm.addUser(user.getSecurityName(), null, user); |
|||
|
|||
this.usm = usm; |
|||
} |
|||
|
|||
private static DefaultMOTable createStaticIfXTable() { |
|||
MOTableSubIndex[] subIndexes = |
|||
new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) }; |
|||
MOTableIndex indexDef = new MOTableIndex(subIndexes, false); |
|||
MOColumn[] columns = new MOColumn[19]; |
|||
int c = 0; |
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifName
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifInMulticastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifInBroadcastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifOutMulticastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifOutBroadcastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInOctets
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInUcastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInMulticastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInBroadcastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutOctets
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutUcastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutMulticastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutBroadcastPkts
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_INTEGER, |
|||
MOAccessImpl.ACCESS_READ_WRITE); // ifLinkUpDownTrapEnable
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_GAUGE32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifHighSpeed
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_INTEGER, |
|||
MOAccessImpl.ACCESS_READ_WRITE); // ifPromiscuousMode
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_INTEGER, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifConnectorPresent
|
|||
columns[c++] = |
|||
new MOMutableColumn(c, SMIConstants.SYNTAX_OCTET_STRING, // ifAlias
|
|||
MOAccessImpl.ACCESS_READ_WRITE, null); |
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_TIMETICKS, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifCounterDiscontinuityTime
|
|||
|
|||
DefaultMOTable ifXTable = |
|||
new DefaultMOTable(new OID("1.3.6.1.2.1.31.1.1.1"), indexDef, columns); |
|||
MOMutableTableModel model = (MOMutableTableModel) ifXTable.getModel(); |
|||
Variable[] rowValues1 = new Variable[] { |
|||
new OctetString("Ethernet-0"), |
|||
new Integer32(1), |
|||
new Integer32(2), |
|||
new Integer32(3), |
|||
new Integer32(4), |
|||
new Integer32(5), |
|||
new Integer32(6), |
|||
new Integer32(7), |
|||
new Integer32(8), |
|||
new Integer32(9), |
|||
new Integer32(10), |
|||
new Integer32(11), |
|||
new Integer32(12), |
|||
new Integer32(13), |
|||
new Integer32(14), |
|||
new Integer32(15), |
|||
new Integer32(16), |
|||
new OctetString("My eth"), |
|||
new TimeTicks(1000) |
|||
}; |
|||
Variable[] rowValues2 = new Variable[] { |
|||
new OctetString("Loopback"), |
|||
new Integer32(21), |
|||
new Integer32(22), |
|||
new Integer32(23), |
|||
new Integer32(24), |
|||
new Integer32(25), |
|||
new Integer32(26), |
|||
new Integer32(27), |
|||
new Integer32(28), |
|||
new Integer32(29), |
|||
new Integer32(30), |
|||
new Integer32(31), |
|||
new Integer32(32), |
|||
new Integer32(33), |
|||
new Integer32(34), |
|||
new Integer32(35), |
|||
new Integer32(36), |
|||
new OctetString("My loop"), |
|||
new TimeTicks(2000) |
|||
}; |
|||
model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1)); |
|||
model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2)); |
|||
ifXTable.setVolatile(true); |
|||
return ifXTable; |
|||
} |
|||
|
|||
private static DefaultMOTable createStaticIfTable() { |
|||
MOTableSubIndex[] subIndexes = |
|||
new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) }; |
|||
MOTableIndex indexDef = new MOTableIndex(subIndexes, false); |
|||
MOColumn[] columns = new MOColumn[8]; |
|||
int c = 0; |
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_INTEGER, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifIndex
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifDescr
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_INTEGER, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifType
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_INTEGER, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_GAUGE32, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
|
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifPhysAddress
|
|||
columns[c++] = |
|||
new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, // ifAdminStatus
|
|||
MOAccessImpl.ACCESS_READ_WRITE, null); |
|||
columns[c++] = |
|||
new MOColumn(c, SMIConstants.SYNTAX_INTEGER, |
|||
MOAccessImpl.ACCESS_READ_ONLY); // ifOperStatus
|
|||
|
|||
DefaultMOTable ifTable = |
|||
new DefaultMOTable(new OID("1.3.6.1.2.1.2.2.1"), indexDef, columns); |
|||
MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel(); |
|||
Variable[] rowValues1 = new Variable[] { |
|||
new Integer32(1), |
|||
new OctetString("eth0"), |
|||
new Integer32(6), |
|||
new Integer32(1500), |
|||
new Gauge32(100000000), |
|||
new OctetString("00:00:00:00:01"), |
|||
new Integer32(1), |
|||
new Integer32(1) |
|||
}; |
|||
Variable[] rowValues2 = new Variable[] { |
|||
new Integer32(2), |
|||
new OctetString("loopback"), |
|||
new Integer32(24), |
|||
new Integer32(1500), |
|||
new Gauge32(10000000), |
|||
new OctetString("00:00:00:00:02"), |
|||
new Integer32(1), |
|||
new Integer32(1) |
|||
}; |
|||
model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1)); |
|||
model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2)); |
|||
ifTable.setVolatile(true); |
|||
return ifTable; |
|||
} |
|||
|
|||
private static DefaultMOTable createStaticSnmp4sTable() { |
|||
MOTableSubIndex[] subIndexes = |
|||
new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) }; |
|||
MOTableIndex indexDef = new MOTableIndex(subIndexes, false); |
|||
MOColumn[] columns = new MOColumn[8]; |
|||
int c = 0; |
|||
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_NULL, MOAccessImpl.ACCESS_READ_ONLY); // testNull
|
|||
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // testBoolean
|
|||
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifType
|
|||
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
|
|||
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_GAUGE32, MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
|
|||
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING, MOAccessImpl.ACCESS_READ_ONLY); //ifPhysAddress
|
|||
columns[c++] = new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_WRITE, |
|||
null); |
|||
// ifAdminStatus
|
|||
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); |
|||
// ifOperStatus
|
|||
|
|||
DefaultMOTable ifTable = |
|||
new DefaultMOTable(new OID("1.3.6.1.4.1.50000.1.1"), indexDef, columns); |
|||
MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel(); |
|||
Variable[] rowValues1 = new Variable[] { |
|||
new Integer32(1), |
|||
new OctetString("eth0"), |
|||
new Integer32(6), |
|||
new Integer32(1500), |
|||
new Gauge32(100000000), |
|||
new OctetString("00:00:00:00:01"), |
|||
new Integer32(1), |
|||
new Integer32(1) |
|||
}; |
|||
Variable[] rowValues2 = new Variable[] { |
|||
new Integer32(2), |
|||
new OctetString("loopback"), |
|||
new Integer32(24), |
|||
new Integer32(1500), |
|||
new Gauge32(10000000), |
|||
new OctetString("00:00:00:00:02"), |
|||
new Integer32(1), |
|||
new Integer32(1) |
|||
}; |
|||
model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1)); |
|||
model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2)); |
|||
ifTable.setVolatile(true); |
|||
return ifTable; |
|||
} |
|||
|
|||
protected void initTransportMappings() throws IOException { |
|||
transportMappings = new TransportMapping[2]; |
|||
Address addr = GenericAddress.parse(address); |
|||
TransportMapping tm = |
|||
TransportMappings.getInstance().createTransportMapping(addr); |
|||
transportMappings[0] = tm; |
|||
transportMappings[1] = new DefaultTcpTransportMapping(new TcpAddress(address)); |
|||
} |
|||
|
|||
public void start(String ip, String port) throws IOException { |
|||
address = ip + "/" + port; |
|||
//BasicConfigurator.configure();
|
|||
init(); |
|||
addShutdownHook(); |
|||
// loadConfig(ImportModes.REPLACE_CREATE);
|
|||
getServer().addContext(new OctetString("public")); |
|||
finishInit(); |
|||
run(); |
|||
sendColdStartNotification(); |
|||
} |
|||
|
|||
protected void unregisterManagedObjects() { |
|||
// here we should unregister those objects previously registered...
|
|||
} |
|||
|
|||
protected void addCommunities(SnmpCommunityMIB communityMIB) { |
|||
Variable[] com2sec = new Variable[] { |
|||
new OctetString("public"), // community name
|
|||
new OctetString("cpublic"), // security name
|
|||
getAgent().getContextEngineID(), // local engine ID
|
|||
new OctetString("public"), // default context name
|
|||
new OctetString(), // transport tag
|
|||
new Integer32(StorageType.nonVolatile), // storage type
|
|||
new Integer32(RowStatus.active) // row status
|
|||
}; |
|||
MOTableRow row = |
|||
communityMIB.getSnmpCommunityEntry().createRow( |
|||
new OctetString("public2public").toSubIndex(true), com2sec); |
|||
communityMIB.getSnmpCommunityEntry().addRow((SnmpCommunityMIB.SnmpCommunityEntryRow) row); |
|||
// snmpCommunityMIB.setSourceAddressFiltering(true);
|
|||
} |
|||
|
|||
protected void registerSnmpMIBs() { |
|||
heartbeatMIB = new Snmp4jHeartbeatMib(super.getNotificationOriginator(), |
|||
new OctetString(), |
|||
super.snmpv2MIB.getSysUpTime()); |
|||
agentppSimulationMIB = new AgentppSimulationMib(); |
|||
super.registerSnmpMIBs(); |
|||
} |
|||
|
|||
protected void initMessageDispatcher() { |
|||
this.dispatcher = new MessageDispatcherImpl(); |
|||
this.mpv3 = new MPv3(this.agent.getContextEngineID().getValue()); |
|||
this.usm = new USM(SecurityProtocols.getInstance(), this.agent.getContextEngineID(), this.updateEngineBoots()); |
|||
SecurityModels.getInstance().addSecurityModel(this.usm); |
|||
SecurityProtocols.getInstance().addDefaultProtocols(); |
|||
this.dispatcher.addMessageProcessingModel(new MPv1()); |
|||
this.dispatcher.addMessageProcessingModel(new MPv2c()); |
|||
this.dispatcher.addMessageProcessingModel(this.mpv3); |
|||
this.initSnmpSession(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.snmp; |
|||
|
|||
import java.io.IOException; |
|||
import java.util.Map; |
|||
import java.util.Scanner; |
|||
|
|||
public class SnmpTestV2 { |
|||
public static void main(String[] args) throws IOException { |
|||
SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public"); |
|||
|
|||
device.start(); |
|||
device.setUpMappings(Map.of( |
|||
".1.3.6.1.2.1.1.1.50", "12", |
|||
".1.3.6.1.2.1.2.1.52", "56", |
|||
".1.3.6.1.2.1.3.1.54", "yes", |
|||
".1.3.6.1.2.1.7.1.58", "" |
|||
)); |
|||
|
|||
|
|||
// while (true) {
|
|||
// new Scanner(System.in).nextLine();
|
|||
// device.sendTrap("127.0.0.1", 1062, Map.of(".1.3.6.1.2.87.1.56", "12"));
|
|||
// System.out.println("sent");
|
|||
// }
|
|||
|
|||
// Snmp snmp = new Snmp(device.transportMappings[0]);
|
|||
// device.snmp.addCommandResponder(event -> {
|
|||
// System.out.println(event);
|
|||
// });
|
|||
|
|||
new Scanner(System.in).nextLine(); |
|||
} |
|||
|
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue