luci: show wifi vlan in the associated wireless stations list
authorTobias Waldvogel <tobias.waldvogel@gmail.com>
Sun, 5 Jan 2025 17:14:06 +0000 (18:14 +0100)
committerPaul Donald <newtwen+github@gmail.com>
Sun, 23 Feb 2025 18:51:07 +0000 (12:51 -0600)
This patch adds a badge to the associated wireless stations with the vlan id
and name. It is displayed in the same color as the network, to which it is
bridged, so the color corresponds to the color in the network configuration
page.

Signed-off-by: Tobias Waldvogel <tobias.waldvogel@gmail.com>
modules/luci-base/htdocs/luci-static/resources/network.js
modules/luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js
modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js

index 394dffc3f535900e62c15eeac3f475500700bda0..db471f29edf190911b0d65f4241242bb0d7ec63b 100644 (file)
@@ -639,7 +639,7 @@ function enumerateNetworks() {
 }
 
 
-var Hosts, Network, Protocol, Device, WifiDevice, WifiNetwork;
+var Hosts, Network, Protocol, Device, WifiDevice, WifiNetwork, WifiVlan;
 
 /**
  * @class network
@@ -4150,16 +4150,42 @@ WifiNetwork = baseclass.extend(/** @lends LuCI.network.WifiNetwork.prototype */
         */
        getAssocList: function() {
                var tasks = [];
-               var ifnames = [ this.getIfname() ].concat(this.getVlanIfnames());
+               var station;
 
-               for (var i = 0; i < ifnames.length; i++)
-                       tasks.push(callIwinfoAssoclist(ifnames[i]));
+               for (let vlan of this.getVlans())
+                       tasks.push(callIwinfoAssoclist(vlan.getIfname()).then(
+                               function(stations) {
+                                       for (station of stations)
+                                               station.vlan = vlan;
+
+                                       return stations;
+                               })
+                       );
+
+               tasks.push(callIwinfoAssoclist(this.getIfname()));
 
                return Promise.all(tasks).then(function(values) {
                        return Array.prototype.concat.apply([], values);
                });
        },
 
+       /**
+        * Fetch the vlans for this network.
+        *
+        * @returns {Array<LuCI.network.WifiVlan>}
+        * Returns an array of vlans for this network.
+        */
+       getVlans: function() {
+               var vlans = [];
+               var vlans_ubus = this.ubus('net', 'vlans');
+
+               if (vlans_ubus)
+                       for (let vlan of vlans_ubus)
+                               vlans.push(new WifiVlan(vlan));
+
+               return vlans;
+       },
+
        /**
         * Query the current operating frequency of the wireless network.
         *
@@ -4436,4 +4462,77 @@ WifiNetwork = baseclass.extend(/** @lends LuCI.network.WifiNetwork.prototype */
        }
 });
 
+/**
+ * @class
+ * @memberof LuCI.network
+ * @hideconstructor
+ * @classdesc
+ *
+ * A `Network.WifiVlan` class instance represents a vlan on a WifiNetwork.
+ */
+WifiVlan = baseclass.extend(/** @lends LuCI.network.WifiVlan.prototype */ {
+       __init__: function(vlan) {
+               this.ifname = vlan.ifname;
+               if (L.isObject(vlan.config)) {
+                       this.vid = vlan.config.vid;
+                       this.name = vlan.config.name;
+
+                       if (Array.isArray(vlan.config.network) && vlan.config.network.length)
+                               this.network = vlan.config.network[0];
+               }
+       },
+
+       /**
+        * Get the name of the wifi vlan.
+        *
+        * @returns {string}
+        * Returns the name.
+        */
+       getName: function() {
+               return this.name;
+       },
+
+       /**
+        * Get the vlan id of the wifi vlan.
+        *
+        * @returns {number}
+        * Returns the vlan id.
+        */
+       getVlanId: function() {
+               return this.vid;
+       },
+
+       /**
+        * Get the network of the wifi vlan.
+        *
+        * @returns {string}
+        * Returns the network.
+        */
+       getNetwork: function() {
+               return this.network;
+       },
+
+       /**
+        * Get the Linux network device name of the wifi vlan.
+        *
+        * @returns {string}
+        * Returns the current network device name for this wifi vlan.
+        */
+       getIfname: function() {
+               return this.ifname;
+       },
+
+       /**
+        * Get a long description string for the wifi vlan.
+        *
+        * @returns {string}
+        * Returns a string containing the vlan id and the vlan name,
+        * if it is different than the vlan id
+        */
+       getI18n: function() {
+               var name =  this.name && this.name != this.vid ? ' (' + this.name + ')' : '';
+               return 'vlan %d%s'.format(this.vid, name);
+       },
+});
+
 return Network;
