diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 9e60eb4..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 784c618..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -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}} diff --git a/.gitignore b/.gitignore index 6bd6890..4356303 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build .DS_Store snmp-mqtt +src/smq/smq diff --git a/.goreleaser.yml b/.goreleaser.yml deleted file mode 100644 index 0eb367c..0000000 --- a/.goreleaser.yml +++ /dev/null @@ -1,13 +0,0 @@ -env: - - GO111MODULE=on -before: - hooks: - - go mod tidy -builds: -- env: - - CGO_ENABLED=0 - goos: - - linux - goarch: - - arm - - amd64 diff --git a/Makefile b/Makefile deleted file mode 100644 index 441af87..0000000 --- a/Makefile +++ /dev/null @@ -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}' - diff --git a/config/config.go b/config/config.go deleted file mode 100644 index fb5961f..0000000 --- a/config/config.go +++ /dev/null @@ -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 -} diff --git a/endpoints.json b/endpoints.json deleted file mode 100644 index 68b35ef..0000000 --- a/endpoints.json +++ /dev/null @@ -1,122 +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" - } - ] - }, - { - "endpoint": "172.18.0.53", - "community": "public", - "oidTopics": [ - { - "oid": ".1.3.6.1.4.1.318.1.1.1.3.2.1.0", - "topic": "power/dansDesk/lineVoltage" - }, - { - "oid": ".1.3.6.1.4.1.318.1.1.1.4.2.4.0", - "topic": "power/dansDesk/outputCurrent" - }, - { - "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.1.0", - "topic": "power/dansDesk/batteryCapacity" - }, - { - "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.3.0", - "topic": "power/dansDesk/batteryRuntime" - }, - { - "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.2.0", - "topic": "power/dansDesk/batteryTemperature" - }, - { - "oid": ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1", - "topic": "power/dansDesk/temperatureProbe" - } - ] - } - ] -} \ No newline at end of file diff --git a/go.mod b/go.mod deleted file mode 100644 index c7bb685..0000000 --- a/go.mod +++ /dev/null @@ -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 -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 67c1b0c..0000000 --- a/go.sum +++ /dev/null @@ -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= diff --git a/snmp-mqtt.go b/snmp-mqtt.go deleted file mode 100644 index e4d74bc..0000000 --- a/snmp-mqtt.go +++ /dev/null @@ -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= SNMP Endpoints Map File [default: ./endpoints.json] - --server= MQTT server host/IP [default: 127.0.0.1] - --port= MQTT server port [default: 1883] - --clientid= MQTT client identifier [default: snmp] - --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) -} diff --git a/snmp/snmp.go b/snmp/snmp.go deleted file mode 100644 index fc446c7..0000000 --- a/snmp/snmp.go +++ /dev/null @@ -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) -} diff --git a/src/smq/config-test.json b/src/smq/config-test.json new file mode 100644 index 0000000..88c01dc --- /dev/null +++ b/src/smq/config-test.json @@ -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" + } + ] + } + ] +} diff --git a/src/smq/config.json b/src/smq/config.json new file mode 100644 index 0000000..6b2e852 --- /dev/null +++ b/src/smq/config.json @@ -0,0 +1,33 @@ +{ + "mqtt": { + "broker": "mqtt://emqx01-anonymous-cluster-internal.broker.svc.cluster.local:1883", + "tlsEnable": "false", + "topicPre": "snmp" + }, + "interval": 60, + "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" + }, + { + "oid": ".1.3.6.1.2.1.31.1.1.1.10.4", + "label": "wan-out" + }, + { + "oid": ".1.3.6.1.2.1.31.1.1.1.6.2", + "label": "lan-in" + }, + { + "oid": ".1.3.6.1.2.1.31.1.1.1.10.2", + "label": "lan-out" + } + ] + } + ] +} diff --git a/src/smq/config/config.go b/src/smq/config/config.go new file mode 100644 index 0000000..a517bf8 --- /dev/null +++ b/src/smq/config/config.go @@ -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) + } +} + diff --git a/src/smq/go.mod b/src/smq/go.mod new file mode 100644 index 0000000..2871d0c --- /dev/null +++ b/src/smq/go.mod @@ -0,0 +1,15 @@ +module smq + +go 1.21.3 + +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.15.0 // indirect + golang.org/x/sync v0.1.0 // indirect +) diff --git a/src/smq/go.sum b/src/smq/go.sum new file mode 100644 index 0000000..385bb29 --- /dev/null +++ b/src/smq/go.sum @@ -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= diff --git a/src/smq/mqtt/mqtt.go b/src/smq/mqtt/mqtt.go new file mode 100644 index 0000000..bc60607 --- /dev/null +++ b/src/smq/mqtt/mqtt.go @@ -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) +} + diff --git a/src/smq/smq b/src/smq/smq new file mode 100755 index 0000000..f5e898d Binary files /dev/null and b/src/smq/smq differ diff --git a/src/smq/snmp-mqtt.go b/src/smq/snmp-mqtt.go new file mode 100644 index 0000000..625239a --- /dev/null +++ b/src/smq/snmp-mqtt.go @@ -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") +} diff --git a/src/smq/snmp/snmp.go b/src/smq/snmp/snmp.go new file mode 100644 index 0000000..74596af --- /dev/null +++ b/src/smq/snmp/snmp.go @@ -0,0 +1,103 @@ +package snmp + +import ( + "fmt" + "log" + "time" + "encoding/json" + + "smq/config" + "smq/mqtt" + + "github.com/gosnmp/gosnmp" +) + +type variable_t struct { + Label string `json:"label"` + Variable string `json:"variable"` + Value string `json:"value"` +} + +type message_t struct { + Device string `json:"device"` + Label string `json:"label"` + Variables map[string]variable_t `json:"variables"` +} + + +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)) + } + + log.Printf("%s = %s", oidTopic.OID, convertedValue) + v := variable_t { + Label: oidTopic.Label, + Variable: oidTopic.OID, + Value: convertedValue, + } + 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() { +}