Compare commits

...

32 Commits
v0.1 ... master

Author SHA1 Message Date
9f19e12375
fix config 2025-03-11 07:40:14 +01:00
917db84ebb harrison 2025-03-10 21:36:33 +01:00
7c7b175893
add new oid, 3
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-13 10:52:46 +01:00
057b2c3776
add new oid, 2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-13 10:47:46 +01:00
15cfb7b51c
add new oid
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-13 10:43:06 +01:00
2852f871ec
new points,2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 18:36:31 +01:00
cdb2eeceed
new points
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 18:01:10 +01:00
31a548f08e david
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-02-10 15:18:22 +01:00
e0a44205b1 add status
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-10 13:52:16 +01:00
81378d80e4 add unit in variable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-10 13:45:39 +01:00
b0def60a92 fix, 2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-02-10 13:43:15 +01:00
9846f70e5c fix, 1
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-02-10 13:40:06 +01:00
2d2e251f0b adjust for dtrack
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-02-10 13:38:19 +01:00
aa7498d93e
change cluster switch
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-05-06 19:00:13 +02:00
b5b2e3ac0d
nothing
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-01-29 22:35:52 +01:00
ad9b3625e1
fix anno
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-01-26 18:28:12 +01:00
56aec29c76
add cluster switch to config
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-01-26 14:39:08 +01:00
85124d028d
use 64bit counter
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-01-25 16:10:53 +01:00
4ed32f8314
fix struct
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-01-25 15:24:56 +01:00
4222e19573
skip diff value
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-01-25 15:22:29 +01:00
11e63155bc
diff default value
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-01-25 15:18:29 +01:00
353a3780c6
anno
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-01-25 15:00:40 +01:00
f407198c5b
fix config
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-01-25 14:35:41 +01:00
cf4c3d9ed0
ci script
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-01-25 14:31:06 +01:00
1c19117170
diff values 2024-01-25 14:22:12 +01:00
0f89fcf325
fix dockerfile 2024-01-25 12:45:49 +01:00
1ce208d38b
modified readme 2024-01-25 12:40:25 +01:00
5db2f64a54
modified readme 2024-01-25 12:39:35 +01:00
5b77a6a059
my own modifications, part 1 2024-01-25 12:35:27 +01:00
b68e929c57 add incomplete ci script 2024-01-23 16:15:26 +01:00
89089f429c add Dockerfile 2024-01-23 16:12:50 +01:00
Daniel Chote
5021794990 adding desk UPS to the mix along with its external temperature probe 2019-11-20 08:36:50 -05:00
27 changed files with 782 additions and 531 deletions

View File

@ -1,61 +0,0 @@
name: Build and Test
on:
push:
branches:
- master
pull_request:
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
- name: Check out code
uses: actions/checkout@v1
- name: Lint Go Code
run: |
export PATH=$PATH:$(go env GOPATH)/bin # temporary fix. See https://github.com/actions/setup-go/issues/14
go get -u golang.org/x/lint/golint
make lint
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
- name: Check out code
uses: actions/checkout@v1
- name: Run Unit tests
run: |
export PATH=$PATH:$(go env GOPATH)/bin
make test-coverage
build:
name: Build
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
- name: Check out code
uses: actions/checkout@v1
- name: Build Everything
run: |
export PATH=$PATH:$(go env GOPATH)/bin
make build

View File

@ -1,25 +0,0 @@
name: Release
on:
create:
tags:
- v*
jobs:
release:
name: Publish Release
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v1
- name: Validates GO releaser config
uses: docker://goreleaser/goreleaser:latest
with:
args: check
- name: Create release on GitHub
uses: docker://goreleaser/goreleaser:latest
with:
args: release
env:
GITHUB_TOKEN: ${{secrets.GORELEASER_GITHUB_TOKEN}}

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
build build
.DS_Store .DS_Store
snmp-mqtt
src/smq/smq

View File

@ -1,13 +0,0 @@
env:
- GO111MODULE=on
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- arm
- amd64

77
.woodpecker.yml Normal file
View File

