Skip to content

Commit

Permalink
[TASK] add respond(d) daemon - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
genofire committed Jan 24, 2019
1 parent bd13b99 commit b65cb15
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 54 deletions.
31 changes: 4 additions & 27 deletions cmd/config.go
@@ -1,54 +1,31 @@
package cmd

import (
"fmt"
"io/ioutil"
"os"

"github.com/naoina/toml"

"github.com/FreifunkBremen/yanic/database"
"github.com/FreifunkBremen/yanic/respond"
"github.com/FreifunkBremen/yanic/runtime"
"github.com/FreifunkBremen/yanic/webserver"
)

// Config represents the whole configuration
type Config struct {
Respondd respond.Config
Webserver webserver.Config
Nodes runtime.NodesConfig
Database database.Config
}

var (
configPath string
collector *respond.Collector
nodes *runtime.Nodes
)

func loadConfig() *Config {
config, err := ReadConfigFile(configPath)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to load config file:", err)
os.Exit(2)
}
return config
}

// ReadConfigFile reads a config model from path of a yml file
func ReadConfigFile(path string) (config *Config, err error) {
config = &Config{}

func ReadConfigFile(path string, config interface{}) error {
file, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
return err
}

err = toml.Unmarshal(file, config)
if err != nil {
return nil, err
return err
}

return
return nil
}
8 changes: 5 additions & 3 deletions cmd/config_test.go
Expand Up @@ -10,7 +10,9 @@ import (
func TestReadConfig(t *testing.T) {
assert := assert.New(t)

config, err := ReadConfigFile("../config_example.toml")
config := &ServeConfig{}
err := ReadConfigFile("../config_example.toml", config)

assert.NoError(err)
assert.NotNil(config)

Expand Down Expand Up @@ -40,11 +42,11 @@ func TestReadConfig(t *testing.T) {
},
}, meshviewer)

_, err = ReadConfigFile("testdata/config_invalid.toml")
err = ReadConfigFile("testdata/config_invalid.toml", config)
assert.Error(err, "not unmarshalable")
assert.Contains(err.Error(), "invalid TOML syntax")

