// Write a full nodes.json style document based on the current // database contents. func (db *NodeDB) GenerateNodesJSON(w io.Writer, offlineDuration time.Duration) { data := db.cacheExportNodes.get(func() []byte { nodejs := NodesJSON{ Nodes: make(map[string]*NodesJSONData), Timestamp: NodesJSONTime(time.Now()), Version: 1, } db.Main.View(func(tx *bolt.Tx) error { nodeinfo := &NodeInfo{} nmeta := store.NewMeta(nodeinfo) return db.Main.ForEach(tx, nmeta, func(cursor *bolt.Cursor) (bool, error) { data, err := db.getNodesJSONData(tx, nmeta, offlineDuration) if err == nil { nodejs.Nodes[data.NodeInfo.NodeID] = data } else { log.Printf("NodeDB: can not generate node info JSON for %v: %v", alfred.HardwareAddr(nmeta.Key()), err) } return false, nil }) }) buf := new(bytes.Buffer) enc := json.NewEncoder(buf) if err := enc.Encode(&nodejs); err != nil { return []byte{} } return buf.Bytes() }) w.Write(data) }
// read structured information from A.L.F.R.E.D. packet func (ni *NodeInfo) ReadAlfred(data alfred.Data) error { ni.Source = alfred.HardwareAddr(data.Source) ni.Data = &NodeInfoData{} return readJSON(data, NODEINFO_PACKETTYPE, NODEINFO_PACKETVERSION, ni.Data) }
// Assemble data elements for a mesh node from database. // This operation assumes the database is already locked by the caller. func (db *NodeDB) getNodesJSONData(tx *bolt.Tx, nmeta *store.Meta, offlineDuration time.Duration) (*NodesJSONData, error) { data := &NodesJSONData{} nodeinfo := &NodeInfo{} if err := nmeta.GetItem(nodeinfo); err != nil { return data, err } data.NodeInfo = *nodeinfo.Data // make a copy // earliest datestamp is the "first seen" time, // latest datestamp is the "last seen" time firstseen := nmeta.Created lastseen := nmeta.Updated statistics := &Statistics{} smeta := store.NewMeta(statistics) if db.Main.Get(tx, nmeta.Key(), smeta) == nil { if smeta.Created.Before(firstseen) { firstseen = smeta.Created } if lastseen.Before(smeta.Updated) { lastseen = smeta.Updated } if smeta.GetItem(statistics) == nil { statdata := statistics.Data if statdata.Memory != nil { if statdata.Memory.Total != 0 { // this calculation is a bit stupid, but compatible with ffmap-backend: data.Statistics.MemoryUsage = 1.0 - (float64(statdata.Memory.Free) / float64(statdata.Memory.Total)) } else { data.Statistics.MemoryUsage = 1 } } data.Statistics.Uptime = statdata.Uptime if statdata.Clients != nil { data.Statistics.Clients = statdata.Clients.Total } if statdata.Gateway != nil { data.Statistics.Gateway = statdata.Gateway } data.Statistics.LoadAvg = statdata.LoadAvg data.Statistics.RootFSUsage = statdata.RootFSUsage } } vis := &VisData{} vmeta := store.NewMeta(vis) if db.Main.Get(tx, nmeta.Key(), vmeta) == nil { if vmeta.Created.Before(firstseen) { firstseen = vmeta.Created } if lastseen.Before(vmeta.Updated) { lastseen = vmeta.Updated } } data.FirstSeen = NodesJSONTime(firstseen) data.LastSeen = NodesJSONTime(lastseen) // set gateway flag when we have the node's address in // our list of gateways nodeid, _ := db.ResolveNodeID(tx, alfred.HardwareAddr(nmeta.Key())) data.Flags.Gateway = db.Main.Exists(tx, []byte(nodeid), &Gateway{}) // online state is determined by the time we have last // seen a mesh node offline := time.Now().Sub(time.Time(data.LastSeen)) if offline < offlineDuration { data.Flags.Online = true } else { data.Flags.Online = false } return data, nil }