@ -0,0 +1,77 @@
steps:
build:
image: golang:1.22.5-alpine3.20
commands:
- GOPATH=/woodpecker/go
- ls -l
- cd src/smq
- go mod tidy
- go build -a -installsuffix nocgo -o smq snmp-mqtt.go
- cp smq ../..
when:
- event: [push, tag]
scan:
image: quay.io/wollud1969/woodpecker-helper:0.5.1
environment:
TRIVY_TOKEN:
from_secret: trivy_token
TRIVY_URL:
from_secret: trivy_url
DTRACK_API_KEY:
from_secret: dtrack_api_key
DTRACK_API_URL:
from_secret: dtrack_api_url
commands:
- export GOPATH=/woodpecker/go # the export is required, otherwise trivy will not consider the variable
- HOME=/home/`id -nu`
- TAG="${CI_COMMIT_TAG:-$CI_COMMIT_SHA}"
- |
trivy fs \
--server $TRIVY_URL \
--token $TRIVY_TOKEN \
--format cyclonedx \
--scanners license \
--output /tmp/sbom.xml \
.
- cat /tmp/sbom.xml
- |
curl -X "POST" \
-H "Content-Type: multipart/form-data" \
-H "X-Api-Key: $DTRACK_API_KEY" \
-F "autoCreate=true" \
-F "projectName=$CI_REPO" \
-F "projectVersion=$TAG" \
-F "bom=@/tmp/sbom.xml"\
"$DTRACK_API_URL/api/v1/bom"
when:
- event: [push, tag]
dockerize:
image: plugins/kaniko
settings:
repo: ${FORGE_NAME}/${CI_REPO}
registry:
from_secret: container_registry
tags: latest,${CI_COMMIT_SHA},${CI_COMMIT_TAG}
username:
from_secret: container_registry_username
password:
from_secret: container_registry_password
dockerfile: Dockerfile
when:
- event: [push, tag]
deploy:
image: quay.io/wollud1969/woodpecker-helper:0.5.1
environment:
KUBE_CONFIG_CONTENT:
from_secret: kube_config
commands:
- export IMAGE_TAG=$CI_COMMIT_TAG
- printf "$KUBE_CONFIG_CONTENT" > /tmp/kubeconfig
- export KUBECONFIG=/tmp/kubeconfig
- ./deployment/deploy.sh
when:
- event: tag

9
Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM scratch
ENV SNMP_MQTT_CONF ""
COPY smq ./
ENTRYPOINT ["./smq"]

View File

@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

View File

@ -1,45 +0,0 @@
PROJECT_NAME := "snmp-mqtt"
PKG := "github.com/dchote/$(PROJECT_NAME)"
PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/)
GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go)
.PHONY: all dep lint vet test test-coverage build clean
all: build
dep: ## Get the dependencies
@echo Installing dependencies
@go mod download
lint: ## Lint Golang files
@golint -set_exit_status ${PKG_LIST}
vet: ## Run go vet
@go vet ${PKG_LIST}
test: ## Run unittests
@go test -short ${PKG_LIST}
test-coverage: ## Run tests with coverage
@go test -short -coverprofile cover.out -covermode=atomic ${PKG_LIST}
@cat cover.out >> coverage.txt
build: dep ## Build the binary file
@echo Building native binary
@go build -i -o build/snmp-mqtt $(PKG)
linux: build
@echo Building Linux binary
@env CC=x86_64-linux-musl-gcc GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -ldflags "-linkmode external -extldflags -static" -o build/snmp-mqtt
raspi: build
@echo Building Rasperry Pi Linux binary
@env GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -o build/snmp-mqtt
clean: ## Remove previous build
@rm -f $(PROJECT_NAME)/build
help: ## Display this help screen
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

View File

