diff options
author | Florian Eckert <fe@dev.tdt.de> | 2021-07-02 15:23:39 +0200 |
---|---|---|
committer | Florian Eckert <fe@dev.tdt.de> | 2021-07-09 08:38:11 +0200 |
commit | f5f6b3e4f8e3faed4e9c9926e283c9cf226d4bd8 (patch) | |
tree | e2718f194ede046a90274443f6b61198f35a0961 /applications/luci-app-mwan3/htdocs | |
parent | 12067c8308936abb567001650105b33bec9a56a9 (diff) |
luci-app-mwan3: convert to JS
Signed-off-by: Florian Eckert <fe@dev.tdt.de>
Diffstat (limited to 'applications/luci-app-mwan3/htdocs')
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 "#!/bin/sh" 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: </strong>%h</div>'.format(_('Interface'), iface); + statusview += '<div><strong>%h: </strong>%h</div>'.format(_('Status'), state); + + if (time) + statusview += '<div><strong>%h: </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; + } +}); |