_, err = ReadConfigFile("testdata/adsa.toml")
err = ReadConfigFile("testdata/adsa.toml", config)
assert.Error(err, "not found able")
assert.Contains(err.Error(), "no such file or directory")
}
5 changes: 4 additions & 1 deletion cmd/import.go
Expand Up @@ -19,7 +19,10 @@ var importCmd = &cobra.Command{
path := args[0]
site := args[1]
domain := args[2]
config := loadConfig()
config := &ServeConfig{}
if err := ReadConfigFile(configPath, config); err != nil {
log.Panicf("unable to load config file: %s", err)
}

err := allDatabase.Start(config.Database)
if err != nil {
Expand Down
40 changes: 40 additions & 0 deletions cmd/respondd.go
@@ -0,0 +1,40 @@
package cmd

import (
"os"
"os/signal"
"syscall"

"github.com/bdlm/log"
"github.com/spf13/cobra"

"github.com/FreifunkBremen/yanic/respond"
)

// serveCmd represents the serve command
var responddCMD = &cobra.Command{
Use: "respondd",
Short: "Runs a respond daemon",
Example: "yanic respondd --config /etc/respondd.toml",
Run: func(cmd *cobra.Command, args []string) {
daemon := &respond.Daemon{}
if err := ReadConfigFile(configPath, daemon); err != nil {
log.Panicf("unable to load config file: %s", err)
}

go daemon.Start()

log.Info("respondd daemon started")
// Wait for INT/TERM
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
sig := <-sigs
log.Infof("received %s", sig)

},
}

func init() {
RootCmd.AddCommand(responddCMD)
responddCMD.Flags().StringVarP(&configPath, "config", "c", "config-respondd.toml", "Path to configuration file")
}
20 changes: 15 additions & 5 deletions cmd/serve.go
Expand Up @@ -11,30 +11,40 @@ import (

allDatabase "github.com/FreifunkBremen/yanic/database/all"
allOutput "github.com/FreifunkBremen/yanic/output/all"
"github.com/FreifunkBremen/yanic/database"
"github.com/FreifunkBremen/yanic/respond"
"github.com/FreifunkBremen/yanic/runtime"
"github.com/FreifunkBremen/yanic/webserver"
)

// Config represents the whole configuration
type ServeConfig struct {
Respondd respond.Config
Webserver webserver.Config
Nodes runtime.NodesConfig
Database database.Config
}

// serveCmd represents the serve command
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Runs the yanic server",
Example: "yanic serve --config /etc/yanic.toml",
Run: func(cmd *cobra.Command, args []string) {
config := loadConfig()
config := &ServeConfig{}
if err := ReadConfigFile(configPath, config); err != nil {
log.Panicf("unable to load config file: %s", err)
}

err := allDatabase.Start(config.Database)
if err != nil {
if err := allDatabase.Start(config.Database); err != nil {
log.Panicf("could not connect to database: %s", err)
}
defer allDatabase.Close()

nodes = runtime.NewNodes(&config.Nodes)
nodes.Start()

err = allOutput.Start(nodes, config.Nodes)
if err != nil {
if err := allOutput.Start(nodes, config.Nodes); err != nil {
log.Panicf("error on init outputs: %s", err)
}
defer allOutput.Close()
Expand Down
1 change: 1 addition & 0 deletions config-respondd.toml
2 changes: 2 additions & 0 deletions config-respondd_example.toml
@@ -0,0 +1,2 @@
multi_instance = false
data_interval = "3m"
6 changes: 3 additions & 3 deletions data/response.go
Expand Up @@ -2,7 +2,7 @@ package data

// ResponseData struct
type ResponseData struct {
Neighbours *Neighbours `json:"neighbours"`
Nodeinfo *Nodeinfo `json:"nodeinfo"`
Statistics *Statistics `json:"statistics"`
Nodeinfo *Nodeinfo `json:"nodeinfo" toml:"nodeinfo"`
Statistics *Statistics `json:"statistics" toml:"statistics"`
Neighbours *Neighbours `json:"neighbours" toml:"neighbours"`
}
15 changes: 0 additions & 15 deletions respond/collector.go
@@ -1,9 +1,6 @@
package respond

import (
"bytes"
"compress/flate"
"encoding/json"
"fmt"
"net"
"time"
Expand Down Expand Up @@ -245,18 +242,6 @@ func (coll *Collector) parser() {
}
}

func (res *Response) parse() (*data.ResponseData, error) {
// Deflate
deflater := flate.NewReader(bytes.NewReader(res.Raw))
defer deflater.Close()

// Unmarshal
rdata := &data.ResponseData{}
err := json.NewDecoder(deflater).Decode(rdata)

return rdata, err
}

func (coll *Collector) saveResponse(addr *net.UDPAddr, res *data.ResponseData) {
// Search for NodeID
var nodeID string
Expand Down
125 changes: 125 additions & 0 deletions respond/daemon.go
@@ -0,0 +1,125 @@
package respond

import (
"time"
"os"
"io/ioutil"
"strings"
"net"

"github.com/bdlm/log"

"github.com/FreifunkBremen/yanic/data"
"github.com/FreifunkBremen/yanic/lib/duration"
)

type Daemon struct {
MultiInstance bool `toml:"multi_instance"`
DataInterval duration.Duration `toml:"data_interval"`
Data *data.ResponseData `toml:"data"`
}

func (d *Daemon) Start() {
if d.Data == nil {
d.Data = &data.ResponseData{}
}

d.updateData()
go d.updateWorker()

socket, err := net.ListenMulticastUDP("udp6", nil, &net.UDPAddr{
IP: net.ParseIP(multicastAddressDefault),
Port: port,
})
if err != nil {
log.Fatal(err)
}

socket.SetReadBuffer(maxDataGramSize)

// Loop forever reading from the socket
for {
buf := make([]byte, maxDataGramSize)
n, src, err := socket.ReadFromUDP(buf)
if err != nil {
log.Errorf("ReadFromUDP failed: %s", err)
}
raw := make([]byte, n)
copy(raw, buf)
log.WithFields(map[string]interface{}{
"bytes": n,
"data": string(raw),
"src": src.String(),
}).Debug("recieve request")

// TODO handle single request

res, err := NewRespone(d.Data, src)
if err != nil {
log.Errorf("Decode failed: %s", err)
}else{
n, err := socket.WriteToUDP(res.Raw, res.Address);
if err != nil {
log.Errorf("WriteToUDP failed: %s", err)
} else {
log.WithFields(map[string]interface{}{
"bytes": n,
"dest": res.Address.String(),
}).Debug("send respond")
}
}

}
}


func trim(s string) string {
return strings.TrimSpace(strings.Trim(s, "\n"))
}

func (d *Daemon) updateWorker() {
c := time.Tick(d.DataInterval.Duration)

for range c {
d.updateData()
}
}

func (d *Daemon) updateData() {
nodeID := ""
// Nodeinfo
if d.Data.Nodeinfo == nil {
d.Data.Nodeinfo = &data.Nodeinfo{}
} else {
nodeID = d.Data.Nodeinfo.NodeID
}
if d.Data.Nodeinfo.Hostname == "" {
d.Data.Nodeinfo.Hostname, _ = os.Hostname()
}

// Statistics
if d.Data.Statistics == nil {
d.Data.Statistics = &data.Statistics{}
} else if nodeID == "" {
nodeID = d.Data.Statistics.NodeID
}


// Neighbours
if d.Data.Neighbours == nil {
d.Data.Neighbours = &data.Neighbours{}
} else if nodeID == "" {
nodeID = d.Data.Neighbours.NodeID
}



if nodeID == "" {
if v, err := ioutil.ReadFile("/etc/machine-id"); err == nil {
nodeID = trim(string(v))[:12]
}
}
d.Data.Nodeinfo.NodeID = nodeID
d.Data.Statistics.NodeID = nodeID
d.Data.Neighbours.NodeID = nodeID
}

0 comments on commit b65cb15

Please sign in to comment.