summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJo-Philipp Wich <jow@openwrt.org>2014-02-01 18:25:50 +0000
committerJo-Philipp Wich <jow@openwrt.org>2014-02-01 18:27:37 +0000
commit639d2147af1293101ce10b4d67bde91a42da6926 (patch)
tree90cb708ff59b4d53f6f25c7acfd5a90c5f7f614a
parentee05d423509af67d650666b11580fd15beae7f32 (diff)
downloadluci2-ui-639d2147af1293101ce10b4d67bde91a42da6926.tar.gz
luci2: Initial network interface configuration view
-rw-r--r--luci2/htdocs/luci2/template/network.interfaces.htm1
-rw-r--r--luci2/htdocs/luci2/view/network.interfaces.js361
-rw-r--r--luci2/share/acl.d/luci2.json39
-rw-r--r--luci2/share/menu.d/network.json6
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" ],