├── .github └── workflows │ ├── main.yml │ └── release.yml ├── .gitignore ├── .goreleaser.yml ├── LICENSE ├── Makefile ├── README.md ├── config └── config.go ├── endpoints.json ├── go.mod ├── go.sum ├── snmp-mqtt.go └── snmp └── snmp.go /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | 8 | jobs: 9 | lint: 10 | name: Lint 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Set up Go 14 | uses: actions/setup-go@v1 15 | with: 16 | go-version: 1.13 17 | 18 | - name: Check out code 19 | uses: actions/checkout@v1 20 | 21 | - name: Lint Go Code 22 | run: | 23 | export PATH=$PATH:$(go env GOPATH)/bin # temporary fix. See https://github.com/actions/setup-go/issues/14 24 | go get -u golang.org/x/lint/golint 25 | make lint 26 | 27 | test: 28 | name: Test 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Set up Go 32 | uses: actions/setup-go@v1 33 | with: 34 | go-version: 1.13 35 | 36 | - name: Check out code 37 | uses: actions/checkout@v1 38 | 39 | - name: Run Unit tests 40 | run: | 41 | export PATH=$PATH:$(go env GOPATH)/bin 42 | make test-coverage 43 | 44 | 45 | build: 46 | name: Build 47 | runs-on: ubuntu-latest 48 | needs: [lint, test] 49 | steps: 50 | - name: Set up Go 51 | uses: actions/setup-go@v1 52 | with: 53 | go-version: 1.13 54 | 55 | - name: Check out code 56 | uses: actions/checkout@v1 57 | 58 | - name: Build Everything 59 | run: | 60 | export PATH=$PATH:$(go env GOPATH)/bin 61 | make build 62 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | create: 4 | tags: 5 | - v* 6 | 7 | jobs: 8 | release: 9 | name: Publish Release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check out code 13 | uses: actions/checkout@v1 14 | 15 | - name: Validates GO releaser config 16 | uses: docker://goreleaser/goreleaser:latest 17 | with: 18 | args: check 19 | 20 | - name: Create release on GitHub 21 | uses: docker://goreleaser/goreleaser:latest 22 | with: 23 | args: release 24 | env: 25 | GITHUB_TOKEN: ${{secrets.GORELEASER_GITHUB_TOKEN}} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | env: 2 | - GO111MODULE=on 3 | before: 4 | hooks: 5 | - go mod tidy 6 | builds: 7 | - env: 8 | - CGO_ENABLED=0 9 | goos: 10 | - linux 11 | goarch: 12 | - arm 13 | - amd64 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Daniel Chote 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME := "snmp-mqtt" 2 | PKG := "github.com/dchote/$(PROJECT_NAME)" 3 | PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/) 4 | GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go) 5 | 6 | .PHONY: all dep lint vet test test-coverage build clean 7 | 8 | all: build 9 | 10 | dep: ## Get the dependencies 11 | @echo Installing dependencies 12 | @go mod download 13 | 14 | lint: ## Lint Golang files 15 | @golint -set_exit_status ${PKG_LIST} 16 | 17 | vet: ## Run go vet 18 | @go vet ${PKG_LIST} 19 | 20 | test: ## Run unittests 21 | @go test -short ${PKG_LIST} 22 | 23 | test-coverage: ## Run tests with coverage 24 | @go test -short -coverprofile cover.out -covermode=atomic ${PKG_LIST} 25 | @cat cover.out >> coverage.txt 26 | 27 | build: dep ## Build the binary file 28 | @echo Building native binary 29 | @go build -i -o build/snmp-mqtt $(PKG) 30 | 31 | linux: build 32 | @echo Building Linux binary 33 | @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 34 | 35 | raspi: build 36 | @echo Building Rasperry Pi Linux binary 37 | @env GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -o build/snmp-mqtt 38 | 39 | 40 | clean: ## Remove previous build 41 | @rm -f $(PROJECT_NAME)/build 42 | 43 | help: ## Display this help screen 44 | @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # snmp-mqtt 2 | 3 | A simple go app that reads SNMP values and publishes it to the specified MQTT endpoint at the specified interval. 4 | 5 | Please download the precompiled binary from the releases page: https://github.com/dchote/snmp-mqtt/releases 6 | 7 | ``` 8 | Usage: snmp-mqtt [options] 9 | 10 | Options: 11 | --endpoints_map= SNMP Endpoints Map File [default: ./endpoints.json] 12 | --server= MQTT server host/IP [default: 127.0.0.1] 13 | --port= MQTT server port [default: 1883] 14 | --topic= MQTT topic prefix [default: snmp] 15 | --clientid= MQTT client identifier [default: snmp] 16 | --interval= Poll interval (seconds) [default: 5] 17 | -h, --help Show this screen. 18 | -v, --version Show version. 19 | ``` 20 | 21 | An example endpoints.json file: 22 | ``` 23 | { 24 | "snmpEndpoints": [ 25 | { 26 | "endpoint": "172.18.0.1", 27 | "community": "public", 28 | "oidTopics": [ 29 | { 30 | "oid": ".1.3.6.1.2.1.31.1.1.1.6.4", 31 | "topic": "router/bytesIn" 32 | }, 33 | { 34 | "oid": ".1.3.6.1.2.1.31.1.1.1.10.4", 35 | "topic": "router/bytesOut" 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | ``` -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | // OIDTopicObject maps OIDs to MQTT topics 10 | type OIDTopicObject struct { 11 | OID string `json:"oid"` 12 | Topic string `json:"topic"` 13 | } 14 | 15 | // SNMPEndpointObject is the SNMP Endpoint definition 16 | type SNMPEndpointObject struct { 17 | Endpoint string `json:"endpoint"` 18 | Community string `json:"community"` 19 | OIDTopics []OIDTopicObject `json:"oidTopics"` 20 | } 21 | 22 | // SNMPMapObject basic map of endpoints 23 | type SNMPMapObject struct { 24 | SNMPEndpoints []SNMPEndpointObject `json:"snmpEndpoints"` 25 | } 26 | 27 | var ( 28 | // SNMPMap is the loaded JSON configuration 29 | SNMPMap *SNMPMapObject 30 | 31 | // Server is the MQTT server address 32 | Server string 33 | 34 | // Port is the MQTT server listen port 35 | Port int 36 | 37 | // ClientID is how the name of the client 38 | ClientID string 39 | 40 | // Interval is the poll interval in seconds 41 | Interval int 42 | ) 43 | 44 | // ConnectionString returns the MQTT connection string 45 | func ConnectionString() string { 46 | return "tcp://" + Server + ":" + strconv.Itoa(Port) 47 | } 48 | 49 | // LoadMap loads the file in to the struct 50 | func LoadMap(file string) error { 51 | configFile, err := os.Open(file) 52 | defer configFile.Close() 53 | if err != nil { 54 | return err 55 | } 56 | 57 | jsonParser := json.NewDecoder(configFile) 58 | err = jsonParser.Decode(&SNMPMap) 59 | 60 | if err != nil { 61 | return err 62 | } 63 | 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /endpoints.json: -------------------------------------------------------------------------------- 1 | { 2 | "snmpEndpoints": [ 3 | { 4 | "endpoint": "172.18.0.1", 5 | "community": "public", 6 | "oidTopics": [ 7 | { 8 | "oid": ".1.3.6.1.2.1.31.1.1.1.6.4", 9 | "topic": "network/router/interfaces/wan/bytesIn" 10 | }, 11 | { 12 | "oid": ".1.3.6.1.2.1.31.1.1.1.10.4", 13 | "topic": "network/router/interfaces/wan/bytesOut" 14 | }, 15 | { 16 | "oid": ".1.3.6.1.2.1.31.1.1.1.6.2", 17 | "topic": "network/router/interfaces/lan/bytesIn" 18 | }, 19 | { 20 | "oid": ".1.3.6.1.2.1.31.1.1.1.10.2", 21 | "topic": "network/router/interfaces/lan/bytesOut" 22 | } 23 | ] 24 | }, 25 | { 26 | "endpoint": "172.18.0.50", 27 | "community": "public", 28 | "oidTopics": [ 29 | { 30 | "oid": ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.2.1", 31 | "topic": "power/rackPDU/outputCurrentX10" 32 | }, 33 | { 34 | "oid": ".1.3.6.1.4.1.318.1.1.12.1.16.0", 35 | "topic": "power/rackPDU/watts" 36 | } 37 | ] 38 | }, 39 | { 40 | "endpoint": "172.18.0.51", 41 | "community": "public", 42 | "oidTopics": [ 43 | { 44 | "oid": ".1.3.6.1.4.1.318.1.1.1.3.2.1.0", 45 | "topic": "power/rackUPS/lineVoltage" 46 | }, 47 | { 48 | "oid": ".1.3.6.1.4.1.318.1.1.1.4.2.4.0", 49 | "topic": "power/rackUPS/outputCurrent" 50 | }, 51 | { 52 | "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.1.0", 53 | "topic": "power/rackUPS/batteryCapacity" 54 | }, 55 | { 56 | "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.3.0", 57 | "topic": "power/rackUPS/batteryRuntime" 58 | }, 59 | { 60 | "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.2.0", 61 | "topic": "power/rackUPS/batteryTemperature" 62 | } 63 | ] 64 | }, 65 | { 66 | "endpoint": "172.18.0.52", 67 | "community": "public", 68 | "oidTopics": [ 69 | { 70 | "oid": ".1.3.6.1.4.1.318.1.1.1.3.2.1.0", 71 | "topic": "power/printerUPS/lineVoltage" 72 | }, 73 | { 74 | "oid": ".1.3.6.1.4.1.318.1.1.1.4.2.4.0", 75 | "topic": "power/printerUPS/outputCurrent" 76 | }, 77 | { 78 | "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.1.0", 79 | "topic": "power/printerUPS/batteryCapacity" 80 | }, 81 | { 82 | "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.3.0", 83 | "topic": "power/printerUPS/batteryRuntime" 84 | }, 85 | { 86 | "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.2.0", 87 | "topic": "power/printerUPS/batteryTemperature" 88 | } 89 | ] 90 | }, 91 | { 92 | "endpoint": "172.18.0.53", 93 | "community": "public", 94 | "oidTopics": [ 95 | { 96 | "oid": ".1.3.6.1.4.1.318.1.1.1.3.2.1.0", 97 | "topic": "power/dansDesk/lineVoltage" 98 | }, 99 | { 100 | "oid": ".1.3.6.1.4.1.318.1.1.1.4.2.4.0", 101 | "topic": "power/dansDesk/outputCurrent" 102 | }, 103 | { 104 | "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.1.0", 105 | "topic": "power/dansDesk/batteryCapacity" 106 | }, 107 | { 108 | "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.3.0", 109 | "topic": "power/dansDesk/batteryRuntime" 110 | }, 111 | { 112 | "oid": ".1.3.6.1.4.1.318.1.1.1.2.2.2.0", 113 | "topic": "power/dansDesk/batteryTemperature" 114 | }, 115 | { 116 | "oid": ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1", 117 | "topic": "power/dansDesk/temperatureProbe" 118 | } 119 | ] 120 | } 121 | ] 122 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dchote/snmp-mqtt 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 7 | github.com/eclipse/paho.mqtt.golang v1.2.0 8 | github.com/soniah/gosnmp v1.22.0 9 | golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= 4 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 5 | github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0= 6 | github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= 7 | github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= 8 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 9 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 10 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 11 | github.com/soniah/gosnmp v1.22.0 h1:jVJi8+OGvR+JHIaZKMmnyNP0akJd2vEgNatybwhZvxg= 12 | github.com/soniah/gosnmp v1.22.0/go.mod h1:DuEpAS0az51+DyVBQwITDsoq4++e3LTNckp2GoasF2I= 13 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 14 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 15 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 16 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 17 | golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA= 18 | golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 19 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 20 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 21 | -------------------------------------------------------------------------------- /snmp-mqtt.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | 9 | "github.com/dchote/snmp-mqtt/config" 10 | "github.com/dchote/snmp-mqtt/snmp" 11 | 12 | "github.com/docopt/docopt-go" 13 | ) 14 | 15 | var exitChan = make(chan int) 16 | 17 | // VERSION beause... 18 | const VERSION = "0.0.1" 19 | 20 | func cliArguments() { 21 | usage := ` 22 | Usage: snmp-mqtt [options] 23 | 24 | Options: 25 | --endpoints_map= SNMP Endpoints Map File [default: ./endpoints.json] 26 | --server= MQTT server host/IP [default: 127.0.0.1] 27 | --port= MQTT server port [default: 1883] 28 | --clientid= MQTT client identifier [default: snmp] 29 | --interval= Poll interval (seconds) [default: 5] 30 | -h, --help Show this screen. 31 | -v, --version Show version. 32 | ` 33 | args, _ := docopt.ParseArgs(usage, os.Args[1:], VERSION) 34 | 35 | mapFile, _ := args.String("--endpoints_map") 36 | err := config.LoadMap(mapFile) 37 | if err != nil { 38 | log.Println(err) 39 | log.Fatal("error opening " + mapFile) 40 | } 41 | 42 | config.Server, _ = args.String("--server") 43 | config.Port, _ = args.Int("--port") 44 | config.ClientID, _ = args.String("--clientid") 45 | config.Interval, _ = args.Int("--interval") 46 | 47 | log.Printf("server: %s, port: %d, client identifier: %s, poll interval: %d", config.Server, config.Port, config.ClientID, config.Interval) 48 | } 49 | 50 | // sigChannelListen basic handlers for inbound signals 51 | func sigChannelListen() { 52 | signalChan := make(chan os.Signal, 1) 53 | code := 0 54 | 55 | signal.Notify(signalChan, os.Interrupt) 56 | signal.Notify(signalChan, os.Kill) 57 | signal.Notify(signalChan, syscall.SIGTERM) 58 | 59 | select { 60 | case sig := <-signalChan: 61 | log.Printf("Received signal %s. shutting down", sig) 62 | case code = <-exitChan: 63 | switch code { 64 | case 0: 65 | log.Println("Shutting down") 66 | default: 67 | log.Println("*Shutting down") 68 | } 69 | } 70 | 71 | os.Exit(code) 72 | } 73 | 74 | func main() { 75 | cliArguments() 76 | 77 | // catch signals 78 | go sigChannelListen() 79 | 80 | // run sensor poll loop 81 | snmp.Init() 82 | 83 | os.Exit(0) 84 | } 85 | -------------------------------------------------------------------------------- /snmp/snmp.go: -------------------------------------------------------------------------------- 1 | package snmp 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | "sync" 8 | "time" 9 | 10 | "github.com/dchote/snmp-mqtt/config" 11 | 12 | "github.com/eclipse/paho.mqtt.golang" 13 | "github.com/soniah/gosnmp" 14 | ) 15 | 16 | var () 17 | 18 | // Init contains the generic read/publish loop 19 | func Init() { 20 | opts := mqtt.NewClientOptions().AddBroker(config.ConnectionString()).SetClientID(config.ClientID) 21 | opts.SetKeepAlive(2 * time.Second) 22 | opts.SetPingTimeout(1 * time.Second) 23 | 24 | client := mqtt.NewClient(opts) 25 | if token := client.Connect(); token.Wait() && token.Error() != nil { 26 | panic(token.Error()) 27 | } 28 | 29 | var wg sync.WaitGroup 30 | 31 | for { 32 | wg.Add(1) 33 | 34 | go func() { 35 | defer wg.Done() 36 | 37 | for _, endpoint := range config.SNMPMap.SNMPEndpoints { 38 | log.Println("Polling endpoint " + endpoint.Endpoint) 39 | 40 | snmp := gosnmp.GoSNMP{} 41 | 42 | snmp.Target = endpoint.Endpoint 43 | snmp.Port = 161 44 | snmp.Version = gosnmp.Version2c 45 | snmp.Community = endpoint.Community 46 | 47 | snmp.Timeout = time.Duration(5 * time.Second) 48 | err := snmp.Connect() 49 | if err != nil { 50 | log.Fatal("SNMP Connect error\n") 51 | } 52 | 53 | oids := []string{} 54 | 55 | for _, oidTopic := range endpoint.OIDTopics { 56 | oids = append(oids, oidTopic.OID) 57 | } 58 | 59 | result, err := snmp.Get(oids) 60 | if err != nil { 61 | log.Printf("error in Get: %s", err) 62 | } else { 63 | for _, variable := range result.Variables { 64 | for _, oidTopic := range endpoint.OIDTopics { 65 | if strings.Compare(oidTopic.OID, variable.Name) == 0 { 66 | convertedValue := "" 67 | 68 | switch variable.Type { 69 | case gosnmp.OctetString: 70 | convertedValue = string(variable.Value.([]byte)) 71 | default: 72 | convertedValue = fmt.Sprintf("%d", gosnmp.ToBigInt(variable.Value)) 73 | } 74 | 75 | log.Printf("%s = %s", oidTopic.Topic, convertedValue) 76 | token := client.Publish(oidTopic.Topic, 0, false, convertedValue) 77 | 78 | token.Wait() 79 | if token.Error() != nil { 80 | log.Fatal(token.Error()) 81 | } 82 | } 83 | } 84 | } 85 | } 86 | snmp.Conn.Close() 87 | } 88 | 89 | }() 90 | 91 | time.Sleep(time.Duration(config.Interval) * time.Second) 92 | } 93 | 94 | wg.Wait() 95 | 96 | client.Disconnect(250) 97 | time.Sleep(1 * time.Second) 98 | } 99 | --------------------------------------------------------------------------------