summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-mwan3/htdocs
diff options
context:
space:
mode:
Diffstat (limited to 'applications/luci-app-mwan3/htdocs')
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/mwan3.css8
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/globals.js43
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/interface.js276
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/member.js43
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/notify.js52
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/policy.js46
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/rule.js107
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/detail.js22
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/diagnostics.js116
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/overview.js104
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/troubleshooting.js22
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/status/include/90_mwan3.js117
12 files changed, 956 insertions, 0 deletions
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/mwan3.css b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/mwan3.css
new file mode 100644
index 0000000000..ebe7764c83
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/mwan3.css
@@ -0,0 +1,8 @@
+#mwan3-service-status > .alert-message {
+ display: inline-block;
+ margin: 1rem;
+ padding: 1rem;
+ width: 15rem;
+ height: 6rem;
+ vertical-align: middle;
+}
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/globals.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/globals.js
new file mode 100644
index 0000000000..b8c12c0e9b
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/globals.js
@@ -0,0 +1,43 @@
+'use strict';
+'require form';
+'require view';
+
+return view.extend({
+
+ render: function () {
+ var m, s, o;
+
+ m = new form.Map('mwan3', _('MultiWAN Manager - Globals'));
+
+ s = m.section(form.NamedSection, 'globals', 'globals');
+
+ o = s.option(form.Value, 'mmx_mask', _('Firewall mask'),
+ _('Enter value in hex, starting with <code>0x</code>'));
+ o.datatype = 'hex(4)';
+ o.default = '0x3F00';
+
+ o = s.option(form.Flag, 'logging', _('Logging'),
+ _('Enables global firewall logging'));
+
+ o = s.option(form.ListValue, 'loglevel', _('Loglevel'),
+ _('Firewall loglevel'));
+ o.default = 'notice';
+ o.value('emerg', _('Emergency'));
+ o.value('alert', _('Alert'));
+ o.value('crit', _('Critical'));
+ o.value('error', _('Error'));
+ o.value('warning', _('Warning'));
+ o.value('notice', _('Notice'));
+ o.value('info', _('Info'));
+ o.value('debug', _('Debug'));
+ o.depends('logging', '1');
+
+ o = s.option(form.DynamicList, 'rt_table_lookup',
+ _('Routing table lookup'),
+ _('Also scan this Routing table for connected networks'));
+ o.datatype = 'uinteger';
+ o.value('220', _('Routing table %d').format('220'));
+
+ return m.render();
+ }
+})
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/interface.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/interface.js
new file mode 100644
index 0000000000..3615d223ec
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/interface.js
@@ -0,0 +1,276 @@
+'use strict';
+'require form';
+'require fs';
+'require view';
+'require uci';
+
+return view.extend({
+ load: function() {
+ return Promise.all([
+ L.resolveDefault(fs.stat('/usr/bin/httping'), {}),
+ L.resolveDefault(fs.stat('/usr/bin/nping'), {}),
+ L.resolveDefault(fs.stat('/usr/bin/arping'), {}),
+ uci.load('network')
+ ]);
+ },
+
+ render: function (stats) {
+ var m, s, o;
+
+ m = new form.Map('mwan3', _('MultiWAN Manager - Interfaces'),
+ _('Mwan3 requires that all interfaces have a unique metric configured in /etc/config/network.') + '<br />' +
+ _('Names must match the interface name found in /etc/config/network.') + '<br />' +
+ _('Names may contain characters A-Z, a-z, 0-9, _ and no spaces-') + '<br />' +
+ _('Interfaces may not share the same name as configured members, policies or rules.'));
+
+ s = m.section(form.GridSection, 'interface');
+ s.addremove = true;
+ s.anonymous = false;
+ s.nodescriptions = true;
+
+ o = s.option(form.Flag, 'enabled', _('Enabled'));
+ o.default = false;
+
+ o = s.option(form.ListValue, 'initial_state', _('Initial state'),
+ _('Expect interface state on up event'));
+ o.default = 'online';
+ o.value('online', _('Online'));
+ o.value('offline', _('Offline'));
+ o.modalonly = true;
+
+ o = s.option(form.ListValue, 'family', _('Internet Protocol'));
+ o.default = 'ipv4';
+ o.value('ipv4', _('IPv4'));
+ o.value('ipv6', _('IPv6'));
+ o.modalonly = true;
+
+ o = s.option(form.DynamicList, 'track_ip', _('Tracking hostname or IP address'),
+ _('This hostname or IP address will be pinged to determine if the link is up or down. Leave blank to assume interface is always online'));
+ o.datatype = 'host';
+ o.modalonly = true;
+
+ o = s.option(form.ListValue, 'track_method', _('Tracking method'));
+ o.default = 'ping';
+ o.value('ping');
+ if (stats[0].type === 'file') {
+ o.value('httping');
+ }
+ if (stats[1].type === 'file') {
+ o.value('nping-tcp');
+ o.value('nping-udp');
+ o.value('nping-icmp');
+ o.value('nping-arp');
+ }
+ if (stats[2].type === 'file') {
+ o.value('arping');
+ }
+
+ o = s.option(form.Flag, 'httping_ssl', _('Enable ssl tracking'),
+ _('Enables https tracking on ssl port 443'));
+ o.depends('track_method', 'httping');
+ o.rmempty = false;
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'reliability', _('Tracking reliability'),
+ _('Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up'));
+ o.datatype = 'range(1, 100)';
+ o.default = '1';
+
+ o = s.option(form.ListValue, 'count', _('Ping count'));
+ o.default = '1';
+ o.value('1');
+ o.value('2');
+ o.value('3');
+ o.value('4');
+ o.value('5');
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'size', _('Ping size'));
+ o.default = '56';
+ o.depends('track_method', 'ping');
+ o.value('8');
+ o.value('24');
+ o.value('56');
+ o.value('120');
+ o.value('248');
+ o.value('504');
+ o.value('1016');
+ o.value('1472');
+ o.value('2040');
+ o.datatype = 'range(1, 65507)';
+ o.modalonly = true;
+
+ o =s.option(form.Value, 'max_ttl', _('Max TTL'));
+ o.default = '60';
+ o.depends('track_method', 'ping');
+ o.value('10');
+ o.value('20');
+ o.value('30');
+ o.value('40');
+ o.value('50');
+ o.value('60');
+ o.value('70');
+ o.datatype = 'range(1, 255)';
+ o.modalonly = true;
+
+ o = s.option(form.Flag, 'check_quality', _('Check link quality'));
+ o.depends('track_method', 'ping');
+ o.default = false;
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'failure_latency', _('Failure latency [ms]'));
+ o.depends('check_quality', '1');
+ o.default = '1000';
+ o.value('25');
+ o.value('50');
+ o.value('75');
+ o.value('100');
+ o.value('150');
+ o.value('200');
+ o.value('250');
+ o.value('300');
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'failure_loss', _('Failure packet loss [%]'));
+ o.depends('check_quality', '1');
+ o.default = '40';
+ o.value('2');
+ o.value('5');
+ o.value('10');
+ o.value('20');
+ o.value('25');
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'recovery_latency', _('Recovery latency [ms]'));
+ o.depends('check_quality', '1');
+ o.default = '500';
+ o.value('25');
+ o.value('50');
+ o.value('75');
+ o.value('100');
+ o.value('150');
+ o.value('200');
+ o.value('250');
+ o.value('300');
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'recovery_loss', _('Recovery packet loss [%]'));
+ o.depends('check_quality', '1');
+ o.default = '10';
+ o.value('2');
+ o.value('5');
+ o.value('10');
+ o.value('20');
+ o.value('25');
+ o.modalonly = true;
+
+ o = s.option(form.ListValue, "timeout", _("Ping timeout"));
+ o.default = '4';
+ for (var i = 1; i <= 10; i++)
+ o.value(String(i), N_(i, '%d second', '%d seconds').format(i));
+ o.modalonly = true;
+
+ o = s.option(form.ListValue, 'interval', _('Ping interval'));
+ o.default = '10';
+ o.value('1', _('%d second').format('1'));
+ o.value('3', _('%d seconds').format('3'));
+ o.value('5', _('%d seconds').format('5'));
+ o.value('10', _('%d seconds').format('10'));
+ o.value('20', _('%d seconds').format('20'));
+ o.value('30', _('%d seconds').format('30'));
+ o.value('60', _('%d minute').format('1'));
+ o.value('300', _('%d minutes').format('5'));
+ o.value('600', _('%d minutes').format('10'));
+ o.value('900', _('%d minutes').format('15'));
+ o.value('1800', _('%d minutes').format('30'));
+ o.value('3600', _('%d hour').format('1'));
+
+ o = s.option(form.Value, 'failure_interval', _('Failure interval'),
+ _('Ping interval during failure detection'));
+ o.default = '5';
+ o.value('1', _('%d second').format('1'));
+ o.value('3', _('%d seconds').format('3'));
+ o.value('5', _('%d seconds').format('5'));
+ o.value('10', _('%d seconds').format('10'));
+ o.value('20', _('%d seconds').format('20'));
+ o.value('30', _('%d seconds').format('30'));
+ o.value('60', _('%d minute').format('1'));
+ o.value('300', _('%d minutes').format('5'));
+ o.value('600', _('%d minutes').format('10'));
+ o.value('900', _('%d minutes').format('15'));
+ o.value('1800', _('%d minutes').format('30'));
+ o.value('3600', _('%d hour').format('1'));
+ o.modalonly = true;
+
+ o = s.option(form.Flag, 'keep_failure_interval', _('Keep failure interval'),
+ _('Keep ping failure interval during failure state'));
+ o.default = false;
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'recovery_interval', _('Recovery interval'),
+ _('Ping interval during failure recovering'));
+ o.default = '5';
+ o.value('1', _('%d second').format('1'));
+ o.value('3', _('%d seconds').format('3'));
+ o.value('5', _('%d seconds').format('5'));
+ o.value('10', _('%d seconds').format('10'));
+ o.value('20', _('%d seconds').format('20'));
+ o.value('30', _('%d seconds').format('30'));
+ o.value('60', _('%d minute').format('1'));
+ o.value('300', _('%d minutes').format('5'));
+ o.value('600', _('%d minutes').format('10'));
+ o.value('900', _('%d minutes').format('15'));
+ o.value('1800', _('%d minutes').format('30'));
+ o.value('3600', _('%d hour').format('1'));
+ o.modalonly = true;
+
+ o = s.option(form.ListValue, 'down', _('Interface down'),
+ _('Interface will be deemed down after this many failed ping tests'));
+ o.default = '5';
+ o.value('1');
+ o.value('2');
+ o.value('3');
+ o.value('4');
+ o.value('5');
+ o.value('6');
+ o.value('7');
+ o.value('8');
+ o.value('9');
+ o.value('10');
+
+ o = s.option(form.ListValue, 'up', _('Interface up'),
+ _('Downed interface will be deemed up after this many successful ping tests'));
+ o.default = "5";
+ o.value('1');
+ o.value('2');
+ o.value('3');
+ o.value('4');
+ o.value('5');
+ o.value('6');
+ o.value('7');
+ o.value('8');
+ o.value('9');
+ o.value('10');
+
+ o = s.option(form.ListValue, 'flush_conntrack', _('Flush conntrack table'),
+ _('Flush global firewall conntrack table on interface events'));
+ o.value('ifup', _('ifup (netifd)'));
+ o.value('ifdown', _('ifdown (netifd)'));
+ o.value('connected', _('connected (mwan3)'));
+ o.value('disconnected', _('disconnected (mwan3)'));
+ o.modalonly = true;
+
+ o = s.option(form.DummyValue, 'metric', _('Metric'),
+ _('This displays the metric assigned to this interface in /etc/config/network'));
+ o.rawhtml = true;
+ o.cfgvalue = function(s) {
+ var metric = uci.get('network', s, 'metric')
+ if (metric)
+ return metric;
+ else
+ return _('No interface metric set!');
+ }
+
+ return m.render();
+ }
+})
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/member.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/member.js
new file mode 100644
index 0000000000..c49cc6e1ef
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/member.js
@@ -0,0 +1,43 @@
+'use strict';
+'require form';
+'require view';
+'require uci';
+
+return view.extend({
+ load: function() {
+ return Promise.all([
+ uci.load('mwan3')
+ ]);
+ },
+
+ render: function () {
+ var m, s, o;
+
+ m = new form.Map('mwan3', _('MultiWAN Manager - Members'),
+ _('Members are profiles attaching a metric and weight to an MWAN interface.') + '<br />' +
+ _('Names may contain characters A-Z, a-z, 0-9, _ and no spaces.') + '<br />' +
+ _('Members may not share the same name as configured interfaces, policies or rules.'));
+
+ s = m.section(form.GridSection, 'member');
+ s.addremove = true;
+ s.anonymous = false;
+ s.nodescriptions = true;
+
+ o = s.option(form.ListValue, 'interface', _('Interface'));
+ var options = uci.sections('mwan3', 'interface')
+ for (var i = 0; i < options.length; i++) {
+ var value = options[i]['.name'];
+ o.value(value);
+ }
+
+ o = s.option(form.Value, 'metric', _('Metric'),
+ _('Acceptable values: 1-256. Defaults to 1 if not set'));
+ o.datatype = 'range(1, 256)';
+
+ o = s.option(form.Value, 'weight', ('Weight'),
+ _('Acceptable values: 1-1000. Defaults to 1 if not set'));
+ o.datatype = 'range(1, 1000)';
+
+ return m.render();
+ }
+})
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/notify.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/notify.js
new file mode 100644
index 0000000000..ed27535c2a
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/notify.js
@@ -0,0 +1,52 @@
+'use strict';
+'require view';
+'require fs';
+'require ui';
+
+var isReadonlyView = !L.hasViewPermission() || null;
+
+return view.extend({
+ load: function() {
+ return L.resolveDefault(fs.read('/etc/mwan3.user'), '');
+ },
+
+ handleSave: function(ev) {
+ var value = (document.querySelector('textarea').value || '').trim().replace(/\r\n/g, '\n') + '\n';
+
+ return fs.write('/etc/mwan3.user', value).then(function(rc) {
+ document.querySelector('textarea').value = value;
+ ui.addNotification(null, E('p', _('Contents have been saved.')), 'info');
+ }).catch(function(e) {
+ ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message)));
+ });
+ },
+
+ render: function(mwan3user) {
+ return E([
+ E('h2', _('MultiWAN Manager - Notify')),
+ E('p', { 'class': 'cbi-section-descr' },
+ _('This section allows you to modify the content of \"/etc/mwan3.user\".') + '<br/>' +
+ _('The file is also preserved during sysupgrade.') + '<br/>' +
+ '<br />' +
+ _('Notes:') + '<br />' +
+ _('This file is interpreted as a shell script.') + '<br />' +
+ _('The first line of the script must be &#34;#!/bin/sh&#34; without quotes.') + '<br />' +
+ _('Lines beginning with # are comments and are not executed.') + '<br />' +
+ _('Put your custom mwan3 action here, they will be executed with each netifd hotplug interface event on interfaces for which mwan3 is enabled.') + '<br />' +
+ '<br />' +
+ _('There are three main environment variables that are passed to this script.') + '<br />' +
+ '<br />' +
+ _('%s: Name of the action that triggered this event').format('$ACTION') + '<br />' +
+ _('* %s: Is called by netifd and mwan3track').format('ifup') + '<br />' +
+ _('* %s: Is called by netifd and mwan3track').format('ifdown') + '<br />' +
+ _('* %s: Is only called by mwan3track if tracking was successful').format('connected') + '<br />' +
+ _('* %s: Is only called by mwan3track if tracking has failed').format('disonnected') + '<br />' +
+ _('%s: Name of the interface which went up or down (e.g. \"wan\" or \"wwan\")').format('$INTERFACE') + '<br />' +
+ _('%s: Name of Physical device which interface went up or down (e.g. \"eth0\" or \"wwan0\")').format('$DEVICE') + '<br />'),
+ E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 10, 'disabled': isReadonlyView }, [ mwan3user != null ? mwan3user : '' ]))
+ ]);
+ },
+
+ handleSaveApply: null,
+ handleReset: null
+});
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/policy.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/policy.js
new file mode 100644
index 0000000000..d39eb3bfba
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/policy.js
@@ -0,0 +1,46 @@
+'use strict';
+'require form';
+'require view';
+'require uci';
+
+return view.extend({
+ load: function() {
+ return Promise.all([
+ uci.load('mwan3')
+ ]);
+ },
+
+ render: function () {
+ var m, s, o;
+
+ m = new form.Map('mwan3', _('MultiWAN Manager - Policies'),
+ _('Policies are profiles grouping one or more members controlling how Mwan3 distributes traffic.') +
+ _('Member interfaces with lower metrics are used first.') +
+ _('Member interfaces with the same metric will be load-balanced.') +
+ _('Load-balanced member interfaces distribute more traffic out those with higher weights.') +
+ _('Names may contain characters A-Z, a-z, 0-9, _ and no spaces.') +
+ _('Names must be 15 characters or less.') +
+ _('Policies may not share the same name as configured interfaces, members or rules'));
+
+ s = m.section(form.GridSection, 'policy');
+ s.addremove = true;
+ s.anonymous = false;
+ s.nodescriptions = true;
+
+ o = s.option(form.DynamicList, 'use_member', _('Member used'));
+ var options = uci.sections('mwan3', 'member')
+ for (var i = 0; i < options.length; i++) {
+ var value = options[i]['.name'];
+ o.value(value);
+ }
+
+ o = s.option(form.ListValue, 'last_resort', _('Last resort'),
+ _('When all policy members are offline use this behavior for matched traffic'));
+ o.default = 'unreachable';
+ o.value('unreachable', _('unreachable (reject)'));
+ o.value('blackhole', _('blackhole (drop)'));
+ o.value('default', _('default (use main routing table)'));
+
+ return m.render();
+ }
+})
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/rule.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/rule.js
new file mode 100644
index 0000000000..76020173e9
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/rule.js
@@ -0,0 +1,107 @@
+'use strict';
+'require form';
+'require fs';
+'require view';
+'require uci';
+
+return view.extend({
+ load: function() {
+ return Promise.all([
+ fs.exec_direct('/usr/libexec/luci-mwan3', ['ipset', 'dump']),
+ uci.load('mwan3')
+ ]);
+ },
+
+ render: function (data) {
+ var m, s, o;
+
+ m = new form.Map('mwan3', _('MultiWAN Manager - Rules'),
+ _('Rules specify which traffic will use a particular MWAN policy.') + '<br />' +
+ _('Rules are based on IP address, port or protocol.') + '<br />' +
+ _('Rules are matched from top to bottom.') + '<br />' +
+ _('Rules below a matching rule are ignored.') + '<br />' +
+ _('Traffic not matching any rule is routed using the main routing table.') + '<br />' +
+ _('Traffic destined for known (other than default) networks is handled by the main routing table.') + '<br />' +
+ _('Traffic matching a rule, but all WAN interfaces for that policy are down will be blackholed.') + '<br />' +
+ _('Names may contain characters A-Z, a-z, 0-9, _ and no spaces.') + '<br />' +
+ _('Rules may not share the same name as configured interfaces, members or policies.'));
+
+ s = m.section(form.GridSection, 'rule');
+ s.addremove = true;
+ s.anonymous = false;
+ s.nodescriptions = true;
+
+ o = s.option(form.ListValue, 'family', _('Internet Protocol'));
+ o.default = '';
+ o.value('', _('IPv4 and IPv6'));
+ o.value('ipv4', _('IPv4 only'));
+ o.value('ipv6', _('IPv6 only'));
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'src_ip', _('Source address'),
+ _('Supports CIDR notation (eg \"192.168.100.0/24\") without quotes'));
+ o.datatype = 'ipaddr';
+
+ o = s.option(form.Value, 'src_port', _('Source port'),
+ _('May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or as a portrange (eg \"1024:2048\") without quotes'));
+ o.depends('proto', 'tcp');
+ o.depends('proto', 'udp');
+
+ o = s.option(form.Value, 'dest_ip', _('Destination address'),
+ _('Supports CIDR notation (eg \"192.168.100.0/24\") without quotes'));
+ o.datatype = 'ipaddr';
+
+ o = s.option(form.Value, 'dest_port', _('Destination port'),
+ _('May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or as a portrange (eg \"1024:2048\") without quotes'));
+ o.depends('proto', 'tcp');
+ o.depends('proto', 'udp');
+
+ o = s.option(form.Value, 'proto', _('Protocol'),
+ _('View the content of /etc/protocols for protocol description'));
+ o.default = 'all';
+ o.rmempty = false;
+ o.value('all');
+ o.value('tcp');
+ o.value('udp');
+ o.value('icmp');
+ o.value('esp');
+
+ o = s.option(form.ListValue, 'sticky', _('Sticky'),
+ _('Traffic from the same source IP address that previously matched this rule within the sticky timeout period will use the same WAN interface'));
+ o.default = '0';
+ o.value('1', _('Yes'));
+ o.value('0', _('No'));
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'timeout', _('Sticky timeout'),
+ _('Seconds. Acceptable values: 1-1000000. Defaults to 600 if not set'));
+ o.datatype = 'range(1, 1000000)';
+ o.modalonly = true;
+
+ o = s.option(form.Value, 'ipset', _('IPset'),
+ _('Name of IPset rule. Requires IPset rule in /etc/dnsmasq.conf (eg \"ipset=/youtube.com/youtube\")'));
+ o.value('', _('-- Please choose --'));
+ var ipsets = data[0].split(/\n/);
+ for (var i = 0; i < ipsets.length; i++) {
+ if (ipsets[i].length > 0)
+ o.value(ipsets[i]);
+ }
+ o.modalonly = true;
+
+ o = s.option(form.Flag, 'logging', _('Logging'),
+ _('Enables firewall rule logging (global mwan3 logging must also be enabled)'));
+ o.modalonly = true;
+
+ o = s.option(form.ListValue, 'use_policy', _('Policy assigned'));
+ var options = uci.sections('mwan3', 'policy')
+ for (var i = 0; i < options.length; i++) {
+ var value = options[i]['.name'];
+ o.value(value);
+ }
+ o.value('unreachable', _('unreachable (reject)'));
+ o.value('blackhole', _('blackhole (drop)'));
+ o.value('default', _('default (use main routing table)'));
+
+ return m.render();
+ }
+})
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/detail.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/detail.js
new file mode 100644
index 0000000000..552b1321fb
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/detail.js
@@ -0,0 +1,22 @@
+'use strict';
+'require fs';
+'require view';
+
+return view.extend({
+ load: function() {
+ return L.resolveDefault(fs.exec_direct('/usr/sbin/mwan3', [ 'status' ]),'');
+ },
+
+ render: function (report) {
+ return E('div', { 'class': 'cbi-map', 'id': 'map' }, [
+ E('h2', _('MultiWAN Manager - Status')),
+ E('div', { 'class': 'cbi-section' }, [
+ E('pre', [ report ])
+ ]),
+ ])
+ },
+
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null
+})
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/diagnostics.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/diagnostics.js
new file mode 100644
index 0000000000..71bee68f88
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/diagnostics.js
@@ -0,0 +1,116 @@
+'use strict';
+'require fs';
+'require uci';
+'require dom';
+'require ui';
+'require view';
+
+return view.extend({
+ handleCommand: function(exec, args) {
+ var buttons = document.querySelectorAll('.cbi-button');
+
+ for (var i = 0; i < buttons.length; i++)
+ buttons[i].setAttribute('disabled', 'true');
+
+ return fs.exec(exec, args).then(function(res) {
+ var out = document.querySelector('.command-output');
+ out.style.display = '';
+
+ dom.content(out, [ res.stdout || '', res.stderr || '' ]);
+ }).catch(function(err) {
+ ui.addNotification(null, E('p', [ err ]))
+ }).finally(function() {
+ for (var i = 0; i < buttons.length; i++)
+ buttons[i].removeAttribute('disabled');
+ });
+ },
+
+ handleAction: function(ev) {
+ var iface = document.getElementById('iface');
+ var task = document.getElementById('task');
+
+ switch (task.value) {
+ case 'gateway':
+ return this.handleCommand('/usr/libexec/luci-mwan3',
+ [ 'diag', 'gateway', iface.value ]);
+ case 'tracking':
+ return this.handleCommand('/usr/libexec/luci-mwan3',
+ [ 'diag', 'tracking', iface.value ]);
+ case 'rules':
+ return this.handleCommand('/usr/libexec/luci-mwan3',
+ [ 'diag', 'rules', iface.value ]);
+ case 'routes':
+ return this.handleCommand('/usr/libexec/luci-mwan3',
+ [ 'diag', 'routes', iface.value ]);
+ case 'ifup':
+ return this.handleCommand('/usr/sbin/mwan3',
+ [ 'ifup', iface.value]);
+ case 'ifdown':
+ return this.handleCommand('/usr/sbin/mwan3',
+ [ 'ifdown', iface.value]);
+ }
+ },
+
+ load: function() {
+ return Promise.all([
+ uci.load('mwan3')
+ ]);
+ },
+
+ render: function () {
+
+ var taskSel = [
+ E('option', { 'value': 'gateway' }, [ _('Ping default gateway') ]),
+ E('option', { 'value': 'tracking' }, [ _('Ping tracking IP') ]),
+ E('option', { 'value': 'rules' }, [ _('Check IP rules') ]),
+ E('option', { 'value': 'routes' }, [ _('Check routing table') ]),
+ E('option', { 'value': 'ifup' }, [ _('Hotplug ifup') ]),
+ E('option', { 'value': 'ifdown' }, [ _('Hotplug ifdown') ])
+ ];
+
+ var ifaceSel = [E('option', { value: '' }, [_('-- Interface Selection --')])];
+
+ var options = uci.sections('mwan3', 'interface')
+ for (var i = 0; i < options.length; i++) {
+ ifaceSel.push(E('option', { 'value': options[i]['.name'] }, options[i]['.name']));
+ }
+
+ return E('div', { 'class': 'cbi-map', 'id': 'map' }, [
+ E('h2', {}, [ _('MultiWAN Manager - Diagnostics') ]),
+ E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'cbi-section-node' }, [
+ E('div', { 'class': 'cbi-value' }, [
+ E('label', { 'class': 'cbi-value-title' }, [ _('Interface') ]),
+ E('div', { 'class': 'cbi-value-field' }, [
+ E('select', {'class': 'cbi-input-select', 'id': 'iface'},
+ ifaceSel
+ )
+ ])
+ ]),
+ E('div', { 'class': 'cbi-value' }, [
+ E('label', { 'class': 'cbi-value-title' }, [ _('Task') ]),
+ E('div', { 'class': 'cbi-value-field' }, [
+ E('select', { 'class': 'cbi-input-select', 'id': 'task' },
+ taskSel
+ )
+ ])
+ ])
+ ])
+ ]),
+ '\xa0',
+ E('pre', { 'class': 'command-output', 'style': 'display:none' }),
+ '\xa0',
+ E('div', { 'class': 'right' }, [
+ E('button', {
+ 'class': 'cbi-button cbi-button-apply',
+ 'id': 'execute',
+ 'click': ui.createHandlerFn(this, 'handleAction')
+ }, [ _('Execute') ]),
+ ]),
+ ]);
+ },
+
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null
+})
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/overview.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/overview.js
new file mode 100644
index 0000000000..14e0b0252b
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/overview.js
@@ -0,0 +1,104 @@
+'use strict';
+'require poll';
+'require view';
+'require rpc';
+
+var callMwan3Status = rpc.declare({
+ object: 'mwan3',
+ method: 'status',
+ expect: { },
+});
+
+document.querySelector('head').appendChild(E('link', {
+ 'rel': 'stylesheet',
+ 'type': 'text/css',
+ 'href': L.resource('view/mwan3/mwan3.css')
+}));
+
+function renderMwan3Status(status) {
+ if (!status.interfaces)
+ return '<strong>%h</strong>'.format(_('No MWAN interfaces found'));
+
+ var statusview = '';
+ for ( var iface in status.interfaces) {
+ var state = '';
+ var css = '';
+ var time = '';
+ var tname = '';
+ switch (status.interfaces[iface].status) {
+ case 'online':
+ state = _('Online');
+ css = 'success';
+ time = '%t'.format(status.interfaces[iface].online);
+ tname = _('Uptime');
+ css = 'success';
+ break;
+ case 'offline':
+ state = _('Offline');
+ css = 'danger';
+ time = '%t'.format(status.interfaces[iface].offline);
+ tname = _('Downtime');
+ break;
+ case 'notracking':
+ state = _('No Tracking');
+ if ((status.interfaces[iface].uptime) > 0) {
+ css = 'success';
+ time = '%t'.format(status.interfaces[iface].uptime);
+ tname = _('Uptime');
+ }
+ else {
+ css = 'warning';
+ time = '';
+ tname = '';
+ }
+ break;
+ default:
+ state = _('Disabled');
+ css = 'warning';
+ time = '';
+ tname = '';
+ break;
+ }
+
+ statusview += '<div class="alert-message %h">'.format(css);
+ statusview += '<div><strong>%h:&nbsp;</strong>%h</div>'.format(_('Interface'), iface);
+ statusview += '<div><strong>%h:&nbsp;</strong>%h</div>'.format(_('Status'), state);
+
+ if (time)
+ statusview += '<div><strong>%h:&nbsp;</strong>%h</div>'.format(tname, time);
+
+ statusview += '</div>';
+ }
+
+ return statusview;
+}
+
+return view.extend({
+ load: function() {
+ return Promise.all([
+ callMwan3Status(),
+ ]);
+ },
+
+ render: function (data) {
+ poll.add(function() {
+ return callMwan3Status().then(function(result) {
+ var view = document.getElementById('mwan3-service-status');
+ view.innerHTML = renderMwan3Status(result);
+ });
+ });
+
+ return E('div', { class: 'cbi-map' }, [
+ E('h2', [ _('MultiWAN Manager - Overview') ]),
+ E('div', { class: 'cbi-section' }, [
+ E('div', { 'id': 'mwan3-service-status' }, [
+ E('em', { 'class': 'spinning' }, [ _('Collecting data ...') ])
+ ])
+ ])
+ ]);
+ },
+
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null
+})
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/troubleshooting.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/troubleshooting.js
new file mode 100644
index 0000000000..6446125175
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/troubleshooting.js
@@ -0,0 +1,22 @@
+'use strict';
+'require fs';
+'require view';
+
+return view.extend({
+ load: function() {
+ return L.resolveDefault(fs.exec_direct('/usr/sbin/mwan3', [ 'internal', 'ipv4' ]),'');
+ },
+
+ render: function (report) {
+ return E('div', { 'class': 'cbi-map', 'id': 'map' }, [
+ E('h2', _('MultiWAN Manager - Troubleshooting')),
+ E('div', { 'class': 'cbi-section' }, [
+ E('pre', [ report ])
+ ]),
+ ])
+ },
+
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null
+})
diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/status/include/90_mwan3.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/status/include/90_mwan3.js
new file mode 100644
index 0000000000..0fc11550e9
--- /dev/null
+++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/status/include/90_mwan3.js
@@ -0,0 +1,117 @@
+'use strict';
+'require baseclass';
+'require rpc';
+
+var callMwan3Status = rpc.declare({
+ object: 'mwan3',
+ method: 'status',
+ expect: { },
+});
+
+document.querySelector('head').appendChild(E('link', {
+ 'rel': 'stylesheet',
+ 'type': 'text/css',
+ 'href': L.resource('view/mwan3/mwan3.css')
+}));
+
+return baseclass.extend({
+ title: _('MultiWAN Manager'),
+
+ load: function() {
+ return Promise.all([
+ callMwan3Status(),
+ ]);
+ },
+
+ render: function (result) {
+ if (!result[0].interfaces)
+ return null;
+
+ var container = E('div', { 'id': 'mwan3-service-status' });
+ var iface;
+ for ( iface in result[0].interfaces) {
+ var state = '';
+ var css = '';
+ var time = '';
+ var tname = '';
+ switch (result[0].interfaces[iface].status) {
+ case 'online':
+ state = _('Online');
+ css = 'alert-message success';
+ time = '%t'.format(result[0].interfaces[iface].online);
+ tname = _('Uptime');
+ break;
+ case 'offline':
+ state = _('Offline');
+ css = 'alert-message danger';
+ time = '%t'.format(result[0].interfaces[iface].offline);
+ tname = _('Downtime');
+ break;
+ case 'notracking':
+ state = _('No Tracking');
+ if ((result[0].interfaces[iface].uptime) > 0) {
+ css = 'alert-message success';
+ time = '%t'.format(result[0].interfaces[iface].uptime);
+ tname = _('Uptime');
+ }
+ else {
+ css = 'alert-message warning';
+ time = '';
+ tname = '';
+ }
+ break;
+ default:
+ css = 'alert-message warning';
+ state = _('Disabled');
+ time = '';
+ tname = '';
+ break;
+ }
+
+ if (time !== '' ) {
+ container.appendChild(
+ E('div', { 'class': css }, [
+ E('div', {}, [
+ E('strong', {}, [
+ _('Interface'), ':', ' '
+ ]),
+ iface
+ ]),
+ E('div', {}, [
+ E('strong', {}, [
+ _('Status'), ':', ' '
+ ]),
+ state
+ ]),
+ E('div', {}, [
+ E('strong', {}, [
+ tname, ':', ' '
+ ]),
+ time
+ ])
+ ])
+ );
+ }
+ else {
+ container.appendChild(
+ E('div', { 'class': css }, [
+ E('div', {}, [
+ E('strong', {}, [
+ _('Interface'), ':', ' '
+ ]),
+ iface
+ ]),
+ E('div', {}, [
+ E('strong', {}, [
+ _('Status'), ':', ' '
+ ]),
+ state
+ ])
+ ])
+ );
+ }
+ }
+
+ return container;
+ }
+});