index 97c9344890c16e78f90f5db65fe940e52c026625..e76b2b7111e32a2a6d1f2e04e89cba7db4aafee9 100644 (file)
@@ -765,6 +765,25 @@ return view.extend({
                                ])
                        ];
 
+                       var zones = data[4];
+                       if (bss.vlan) {
+                               var desc = bss.vlan.getI18n();
+                               var vlan_network = bss.vlan.getNetwork();
+                               var vlan_zone;
+
+                               if (vlan_network && zones)
+                                       for (let zone of zones)
+                                               if (zone.getNetworks().includes(vlan_network))
+                                                       vlan_zone = zone;
+
+                               row[0].insertBefore(
+                                       E('div', {
+                                               'class' : 'zonebadge',
+                                               'title' : desc,
+                                               'style' : firewall.getZoneColorStyle(vlan_zone)
+                                       }, [ desc ]), row[0].firstChild);
+                       }
+
                        if (bss.network.isClientDisconnectSupported()) {
                                if (table.firstElementChild.childNodes.length < 6)
                                        table.firstElementChild.appendChild(E('th', { 'class': 'th cbi-section-actions'}));
@@ -803,7 +822,8 @@ return view.extend({
                return Promise.all([
                        uci.changes(),
                        uci.load('wireless'),
-                       uci.load('system')
+                       uci.load('system'),
+                       firewall.getZones(),
                ]);
        },
 
@@ -823,11 +843,11 @@ return view.extend({
                params: [ 'config', 'section', 'name' ]
        }),
 
-       render: function() {
+       render: function(data) {
                if (this.checkAnonymousSections())
                        return this.renderMigration();
                else
-                       return this.renderOverview();
+                       return this.renderOverview(data[3]);
        },
 
        handleMigration: function(ev) {
@@ -862,7 +882,7 @@ return view.extend({
                ]);
        },
 
-       renderOverview: function() {
+       renderOverview: function(zones) {
                var m, s, o;
 
                m = new form.Map('wireless');
@@ -2404,6 +2424,10 @@ return view.extend({
                                                        return hosts_radios_wifis;
                                                });
                                        }, network))
+                                       .then(L.bind(function(zones, data) {
+                                               data.push(zones);
+                                               return data;
+                                       }, network, zones))
                                        .then(L.bind(this.poll_status, this, nodes));
                        }, this), 5);
 
index 8054230530768b02cad4946ba05fd19329bd4305..c77e88fb76f38add38778d0b51959039e4c36870 100644 (file)
@@ -5,6 +5,7 @@
 'require uci';
 'require fs';
 'require rpc';
+'require firewall';
 
 return baseclass.extend({
        title: _('Wireless'),
@@ -184,6 +185,7 @@ return baseclass.extend({
                        network.getHostHints(),
                        this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'read'),
                        this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'write'),
+                       firewall.getZones(),
                        L.hasSystemFeature('wifi') ? L.resolveDefault(uci.load('wireless')) : L.resolveDefault(),
                ]).then(L.bind(function(data) {
                        var tasks = [],
@@ -216,7 +218,8 @@ return baseclass.extend({
                    networks = data[1],
                    hosthints = data[2],
                    hasReadPermission = data[3],
-                   hasWritePermission = data[4];
+                   hasWritePermission = data[4],
+                   zones = data[5];
 
                var table = E('div', { 'class': 'network-status-table' });
 
@@ -326,6 +329,24 @@ return baseclass.extend({
                                        ])
                                ];
 
+                               if (bss.vlan) {
+                                       var desc = bss.vlan.getI18n();
+                                       var vlan_network = bss.vlan.getNetwork();
+                                       var vlan_zone;
+
+                                       if (vlan_network)
+                                               for (let zone of zones)
+                                                       if (zone.getNetworks().includes(vlan_network))
+                                                               vlan_zone = zone;
+
+                                       row[0].insertBefore(
+                                               E('div', {
+                                                       'class' : 'zonebadge',
+                                                       'title' : desc,
+                                                       'style' : firewall.getZoneColorStyle(vlan_zone)
+                                               }, [ desc ]), row[0].firstChild);
+                               }
+
                                if (networks[i].isClientDisconnectSupported() && hasWritePermission) {
                                        if (assoclist.firstElementChild.childNodes.length < 6)
                                                assoclist.firstElementChild.appendChild(E('th', { 'class': 'th cbi-section-actions' }));