[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 {
yanicDialer := yanic.Dial(config.Yanic.Type, config.Yanic.Address)
yanicDialer.NodeHandler = nodes.AddNode
yanicDialer.NodeHandler = nodes.LearnNode
yanicDialer.GlobalsHandler = func(data *runtimeYanic.GlobalStats) {
stats = data
websocket.NotifyStats(data)

View File

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

View File

@ -14,7 +14,7 @@ import (
type Nodes struct {
List map[string]*Node `json:"nodes"`
ToUpdate map[string]*Node
Current map[string]*Node `json:"-"`
ssh *ssh.Manager
statePath string
iface string
@ -25,7 +25,7 @@ type Nodes struct {
func NewNodes(path string, iface string, mgmt *ssh.Manager) *Nodes {
nodes := &Nodes{
List: make(map[string]*Node),
ToUpdate: make(map[string]*Node),
Current: make(map[string]*Node),
ssh: mgmt,
statePath: path,
iface: iface,
@ -34,33 +34,25 @@ func NewNodes(path string, iface string, mgmt *ssh.Manager) *Nodes {
return nodes
}
func (nodes *Nodes) AddNode(n *yanic.Node) {
func (nodes *Nodes) LearnNode(n *yanic.Node) {
node := NewNode(n)
if node == nil {
return
}
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.Unlock()
if cNode := nodes.List[node.NodeID]; cNode != nil {
cNode.Lastseen = jsontime.Now()
cNode.Stats = node.Stats
if uNode, ok := nodes.ToUpdate[node.NodeID]; ok {
uNode.Lastseen = jsontime.Now()
uNode.Stats = node.Stats
if nodes.List[node.NodeID].IsEqual(node) {
delete(nodes.ToUpdate, node.NodeID)
nodes.List[node.NodeID] = node
nodes.notify(node, true)
} else {
nodes.notify(uNode, false)
}
defer nodes.Unlock()
if lNode := nodes.List[node.NodeID]; lNode != nil {
lNode.Lastseen = jsontime.Now()
lNode.Stats = node.Stats
} else {
nodes.List[node.NodeID] = node
nodes.notify(node, true)
}
logger.Debugf("know already these node")
if _, ok := nodes.Current[node.NodeID]; ok {
nodes.Current[node.NodeID] = node
nodes.notify(node, false)
return
}
// session := nodes.ssh.ConnectTo(node.Address)
@ -72,16 +64,16 @@ func (nodes *Nodes) AddNode(n *yanic.Node) {
uptime := ssh.SSHResultToString(result)
logger.Infof("new node with uptime: %s", uptime)
nodes.List[node.NodeID] = node
nodes.notify(node, true)
nodes.Current[node.NodeID] = node
nodes.notify(node, false)
}
func (nodes *Nodes) AddNotify(f func(*Node, bool)) {
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 {
f(node, real)
f(node, system)
}
}
@ -95,16 +87,16 @@ func (nodes *Nodes) UpdateNode(node *Node) {
if n, ok := nodes.List[node.NodeID]; ok {
go node.SSHUpdate(nodes.ssh, nodes.iface, n)
}
nodes.ToUpdate[node.NodeID] = node
nodes.notify(node, false)
nodes.List[node.NodeID] = node
nodes.notify(node, true)
}
func (nodes *Nodes) Updater() {
nodes.Lock()
defer nodes.Unlock()
for nodeid := range nodes.ToUpdate {
if node := nodes.List[nodeid]; node != nil {
go node.SSHSet(nodes.ssh, nodes.iface)
for nodeid, node := range nodes.List {
if n, ok := nodes.Current[nodeid]; ok {
go node.SSHUpdate(nodes.ssh, nodes.iface, n)
}
}
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 {
background: #ccc;
}
table.nodes tbody tr.offline{
background: #ffb400;
}
table.nodes tbody tr.offline,
table.nodes tbody tr.offline:hover{
background: #dc0067;
}
table.nodes tbody tr.unseen{
background: #009ee0;
}
table tr.line td,table tr.line th {
border-bottom: 1px solid #ffb400;
@ -178,3 +179,14 @@ a {
color: #dc0067;
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 = {
'title': 'FreifunkManager - Breminale',
'backend': `ws://${location.host}/websocket`,
'node': {
// Minuten till is shown as offline
'offline': 5
},
'map': {
'view': {
'bound': [53.07103, 8.81624],

View File

@ -1,5 +1,6 @@
/* exported guiList */
/* global domlib,store,router */
/* global config,domlib,store,router,socket */
/* eslint max-lines: [off] */
const guiList = {};
@ -14,7 +15,8 @@ const guiList = {};
sortReverse = false,
sortIndex = null,
hostnameFilter = null,
nodeidFilter = null;
nodeidFilter = null,
editing = false;
// eslint-disable-next-line id-length
function sort (a, b) {
@ -69,52 +71,160 @@ const guiList = {};
lastseen = domlib.newAt(tr, 'td'),
nodeID = domlib.newAt(tr, 'td'),
hostname = domlib.newAt(tr, 'td'),
hostnameInput = domlib.newAt(hostname, 'input'),
freq = domlib.newAt(tr, 'td'),
curchannel = 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'),
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'),
chanUtil = domlib.newAt(tr, 'td'),
option = domlib.newAt(tr, 'td'),
edit = domlib.newAt(option, 'div');
startdate.setMinutes(startdate.getMinutes() - 1);
startdate.setMinutes(startdate.getMinutes() - config.node.offline);
if (new Date(node.lastseen) < startdate) {
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);
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 = '5 Ghz';
/* eslint-disable no-underscore-dangle */
if (node._wireless) {
domlib.newAt(curchannel, 'span').innerHTML = node._wireless.channel24 || '-';
domlib.newAt(curchannel, 'span').innerHTML = node._wireless.channel5 || '-';
}
/* 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 */
if (node._wireless) {
domlib.newAt(curpower, 'span').innerHTML = node._wireless.txpower24 || '-';
domlib.newAt(curpower, 'span').innerHTML = node._wireless.txpower5 || '-';
}
/* eslint-enable no-underscore-dangle */
domlib.newAt(power, 'span').innerHTML = node.wireless.txpower24 || '-';
domlib.newAt(power, 'span').innerHTML = node.wireless.txpower5 || '-';
power24Input.value = node.wireless.txpower24 || '';
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.wifi5;
/* eslint-disable id-length, no-magic-numbers,one-var */
const chanUtil24 = node.statistics.wireless.filter((d) => d.frequency < 5000)[0] || {},
chanUtil5 = node.statistics.wireless.filter((d) => d.frequency > 5000)[0] || {};
const chanUtil24 = node.statistics.wireless
? 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 */
domlib.newAt(chanUtil, 'span').innerHTML = chanUtil24.ChanUtil || '-';
@ -130,6 +240,9 @@ const guiList = {};
}
function update () {
if (editing) {
return;
}
domlib.removeChildren(tbody);
let nodes = store.getNodes();

View File

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

View File

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

View File

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

View File

@ -17,30 +17,37 @@ const store = {
(function init () {
'use strict';
const list = {},
toupdate = {};
const current = {},
list = {};
function getNode (nodeid) {
let node = {};
if (toupdate[nodeid]) {
node = toupdate[nodeid];
} else if (list[nodeid]) {
if (list[nodeid]) {
node = list[nodeid];
} else {
return null;
}
if (current[nodeid]) {
const cNode = current[nodeid];
// eslint-disable-next-line no-underscore-dangle
node._wireless = list[nodeid].wireless;
node._wireless = cNode.wireless;
node.lastseen = cNode.lastseen;
}
} else {
// eslint-disable-next-line camelcase
node.node_id = nodeid;
node.wireless = {};
node.location = {};
list[nodeid] = node;
}
return node;
}
store.updateNode = function updateNode (node, real) {
if (real) {
store.updateNode = function updateNode (node, system) {
if (system) {
list[node.node_id] = node;
} 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() {
for _, node := range nodes.List {
c.Write(&Message{Type: MessageTypeCurrentNode, Node: node})
c.Write(&Message{Type: MessageTypeSystemNode, Node: node})
}
for _, node := range nodes.ToUpdate {
c.Write(&Message{Type: MessageTypeUpdateNode, Node: node})
for _, node := range nodes.Current {
c.Write(&Message{Type: MessageTypeCurrentNode, Node: node})
}
}
func (c *Client) handleMessage(msg *Message) {
switch msg.Type {
case MessageTypeUpdateNode:
case MessageTypeSystemNode:
nodes.UpdateNode(msg.Node)
}
}

View File

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

View File

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