diff --git a/config_example.yaml b/config_example.yaml index 6eb73db..509a25c 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -15,9 +15,6 @@ nodes: saveinterval: 5 aliases_enable: false aliases_path: /var/www/html/meshviewer/data/aliases.json - vpn_addresses: - - da:25:d6:5c:97:6f - - da:62:f2:70:c8:8d influxdb: enable: false database: ffhb diff --git a/main.go b/main.go index 07d2562..7a6cd76 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,7 @@ var ( wsserverForNodes *websocketserver.Server collector *respond.Collector statsDb *StatsDb - nodes = models.NewNodes() + nodes *models.Nodes //aliases = models.NewNodes() ) @@ -30,18 +30,7 @@ func main() { flag.StringVar(&configFile, "config", "config.yml", "path of configuration file (default:config.yaml)") flag.Parse() config = models.ConfigReadFile(configFile) - - if config.Nodes.Enable { - go nodes.Saver(config) - } - - if config.Webserver.Enable { - if config.Webserver.WebsocketNode { - wsserverForNodes = websocketserver.NewServer("/nodes") - go wsserverForNodes.Listen() - } - http.Handle("/", http.FileServer(http.Dir(config.Webserver.Webroot))) - } + nodes = models.NewNodes(config) if config.Influxdb.Enable { statsDb = NewStatsDb() @@ -52,9 +41,18 @@ func main() { collector = respond.NewCollector("nodeinfo statistics neighbours", collectInterval, onReceive) } - // TODO bad + if config.Webserver.WebsocketNode { + wsserverForNodes = websocketserver.NewServer("/nodes") + go wsserverForNodes.Listen() + } + if config.Webserver.Enable { - log.Fatal(http.ListenAndServe(net.JoinHostPort(config.Webserver.Address, config.Webserver.Port), nil)) + http.Handle("/", http.FileServer(http.Dir(config.Webserver.Webroot))) + + address := net.JoinHostPort(config.Webserver.Address, config.Webserver.Port) + log.Println("starting webserver on", address) + // TODO bad + log.Fatal(http.ListenAndServe(address, nil)) } // Wait for INT/TERM diff --git a/models/graph.go b/models/graph.go index 72d5381..c033348 100644 --- a/models/graph.go +++ b/models/graph.go @@ -26,18 +26,13 @@ type GraphBuilder struct { vpn map[string]interface{} // IDs/addresses of VPN servers } -func (nodes *Nodes) BuildGraph(vpnAddresses []string) *Graph { +func (nodes *Nodes) BuildGraph() *Graph { builder := &GraphBuilder{ macToID: make(map[string]string), links: make(map[string]*GraphLink), vpn: make(map[string]interface{}), } - // read VPN addresses into map - for _, address := range vpnAddresses { - builder.vpn[address] = nil - } - builder.readNodes(nodes.List) graph := &Graph{Version: 2} diff --git a/models/nodes.go b/models/nodes.go index 03b862c..0b62394 100644 --- a/models/nodes.go +++ b/models/nodes.go @@ -30,16 +30,23 @@ type Nodes struct { Version int `json:"version"` Timestamp jsontime.Time `json:"timestamp"` List map[string]*Node `json:"nodes"` // the current nodemap, indexed by node ID + config *Config sync.Mutex } -// NewNodes create Nodes structs (cache DB) -func NewNodes() *Nodes { +// NewNodes create Nodes structs +func NewNodes(config *Config) *Nodes { nodes := &Nodes{ - Version: 2, - List: make(map[string]*Node), + List: make(map[string]*Node), + config: config, } + if config.Nodes.NodesPath != "" { + nodes.load() + } + go nodes.worker() + + nodes.Version = 2 return nodes } @@ -76,39 +83,55 @@ func (nodes *Nodes) Update(nodeID string, res *data.ResponseData) { } } -// Saves the cached DB to json file periodically -func (nodes *Nodes) Saver(config *Config) { - c := time.Tick(time.Second * time.Duration(config.Nodes.SaveInterval)) +// Periodically saves the cached DB to json file +func (nodes *Nodes) worker() { + c := time.Tick(time.Second * 5) for range c { log.Println("saving", len(nodes.List), "nodes") - nodes.Timestamp = time.Now() + nodes.Timestamp = jsontime.Now() nodes.Lock() - if path := config.Nodes.NodesPath; path != "" { + + // serialize nodes + save(nodes, nodes.config.Nodes.NodesPath) + + if path := nodes.config.Nodes.GraphsPath; path != "" { save(nodes, path) } - if path := config.Nodes.GraphsPath; path != "" { - save(nodes.BuildGraph(config.Nodes.VpnAddresses), path) - } + nodes.Unlock() } } +func (nodes *Nodes) load() { + path := nodes.config.Nodes.NodesPath + log.Println("loading", path) + + if data, err := ioutil.ReadFile(path); err == nil { + if err := json.Unmarshal(data, nodes); err == nil { + log.Println("loaded", len(nodes.List), "nodes") + } else { + log.Println("failed to unmarshal nodes:", err) + } + + } else { + log.Println("failed loading cached nodes:", err) + } +} + func save(input interface{}, outputFile string) { data, err := json.Marshal(input) - if err != nil { log.Panic(err) } tmpFile := outputFile + ".tmp" - err = ioutil.WriteFile(tmpFile, data, 0644) - if err != nil { + if err := ioutil.WriteFile(tmpFile, data, 0644); err != nil { log.Panic(err) } - err = os.Rename(tmpFile, outputFile) - if err != nil { + + if err := os.Rename(tmpFile, outputFile); err != nil { log.Panic(err) } } diff --git a/models/nodes_test.go b/models/nodes_test.go new file mode 100644 index 0000000..51b54e8 --- /dev/null +++ b/models/nodes_test.go @@ -0,0 +1,25 @@ +package models + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLoadAndSave(t *testing.T) { + assert := assert.New(t) + + config := &Config{} + config.Nodes.NodesPath = "testdata/nodes.json" + + nodes := &Nodes{config: config} + nodes.load() + + tmpfile, _ := ioutil.TempFile("/tmp", "nodes") + save(nodes, tmpfile.Name()) + os.Remove(tmpfile.Name()) + + assert.Equal(1, len(nodes.List)) +} diff --git a/models/testdata/nodes.json b/models/testdata/nodes.json new file mode 100644 index 0000000..a7d7671 --- /dev/null +++ b/models/testdata/nodes.json @@ -0,0 +1 @@ +{"nodes": {"f4f26dd7a30a": {"firstseen": "2016-03-10T12:12:01"}}}