Compare commits
1 Commits
master
...
respondd-c
Author | SHA1 | Date | |
---|---|---|---|
|
2f6e22f8cc |
30
contrib/respondd-crashed/README.md
Normal file
30
contrib/respondd-crashed/README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# respondd-crashed
|
||||||
|
|
||||||
|
This tool ping every "offline" node at every ip address of a meshviewer.json to detect, if a respondd deamon is not running anymore.
|
||||||
|
|
||||||
|
|
||||||
|
## give access to run ping
|
||||||
|
```bash
|
||||||
|
sudo setcap cap_net_raw=+ep %GOPATH/bin/respondd-crashed
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Usage of respondd-crashed:
|
||||||
|
-ll-iface string
|
||||||
|
interface to ping linklocal-address
|
||||||
|
-loglevel uint
|
||||||
|
Show log message starting at level (default 40)
|
||||||
|
-meshviewer-path string
|
||||||
|
path to meshviewer.json from yanic (default "meshviewer.json")
|
||||||
|
-ping-count int
|
||||||
|
count of pings (default 3)
|
||||||
|
-ping-timeout duration
|
||||||
|
timeout to wait for response (default 5s)
|
||||||
|
-run-every duration
|
||||||
|
repeat check every (default 1m0s)
|
||||||
|
-status-path string
|
||||||
|
path to store status (default "respondd-crashed.json")
|
||||||
|
-timestamps
|
||||||
|
Enables timestamps for log output
|
24
contrib/respondd-crashed/helper.go
Normal file
24
contrib/respondd-crashed/helper.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func JSONRequest(url string, value interface{}) error {
|
||||||
|
var netClient = &http.Client{
|
||||||
|
Timeout: time.Second * 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := netClient.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
1
contrib/respondd-crashed/helper_test.go
Normal file
1
contrib/respondd-crashed/helper_test.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package main
|
34
contrib/respondd-crashed/hook.go
Normal file
34
contrib/respondd-crashed/hook.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/bdlm/log"
|
||||||
|
stdLogger "github.com/bdlm/std/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Hook struct{}
|
||||||
|
|
||||||
|
func (hook *Hook) Fire(entry *log.Entry) error {
|
||||||
|
switch entry.Level {
|
||||||
|
case log.PanicLevel:
|
||||||
|
entry.Logger.Out = os.Stderr
|
||||||
|
case log.FatalLevel:
|
||||||
|
entry.Logger.Out = os.Stderr
|
||||||
|
case log.ErrorLevel:
|
||||||
|
entry.Logger.Out = os.Stderr
|
||||||
|
case log.WarnLevel:
|
||||||
|
entry.Logger.Out = os.Stdout
|
||||||
|
case log.InfoLevel:
|
||||||
|
entry.Logger.Out = os.Stdout
|
||||||
|
case log.DebugLevel:
|
||||||
|
entry.Logger.Out = os.Stdout
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *Hook) Levels() []stdLogger.Level {
|
||||||
|
return log.AllLevels
|
||||||
|
}
|
86
contrib/respondd-crashed/main.go
Normal file
86
contrib/respondd-crashed/main.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bdlm/log"
|
||||||
|
stdLogger "github.com/bdlm/std/logger"
|
||||||
|
"github.com/digineo/go-ping"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
timestamps bool
|
||||||
|
loglevel uint
|
||||||
|
|
||||||
|
runEvery time.Duration
|
||||||
|
|
||||||
|
iface string
|
||||||
|
|
||||||
|
pingCount int
|
||||||
|
pingTimeout time.Duration
|
||||||
|
|
||||||
|
meshviewerPATH string
|
||||||
|
statusPath string
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.BoolVar(×tamps, "timestamps", false, "Enables timestamps for log output")
|
||||||
|
flag.UintVar(&loglevel, "loglevel", 40, "Show log message starting at level")
|
||||||
|
|
||||||
|
flag.DurationVar(&runEvery, "run-every", time.Duration(time.Minute), "repeat check every")
|
||||||
|
|
||||||
|
flag.StringVar(&iface, "ll-iface", "", "interface to ping linklocal-address")
|
||||||
|
|
||||||
|
flag.IntVar(&pingCount, "ping-count", 3, "count of pings")
|
||||||
|
flag.DurationVar(&pingTimeout, "ping-timeout", time.Duration(time.Second*5), "timeout to wait for response")
|
||||||
|
|
||||||
|
flag.StringVar(&statusPath, "status-path", "respondd-crashed.json", "path to store status")
|
||||||
|
flag.StringVar(&meshviewerPATH, "meshviewer-path", "meshviewer.json", "path to meshviewer.json from yanic")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
log.AddHook(&Hook{})
|
||||||
|
log.SetLevel(stdLogger.Level(loglevel))
|
||||||
|
log.SetFormatter(&log.TextFormatter{
|
||||||
|
DisableTimestamp: timestamps,
|
||||||
|
})
|
||||||
|
|
||||||
|
pinger, err := ping.New("", "::")
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("not able to bind pinger: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
timer := time.NewTimer(runEvery)
|
||||||
|
|
||||||
|
stop := false
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
|
log.Info("start tester")
|
||||||
|
|
||||||
|
func() {
|
||||||
|
wg.Add(1)
|
||||||
|
for !stop {
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
run(pinger)
|
||||||
|
timer.Reset(runEvery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer.Stop()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
sig := <-sigs
|
||||||
|
stop = true
|
||||||
|
wg.Wait()
|
||||||
|
log.Infof("stopped: %s", sig)
|
||||||
|
|
||||||
|
}
|
119
contrib/respondd-crashed/run.go
Normal file
119
contrib/respondd-crashed/run.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/bdlm/log"
|
||||||
|
"github.com/digineo/go-ping"
|
||||||
|
|
||||||
|
meshviewerFFRGB "github.com/FreifunkBremen/yanic/output/meshviewer-ffrgb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pingNode(pinger *ping.Pinger, node *meshviewerFFRGB.Node, addrStr string) bool {
|
||||||
|
logNode := log.WithField("node_id", node.NodeID)
|
||||||
|
|
||||||
|
addr, err := net.ResolveIPAddr("ip6", addrStr)
|
||||||
|
if err != nil {
|
||||||
|
logNode.Warnf("error parse ip address for ping: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if addrStr[:5] == "fe80:" {
|
||||||
|
if iface == "" {
|
||||||
|
logNode.Debug("skip ll-addr")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
addr.Zone = iface
|
||||||
|
}
|
||||||
|
logNode = logNode.WithField("addr", addr.String())
|
||||||
|
|
||||||
|
_, err = pinger.PingAttempts(addr, pingTimeout, pingCount)
|
||||||
|
|
||||||
|
logNode.WithFields(map[string]interface{}{
|
||||||
|
"success": err == nil,
|
||||||
|
}).Debug("pong")
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(pinger *ping.Pinger) {
|
||||||
|
status := &Status{NodesCrashed: []*Node{}}
|
||||||
|
var meshviewerjson meshviewerFFRGB.Meshviewer
|
||||||
|
|
||||||
|
if meshviewerPATH[:4] == "http" {
|
||||||
|
if err := JSONRequest(meshviewerPATH, &meshviewerjson); err != nil {
|
||||||
|
status.Error = err.Error()
|
||||||
|
log.Errorf("error during fetch meshviewer.json: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
meshviewerFile, err := os.Open(meshviewerPATH)
|
||||||
|
if err != nil {
|
||||||
|
status.Error = err.Error()
|
||||||
|
log.Errorf("error during fetch meshviewer.json: %s", err)
|
||||||
|
} else if err := json.NewDecoder(meshviewerFile).Decode(&meshviewerjson); err != nil {
|
||||||
|
status.Error = err.Error()
|
||||||
|
log.Errorf("error during decode meshviewer.json: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("fetched meshviewer.json")
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(len(meshviewerjson.Nodes))
|
||||||
|
|
||||||
|
offline := 0
|
||||||
|
for _, node := range meshviewerjson.Nodes {
|
||||||
|
go func(node *meshviewerFFRGB.Node) {
|
||||||
|
defer wg.Done()
|
||||||
|
if node.IsOnline {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logNode := log.WithField("node", node.NodeID)
|
||||||
|
wgNode := sync.WaitGroup{}
|
||||||
|
wgNode.Add(len(node.Addresses))
|
||||||
|
offline += 1
|
||||||
|
notReachable := true
|
||||||
|
for _, addr := range node.Addresses {
|
||||||
|
go func(node *meshviewerFFRGB.Node, addr string) {
|
||||||
|
if ok := pingNode(pinger, node, addr); ok {
|
||||||
|
notReachable = false
|
||||||
|
}
|
||||||
|
wgNode.Done()
|
||||||
|
}(node, addr)
|
||||||
|
}
|
||||||
|
wgNode.Wait()
|
||||||
|
if !notReachable {
|
||||||
|
logNode.Info("add to crashed list")
|
||||||
|
status.AddNode(node)
|
||||||
|
}
|
||||||
|
}(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
status.Lock()
|
||||||
|
status.NodesCount = len(meshviewerjson.Nodes)
|
||||||
|
status.NodesOfflineCount = offline
|
||||||
|
status.Unlock()
|
||||||
|
|
||||||
|
tmpFile := statusPath + ".tmp"
|
||||||
|
statusFile, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("unable to open status file: %s", err)
|
||||||
|
}
|
||||||
|
defer statusFile.Close()
|
||||||
|
|
||||||
|
if err := json.NewEncoder(statusFile).Encode(status); err != nil {
|
||||||
|
log.Warnf("unable to write status json: %s", err)
|
||||||
|
}
|
||||||
|
if err := os.Rename(tmpFile, statusPath); err != nil {
|
||||||
|
log.Warnf("unable to move status file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithFields(map[string]interface{}{
|
||||||
|
"count_meshviewer": status.NodesCount,
|
||||||
|
"count_offline": status.NodesOfflineCount,
|
||||||
|
"count_status": len(status.NodesCrashed),
|
||||||
|
}).Info("test complete")
|
||||||
|
}
|
31
contrib/respondd-crashed/status.go
Normal file
31
contrib/respondd-crashed/status.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
meshviewerFFRGB "github.com/FreifunkBremen/yanic/output/meshviewer-ffrgb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
NodeID string `json:"node_id"`
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
Addresses []string `json:"addresses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
NodesCount int `json:"nodes_count"`
|
||||||
|
NodesOfflineCount int `json:"nodes_offline_count"`
|
||||||
|
NodesCrashed []*Node `json:"nodes_crashed"`
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Status) AddNode(node *meshviewerFFRGB.Node) {
|
||||||
|
s.Lock()
|
||||||
|
s.NodesCrashed = append(s.NodesCrashed, &Node{
|
||||||
|
NodeID: node.NodeID,
|
||||||
|
Hostname: node.Hostname,
|
||||||
|
Addresses: node.Addresses,
|
||||||
|
})
|
||||||
|
s.Unlock()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user