gluon
gluon copied to clipboard
RFC: A New Concept for Gluon Config Generation
Hi all,
in the last days I have been working on a concept to improve the config generation of Gluon. Please give me some feedback on this concept. Since this concept is a very raw design, feel free to leave raw comments. Early feedback is more important than accurate feedback. So especially if you have concerns but no solutions yet, please raise the concerns.
Also please use the 👍/:eyes:/👎 to indicate your first gut feelings, whether this is going in the right direction:
- 👍 = Most of this concept looks good to me.
- :eyes: = I appreciate your efforts, but I think some critical parts need to be discussed and maybe done differently.
- 👎 = I appreciate your efforts, but the aims are completely outside of Gluon's objective.
Kind regards, lemoer
A New Concept for Gluon Config Generation
Aims
- Provide a REST API to reconfigure nodes.
- Enable reconfiguration without reboot.
- Especially: Only restart as many daemons as necessary.
- Enable automatic reset of the configuration (if the user does not confirm that everything still works).
1. API Idea
- Introduce a new config named local.json.
- This new config file would contain all the device configurations made by the user.
- Uci values are derived from local.json using scripts in /lib/gluon/upgrade/*.lua. (Similar to the site.json)
- As the local.json is stored on the device, this means, that no values are stored exclusively in uci anymore.
- But if desired, values can exclusively be stored in local.json.
-
JSON-Schema is used to verify this config.
- Since gluon is modular, this JSON-schema must be assembled dynamically based on the installed packages.
- Most likely, we can reuse the schema code from ECE. See docs here.
- From now on, this is called
local-schema.json
.
- REST API:
-
GET /local.json
-
POST /local.json
-
PUT /local.json
-
GET /local-schema.json
- If the posted
local.json
is not matching the JSON-Schema, the firmware will reject it.
-
- Config-Mode:
- Discard the current config-mode approach and use: javascript app + REST API.
- Use JSON-Editor (or something similar) to build the web-forms.
- This Javascript-App is able to generate web forms based on a JSON-Schema automatically.
- This can be done based on the schema
local-schema.json
.
- This can be done based on the schema
- Size: ~80 KB (minified & gzipped)
- Link to Example using JSON-Editor
- This Javascript-App is able to generate web forms based on a JSON-Schema automatically.
- Perform an HTTP POST or PUT with a complete local.json.
- Migration:
- In case, a gluon package changes the way it stores data to the local.json, the upgrade scripts in /lib/gluon/upgrade/*.lua need to take care of this and migrate the old local.json to the new local.json.
- The migrated local.json MUST match with the JSON-Schema of the new firmware.
- TO BE DISCUSSED: What happens, if it does not? Rejecting the local.json it is not possible, because we have no ability to interact with the user during the migration.
- Verification and the derivation of uci config would be separated:
- Verification of the local.json would be done using JSON-Schema.
- Uci config are derived using scripts in /lib/gluon/upgrade/*.lua
- This concept has also other benefits:
- Moving the configuration of a node to new hardware becomes easier.
- If a user wants to move the his configuration to new hardware, the local.json can be used for this purpose:
- Simply export the local.json from the old node and then import it to the new node.
- Maybe we can use JSON-Schema also to check the site.conf one day.
- In case, we introduce "pre-configured images" #2172, the local.json could be used to implement this.
- We can eliminate the situation where some uci values are used to generate other uci values. Such cyclic dependencies are not very transparent. A clear data flow direction is preferable.
- Moving the configuration of a node to new hardware becomes easier.
- Potential problems:
- Some config-mode pages depend on the sysconfig or on the site.conf?
-
TO BE DISCUSSED: Which REST API Technology? There are two options:
- gluon-web based REST API
- ubus and rpcd based REST API
2. Allowing Reconfiguration without Reboot
- The gluon-reload framework already exists.
- Before this concept, however, all daemons are restarted by gluon-reload. This is exhausting.
- For example, it causes a VPN reconnect even if the VPN configuration has not changed. This causes the TQ for the mesh VPN connection to drop to zero. Since the TQ is slow to rise again, this causes a connection failure of about 30 seconds.
- To change this, the following steps will be implemented to apply a config change:
- The new local.json is stored in /var/gluon/local-new.json.
-
gluon-reconfigure
is executed to generate the new uci configs.-
uci:commit()
is not yet called here!
-
- Call
gluon-reload
:-
gluon-reload
will useuci changes
to see which uci configs/uci sections are changed. - From that, it will only restart the necessary daemons.
-
- Persist the config:
- If the user has confirmed, that the device is still working:
-
uci:commit()
is called. - The old local.json is replaced by the new local-new.json.
-
- If the user hasn't confirmed, that the device is still working after 30s:
-
uci:revert()
is called. - The old local-new.json is deleted.
-
- If the device has a power outage during that time:
- The old config state is automatically recovered, as nothing has been written to the flash yet.
- If the user has confirmed, that the device is still working:
http://netjson.org/#third ?
I have mixed feelings regarding an ever open API.
Mandatory relying on Javascript for the configmode is likely raising the hurdle for not just a few users.
I like a lot about the goal of part two though.
http://netjson.org/#third ?
The scope of Netjson is very different from the current Gluon configuration approach. Netjson is a low-level definition of device configuration. The level of detail of Netjson is on par with the OpenWrt uci config. In fact, you can represent almost any possible OpenWrt router configuration in Netjson. In Gluon, we have a different approach. We only allow high-level configuration options like Is MeshOnWAN active?
. Low-level configuration is derived in an automated way from the high-level configuration, and directly changing the low-level configuration is explicitly unsupported.
This has two advantages:
- The Gluon firmware is easy to use even for non-technical people.
- We are able to provide unattended upgrades.
If we were to open the way for low-level configuration and officially support it, I think Gluon would become a maintenance hell.
I have mixed feelings regarding an ever open API.
I would like to postpone the discussion of this concern to a later date. This API does not have to be made openly accessible. There are many technical ways to protect it: using HTTPS, SSH tunneling, enabling the API only in configuration mode, exposing it only on a special Lan port, ... However, for the implementation of the API itself, this is not important for now. You can always just put security in front of an HTTP API.
Mandatory relying on Javascript for the configmode is likely raising the hurdle for not just a few users.
In fact, this is one of the choices to be made. The other option would be to add the REST API as an additional way to configure the node besides the existing config-mode
. But then we would have to maintain two separate ways to configure a device. In my opinion, this should be avoided.
- I think this proposal contains at least two different ideas that should be discussed separately. Allowing runtime reconfiguration is orthogonal to the move to a REST API. JSON config storage could be seen as a third proposed change.
- Replacing the config mode with a SPA + API sounds like a good idea to me. If we can improve usability, I don't have any issues with sacrificing the half-broken functionality our config mode currently provides without JS.
- Storing all user configuration in a single place sounds good to me (whether that means a single file or one file per package/feature/subsystem remains to be discussed - not having UCI as the primary source would be an improvement IMO, but we need a story for console-based configuration via SSH)
- Allowing reconfiguration without a reboot seems risky to me, and I don't think there is much value in reducing daemon restarts if it increases code complexity. The save/revert/commit to test configuration would increase our use of UCI again.
- I like our security model of requiring physical access to change the configuration
Okay, you are right. That there are orthogonal ideas. I would like to focus on the REST API idea first.
Some Requirements for the REST API:
- REQ1: Packages must be able to add configuration options.
- REQ2: The available configuration options may depend on additional conditions. (E.g. on hardware, site config, ...).
- REQ3: Versioning. Always add a version number to your REST APIs.
- REQ4: The user of the API should be able to discover the available configuration options in an automated manner.
Especially REQ4 is challenging to realize, incorporating all the possibilities resulting from REQ1, REQ2 and REQ3. Even more, if we intend to provide a somewhat intuitive API.
"Nice To Haves" (NTH) for the REST API:
- NTH1: A single API resource (JSON object) representing the whole configuration of the router.
- (To those of you, which are not familiar with the REST jargon: API resource = URL)
- I suspect, this will simplify the code in the SPA.
- This resource could be downloaded as a "backup" of the router. And maybe it could be restored one day.
- Of course, packages add configuration options, so it may appeal to assign an individual API resource to each package.
- I doubt, that the assignment package <-> resource would make sense at all. There may be inter-dependencies between packages, or packages would want to add configuration options to other packages resources or so.
- Furthermore there are not only conditional config options due to REQ1, but also due to REQ2.
- While it makes sense to discuss the orthogonal ideas orthogonal to each other, it is still worth mentioning that there are points of contact. If we decide that the "json config storage" idea is a good idea, the json object from the REST API could directly be saved to disk (after verification).
- NTH2: A standardized way to fulfill REQ4.
- NTH2a: One way to realize this would be to provide a json-schema.
- An advantage of this would be, that a potential SPA could directly derive other aspects (like value types, etc.) from the schema as well.
- If we add a json-schema implementation to gluon, we could also use this schema for the verification of the config.
- A disadvantage would be, that this schema would have to be merged from multiple packages.
- NTH2b: Another way would be, to define a first level information, which contains the "featureset" of the particular router. (This could be implemented using a json array like:
{ ..., "featureset": ["has_fastd", "has_lan_port", ...] }
.)- An implication of this way would be, that conditional configuration options could only depend on the "featureset".
- I am not sure, if all conditional configuration options from REQ1 and REQ2 could be described by a "featureset". (Please comment on this!)
- NTH2a: One way to realize this would be to provide a json-schema.
My Current Feeling
Currently I tend towards implementing NTH1 and NTH2b and towards using something similar to the check_site.lua
framework for the verification of the config.
What I Need From You
- Please comment on the idea.
- Did I miss requirements?
- Are there other NTH, that you desire?
- Should I go ahead?
- Is my reasoning understandable?
- Are there any other aspects, which are worth mentioning in that context?
- What do you think about "my current feeling"?
I just created an example how a single JSON resource could look like. It is based on a current image from Freifunk Hannover. The JSON structure is oriented to the current structure of the config-mode web interface. This example can be used for discussion. It gives an overview of which config options we currently support in the web interface.
EDIT: Some options have been added.
{
"wizard": {
"hostname": "freifunk-foobar",
"mesh-vpn": {
"enabled": true,
"bandwidth_limit": {
"enabled": true,
"up": 40000,
"down": false
}
},
"domain": "burgdorf",
"location": {
"share_location": true,
"lat": 57.0912121,
"lon": 10.2121211,
"altitude": 2.332
},
"contact": "[email protected]",
"is_outdoor_device": false
},
"advanced": {
"remote-access": {
"password": "1337",
"ssh-keys": [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK4qBzYhG0oXJCAVr5Bgh9dJ2ZqgzsB9oTGZlBn5ZIjO lemoer@orange",
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK4qBzYhG0oXJCAVr5Bgh9dJ2ZqgzsB9oTGZlBn5ZIjO lemoer@orange"
]
},
"autoupdates": {
"enabled": true,
"branch": "stable",
"minute": 23
},
"private-wifi": {
"enabled": true,
"ssid": "my-very-clever-ssid",
"key": "das-ist-mein-cleveres-passwort",
"encryption": "wpa2",
"management-frame-protection": false
},
"wifi-radios": {
"2-4GHz": {
"mesh_enabled": true,
"client_enabled": false,
"tx_power_dbm": 13.0
},
"5GHz": {
"mesh_enabled": true,
"client_enabled": false,
"tx_power_dbm": 13.0,
"outdoor_htmode": "default"
}
},
"network": {
"wan": {
"ipv4": {
"config": "static",
"ip": "192.168.0.123",
"netmask": "255.255.255.0",
"gateway": "192.168.0.1"
},
"ipv6": {
"config": "automatic"
},
"dns_servers": ["8.8.8.8", "1.1.1.1"]
},
"mesh_on_lan": true,
"mesh_on_wan": false
},
"logging": {
"enabled": true,
"ip": "192.168.0.1",
"port": 514
},
"mesh-vpn": {
"security": "performance"
},
"node-role": "Offloader"
}
}
I am currently preparing a table of external dependencies of the json structure. This will probably help to make decisions. I think I will be able to post this on sunday.
I finished the two tables. Please see below. In general, the options are referenced by their JSON path in the (updated) JSON structure above. However, the tables are based on the code in the current gluon-config-mode-* and gluon-web-* packages. Therefore the tables below represent the status quo of the dependencies of the options in the config mode in gluon.
These tables should provide an overview of the complexity of the json config. They can be used to make design decisions for the API.
I will post some questions, on which we need to decide (in my opinion) later on.
External Dependencies of the JSON structure
# | Package | Dependency Type | Option(s) | Description |
---|---|---|---|---|
E1 | gluon-config-mode-autoupdater | Package may not be installed. | No influence on config. | |
E2 | gluon-config-mode-autoupdater | Autoupdater is enabled/enabled. | Shows a message in the wizard or not. | |
E3 | gluon-config-mode-contact-info | Package may not be installed. | wizard.contact | Option is not available. |
E4 | gluon-config-mode-domain-select | Package may not be installed. | wizard.domain | Option is not available. |
E5 | gluon-config-mode-domain-select | Dependency to site.conf. | wizard.domain | Available choices depend on site.conf. |
E6 | gluon-config-mode-geo-location | Package may not be installed. | wizard.location.* | Options are not available. |
E7 | gluon-config-mode-geo-location | Dependency to site.conf. | wizard.location.altitude | Option can be disabled in site.conf |
E8 | gluon-config-mode-mesh-vpn | Package may not be installed. | wizard.mesh_vpn.* | Options are not available. |
E9 | gluon-config-mode-mesh-vpn | VPN is enabled/disabled. | Shows the pubkey in the reboot page or not. | |
E10 | gluon-config-mode-outdoor | Package may not be installed. | wizard.is_outdoor_device | Option is not available. |
E11 | gluon-config-mode-outdoor | Dependency to Hardware. | wizard.is_outdoor_device | Option is not available, depending if the device supports outdoor frequencies (or so…) |
E12 | gluon-config-mode-outdoor | Dependency to UCI value. | wizard.is_outdoor_device | Option is not available, depending if the uci option preserve_channels is set. |
E13 | gluon-web-admin | Package may not be installed. | advanced.remote_access.* | Options are not available. |
E14 | gluon-web-admin | Dependency to site.conf. | advanced.remote_access.password | Option can be disabled in site.conf. |
E15 | gluon-web-autoupdater | Package may not be installed. | advanced.autoupdater.* | Options are not available. |
E16 | gluon-web-autoupdater | Dependency to site.conf. | advanced.autoupdater.branch | Available choices for option depend on site.conf. |
E17 | gluon-web-logging | Package may not be installed. | advanced.logging.* | Options are not available. |
E18 | gluon-web-mesh-vpn-fastd | Package may not be installed. | advanced.mesh-vpn.security | Option is not available. |
E19 | gluon-web-network | Package may not be installed. | advanced.network.* | Options are not available. |
E20 | gluon-web-network | Dependency to UCI value. | advanced.network.dns_servers | Option is not available, depending if the uci config gluon-wan-dnsmasq.static is available. |
E21 | gluon-web-node-role | Package may not be installed. | advanced.node-role | Options are not available. |
E22 | gluon-web-node-role | Dependency to site.conf. | advanced.node-role | Available choices depend on site.conf. |
E23 | gluon-web-private-wifi | Package may not be installed. | advanced.private-wifi.* | Options are not available. |
E24 | gluon-web-private-wifi | Dependency to Hardware. | advanced.private-wifi.encryption | Available choices depend on the hardware platform. |
E25 | gluon-web-private-wifi | Dependency to Hardware. | advanced.private-wifi.management-frame-protection | Available choices depend on the hardware platform. |
E26 | gluon-web-wifi-config | Package may not be installed. | advanced.wifi-radios.* | Options are not available. |
E27 | gluon-web-wifi-config | Dependency to Hardware. (Or UCI) | advanced.wifi-radios.2-4GHz.* | Options may not be available depending on the hardware. |
E28 | gluon-web-wifi-config | Dependency to Hardware. (Or UCI) | advanced.wifi-radios.5GHz.* | Options may not be available depending on the hardware. |
E29 | gluon-web-wifi-config | Dependency to Hardware. (Or UCI) | advanced.wifi-radios.* | Actually there may be more than one radio for each frequency, so advanced.wifi-radios.* must be somewhat different! |
E30 | gluon-web-wifi-config | Dependency to Hardware. | advanced.wifi-radios.*.tx_power | Available choices depend on the hardware platform. |
E31 | gluon-web-wifi-config | Dupplicate option. | wizard.is_outdoor_device | The option wizard.is_outdoor_device is also shown here. |
E32 | gluon-web-wifi-config | Dependency to Hardware. | advanced.wifi-radios.5GHz.outdoor_htmode | Available choices depend on the hardware. |
Internal Dependencies of the JSON structure
# | Package | Option (only allowed if) | Condition |
---|---|---|---|
I1 | gluon-config-mode-geo-location | wizard.location.lat | wizard.location.lon must be set. |
I2 | gluon-config-mode-geo-location | wizard.location.lon | wizard.location.lat must be set. |
I3 | gluon-config-mode-geo-location | wizard.location.altitude | wizard.location.{lon,lan} must be set both. |
I4 | gluon-config-mode-mesh-vpn | wizard.mesh-vpn.bandwidth_limit.* | wizard.mesh-vpn.enabled must be true. |
I5 | gluon-web-network | advanced.network.wan.ipv4.ip | advanced.network.wan.ipv4.config must be „static“ |
I6 | gluon-web-network | advanced.network.wan.ipv4.netmask | advanced.network.wan.ipv4.config must be „static“ |
I7 | gluon-web-network | advanced.network.wan.ipv4.gateway | advanced.network.wan.ipv4.config must be „static“ |
I8 | gluon-web-network | advanced.network.wan.ipv6.ip | advanced.network.wan.ipv6.config must be „static“ |
I9 | gluon-web-network | advanced.network.wan.ipv6.netmask | advanced.network.wan.ipv6.config must be „static“ |
I10 | gluon-web-network | advanced.network.wan.ipv6.gateway | advanced.network.wan.ipv6.config must be „static“ |
I11 | gluon-web-private-wifi | advanced.private-wifi.ssid | advanced.private-wifi.enabled must be true. |
I12 | gluon-web-private-wifi | advanced.private-wifi.key | advanced.private-wifi.enabled must be true. |
I13 | gluon-web-private-wifi | advanced.private-wifi.encryption | advanced.private-wifi.enabled must be true. |
I14 | gluon-web-private-wifi | advanced.private-wifi.management-frame-protection | advanced.private-wifi.enabled must be true. |
I15 | gluon-web-wifi-config | advanced.wifi-radios.5GHz.mesh_enabled | wizard.is_outdoor_device must be false. |
I16 | gluon-web-wifi-config | advanced.wifi-radios.5GHz.outdoor_htmode | wizard.is_outdoor_device must be true. |
Summary of the External Dependencies
- The available options vary due to external dependencies:
- Due to installed packages (E3, E4, E6, E8, E10, E13, E15, E17, E18, E19, E21, E23, E26)
- Due to site.conf (E7, E14)
- Due to hardware (E11, E27, E28)
- Due to UCI settings (E12, E20)
- For some options, the available choices (in dropdowns) vary due to external dependencies:
- Due to site.conf (E5, E16, E22)
- Due to hardware (E24, E25, E30, E32)
Based on all the (hard to digest) data here, I created #2296. The REST API can be discussed there. I hope things are less abstract based on the proposal in #2296.