@ -1,38 +1,61 @@
# snmp-mqtt # snmp-mqtt
This project is directly derived from https://github.com/dchote/snmp-mqtt
A simple go app that reads SNMP values and publishes it to the specified MQTT endpoint at the specified interval. A simple go app that reads SNMP values and publishes it to the specified MQTT endpoint at the specified interval.
Please download the precompiled binary from the releases page: https://github.com/dchote/snmp-mqtt/releases In constrast to the original version, all configuration must be provided via an environment variable containing
a JSON string.
The MQTT configuration is part of this JSON string.
The topic has been moved to the MQTT configuration and there is now only one topic for all endpoints and variables.
The published message on MQTT looks like this:
``` ```
Usage: snmp-mqtt [options] {"device":"172.16.3.1","label":"router","variables":{"lan-in":{"label":"lan-in","variable":".1.3.6.1.2.1.31.1.1.1.6.2","value":"979673705579"},"lan-out":{"label":"lan-out","variable":".1.3.6.1.2.1.31.1.1.1.10.2","value":"1813410276168"},"wan-in":{"label":"wan-in","variable":".1.3.6.1.2.1.31.1.1.1.6.4","value":"83591215399"},"wan-out":{"label":"wan-out","variable":".1.3.6.1.2.1.31.1.1.1.10.4","value":"83741895468"}}}
Options:
--endpoints_map=<endpoints_map> SNMP Endpoints Map File [default: ./endpoints.json]
--server=<server> MQTT server host/IP [default: 127.0.0.1]
--port=<port> MQTT server port [default: 1883]
--topic=<topic> MQTT topic prefix [default: snmp]
--clientid=<clientid> MQTT client identifier [default: snmp]
--interval=<interval> Poll interval (seconds) [default: 5]
-h, --help Show this screen.
-v, --version Show version.
``` ```
An example endpoints.json file: ```
export SNMP_MQTT_CONF=$(cat config.json)
./smq
```
An example config.json file:
``` ```
{ {
"mqtt": {
"broker": "mqtt://172.23.1.102:1883",
"tlsEnable": "false",
"topic": "snmp"
},
"interval": 10,
"snmpEndpoints": [ "snmpEndpoints": [
{ {
"endpoint": "172.18.0.1", "endpoint": "172.16.3.1",
"label": "router",
"community": "public", "community": "public",
"oidTopics": [ "oidTopics": [
{ {
"oid": ".1.3.6.1.2.1.31.1.1.1.6.4", "oid": ".1.3.6.1.2.1.31.1.1.1.6.4",
"topic": "router/bytesIn" "label": "wan-in",
"diff": "true"
}, },
{ {
"oid": ".1.3.6.1.2.1.31.1.1.1.10.4", "oid": ".1.3.6.1.2.1.31.1.1.1.10.4",
"topic": "router/bytesOut" "label": "wan-out",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.6.2",
"label": "lan-in",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.10.2",
"label": "lan-out",
"diff": "true"
} }
] ]
} }

View File

@ -1,65 +0,0 @@
package config
import (
"encoding/json"
"os"
"strconv"
)
// OIDTopicObject maps OIDs to MQTT topics
type OIDTopicObject struct {
OID string `json:"oid"`
Topic string `json:"topic"`
}
// SNMPEndpointObject is the SNMP Endpoint definition
type SNMPEndpointObject struct {
Endpoint string `json:"endpoint"`
Community string `json:"community"`
OIDTopics []OIDTopicObject `json:"oidTopics"`
}
// SNMPMapObject basic map of endpoints
type SNMPMapObject struct {
SNMPEndpoints []SNMPEndpointObject `json:"snmpEndpoints"`
}
var (
// SNMPMap is the loaded JSON configuration
SNMPMap *SNMPMapObject
// Server is the MQTT server address
Server string
// Port is the MQTT server listen port
Port int
// ClientID is how the name of the client
ClientID string
// Interval is the poll interval in seconds
Interval int
)
// ConnectionString returns the MQTT connection string
func ConnectionString() string {
return "tcp://" + Server + ":" + strconv.Itoa(Port)
}
// LoadMap loads the file in to the struct
func LoadMap(file string) error {
configFile, err := os.Open(file)
defer configFile.Close()
if err != nil {
return err
}
jsonParser := json.NewDecoder(configFile)
err = jsonParser.Decode(&SNMPMap)
if err != nil {
return err
}
return nil
}

116
deployment/config.json Normal file
View File

@ -0,0 +1,116 @@
{
"mqtt": {
"broker": "mqtt://emqx01-anonymous-cluster-internal.broker.svc.cluster.local:1883",
"tlsEnable": "false",
"topic": "snmp"
},
"interval": 60,
"snmpEndpoints": [
{
"endpoint": "172.16.13.10",
"label": "david",
"community": "public",
"oidTopics": [
{
"oid": ".1.3.6.1.4.1.2021.10.1.3.1",
"label": "load1",
"diff": "false"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.6.2",
"label": "lan-in",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.7.2",
"label": "lan-in-pkts",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.10.2",
"label": "lan-out",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.11.2",
"label": "lan-out-pkts",
"diff": "true"
},
{
"oid": ".1.3.6.1.4.1.9676.1",
"label": "time-req-pkts",
"diff": "true"
}
]
},
{
"endpoint": "172.16.13.11",
"label": "harrison",
"community": "public",
"oidTopics": [
{
"oid": ".1.3.6.1.4.1.2021.10.1.3.1",
"label": "load1",
"diff": "false"
},
{
"oid": ".1.3.6.1.4.1.9676.123.1.4",
"label": "stratum",
"diff": "false"
},
{
"oid": ".1.3.6.1.4.1.9676.123.1.7",
"label": "rootdisp",
"diff": "false"
},
{
"oid": ".1.3.6.1.4.1.9676.123.2.3",
"label": "ss-reset",
"diff": "false"
},
{
"oid": ".1.3.6.1.4.1.9676.123.2.10",
"label": "processed-pkts",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.6.2",
"label": "lan-in",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.7.2",
"label": "lan-in-pkts",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.10.2",
"label": "lan-out",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.11.2",
"label": "lan-out-pkts",
"diff": "true"
}
]
},
{
"endpoint": "172.16.3.1",
"label": "router",
"community": "public",
"oidTopics": [
{
"oid": ".1.3.6.1.2.1.31.1.1.1.6.5",
"label": "wan-in",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.10.5",
"label": "wan-out",
"diff": "true"
}
]
}
]
}

View File

@ -0,0 +1,27 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: snmp-mqtt
namespace: homea
labels:
app: snmp-nmqtt
annotations:
secret.reloader.stakater.com/reload: snmp-mqtt-conf
spec:
replicas: 1
selector:
matchLabels:
app: snmp-nmqtt
template:
metadata:
labels:
app: snmp-nmqtt
spec:
containers:
- name: snmp-nmqtt
image: %IMAGE%
envFrom:
- configMapRef:
name: snmp-mqtt-conf

34
deployment/deploy.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/bash
if [ "$IMAGE_TAG" == "" ]; then
echo "Make sure IMAGE_TAG is set"
exit 1
fi
IMAGE_NAME=gitea.hottis.de/wn/snmp-mqtt
NAMESPACE=homea
DEPLOYMENT_DIR=$PWD/deployment
CONFIG_FILE=config.json
pushd $DEPLOYMENT_DIR > /dev/null
kubectl create namespace $NAMESPACE \
--dry-run=client \
-o yaml | \
kubectl -f - apply
kubectl create configmap snmp-mqtt-conf \
--from-literal=SNMP_MQTT_CONF="`cat $CONFIG_FILE`" \
--dry-run=client \
-o yaml \
--save-config | \
kubectl apply -f - -n $NAMESPACE
cat $DEPLOYMENT_DIR/deploy-yml.tmpl | \
sed -e 's,%IMAGE%,'$IMAGE_NAME':'$IMAGE_TAG','g | \
kubectl apply -f - -n $NAMESPACE
popd > /dev/null

4
deployment/pushconfig.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
kubectl create configmap snmp-mqtt-conf --from-literal=SNMP_MQTT_CONF="`cat config.json`" --dry-run=client -o yaml --save-config | kubectl apply -f - -n homea

View File

@ -1,92 +0,0 @@
{
"snmpEndpoints": [
{
"endpoint": "172.18.0.1",
"community": "public",
"oidTopics": [
{
"oid": ".1.3.6.1.2.1.31.1.1.1.6.4",
"topic": "network/router/interfaces/wan/bytesIn"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.10.4",
"topic": "network/router/interfaces/wan/bytesOut"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.6.2",
"topic": "network/router/interfaces/lan/bytesIn"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.10.2",
"topic": "network/router/interfaces/lan/bytesOut"
}
]
},
{
"endpoint": "172.18.0.50",
"community": "public",
"oidTopics": [
{
"oid": ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.2.1",
"topic": "power/rackPDU/outputCurrentX10"
},
{
"oid": ".1.3.6.1.4.1.318.1.1.12.1.16.0",
"topic": "power/rackPDU/watts"
}
]
},
{
"endpoint": "172.18.0.51",
"community": "public",
"oidTopics": [
{
"oid": ".1.3.6.1.4.1.318.1.1.1.3.2.1.0",
"topic": "power/rackUPS/lineVoltage"
},
{
"oid": ".1.3.6.1.4.1.318.1.1.1.4.2.4.0",
"topic": "power/rackUPS/outputCurrent"
},
{
"oid": ".1.3.6.1.4.1.318.1.1.1.2.2.1.0",
"topic": "power/rackUPS/batteryCapacity"
},
{
"oid": ".1.3.6.1.4.1.318.1.1.1.2.2.3.0",
"topic": "power/rackUPS/batteryRuntime"
},
{
"oid": ".1.3.6.1.4.1.318.1.1.1.2.2.2.0",
"topic": "power/rackUPS/batteryTemperature"
}
]
},
{
"endpoint": "172.18.0.52",
"community": "public",
"oidTopics": [
{
"oid": ".1.3.6.1.4.1.318.1.1.1.3.2.1.0",
"topic": "power/printerUPS/lineVoltage"
},
{
"oid": ".1.3.6.1.4.1.318.1.1.1.4.2.4.0",
"topic": "power/printerUPS/outputCurrent"
},
{
"oid": ".1.3.6.1.4.1.318.1.1.1.2.2.1.0",
"topic": "power/printerUPS/batteryCapacity"
},
{
"oid": ".1.3.6.1.4.1.318.1.1.1.2.2.3.0",
"topic": "power/printerUPS/batteryRuntime"
},
{
"oid": ".1.3.6.1.4.1.318.1.1.1.2.2.2.0",
"topic": "power/printerUPS/batteryTemperature"
}
]
}
]
}

10
go.mod
View File

@ -1,10 +0,0 @@
module github.com/dchote/snmp-mqtt
go 1.13
require (
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
github.com/eclipse/paho.mqtt.golang v1.2.0
github.com/soniah/gosnmp v1.22.0
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect
)

20
go.sum
View File

@ -1,20 +0,0 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/soniah/gosnmp v1.22.0 h1:jVJi8+OGvR+JHIaZKMmnyNP0akJd2vEgNatybwhZvxg=
github.com/soniah/gosnmp v1.22.0/go.mod h1:DuEpAS0az51+DyVBQwITDsoq4++e3LTNckp2GoasF2I=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -1,84 +0,0 @@
package main
import (
"log"
"os"
"os/signal"
"syscall"
"github.com/dchote/snmp-mqtt/config"
"github.com/dchote/snmp-mqtt/snmp"
"github.com/docopt/docopt-go"
)
var exitChan = make(chan int)
// VERSION beause...
const VERSION = "0.0.1"
func cliArguments() {
usage := `
Usage: snmp-mqtt [options]
Options:
--endpoints_map=<endpoints_map> SNMP Endpoints Map File [default: ./endpoints.json]
--server=<server> MQTT server host/IP [default: 127.0.0.1]
--port=<port> MQTT server port [default: 1883]
--clientid=<clientid> MQTT client identifier [default: snmp]
--interval=<interval> Poll interval (seconds) [default: 5]
-h, --help Show this screen.
-v, --version Show version.
`
args, _ := docopt.ParseArgs(usage, os.Args[1:], VERSION)
mapFile, _ := args.String("--endpoints_map")
err := config.LoadMap(mapFile)
if err != nil {
log.Println(err)
log.Fatal("error opening " + mapFile)
}
config.Server, _ = args.String("--server")
config.Port, _ = args.Int("--port")
config.ClientID, _ = args.String("--clientid")
config.Interval, _ = args.Int("--interval")
log.Printf("server: %s, port: %d, client identifier: %s, poll interval: %d", config.Server, config.Port, config.ClientID, config.Interval)
}
// sigChannelListen basic handlers for inbound signals
func sigChannelListen() {
signalChan := make(chan os.Signal, 1)
code := 0
signal.Notify(signalChan, os.Interrupt)
signal.Notify(signalChan, os.Kill)
signal.Notify(signalChan, syscall.SIGTERM)
select {
case sig := <-signalChan:
log.Printf("Received signal %s. shutting down", sig)
case code = <-exitChan:
switch code {
case 0:
log.Println("Shutting down")
default:
log.Println("*Shutting down")
}
}
os.Exit(code)
}
func main() {
cliArguments()
// catch signals
go sigChannelListen()
// run sensor poll loop
snmp.Init()
os.Exit(0)
}

View File

@ -1,98 +0,0 @@
package snmp
import (
"fmt"
"log"
"strings"
"sync"
"time"
"github.com/dchote/snmp-mqtt/config"
"github.com/eclipse/paho.mqtt.golang"
"github.com/soniah/gosnmp"
)
var ()
// Init contains the generic read/publish loop
func Init() {
opts := mqtt.NewClientOptions().AddBroker(config.ConnectionString()).SetClientID(config.ClientID)
opts.SetKeepAlive(2 * time.Second)
opts.SetPingTimeout(1 * time.Second)
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
var wg sync.WaitGroup
for {
wg.Add(1)
go func() {
defer wg.Done()
for _, endpoint := range config.SNMPMap.SNMPEndpoints {
log.Println("Polling endpoint " + endpoint.Endpoint)
snmp := gosnmp.GoSNMP{}
snmp.Target = endpoint.Endpoint
snmp.Port = 161
snmp.Version = gosnmp.Version2c
snmp.Community = endpoint.Community
snmp.Timeout = time.Duration(5 * time.Second)
err := snmp.Connect()
if err != nil {
log.Fatal("SNMP Connect error\n")
}
oids := []string{}
for _, oidTopic := range endpoint.OIDTopics {
oids = append(oids, oidTopic.OID)
}
result, err := snmp.Get(oids)
if err != nil {
log.Printf("error in Get: %s", err)
} else {
for _, variable := range result.Variables {
for _, oidTopic := range endpoint.OIDTopics {
if strings.Compare(oidTopic.OID, variable.Name) == 0 {
convertedValue := ""
switch variable.Type {
case gosnmp.OctetString:
convertedValue = string(variable.Value.([]byte))
default:
convertedValue = fmt.Sprintf("%d", gosnmp.ToBigInt(variable.Value))
}
log.Printf("%s = %s", oidTopic.Topic, convertedValue)
token := client.Publish(oidTopic.Topic, 0, false, convertedValue)
token.Wait()
if token.Error() != nil {
log.Fatal(token.Error())
}
}
}
}
}
snmp.Conn.Close()
}
}()
time.Sleep(time.Duration(config.Interval) * time.Second)
}
wg.Wait()
client.Disconnect(250)
time.Sleep(1 * time.Second)
}

37
src/smq/config-test.json Normal file
View File

@ -0,0 +1,37 @@
{
"mqtt": {
"broker": "mqtt://172.23.1.102:1883",
"tlsEnable": "false",
"topic": "snmp"
},
"interval": 10,
"snmpEndpoints": [
{
"endpoint": "172.16.3.1",
"label": "router",
"community": "public",
"oidTopics": [
{
"oid": ".1.3.6.1.2.1.31.1.1.1.6.4",
"label": "wan-in",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.10.4",
"label": "wan-out",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.6.2",
"label": "lan-in",
"diff": "true"
},
{
"oid": ".1.3.6.1.2.1.31.1.1.1.10.2",
"label": "lan-out",
"diff": "true"
}
]
}
]
}

47
src/smq/config/config.go Normal file
View File

@ -0,0 +1,47 @@
package config
import (
"encoding/json"
"os"
"log"
)
type OIDTopicObject struct {
OID string `json:"oid"`
Label string `json:"label"`
Diff string `json:"diff"`
}
// SNMPEndpointObject is the SNMP Endpoint definition
type SNMPEndpointObject struct {
Endpoint string `json:"endpoint"`
Label string `json:"label"`
Community string `json:"community"`
OIDTopics []OIDTopicObject `json:"oidTopics"`
}
type MQTTConfigObject struct {
Broker string `json:"broker"`
Username string `json:"username"`
Password string `json:"password"`
TlsEnable string `json:"tlsEnable"`
Topic string `json:"topic"`
}
type ConfigObject struct {
Mqtt MQTTConfigObject `json:"mqtt"`
Interval int `json:"interval"`
SNMPEndpoints []SNMPEndpointObject `json:"snmpEndpoints"`
}
var Config ConfigObject
func LoadConfiguration() {
cfg := os.Getenv("SNMP_MQTT_CONF")
log.Printf("cfg: %s", cfg)
err := json.Unmarshal([]byte(cfg), &Config)
if err != nil {
log.Fatalf("Unable to parse configuration: %v", err)
}
}

15
src/smq/go.mod Normal file
View File

@ -0,0 +1,15 @@
module smq
go 1.22.5
require (
github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/google/uuid v1.6.0
github.com/gosnmp/gosnmp v1.37.0
)
require (
github.com/gorilla/websocket v1.5.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.1.0 // indirect
)

20
src/smq/go.sum Normal file
View File

@ -0,0 +1,20 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gosnmp/gosnmp v1.37.0 h1:/Tf8D3b9wrnNuf/SfbvO+44mPrjVphBhRtcGg22V07Y=
github.com/gosnmp/gosnmp v1.37.0/go.mod h1:GDH9vNqpsD7f2HvZhKs5dlqSEcAS6s6Qp099oZRCR+M=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

113
src/smq/mqtt/mqtt.go Normal file
View File

@ -0,0 +1,113 @@
package mqtt
import "log"
import "fmt"
import MQTT "github.com/eclipse/paho.mqtt.golang"
import "github.com/google/uuid"
import "crypto/tls"
import "smq/config"
// import "smq/counter"
type Message struct {
Topic string
Payload []byte
}
var OutputChannel chan Message = make(chan Message, 100)
var mqttClient MQTT.Client
func onConnectionLost(client MQTT.Client, error error) {
log.Printf("Connection lost, error %s", error)
}
func onReconnecting(client MQTT.Client, clientOpts *MQTT.ClientOptions) {
log.Println("Oops, connection lost, already reconnecting ...")
}
func onConnect(client MQTT.Client) {
log.Println("Connected")
}
func outputDispatcher(client MQTT.Client) {
for {
select {
case message := <- OutputChannel:
log.Printf("Message arrived in outputDispatcher, topic: %s, payload: %s\n", message.Topic, message.Payload)
if token := client.Publish(message.Topic, 0, false, message.Payload); token.Wait() && token.Error() != nil {
log.Printf("Unable to publish, error %s", token.Error())
}
log.Println("Successfully published")
}
}
}
func Publish(msg []byte) {
message := Message {
Topic: config.Config.Mqtt.Topic,
Payload: msg,
}
select {
case OutputChannel <- message:
{}
default:
log.Printf("Channel full, message %s lost")
}
}
func Start() {
broker := config.Config.Mqtt.Broker
if broker == "" {
log.Fatal("No broker given")
}
prefix := "SMQ"
uuid := uuid.New()
clientId := fmt.Sprintf("%s-%s", prefix, uuid)
opts := MQTT.NewClientOptions().
AddBroker(broker).
SetClientID(clientId).
SetConnectionLostHandler(onConnectionLost).
SetOnConnectHandler(onConnect).
SetReconnectingHandler(onReconnecting).
SetConnectRetry(true)
username := config.Config.Mqtt.Username
if username != "" {
opts.SetUsername(username)
}
password := config.Config.Mqtt.Password
if password != "" {
opts.SetPassword(password)
}
enableTls := config.Config.Mqtt.TlsEnable
if enableTls == "true" {
//log.Println("Enabling TLS connection")
tlsConfig := &tls.Config {
InsecureSkipVerify: true,
}
opts.SetTLSConfig(tlsConfig)
}
log.Println("Broker connecting")
mqttClient = MQTT.NewClient(opts)
if token := mqttClient.Connect(); token.Wait() && token.Error() != nil {
log.Fatalf("Unable to connect to broker %s, error %s", broker, token.Error())
}
//log.Printf("Successfully connected to broker %s", broker)
go outputDispatcher(mqttClient)
return
}
func Stop() {
log.Println("Disconnecting from broker")
mqttClient.Disconnect(250)
}

33
src/smq/snmp-mqtt.go Normal file
View File

@ -0,0 +1,33 @@
package main
import (
"log"
"os"
"os/signal"
"smq/config"
"smq/mqtt"
"smq/snmp"
)
func main() {
log.SetPrefix("SMQ: ")
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("starting")
config.LoadConfiguration()
mqtt.Start()
defer mqtt.Stop()
snmp.Start()
defer snmp.Stop()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
<- c
log.Println("terminating")
}

160
src/smq/snmp/snmp.go Normal file
View File

@ -0,0 +1,160 @@
package snmp
import (
"fmt"
"log"
"time"
"encoding/json"
"strconv"
"math"
"smq/config"
"smq/mqtt"
"github.com/gosnmp/gosnmp"
)
type variable_t struct {
Label string `json:"label"`
Variable string `json:"variable"`
Value string `json:"value"`
Unit string `json:"unit"`
Status string `json:"status"`
}
type message_t struct {
Device string `json:"device"`
Label string `json:"label"`
Variables map[string]variable_t `json:"variables"`
}
type lastValue_t struct {
Timestamp time.Time
Value string
}
var lastValues map[string]lastValue_t = make(map[string]lastValue_t)
func calculateDifference(key string, newValue string) (string, error) {
currentTime := time.Now()
lvv, ok := lastValues[key]
if ok {
log.Printf("lvv for %s exists: %s", key, lvv.Value)
oldValueI, err1 := strconv.Atoi(lvv.Value)
if err1 != nil {
return "", fmt.Errorf("failed to convert lastValue: %s, %v", lvv.Value, err1)
}
newValueI, err2 := strconv.Atoi(newValue)
if err2 != nil {
return "", fmt.Errorf("failed to convert newValue: %s, %v", newValue, err2)
}
diffValueI := newValueI - oldValueI
diffTime := int(math.Round(currentTime.Sub(lvv.Timestamp).Seconds()))
diffValuePerSecond := diffValueI / diffTime
log.Printf("Diff: %d, Duration: %d, Diff per second: %d", diffValueI, diffTime, diffValuePerSecond)
lastValues[key] = lastValue_t {
Timestamp: currentTime,
Value: newValue,
}
return strconv.Itoa(diffValuePerSecond), nil
} else {
log.Printf("create lvv for %s", key)
lastValues[key] = lastValue_t {
Timestamp: currentTime,
Value: newValue,
}
return "0", nil
}
return "0", nil
}
func Start() {
for {
for _, endpoint := range config.Config.SNMPEndpoints {
log.Println("Polling endpoint " + endpoint.Endpoint)
snmp := gosnmp.GoSNMP{}
snmp.Target = endpoint.Endpoint
snmp.Port = 161
snmp.Version = gosnmp.Version2c
snmp.Community = endpoint.Community
snmp.Timeout = time.Duration(5 * time.Second)
err := snmp.Connect()
if err != nil {
log.Fatal("SNMP Connect error\n")
}
oids := []string{}
for _, oidTopic := range endpoint.OIDTopics {
oids = append(oids, oidTopic.OID)
}
result, err := snmp.Get(oids)
if err != nil {
log.Printf("error in Get: %s", err)
} else {
message := message_t {
Device: endpoint.Endpoint,
Label: endpoint.Label,
Variables: make(map[string]variable_t),
}
for _, variable := range result.Variables {
for _, oidTopic := range endpoint.OIDTopics {
if oidTopic.OID == variable.Name {
convertedValue := ""
switch variable.Type {
case gosnmp.OctetString:
convertedValue = string(variable.Value.([]byte))
default:
convertedValue = fmt.Sprintf("%d", gosnmp.ToBigInt(variable.Value))
}
if oidTopic.Diff == "true" {
log.Println("Calculate difference to last value")
key := endpoint.Endpoint + ":" + oidTopic.OID
diff, err := calculateDifference(key, convertedValue)
if err != nil {
log.Printf("Error when building difference: %v", err)
convertedValue = "-1"
} else {
convertedValue = diff
}
}
log.Printf("%s = %s", oidTopic.OID, convertedValue)
v := variable_t {
Label: oidTopic.Label,
Variable: oidTopic.OID,
Value: convertedValue,
Unit: "",
Status: "",
}
message.Variables[oidTopic.Label] = v
// build topic and payload and push into the mqtt.OutputChannel
}
}
}
j, err := json.Marshal(message)
if err != nil {
log.Printf("Unable to marshal message, it is lost: %s, %v", message, err)
} else {
mqtt.Publish(j)
}
}
snmp.Conn.Close()
}
time.Sleep(time.Duration(config.Config.Interval) * time.Second)
}
}
func Stop() {
}

45
tools/mysnmpwalk.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/bash
COMMUNITY=""
HOST=""
BASE_OID=""
while getopts "c:h:b:" option; do
case $option in
c) COMMUNITY=$OPTARG
;;
h) HOST=$OPTARG
;;
b) BASE_OID=$OPTARG
;;
?)
echo "Usage $0 -c COMMUNITY -h HOST -b BASE_OID"
exit 1
;;
esac
done
if [ "$COMMUNITY" = "" ]; then
echo "Set a community using -c"
exit 1
fi
if [ "$HOST" = "" ]; then
echo "Set a host using -h"
exit 1
fi
if [ "$BASE_OID" = "" ]; then
echo "Set a base oid using -b"
exit 1
fi
snmpwalk -v 2c -c $COMMUNITY -On $HOST $BASE_OID | while read -r line; do
oid=`echo $line | awk '{print $1}'`
textoid=`snmptranslate $oid`
value=`echo $line | cut -d ' ' -f 3-`
echo "$oid ($textoid): $value"
done