diff options
author | Jo-Philipp Wich <jow@openwrt.org> | 2014-02-01 18:25:50 +0000 |
---|---|---|
committer | Jo-Philipp Wich <jow@openwrt.org> | 2014-02-01 18:27:37 +0000 |
commit | 639d2147af1293101ce10b4d67bde91a42da6926 (patch) | |
tree | 90cb708ff59b4d53f6f25c7acfd5a90c5f7f614a | |
parent | ee05d423509af67d650666b11580fd15beae7f32 (diff) | |
download | luci2-ui-639d2147af1293101ce10b4d67bde91a42da6926.tar.gz |
luci2: Initial network interface configuration view
-rw-r--r-- | luci2/htdocs/luci2/template/network.interfaces.htm | 1 | ||||
-rw-r--r-- | luci2/htdocs/luci2/view/network.interfaces.js | 361 | ||||
-rw-r--r-- | luci2/share/acl.d/luci2.json | 39 | ||||
-rw-r--r-- | luci2/share/menu.d/network.json | 6 |
4 files changed, 404 insertions, 3 deletions
diff --git a/luci2/htdocs/luci2/template/network.interfaces.htm b/luci2/htdocs/luci2/template/network.interfaces.htm new file mode 100644 index 0000000..ad19e7d --- /dev/null +++ b/luci2/htdocs/luci2/template/network.interfaces.htm @@ -0,0 +1 @@ +<div id="map"></div> diff --git a/luci2/htdocs/luci2/view/network.interfaces.js b/luci2/htdocs/luci2/view/network.interfaces.js new file mode 100644 index 0000000..a344327 --- /dev/null +++ b/luci2/htdocs/luci2/view/network.interfaces.js @@ -0,0 +1,361 @@ +L.ui.view.extend({ + title: L.tr('Interface Overview'), + + pendingRestart: [ ], + pendingShutdown: [ ], + + setUp: L.rpc.declare({ + object: 'luci2.network', + method: 'ifup', + params: [ 'data' ], + expect: { '': { code: -1 } } + }), + + setDown: L.rpc.declare({ + object: 'luci2.network', + method: 'ifdown', + params: [ 'data' ], + expect: { '': { code: -1 } } + }), + + renderDeviceIcon: function(dev, up) + { + var icon = dev ? dev.icon(up) : L.globals.resource + '/icons/ethernet_disabled.png'; + var desc = dev ? '%s (%s)'.format(dev.description(), dev.name()) : L.tr('Network interface not present'); + + return $('<img />') + .attr('title', desc) + .attr('src', icon); + }, + + renderNetworkBadge: function(network, div) + { + var dest = div || $('#network-badge-%s'.format(network.name())); + var device = network.getDevice(); //network.device || { type: 'Network device', device: '?' }; + var subdevs = network.getSubdevices(); + + if (div) + { + var h = $('<div />') + .addClass('ifacebox-head') + .text(network.name()); + + if (network.zone) + h.css('background-color', network.zone.color).attr('title', L.trc('Interface status', 'Part of zone "%s"').format(network.zone.name)); + else + h.css('background-color', '#cccccc').attr('title', L.trc('Interface status', 'Not part of any zone')); + + dest.append(h); + } + else + { + dest.children('div.ifacebox-body').remove(); + } + + var b = $('<div />') + .addClass('ifacebox-body'); + + b.append(this.renderDeviceIcon(device, network.isUp())); + + if (subdevs.length) + { + b.append('('); + + for (var i = 0; i < subdevs.length; i++) + b.append(this.renderDeviceIcon(subdevs[i], subdevs[i].isUp())); + + b.append(')'); + } + + b.append($('<br />')).append($('<small />').text(device ? device.name() : '?')); + + return dest.append(b); + }, + + renderNetworkStatus: function(network, div) + { + var rv = ''; + + if (network.isUp()) + { + rv += '<strong>%s</strong>: %t<br />'.format( + L.tr('Uptime'), + network.getUptime() + ); + } + else + { + rv += '<strong>%s</strong>: %s<br />'.format( + L.tr('Uptime'), + L.tr('Interface is down') + ); + } + + var v4 = network.getIPv4Addrs(); + if (v4.length) + rv += '<strong>%s</strong>: %s<br />'.format( + L.trc('Interface status', 'IPv4'), + v4.join(', ') + ); + + var v6 = network.getIPv6Addrs(); + if (v6.length) + rv += '<strong>%s</strong>: %s<br />'.format( + L.trc('Interface status', 'IPv6'), + v6.join(', ') + ); + + return (div || $('#network-status-%s'.format(network.name()))) + .empty() + .append(rv); + }, + + renderNetworkChart: function(network, div) + { + var dest = (div || $('#network-chart-%s'.format(network.name()))); + + dest.empty(); + + dest.append($('<div />') + .addClass('traffic-chart') + .append($('<span />') + .attr('id', 'network-chart-tx-%s'.format(network.name())) + .hide()) + .append($('<label />'))); + + dest.append($('<div />') + .addClass('traffic-chart') + .append($('<span />') + .attr('id', 'network-chart-rx-%s'.format(network.name())) + .hide()) + .append($('<label />'))); + + dest.append($('<small />') + .addClass('traffic-stats') + .text(L.tr('Loading statistics…'))); + + return dest; + }, + + refreshNetworkStatus: function() + { + var self = this; + var deferreds = [ ]; + + while (self.pendingRestart.length) + deferreds.push(self.setUp(self.pendingRestart.shift())); + + while (self.pendingShutdown.length) + deferreds.push(self.setDown(self.pendingShutdown.shift())); + + return $.when.apply($, deferreds).then(function() { + $('button').prop('disabled', false); + return $.when( + L.NetworkModel.refreshDeviceStatus(), + L.NetworkModel.refreshInterfaceStatus() + ); + }).then(function() { + var networks = L.NetworkModel.getInterfaces(); + + for (var i = 0; i < networks.length; i++) + { + self.renderNetworkBadge(networks[i]); + self.renderNetworkStatus(networks[i]); + } + + var max = 0.1; + var networks = L.NetworkModel.getInterfaces(); + + for (var i = 0; i < networks.length; i++) + { + var network = networks[i]; + var history = network.getTrafficHistory(); + var stats = network.getStatistics(); + + var tx = $('#network-chart-tx-%s'.format(network.name())); + var rx = $('#network-chart-rx-%s'.format(network.name())); + + var tx_rate = history.tx_bytes[history.tx_bytes.length - 1]; + var rx_rate = history.rx_bytes[history.rx_bytes.length - 1]; + + max = Math.max(Math.max.apply(Math, history.rx_bytes), + Math.max.apply(Math, history.tx_bytes), + max); + + for (var j = 0; j < history.rx_bytes.length; j++) + history.rx_bytes[j] = -Math.abs(history.rx_bytes[j]); + + tx.text(history.tx_bytes.join(',')); + rx.text(history.rx_bytes.join(',')); + + tx.next().attr('title', '%.2mB/s'.format(tx_rate)); + rx.next().attr('title', '%.2mB/s'.format(rx_rate)); + + tx.nextAll('label').html('↑ %.2mB/s'.format(tx_rate)); + rx.nextAll('label').html('↓ %.2mB/s'.format(rx_rate)); + + tx.parent().nextAll('small.traffic-stats').html( + '<strong>%s</strong>: %.2mB (%d Pkts.)<br />'.format( + L.trc('Interface status', 'TX'), + stats.tx_bytes, stats.tx_packets) + + '<strong>%s</strong>: %.2mB (%d Pkts.)<br />'.format( + L.trc('Interface status', 'RX'), + stats.rx_bytes, stats.rx_packets)); + } + + for (var i = 0; i < networks.length; i++) + { + var network = networks[i]; + + var tx = $('#network-chart-tx-%s'.format(network.name())); + var rx = $('#network-chart-rx-%s'.format(network.name())); + + tx.peity('line', { width: 200, min: 0, max: max }); + rx.peity('line', { width: 200, min: -max, max: 0 }); + } + + L.ui.loading(false); + }); + }, + + renderContents: function(networks) + { + var self = this; + + var list = new L.ui.table({ + columns: [ { + caption: L.tr('Network'), + width: '120px', + format: function(v) { + var div = $('<div />') + .attr('id', 'network-badge-%s'.format(v.name())) + .addClass('ifacebox'); + + return self.renderNetworkBadge(v, div); + } + }, { + caption: L.tr('Traffic'), + width: '215px', + format: function(v) { + var div = $('<div />').attr('id', 'network-chart-%s'.format(v.name())); + return self.renderNetworkChart(v, div); + } + }, { + caption: L.tr('Status'), + format: function(v) { + var div = $('<small />').attr('id', 'network-status-%s'.format(v.name())); + return self.renderNetworkStatus(v, div); + } + }, { + caption: L.tr('Actions'), + format: function(v, n) { + return $('<div />') + .addClass('btn-group btn-group-sm') + .append(L.ui.button(L.tr('Restart'), 'default', L.tr('Enable or restart interface')) + .click({ self: self, network: v }, self.handleIfup)) + .append(L.ui.button(L.tr('Shutdown'), 'default', L.tr('Shut down interface')) + .click({ self: self, network: v }, self.handleIfdown)) + .append(L.ui.button(L.tr('Edit'), 'primary', L.tr('Edit interface')) + .click({ self: self, network: v }, self.handleEdit)) + .append(L.ui.button(L.tr('Delete'), 'danger', L.tr('Delete interface')) + .click({ self: self, network: v }, self.handleRemove)); + } + } ] + }); + + for (var i = 0; i < networks.length; i++) + list.row([ networks[i], networks[i], networks[i], networks[i] ]); + + self.repeat(self.refreshNetworkStatus, 5000); + + $('#map') + .append(list.render()); + }, + + renderInterfaceForm: function(network) + { + var m = new L.cbi.Modal('network', { + tabbed: true, + caption: 'Interface config', + description: 'I can config interface!!!!' + }); + + + + var s4 = m.section(L.cbi.TypedSection, 'route', { + caption: L.tr('Static IPv4 Routes'), + anonymous: true, + addremove: true, + sortable: true, + add_caption: L.tr('Add new route'), + remove_caption: L.tr('Remove route') + }); + + var ifc = s4.option(L.cbi.ListValue, 'interface', { + caption: L.tr('Interface') + }); + + ifc.value('foo'); + + s4.option(L.cbi.InputValue, 'target', { + caption: L.tr('Target'), + datatype: 'ip4addr' + }); + + s4.option(L.cbi.InputValue, 'netmask', { + caption: L.tr('IPv4-Netmask'), + datatype: 'ip4addr', + placeholder: '255.255.255.255', + optional: true + }); + + s4.option(L.cbi.InputValue, 'gateway', { + caption: L.tr('IPv4-Gateway'), + datatype: 'ip4addr', + optional: true + }); + + s4.option(L.cbi.InputValue, 'metric', { + caption: L.tr('Metric'), + datatype: 'range(0,255)', + placeholder: 0, + optional: true + }); + + s4.option(L.cbi.InputValue, 'mtu', { + caption: L.tr('MTU'), + datatype: 'range(64,9000)', + placeholder: 1500, + optional: true + }); + + return m; + }, + + handleIfup: function(ev) { + this.disabled = true; + this.blur(); + ev.data.self.pendingRestart.push(ev.data.network['interface']); + }, + + handleIfdown: function(ev) { + this.disabled = true; + this.blur(); + ev.data.self.pendingShutdown.push(ev.data.network['interface']); + }, + + handleEdit: function(ev) { + var self = ev.data.self; + var network = ev.data.network; + + return network.createForm(L.cbi.Modal).show(); + }, + + execute: function() { + var self = this; + + return L.NetworkModel.init().then(function() { + self.renderContents(L.NetworkModel.getInterfaces()); + }); + } +}); diff --git a/luci2/share/acl.d/luci2.json b/luci2/share/acl.d/luci2.json index 4d32466..9e21474 100644 --- a/luci2/share/acl.d/luci2.json +++ b/luci2/share/acl.d/luci2.json @@ -308,19 +308,52 @@ "description": "Network, switch and routing configuration", "read": { "ubus": { + "network": [ + "get_proto_handlers" + ], + "network.device": [ + "status" + ], + "network.interface": [ + "dump" + ], + "network.wireless": [ + "status" + ], "luci2.network": [ "switch_list", "switch_info", - "switch_status" + "switch_status", + "device_list" + ], + "luci2.network.bwmon": [ + "devices", + "statistics" ] }, "uci": [ - "network" + "network", + "wireless" + ] + }, + "write": { + "uci": [ + "network", + "wireless" + ] + } + }, + + "firewall": { + "description": "Firewall configuration", + "read": { + "uci": [ + "firewall" ] }, "write": { "uci": [ - "network" + "firewall" ] } } diff --git a/luci2/share/menu.d/network.json b/luci2/share/menu.d/network.json index e577d70..d590046 100644 --- a/luci2/share/menu.d/network.json +++ b/luci2/share/menu.d/network.json @@ -3,6 +3,12 @@ "title": "Network", "index": 30 }, + "network/interfaces": { + "title": "Interfaces", + "acls": [ "network" ], + "view": "network/interfaces", + "index": 10 + }, "network/switch": { "title": "Switch", "acls": [ "network" ], |