luci2: Initial network interface configuration view
[project/luci2/ui.git] / luci2 / htdocs / luci2 / view / network.interfaces.js
1 L.ui.view.extend({
2         title: L.tr('Interface Overview'),
3
4         pendingRestart: [ ],
5         pendingShutdown: [ ],
6
7         setUp: L.rpc.declare({
8                 object: 'luci2.network',
9                 method: 'ifup',
10                 params: [ 'data' ],
11                 expect: { '': { code: -1 } }
12         }),
13
14         setDown: L.rpc.declare({
15                 object: 'luci2.network',
16                 method: 'ifdown',
17                 params: [ 'data' ],
18                 expect: { '': { code: -1 } }
19         }),
20
21         renderDeviceIcon: function(dev, up)
22         {
23                 var icon = dev ? dev.icon(up) : L.globals.resource + '/icons/ethernet_disabled.png';
24                 var desc = dev ? '%s (%s)'.format(dev.description(), dev.name()) : L.tr('Network interface not present');
25
26                 return $('<img />')
27                         .attr('title', desc)
28                         .attr('src', icon);
29         },
30
31         renderNetworkBadge: function(network, div)
32         {
33                 var dest = div || $('#network-badge-%s'.format(network.name()));
34                 var device = network.getDevice(); //network.device || { type: 'Network device', device: '?' };
35                 var subdevs = network.getSubdevices();
36
37                 if (div)
38                 {
39                         var h = $('<div />')
40                                 .addClass('ifacebox-head')
41                                 .text(network.name());
42
43                         if (network.zone)
44                                 h.css('background-color', network.zone.color).attr('title', L.trc('Interface status', 'Part of zone "%s"').format(network.zone.name));
45                         else
46                                 h.css('background-color', '#cccccc').attr('title', L.trc('Interface status', 'Not part of any zone'));
47
48                         dest.append(h);
49                 }
50                 else
51                 {
52                         dest.children('div.ifacebox-body').remove();
53                 }
54
55                 var b = $('<div />')
56                         .addClass('ifacebox-body');
57
58                 b.append(this.renderDeviceIcon(device, network.isUp()));
59
60                 if (subdevs.length)
61                 {
62                         b.append('(');
63
64                         for (var i = 0; i < subdevs.length; i++)
65                                 b.append(this.renderDeviceIcon(subdevs[i], subdevs[i].isUp()));
66
67                         b.append(')');
68                 }
69
70                 b.append($('<br />')).append($('<small />').text(device ? device.name() : '?'));
71
72                 return dest.append(b);
73         },
74
75         renderNetworkStatus: function(network, div)
76         {
77                 var rv = '';
78
79                 if (network.isUp())
80                 {
81                         rv += '<strong>%s</strong>: %t<br />'.format(
82                                 L.tr('Uptime'),
83                                 network.getUptime()
84                         );
85                 }
86                 else
87                 {
88                         rv += '<strong>%s</strong>: %s<br />'.format(
89                                 L.tr('Uptime'),
90                                 L.tr('Interface is down')
91                         );
92                 }
93
94                 var v4 = network.getIPv4Addrs();
95                 if (v4.length)
96                         rv += '<strong>%s</strong>: %s<br />'.format(
97                                 L.trc('Interface status', 'IPv4'),
98                                 v4.join(', ')
99                         );
100
101                 var v6 = network.getIPv6Addrs();
102                 if (v6.length)
103                         rv += '<strong>%s</strong>: %s<br />'.format(
104                                 L.trc('Interface status', 'IPv6'),
105                                 v6.join(', ')
106                         );
107
108                 return (div || $('#network-status-%s'.format(network.name())))
109                         .empty()
110                         .append(rv);
111         },
112
113         renderNetworkChart: function(network, div)
114         {
115                 var dest = (div || $('#network-chart-%s'.format(network.name())));
116
117                 dest.empty();
118
119                 dest.append($('<div />')
120                         .addClass('traffic-chart')
121                         .append($('<span />')
122                                 .attr('id', 'network-chart-tx-%s'.format(network.name()))
123                                 .hide())
124                         .append($('<label />')));
125
126                 dest.append($('<div />')
127                         .addClass('traffic-chart')
128                         .append($('<span />')
129                                 .attr('id', 'network-chart-rx-%s'.format(network.name()))
130                                 .hide())
131                         .append($('<label />')));
132
133                 dest.append($('<small />')
134                         .addClass('traffic-stats')
135                         .text(L.tr('Loading statistics…')));
136
137                 return dest;
138         },
139
140         refreshNetworkStatus: function()
141         {
142                 var self = this;
143                 var deferreds = [ ];
144
145                 while (self.pendingRestart.length)
146                         deferreds.push(self.setUp(self.pendingRestart.shift()));
147
148                 while (self.pendingShutdown.length)
149                         deferreds.push(self.setDown(self.pendingShutdown.shift()));
150
151                 return $.when.apply($, deferreds).then(function() {
152                         $('button').prop('disabled', false);
153                         return $.when(
154                                 L.NetworkModel.refreshDeviceStatus(),
155                                 L.NetworkModel.refreshInterfaceStatus()
156                         );
157                 }).then(function() {
158                         var networks = L.NetworkModel.getInterfaces();
159
160                         for (var i = 0; i < networks.length; i++)
161                         {
162                                 self.renderNetworkBadge(networks[i]);
163                                 self.renderNetworkStatus(networks[i]);
164                         }
165
166                         var max = 0.1;
167                         var networks = L.NetworkModel.getInterfaces();
168
169                         for (var i = 0; i < networks.length; i++)
170                         {
171                                 var network = networks[i];
172                                 var history = network.getTrafficHistory();
173                                 var stats = network.getStatistics();
174
175                                 var tx = $('#network-chart-tx-%s'.format(network.name()));
176                                 var rx = $('#network-chart-rx-%s'.format(network.name()));
177
178                                 var tx_rate = history.tx_bytes[history.tx_bytes.length - 1];
179                                 var rx_rate = history.rx_bytes[history.rx_bytes.length - 1];
180
181                                 max = Math.max(Math.max.apply(Math, history.rx_bytes),
182                                                            Math.max.apply(Math, history.tx_bytes),
183                                                            max);
184
185                                 for (var j = 0; j < history.rx_bytes.length; j++)
186                                         history.rx_bytes[j] = -Math.abs(history.rx_bytes[j]);
187
188                                 tx.text(history.tx_bytes.join(','));
189                                 rx.text(history.rx_bytes.join(','));
190
191                                 tx.next().attr('title', '%.2mB/s'.format(tx_rate));
192                                 rx.next().attr('title', '%.2mB/s'.format(rx_rate));
193
194                                 tx.nextAll('label').html('↑ %.2mB/s'.format(tx_rate));
195                                 rx.nextAll('label').html('↓ %.2mB/s'.format(rx_rate));
196
197                                 tx.parent().nextAll('small.traffic-stats').html(
198                                         '<strong>%s</strong>: %.2mB (%d Pkts.)<br />'.format(
199                                                 L.trc('Interface status', 'TX'),
200                                                 stats.tx_bytes, stats.tx_packets) +
201                                         '<strong>%s</strong>: %.2mB (%d Pkts.)<br />'.format(
202                                                 L.trc('Interface status', 'RX'),
203                                                 stats.rx_bytes, stats.rx_packets));
204                         }
205
206                         for (var i = 0; i < networks.length; i++)
207                         {
208                                 var network = networks[i];
209
210                                 var tx = $('#network-chart-tx-%s'.format(network.name()));
211                                 var rx = $('#network-chart-rx-%s'.format(network.name()));
212
213                                 tx.peity('line', { width: 200, min: 0, max: max });
214                                 rx.peity('line', { width: 200, min: -max, max: 0 });
215                         }
216
217                         L.ui.loading(false);
218                 });
219         },
220
221         renderContents: function(networks)
222         {
223                 var self = this;
224
225                 var list = new L.ui.table({
226                         columns: [ {
227                                 caption: L.tr('Network'),
228                                 width:   '120px',
229                                 format:  function(v) {
230                                         var div = $('<div />')
231                                                 .attr('id', 'network-badge-%s'.format(v.name()))
232                                                 .addClass('ifacebox');
233
234                                         return self.renderNetworkBadge(v, div);
235                                 }
236                         }, {
237                                 caption: L.tr('Traffic'),
238                                 width:   '215px',
239                                 format:  function(v) {
240                                         var div = $('<div />').attr('id', 'network-chart-%s'.format(v.name()));
241                                         return self.renderNetworkChart(v, div);
242                                 }
243                         }, {
244                                 caption: L.tr('Status'),
245                                 format:  function(v) {
246                                         var div = $('<small />').attr('id', 'network-status-%s'.format(v.name()));
247                                         return self.renderNetworkStatus(v, div);
248                                 }
249                         }, {
250                                 caption: L.tr('Actions'),
251                                 format:  function(v, n) {
252                                         return $('<div />')
253                                                 .addClass('btn-group btn-group-sm')
254                                                 .append(L.ui.button(L.tr('Restart'), 'default', L.tr('Enable or restart interface'))
255                                                         .click({ self: self, network: v }, self.handleIfup))
256                                                 .append(L.ui.button(L.tr('Shutdown'), 'default', L.tr('Shut down interface'))
257                                                         .click({ self: self, network: v }, self.handleIfdown))
258                                                 .append(L.ui.button(L.tr('Edit'), 'primary', L.tr('Edit interface'))
259                                                         .click({ self: self, network: v }, self.handleEdit))
260                                                 .append(L.ui.button(L.tr('Delete'), 'danger', L.tr('Delete interface'))
261                                                         .click({ self: self, network: v }, self.handleRemove));
262                                 }
263                         } ]
264                 });
265
266                 for (var i = 0; i < networks.length; i++)
267                         list.row([ networks[i], networks[i], networks[i], networks[i] ]);
268
269                 self.repeat(self.refreshNetworkStatus, 5000);
270
271                 $('#map')
272                         .append(list.render());
273         },
274
275         renderInterfaceForm: function(network)
276         {
277                 var m = new L.cbi.Modal('network', {
278                         tabbed:      true,
279                         caption:     'Interface config',
280                         description: 'I can config interface!!!!'
281                 });
282
283
284
285                 var s4 = m.section(L.cbi.TypedSection, 'route', {
286                         caption:     L.tr('Static IPv4 Routes'),
287                         anonymous:   true,
288                         addremove:   true,
289                         sortable:    true,
290                         add_caption: L.tr('Add new route'),
291                         remove_caption: L.tr('Remove route')
292                 });
293
294                 var ifc = s4.option(L.cbi.ListValue, 'interface', {
295                         caption:     L.tr('Interface')
296                 });
297
298                 ifc.value('foo');
299
300                 s4.option(L.cbi.InputValue, 'target', {
301                         caption:     L.tr('Target'),
302                         datatype:    'ip4addr'
303                 });
304
305                 s4.option(L.cbi.InputValue, 'netmask', {
306                         caption:     L.tr('IPv4-Netmask'),
307                         datatype:    'ip4addr',
308                         placeholder: '255.255.255.255',
309                         optional:    true
310                 });
311
312                 s4.option(L.cbi.InputValue, 'gateway', {
313                         caption:     L.tr('IPv4-Gateway'),
314                         datatype:    'ip4addr',
315                         optional:    true
316                 });
317
318                 s4.option(L.cbi.InputValue, 'metric', {
319                         caption:     L.tr('Metric'),
320                         datatype:    'range(0,255)',
321                         placeholder: 0,
322                         optional:    true
323                 });
324
325                 s4.option(L.cbi.InputValue, 'mtu', {
326                         caption:     L.tr('MTU'),
327                         datatype:    'range(64,9000)',
328                         placeholder: 1500,
329                         optional:    true
330                 });
331
332                 return m;
333         },
334
335         handleIfup: function(ev) {
336                 this.disabled = true;
337                 this.blur();
338                 ev.data.self.pendingRestart.push(ev.data.network['interface']);
339         },
340
341         handleIfdown: function(ev) {
342                 this.disabled = true;
343                 this.blur();
344                 ev.data.self.pendingShutdown.push(ev.data.network['interface']);
345         },
346
347         handleEdit: function(ev) {
348                 var self = ev.data.self;
349                 var network = ev.data.network;
350
351                 return network.createForm(L.cbi.Modal).show();
352         },
353
354         execute: function() {
355                 var self = this;
356
357                 return L.NetworkModel.init().then(function() {
358                         self.renderContents(L.NetworkModel.getInterfaces());
359                 });
360         }
361 });