luci-mod-network: move dnsmasq cfg to own function in dhcp.js
authorDavid Härdeman <[email protected]>
Tue, 21 Oct 2025 20:56:00 +0000 (22:56 +0200)
committerPaul Donald <[email protected]>
Fri, 24 Oct 2025 13:57:43 +0000 (15:57 +0200)
This makes it a bit clearer what is specific to dnsmasq and also
prepares for the following patches. No actual code changes.

Signed-off-by: David Härdeman <[email protected]>
modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js

index 5ae0c12cc28f5c4df09bbb2f157247772b730e2c..ad4d98cfcb5c57d01eff9c94b93bce6a0ede8042 100644 (file)
@@ -237,210 +237,11 @@ return view.extend({
 
                m = new form.Map('dhcp', _('DHCP'));
 
-               s = m.section(form.TypedSection, 'dnsmasq');
-               s.anonymous = false;
-               s.addremove = true;
-               s.addbtntitle = _('Add server instance', 'Dnsmasq instance');
-
-               s.renderContents = function(/* ... */) {
-                       var renderTask = form.TypedSection.prototype.renderContents.apply(this, arguments),
-                           sections = this.cfgsections();
-
-                       return Promise.resolve(renderTask).then(function(nodes) {
-                               if (sections.length < 2) {
-                                       nodes.querySelector('#cbi-dhcp-dnsmasq > h3').remove();
-                                       nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-remove').remove();
-                               }
-                               else {
-                                       nodes.querySelectorAll('#cbi-dhcp-dnsmasq > .cbi-section-remove').forEach(function(div, i) {
-                                               var section = uci.get('dhcp', sections[i]),
-                                                   hline = div.nextElementSibling,
-                                                   btn = div.firstElementChild;
+               s = this.add_dnsmasq_cfg(m, networks);
 
-                                               if (!section || section['.anonymous']) {
-                                                       hline.innerText = i ? _('Unnamed instance #%d', 'Dnsmasq instance').format(i+1) : _('Default instance', 'Dnsmasq instance');
-                                                       btn.innerText = i ? _('Remove instance #%d', 'Dnsmasq instance').format(i+1) : _('Remove default instance', 'Dnsmasq instance');
-                                               }
-                                               else {
-                                                       hline.innerText = _('Instance "%q"', 'Dnsmasq instance').format(section['.name']);
-                                                       btn.innerText = _('Remove instance "%q"', 'Dnsmasq instance').format(section['.name']);
-                                               }
-                                       });
-                               }
-
-                               nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-create input').placeholder = _('New instance name…', 'Dnsmasq instance');
-
-                               return nodes;
-                       });
-               };
-
-
-               s.tab('general', _('General'));
-               s.tab('devices', _('Devices &amp; Ports'));
-               s.tab('logging', _('Log'));
-               s.tab('files', _('Files'));
-               s.tab('relay', _('Relay'));
                s.tab('leases', _('Static Leases'));
                s.tab('pxe_tftp', _('PXE/TFTP'));
 
-               // Begin general
-               s.taboption('general', form.Flag, 'authoritative',
-                       _('Authoritative'),
-                       _('This is the only DHCP server in the local network.'));
-
-               s.taboption('general', form.Value, 'domain',
-                       _('Local domain'),
-                       _('Local domain suffix appended to DHCP names and hosts file entries.'));
-
-               o = s.taboption('general', form.Flag, 'sequential_ip',
-                       _('Allocate IPs sequentially'),
-                       _('Allocate IP addresses sequentially, starting from the lowest available address.'));
-               o.optional = true;
-
-               o = s.taboption('general', form.Value, 'dhcpleasemax',
-                       _('Max. DHCP leases'),
-                       _('Maximum allowed number of active DHCP leases.'));
-               o.optional = true;
-               o.datatype = 'uinteger';
-               o.placeholder = 150;
-               // End general
-
-               // Begin devices
-               o = s.taboption('devices', form.Flag, 'nonwildcard',
-                       _('Non-wildcard'),
-                       _('Bind only to configured interface addresses, instead of the wildcard address.'));
-               o.default = o.enabled;
-               o.optional = false;
-               o.rmempty = true;
-
-               o = s.taboption('devices', widgets.NetworkSelect, 'interface',
-                       _('Listen interfaces'),
-                       _('Listen only on the specified interfaces, and loopback if not excluded explicitly.'));
-               o.multiple = true;
-               o.nocreate = true;
-
-               o = s.taboption('devices', widgets.NetworkSelect, 'notinterface',
-                       _('Exclude interfaces'),
-                       _('Do not listen on the specified interfaces.'));
-               o.loopback = true;
-               o.multiple = true;
-               o.nocreate = true;
-               // End devices
-
-               // Begin logging
-               o = s.taboption('logging', form.Flag, 'logdhcp',
-                       _('Extra DHCP logging'),
-                       _('Log all options sent to DHCP clients and the tags used to determine them.'));
-               o.optional = true;
-
-               o = s.taboption('logging', form.Value, 'logfacility',
-                       _('Log facility'),
-                       _('Set log class/facility for syslog entries.'));
-               o.optional = true;
-               o.value('KERN');
-               o.value('USER');
-               o.value('MAIL');
-               o.value('DAEMON');
-               o.value('AUTH');
-               o.value('LPR');
-               o.value('NEWS');
-               o.value('UUCP');
-               o.value('CRON');
-               o.value('LOCAL0');
-               o.value('LOCAL1');
-               o.value('LOCAL2');
-               o.value('LOCAL3');
-               o.value('LOCAL4');
-               o.value('LOCAL5');
-               o.value('LOCAL6');
-               o.value('LOCAL7');
-               o.value('-', _('stderr'));
-
-               o = s.taboption('logging', form.Flag, 'quietdhcp',
-                       _('Suppress logging'),
-                       _('Suppress logging of the routine operation for the DHCP protocol.'));
-               o.optional = true;
-               o.depends('logdhcp', '0');
-               // End logging
-
-               // Begin files
-               s.taboption('files', form.Flag, 'readethers',
-                       _('Use %s').format('<code>/etc/ethers</code>'),
-                       _('Read %s to configure the DHCP server.').format('<code>/etc/ethers</code>'));
-
-               s.taboption('files', form.Value, 'leasefile',
-                       _('Lease file'),
-                       _('File to store DHCP lease information.'));
-               // End files
-
-               // Begin relay
-               o = s.taboption('relay', form.SectionValue, '__relays__', form.TableSection, 'relay', null,
-                       _('Relay DHCP requests elsewhere. OK: v4↔v4, v6↔v6. Not OK: v4↔v6, v6↔v4.')
-                       + '<br />' + _('Note: you may also need a DHCP Proxy (currently unavailable) when specifying a non-standard Relay To port(<code>addr#port</code>).')
-                       + '<br />' + _('You may add multiple unique Relay To on the same Listen addr.'));
-
-               ss = o.subsection;
-
-               ss.addremove = true;
-               ss.anonymous = true;
-               ss.sortable  = true;
-               ss.rowcolors = true;
-               ss.nodescriptions = true;
-
-               so = ss.option(form.Value, 'local_addr', _('Relay from'));
-               so.rmempty = false;
-               so.datatype = 'ipaddr';
-
-               for (var family = 4; family <= 6; family += 2) {
-                       for (var i = 0; i < networks.length; i++) {
-                               if (networks[i].getName() != 'loopback') {
-                                       var addrs = (family == 6) ? networks[i].getIP6Addrs() : networks[i].getIPAddrs();
-                                       for (var j = 0; j < addrs.length; j++) {
-                                               var addr = addrs[j].split('/')[0];
-                                               so.value(addr, E([], [
-                                                       addr, ' (',
-                                                       widgets.NetworkSelect.prototype.renderIfaceBadge(networks[i]),
-                                                       ')'
-                                               ]));
-                                       }
-                               }
-                       }
-               }
-
-               so = ss.option(form.Value, 'server_addr', _('Relay to address'));
-               so.rmempty = false;
-               so.optional = false;
-               so.placeholder = '192.168.10.1#535';
-               so.validate = function(section, value) {
-                       var m = this.section.formvalue(section, 'local_addr'),
-                           n = this.section.formvalue(section, 'server_addr'),
-                           p;
-
-                       if (!m || !n) {
-                               return _('Both "Relay from" and "Relay to address" must be specified.');
-                       }
-                       else {
-                               p = n.split('#');
-                               if (p.length > 1 && !/^[0-9]+$/.test(p[1]))
-                                       return _('Expected port number.');
-                               else
-                                       n = p[0];
-
-                               if ((validation.parseIPv6(m) && validation.parseIPv6(n)) ||
-                                       validation.parseIPv4(m) && validation.parseIPv4(n))
-                                       return true;
-                               else
-                                       return _('Address families of "Relay from" and "Relay to address" must match.')
-                       }
-                       return true;
-               };
-
-               so = ss.option(widgets.NetworkSelect, 'interface', _('Only accept replies via'));
-               so.optional = true;
-               so.rmempty = false;
-               so.placeholder = 'lan';
-               // End relay
-
                // Begin leases
                o = s.taboption('leases', form.SectionValue, '__leases__', form.GridSection, 'host', null,
                        _('Static leases are used to assign fixed IP addresses and symbolic hostnames to DHCP clients. They are also required for non-dynamic interface configurations where only hosts with a corresponding lease are served.') + '<br /><br />' +
@@ -836,5 +637,211 @@ return view.extend({
 
                        return mapEl;
                });
+       },
+
+       add_dnsmasq_cfg: function(m, networks) {
+               var s, o, ss, so;
+
+               s = m.section(form.TypedSection, 'dnsmasq');
+               s.anonymous = false;
+               s.addremove = true;
+               s.addbtntitle = _('Add server instance', 'Dnsmasq instance');
+               s.renderContents = function(/* ... */) {
+                       var renderTask = form.TypedSection.prototype.renderContents.apply(this, arguments),
+                           sections = this.cfgsections();
+
+                       return Promise.resolve(renderTask).then(function(nodes) {
+                               if (sections.length < 2) {
+                                       nodes.querySelector('#cbi-dhcp-dnsmasq > h3').remove();
+                                       nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-remove').remove();
+                               }
+                               else {
+                                       nodes.querySelectorAll('#cbi-dhcp-dnsmasq > .cbi-section-remove').forEach(function(div, i) {
+                                               var section = uci.get('dhcp', sections[i]),
+                                                   hline = div.nextElementSibling,
+                                                   btn = div.firstElementChild;
+
+                                               if (!section || section['.anonymous']) {
+                                                       hline.innerText = i ? _('Unnamed instance #%d', 'Dnsmasq instance').format(i+1) : _('Default instance', 'Dnsmasq instance');
+                                                       btn.innerText = i ? _('Remove instance #%d', 'Dnsmasq instance').format(i+1) : _('Remove default instance', 'Dnsmasq instance');
+                                               }
+                                               else {
+                                                       hline.innerText = _('Instance "%q"', 'Dnsmasq instance').format(section['.name']);
+                                                       btn.innerText = _('Remove instance "%q"', 'Dnsmasq instance').format(section['.name']);
+                                               }
+                                       });
+                               }
+
+                               nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-create input').placeholder = _('New instance name…', 'Dnsmasq instance');
+
+                               return nodes;
+                       });
+               };
+
+               s.tab('general', _('General'));
+               s.tab('devices', _('Devices &amp; Ports'));
+               s.tab('logging', _('Log'));
+               s.tab('files', _('Files'));
+               s.tab('relay', _('Relay'));
+
+               // Begin general
+               s.taboption('general', form.Flag, 'authoritative',
+                       _('Authoritative'),
+                       _('This is the only DHCP server in the local network.'));
+
+               s.taboption('general', form.Value, 'domain',
+                       _('Local domain'),
+                       _('Local domain suffix appended to DHCP names and hosts file entries.'));
+
+               o = s.taboption('general', form.Flag, 'sequential_ip',
+                       _('Allocate IPs sequentially'),
+                       _('Allocate IP addresses sequentially, starting from the lowest available address.'));
+               o.optional = true;
+
+               o = s.taboption('general', form.Value, 'dhcpleasemax',
+                       _('Max. DHCP leases'),
+                       _('Maximum allowed number of active DHCP leases.'));
+               o.optional = true;
+               o.datatype = 'uinteger';
+               o.placeholder = 150;
+               // End general
+
+               // Begin devices
+               o = s.taboption('devices', form.Flag, 'nonwildcard',
+                       _('Non-wildcard'),
+                       _('Bind only to configured interface addresses, instead of the wildcard address.'));
+               o.default = o.enabled;
+               o.optional = false;
+               o.rmempty = true;
+
+               o = s.taboption('devices', widgets.NetworkSelect, 'interface',
+                       _('Listen interfaces'),
+                       _('Listen only on the specified interfaces, and loopback if not excluded explicitly.'));
+               o.multiple = true;
+               o.nocreate = true;
+
+               o = s.taboption('devices', widgets.NetworkSelect, 'notinterface',
+                       _('Exclude interfaces'),
+                       _('Do not listen on the specified interfaces.'));
+               o.loopback = true;
+               o.multiple = true;
+               o.nocreate = true;
+               // End devices
+
+               // Begin logging
+               o = s.taboption('logging', form.Flag, 'logdhcp',
+                       _('Extra DHCP logging'),
+                       _('Log all options sent to DHCP clients and the tags used to determine them.'));
+               o.optional = true;
+
+               o = s.taboption('logging', form.Value, 'logfacility',
+                       _('Log facility'),
+                       _('Set log class/facility for syslog entries.'));
+               o.optional = true;
+               o.value('KERN');
+               o.value('USER');
+               o.value('MAIL');
+               o.value('DAEMON');
+               o.value('AUTH');
+               o.value('LPR');
+               o.value('NEWS');
+               o.value('UUCP');
+               o.value('CRON');
+               o.value('LOCAL0');
+               o.value('LOCAL1');
+               o.value('LOCAL2');
+               o.value('LOCAL3');
+               o.value('LOCAL4');
+               o.value('LOCAL5');
+               o.value('LOCAL6');
+               o.value('LOCAL7');
+               o.value('-', _('stderr'));
+
+               o = s.taboption('logging', form.Flag, 'quietdhcp',
+                       _('Suppress logging'),
+                       _('Suppress logging of the routine operation for the DHCP protocol.'));
+               o.optional = true;
+               o.depends('logdhcp', '0');
+               // End logging
+
+               // Begin files
+               s.taboption('files', form.Flag, 'readethers',
+                       _('Use %s').format('<code>/etc/ethers</code>'),
+                       _('Read %s to configure the DHCP server.').format('<code>/etc/ethers</code>'));
+
+               s.taboption('files', form.Value, 'leasefile',
+                       _('Lease file'),
+                       _('File to store DHCP lease information.'));
+               // End files
+
+               // Begin relay
+               o = s.taboption('relay', form.SectionValue, '__relays__', form.TableSection, 'relay', null,
+                       _('Relay DHCP requests elsewhere. OK: v4↔v4, v6↔v6. Not OK: v4↔v6, v6↔v4.')
+                       + '<br />' + _('Note: you may also need a DHCP Proxy (currently unavailable) when specifying a non-standard Relay To port(<code>addr#port</code>).')
+                       + '<br />' + _('You may add multiple unique Relay To on the same Listen addr.'));
+
+               ss = o.subsection;
+
+               ss.addremove = true;
+               ss.anonymous = true;
+               ss.sortable  = true;
+               ss.rowcolors = true;
+               ss.nodescriptions = true;
+
+               so = ss.option(form.Value, 'local_addr', _('Relay from'));
+               so.rmempty = false;
+               so.datatype = 'ipaddr';
+
+               for (var family = 4; family <= 6; family += 2) {
+                       for (var i = 0; i < networks.length; i++) {
+                               if (networks[i].getName() != 'loopback') {
+                                       var addrs = (family == 6) ? networks[i].getIP6Addrs() : networks[i].getIPAddrs();
+                                       for (var j = 0; j < addrs.length; j++) {
+                                               var addr = addrs[j].split('/')[0];
+                                               so.value(addr, E([], [
+                                                       addr, ' (',
+                                                       widgets.NetworkSelect.prototype.renderIfaceBadge(networks[i]),
+                                                       ')'
+                                               ]));
+                                       }
+                               }
+                       }
+               }
+
+               so = ss.option(form.Value, 'server_addr', _('Relay to address'));
+               so.rmempty = false;
+               so.optional = false;
+               so.placeholder = '192.168.10.1#535';
+               so.validate = function(section, value) {
+                       var m = this.section.formvalue(section, 'local_addr'),
+                           n = this.section.formvalue(section, 'server_addr'),
+                           p;
+
+                       if (!m || !n) {
+                               return _('Both "Relay from" and "Relay to address" must be specified.');
+                       }
+                       else {
+                               p = n.split('#');
+                               if (p.length > 1 && !/^[0-9]+$/.test(p[1]))
+                                       return _('Expected port number.');
+                               else
+                                       n = p[0];
+
+                               if ((validation.parseIPv6(m) && validation.parseIPv6(n)) ||
+                                       validation.parseIPv4(m) && validation.parseIPv4(n))
+                                       return true;
+                               else
+                                       return _('Address families of "Relay from" and "Relay to address" must match.')
+                       }
+                       return true;
+               };
+
+               so = ss.option(widgets.NetworkSelect, 'interface', _('Only accept replies via'));
+               so.optional = true;
+               so.rmempty = false;
+               so.placeholder = 'lan';
+               // End relay
+
+               return s;
        }
 });