[TASK] add functions to listView + allow unlearn nodes

This commit is contained in:
Martin Geno 2017-05-30 02:16:46 +02:00
parent f48aaf3236
commit f3e9b6c0db
No known key found for this signature in database
GPG Key ID: F0D39A37E925E941
13 changed files with 204 additions and 76 deletions

View File

@ -49,7 +49,7 @@ func main() {
if config.Yanic.Enable { if config.Yanic.Enable {
yanicDialer := yanic.Dial(config.Yanic.Type, config.Yanic.Address) yanicDialer := yanic.Dial(config.Yanic.Type, config.Yanic.Address)
yanicDialer.NodeHandler = nodes.AddNode yanicDialer.NodeHandler = nodes.LearnNode
yanicDialer.GlobalsHandler = func(data *runtimeYanic.GlobalStats) { yanicDialer.GlobalsHandler = func(data *runtimeYanic.GlobalStats) {
stats = data stats = data
websocket.NotifyStats(data) websocket.NotifyStats(data)

View File

@ -26,7 +26,7 @@ type Node struct {
Location data.Location `json:"location"` Location data.Location `json:"location"`
Wireless data.Wireless `json:"wireless"` Wireless data.Wireless `json:"wireless"`
Owner string `json:"owner"` Owner string `json:"owner"`
Address net.IP `json:"address"` Address net.IP `json:"-"`
Stats struct { Stats struct {
Wireless data.WirelessStatistics `json:"wireless"` Wireless data.WirelessStatistics `json:"wireless"`
Clients data.Clients `json:"clients"` Clients data.Clients `json:"clients"`

View File

@ -14,7 +14,7 @@ import (
type Nodes struct { type Nodes struct {
List map[string]*Node `json:"nodes"` List map[string]*Node `json:"nodes"`
ToUpdate map[string]*Node Current map[string]*Node `json:"-"`
ssh *ssh.Manager ssh *ssh.Manager
statePath string statePath string
iface string iface string
@ -25,7 +25,7 @@ type Nodes struct {
func NewNodes(path string, iface string, mgmt *ssh.Manager) *Nodes { func NewNodes(path string, iface string, mgmt *ssh.Manager) *Nodes {
nodes := &Nodes{ nodes := &Nodes{
List: make(map[string]*Node), List: make(map[string]*Node),
ToUpdate: make(map[string]*Node), Current: make(map[string]*Node),
ssh: mgmt, ssh: mgmt,
statePath: path, statePath: path,
iface: iface, iface: iface,
@ -34,33 +34,25 @@ func NewNodes(path string, iface string, mgmt *ssh.Manager) *Nodes {
return nodes return nodes
} }
func (nodes *Nodes) AddNode(n *yanic.Node) { func (nodes *Nodes) LearnNode(n *yanic.Node) {
node := NewNode(n) node := NewNode(n)
if node == nil { if node == nil {
return return
} }
node.Lastseen = jsontime.Now() node.Lastseen = jsontime.Now()
logger := log.Log.WithField("method", "AddNode").WithField("node_id", node.NodeID) logger := log.Log.WithField("method", "LearnNode").WithField("node_id", node.NodeID)
nodes.Lock() nodes.Lock()
nodes.Unlock() defer nodes.Unlock()
if cNode := nodes.List[node.NodeID]; cNode != nil { if lNode := nodes.List[node.NodeID]; lNode != nil {
cNode.Lastseen = jsontime.Now() lNode.Lastseen = jsontime.Now()
cNode.Stats = node.Stats lNode.Stats = node.Stats
if uNode, ok := nodes.ToUpdate[node.NodeID]; ok { } else {
uNode.Lastseen = jsontime.Now() nodes.List[node.NodeID] = node
uNode.Stats = node.Stats nodes.notify(node, true)
if nodes.List[node.NodeID].IsEqual(node) { }
delete(nodes.ToUpdate, node.NodeID) if _, ok := nodes.Current[node.NodeID]; ok {
nodes.List[node.NodeID] = node nodes.Current[node.NodeID] = node
nodes.notify(node, true) nodes.notify(node, false)
} else {
nodes.notify(uNode, false)
}
} else {
nodes.List[node.NodeID] = node
nodes.notify(node, true)
}
logger.Debugf("know already these node")
return return
} }
// session := nodes.ssh.ConnectTo(node.Address) // session := nodes.ssh.ConnectTo(node.Address)
@ -72,16 +64,16 @@ func (nodes *Nodes) AddNode(n *yanic.Node) {
uptime := ssh.SSHResultToString(result) uptime := ssh.SSHResultToString(result)
logger.Infof("new node with uptime: %s", uptime) logger.Infof("new node with uptime: %s", uptime)
nodes.List[node.NodeID] = node nodes.Current[node.NodeID] = node
nodes.notify(node, true) nodes.notify(node, false)
} }
func (nodes *Nodes) AddNotify(f func(*Node, bool)) { func (nodes *Nodes) AddNotify(f func(*Node, bool)) {
nodes.notifyFunc = append(nodes.notifyFunc, f) nodes.notifyFunc = append(nodes.notifyFunc, f)
} }
func (nodes *Nodes) notify(node *Node, real bool) { func (nodes *Nodes) notify(node *Node, system bool) {
for _, f := range nodes.notifyFunc { for _, f := range nodes.notifyFunc {
f(node, real) f(node, system)
} }
} }
@ -95,16 +87,16 @@ func (nodes *Nodes) UpdateNode(node *Node) {
if n, ok := nodes.List[node.NodeID]; ok { if n, ok := nodes.List[node.NodeID]; ok {
go node.SSHUpdate(nodes.ssh, nodes.iface, n) go node.SSHUpdate(nodes.ssh, nodes.iface, n)
} }
nodes.ToUpdate[node.NodeID] = node nodes.List[node.NodeID] = node
nodes.notify(node, false) nodes.notify(node, true)
} }
func (nodes *Nodes) Updater() { func (nodes *Nodes) Updater() {
nodes.Lock() nodes.Lock()
defer nodes.Unlock() defer nodes.Unlock()
for nodeid := range nodes.ToUpdate { for nodeid, node := range nodes.List {
if node := nodes.List[nodeid]; node != nil { if n, ok := nodes.Current[nodeid]; ok {
go node.SSHSet(nodes.ssh, nodes.iface) go node.SSHUpdate(nodes.ssh, nodes.iface, n)
} }
} }
log.Log.Debug("updater per ssh runs") log.Log.Debug("updater per ssh runs")

View File

@ -144,12 +144,13 @@ table.nodes tbody tr:nth-child(odd) {
table.nodes tbody tr:hover { table.nodes tbody tr:hover {
background: #ccc; background: #ccc;
} }
table.nodes tbody tr.offline{ table.nodes tbody tr.offline,
background: #ffb400;
}
table.nodes tbody tr.offline:hover{ table.nodes tbody tr.offline:hover{
background: #dc0067; background: #dc0067;
} }
table.nodes tbody tr.unseen{
background: #009ee0;
}
table tr.line td,table tr.line th { table tr.line td,table tr.line th {
border-bottom: 1px solid #ffb400; border-bottom: 1px solid #ffb400;
@ -178,3 +179,14 @@ a {
color: #dc0067; color: #dc0067;
text-decoration: none; text-decoration: none;
} }
input[readonly] {
border: none;
background: none;
padding: 0px;
font-size: 15px;
color: #333;
line-height: 1.3;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
}

View File

@ -6,6 +6,10 @@
const config = { const config = {
'title': 'FreifunkManager - Breminale', 'title': 'FreifunkManager - Breminale',
'backend': `ws://${location.host}/websocket`, 'backend': `ws://${location.host}/websocket`,
'node': {
// Minuten till is shown as offline
'offline': 5
},
'map': { 'map': {
'view': { 'view': {
'bound': [53.07103, 8.81624], 'bound': [53.07103, 8.81624],

View File

@ -1,5 +1,6 @@
/* exported guiList */ /* exported guiList */
/* global domlib,store,router */ /* global config,domlib,store,router,socket */
/* eslint max-lines: [off] */
const guiList = {}; const guiList = {};
@ -14,7 +15,8 @@ const guiList = {};
sortReverse = false, sortReverse = false,
sortIndex = null, sortIndex = null,
hostnameFilter = null, hostnameFilter = null,
nodeidFilter = null; nodeidFilter = null,
editing = false;
// eslint-disable-next-line id-length // eslint-disable-next-line id-length
function sort (a, b) { function sort (a, b) {
@ -69,52 +71,160 @@ const guiList = {};
lastseen = domlib.newAt(tr, 'td'), lastseen = domlib.newAt(tr, 'td'),
nodeID = domlib.newAt(tr, 'td'), nodeID = domlib.newAt(tr, 'td'),
hostname = domlib.newAt(tr, 'td'), hostname = domlib.newAt(tr, 'td'),
hostnameInput = domlib.newAt(hostname, 'input'),
freq = domlib.newAt(tr, 'td'), freq = domlib.newAt(tr, 'td'),
curchannel = domlib.newAt(tr, 'td'), curchannel = domlib.newAt(tr, 'td'),
channel = domlib.newAt(tr, 'td'), channel = domlib.newAt(tr, 'td'),
channel24Input = domlib.newAt(domlib.newAt(channel, 'span'), 'input'),
channel5Input = domlib.newAt(domlib.newAt(channel, 'span'), 'input'),
curpower = domlib.newAt(tr, 'td'), curpower = domlib.newAt(tr, 'td'),
power = domlib.newAt(tr, 'td'), power = domlib.newAt(tr, 'td'),
power24Input = domlib.newAt(domlib.newAt(power, 'span'), 'input'),
power5Input = domlib.newAt(domlib.newAt(power, 'span'), 'input'),
client = domlib.newAt(tr, 'td'), client = domlib.newAt(tr, 'td'),
chanUtil = domlib.newAt(tr, 'td'), chanUtil = domlib.newAt(tr, 'td'),
option = domlib.newAt(tr, 'td'), option = domlib.newAt(tr, 'td'),
edit = domlib.newAt(option, 'div'); edit = domlib.newAt(option, 'div');
startdate.setMinutes(startdate.getMinutes() - 1); startdate.setMinutes(startdate.getMinutes() - config.node.offline);
if (new Date(node.lastseen) < startdate) { if (new Date(node.lastseen) < startdate) {
tr.classList.add('offline'); tr.classList.add('offline');
} }
// eslint-disable-next-line no-underscore-dangle
if (!node._wireless) {
tr.classList.add('unseen');
}
lastseen.innerHTML = moment(node.lastseen).fromNow(true); lastseen.innerHTML = moment(node.lastseen).fromNow(true);
nodeID.innerHTML = node.node_id; nodeID.innerHTML = node.node_id;
hostname.innerHTML = node.hostname; hostnameInput.value = node.hostname;
hostnameInput.readOnly = true;
hostnameInput.setAttribute('placeholder', 'Hostname');
hostnameInput.addEventListener('dblclick', () => {
editing = true;
hostnameInput.readOnly = false;
});
hostnameInput.addEventListener('focusout', () => {
if (hostnameInput.readOnly) {
return;
}
editing = false;
hostnameInput.readOnly = true;
node.hostname = hostnameInput.value;
socket.sendnode(node);
});
domlib.newAt(freq, 'span').innerHTML = '2.4 Ghz'; domlib.newAt(freq, 'span').innerHTML = '2.4 Ghz';
domlib.newAt(freq, 'span').innerHTML = '5 Ghz'; domlib.newAt(freq, 'span').innerHTML = '5 Ghz';
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
domlib.newAt(curchannel, 'span').innerHTML = node._wireless.channel24 || '-'; if (node._wireless) {
domlib.newAt(curchannel, 'span').innerHTML = node._wireless.channel5 || '-'; domlib.newAt(curchannel, 'span').innerHTML = node._wireless.channel24 || '-';
domlib.newAt(curchannel, 'span').innerHTML = node._wireless.channel5 || '-';
}
/* eslint-enable no-underscore-dangle */ /* eslint-enable no-underscore-dangle */
domlib.newAt(channel, 'span').innerHTML = node.wireless.channel24 || '-';
domlib.newAt(channel, 'span').innerHTML = node.wireless.channel5 || '-'; channel24Input.value = node.wireless.channel24 || '';
channel24Input.type = 'number';
channel24Input.min = 1;
channel24Input.max = 14;
channel24Input.readOnly = true;
channel24Input.setAttribute('placeholder', '-');
channel24Input.addEventListener('dblclick', () => {
editing = true;
channel24Input.readOnly = false;
});
channel24Input.addEventListener('focusout', () => {
if (channel24Input.readOnly) {
return;
}
editing = false;
channel24Input.readOnly = true;
node.wireless.channel24 = parseInt(channel24Input.value, 10);
socket.sendnode(node);
});
channel5Input.value = node.wireless.channel5 || '';
channel5Input.type = 'number';
channel5Input.min = 36;
channel5Input.max = 165;
channel5Input.step = 4;
channel5Input.readOnly = true;
channel5Input.setAttribute('placeholder', '-');
channel5Input.addEventListener('dblclick', () => {
editing = true;
channel5Input.readOnly = false;
});
channel5Input.addEventListener('focusout', () => {
if (channel5Input.readOnly) {
return;
}
editing = false;
channel5Input.readOnly = true;
node.wireless.channel5 = parseInt(channel5Input.value, 10);
socket.sendnode(node);
});
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
domlib.newAt(curpower, 'span').innerHTML = node._wireless.txpower24 || '-'; if (node._wireless) {
domlib.newAt(curpower, 'span').innerHTML = node._wireless.txpower5 || '-'; domlib.newAt(curpower, 'span').innerHTML = node._wireless.txpower24 || '-';
domlib.newAt(curpower, 'span').innerHTML = node._wireless.txpower5 || '-';
}
/* eslint-enable no-underscore-dangle */ /* eslint-enable no-underscore-dangle */
domlib.newAt(power, 'span').innerHTML = node.wireless.txpower24 || '-'; power24Input.value = node.wireless.txpower24 || '';
domlib.newAt(power, 'span').innerHTML = node.wireless.txpower5 || '-'; power24Input.type = 'number';
power24Input.min = 1;
power24Input.max = 23;
power24Input.readOnly = true;
power24Input.setAttribute('placeholder', '-');
power24Input.addEventListener('dblclick', () => {
editing = true;
power24Input.readOnly = false;
});
power24Input.addEventListener('focusout', () => {
if (power24Input.readOnly) {
return;
}
editing = false;
power24Input.readOnly = true;
node.wireless.txpower24 = parseInt(power24Input.value, 10);
socket.sendnode(node);
});
power5Input.value = node.wireless.txpower5 || '';
power5Input.type = 'number';
power5Input.min = 1;
power5Input.max = 23;
power5Input.readOnly = true;
power5Input.setAttribute('placeholder', '-');
power5Input.addEventListener('dblclick', () => {
editing = true;
power5Input.readOnly = false;
});
power5Input.addEventListener('focusout', () => {
if (power5Input.readOnly) {
return;
}
editing = false;
power5Input.readOnly = true;
node.wireless.txpower5 = parseInt(power5Input.value, 10);
socket.sendnode(node);
});
domlib.newAt(client, 'span').innerHTML = node.statistics.clients.wifi24; domlib.newAt(client, 'span').innerHTML = node.statistics.clients.wifi24;
domlib.newAt(client, 'span').innerHTML = node.statistics.clients.wifi5; domlib.newAt(client, 'span').innerHTML = node.statistics.clients.wifi5;
/* eslint-disable id-length, no-magic-numbers,one-var */ /* eslint-disable id-length, no-magic-numbers,one-var */
const chanUtil24 = node.statistics.wireless.filter((d) => d.frequency < 5000)[0] || {}, const chanUtil24 = node.statistics.wireless
chanUtil5 = node.statistics.wireless.filter((d) => d.frequency > 5000)[0] || {}; ? node.statistics.wireless.filter((d) => d.frequency < 5000)[0] || {}
: {},
chanUtil5 = node.statistics.wireless
? node.statistics.wireless.filter((d) => d.frequency > 5000)[0] || {}
: {};
/* eslint-enable id-length, no-magic-numbers,one-var */ /* eslint-enable id-length, no-magic-numbers,one-var */
domlib.newAt(chanUtil, 'span').innerHTML = chanUtil24.ChanUtil || '-'; domlib.newAt(chanUtil, 'span').innerHTML = chanUtil24.ChanUtil || '-';
@ -130,6 +240,9 @@ const guiList = {};
} }
function update () { function update () {
if (editing) {
return;
}
domlib.removeChildren(tbody); domlib.removeChildren(tbody);
let nodes = store.getNodes(); let nodes = store.getNodes();

View File

@ -37,7 +37,7 @@ const guiMap = {};
tx24 = node.wireless.txpower24 || '-', tx24 = node.wireless.txpower24 || '-',
tx5 = node.wireless.txpower5 || '-'; tx5 = node.wireless.txpower5 || '-';
startdate.setMinutes(startdate.getMinutes() - 1); startdate.setMinutes(startdate.getMinutes() - config.node.offline);
if (new Date(node.lastseen) < startdate) { if (new Date(node.lastseen) < startdate) {
className += ' offline'; className += ' offline';
} }

View File

@ -55,7 +55,7 @@ const guiNode = {};
return; return;
} }
startdate.setMinutes(startdate.getMinutes() - 1); startdate.setMinutes(startdate.getMinutes() - config.node.offline);
if (new Date(node.lastseen) < startdate) { if (new Date(node.lastseen) < startdate) {
ago.classList.add('offline'); ago.classList.add('offline');
ago.classList.remove('online'); ago.classList.remove('online');

View File

@ -24,10 +24,10 @@ let socket = {'readyState': 0};
const msg = JSON.parse(raw.data); const msg = JSON.parse(raw.data);
switch (msg.type) { switch (msg.type) {
case 'current': case 'system':
store.updateNode(msg.node, true); store.updateNode(msg.node, true);
break; break;
case 'to-update': case 'current':
store.updateNode(msg.node); store.updateNode(msg.node);
break; break;
case 'stats': case 'stats':
@ -54,7 +54,7 @@ let socket = {'readyState': 0};
const notifyMsg = `Einstellungen für '${node.node_id}' gespeichert.`, const notifyMsg = `Einstellungen für '${node.node_id}' gespeichert.`,
socketMsg = JSON.stringify({ socketMsg = JSON.stringify({
'node': node, 'node': node,
'type': 'to-update' 'type': 'system'
}); });

View File

@ -17,30 +17,37 @@ const store = {
(function init () { (function init () {
'use strict'; 'use strict';
const list = {}, const current = {},
toupdate = {}; list = {};
function getNode (nodeid) { function getNode (nodeid) {
let node = {}; let node = {};
if (toupdate[nodeid]) { if (list[nodeid]) {
node = toupdate[nodeid];
} else if (list[nodeid]) {
node = list[nodeid]; node = list[nodeid];
if (current[nodeid]) {
const cNode = current[nodeid];
// eslint-disable-next-line no-underscore-dangle
node._wireless = cNode.wireless;
node.lastseen = cNode.lastseen;
}
} else { } else {
return null; // eslint-disable-next-line camelcase
node.node_id = nodeid;
node.wireless = {};
node.location = {};
list[nodeid] = node;
} }
// eslint-disable-next-line no-underscore-dangle
node._wireless = list[nodeid].wireless;
return node; return node;
} }
store.updateNode = function updateNode (node, real) { store.updateNode = function updateNode (node, system) {
if (real) { if (system) {
list[node.node_id] = node; list[node.node_id] = node;
} else { } else {
toupdate[node.node_id] = node; current[node.node_id] = node;
} }
}; };

View File

@ -59,16 +59,16 @@ func (c *Client) Listen() {
func (c *Client) allNodes() { func (c *Client) allNodes() {
for _, node := range nodes.List { for _, node := range nodes.List {
c.Write(&Message{Type: MessageTypeCurrentNode, Node: node}) c.Write(&Message{Type: MessageTypeSystemNode, Node: node})
} }
for _, node := range nodes.ToUpdate { for _, node := range nodes.Current {
c.Write(&Message{Type: MessageTypeUpdateNode, Node: node}) c.Write(&Message{Type: MessageTypeCurrentNode, Node: node})
} }
} }
func (c *Client) handleMessage(msg *Message) { func (c *Client) handleMessage(msg *Message) {
switch msg.Type { switch msg.Type {
case MessageTypeUpdateNode: case MessageTypeSystemNode:
nodes.UpdateNode(msg.Node) nodes.UpdateNode(msg.Node)
} }
} }

View File

@ -9,7 +9,7 @@ type Message struct {
} }
const ( const (
MessageTypeUpdateNode = "to-update" MessageTypeSystemNode = "system"
MessageTypeCurrentNode = "current" MessageTypeCurrentNode = "current"
MessageTypeStats = "stats" MessageTypeStats = "stats"
) )

View File

@ -46,10 +46,10 @@ func Start(nodeBind *runtime.Nodes) {
nodes.AddNotify(NotifyNode) nodes.AddNotify(NotifyNode)
} }
func NotifyNode(node *runtime.Node, real bool) { func NotifyNode(node *runtime.Node, system bool) {
msgType := MessageTypeUpdateNode msgType := MessageTypeCurrentNode
if real { if system {
msgType = MessageTypeCurrentNode msgType = MessageTypeSystemNode
} }
SendAll(Message{Type: msgType, Node: node}) SendAll(Message{Type: msgType, Node: node})
} }