-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sys Runtime Configuration Registry #19895
base: master
Are you sure you want to change the base?
Sys Runtime Configuration Registry #19895
Conversation
thanks for this very careful PR description, including the feature matrix comparison! |
Fix example
Fix example
Add storage instance as a parameter
Fix heap storage
Fix vfs storage
Fix regsitry_cli
Remove test namespace
Fix small bug
Migrate to local test namespace
Migrate to local test namespace
Migrate to local test namespace
Migrate to local test namespace
Migrate to local test namespace
Migrate to local test namespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating the PR and providing the metrics.
I see that the LoC have now gone down to 7K. This is still rather large for reviewing. Would it be possible if you split out a PR that does not provide any storage backend yet? E.g. just the base features + test + an example that uses the shell interface, no persistence yet?
I added some nitpicks inline.
const char * const name; /**< String describing the instance. */ | ||
#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ | ||
const void * const data; /**< Struct containing all configuration parameters of the schema. */ | ||
const registry_schema_t *schema; /**< Configuration Schema that the Schema Instance belongs to. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const registry_schema_t *schema; /**< Configuration Schema that the Schema Instance belongs to. */ | |
const registry_schema_t *schema; /**< Configuration Schema that the registry Instance belongs to. */ |
switch (scope) | ||
{ | ||
case REGISTRY_COMMIT_INSTANCE: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
switch (scope) | |
{ | |
case REGISTRY_COMMIT_INSTANCE: | |
switch (scope) { | |
case REGISTRY_COMMIT_INSTANCE: |
There are a number of deviations from the coding convention regarding code formatting. This is not a big deal and coding style is IMO purely about personal preference, but keeping things consistent within the code base does (at least me) help a lot when reading code.
I won't add further comments on style, since this is best left to uncrustify
or clang-format
.
rsource "sys/Kconfig" | ||
rsource "tests/Kconfig" | ||
|
||
endif # MODULE_REGISTRY_NAMESPACE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The last line of this file is not terminated with a single \n
. I think uncrustify
or clang-format
will also correct this. If not, e.g. visual studio code with the config now in the master
branch should also do so on save.
Contribution description
RIOT Runtime Configuration Registry
This is a continuation of the effort previously done in #10622, but its architecture has dramatically changed since then.
Abstract
This PR implements a system level runtime configuration system for RIOT.
A runtime configuration system is in charge of providing a mechanism to set and get the values of Configuration Parameters that are used during the execution of the firmware, as well as a way to persist these values. Runtime configurations are deployment-specific and can be changed on a per node basis.
Appropriate management tools could also enable the configuration of nodes.
Examples of runtime configurations are:
These parameters might have constraints, like a specific order to be applied (due to interdependencies) or value boundaries.
The main advantages of having such a system are:
storage devices
Design
Architecture
The proposed architecture, as shown below, is formed by one or more Applications or Configuration Managers and the RIOT Registry.
The RIOT Registry acts as a common interface to access Runtime Configurations and store them in non-volatile storage.
All runtime configurations can be accessed either from the RIOT application using the provided RIOT Registry interfaces or through the interfaces exposed by the Configuration Managers.
A RIOT Application may interact with a Configuration Manager in order to modify access control rules or enable different exposed interfaces.
Path Based Configuration Managers
These Configuration Managers are a simple representation of the default configuration structure of the RIOT Registry.
They use either the
int_path
or thestring_path
Registry extension to expose the parameters using a path.Custom Schema Based Configuration Managers
These Configuration Managers have their own configuration structure (custom predefined object models etc.) and can not automatically be mapped to / from the RIOT Registry itself.
To make them work, a custom mapping module needs to be implemented, which maps each Configuration Parameter from the registry to the correct format of the Configuration Manager.
Namespaces and Storages
The RIOT Registry interacts with RIOT modules via
Configuration Schemas
, and with non-volatile storages viaStorages
.This way the functionality of the RIOT Registry is independent of the functionality of a
module
orstorage
implementation.It is possible to get or set the values of
Configuration Parameters
.It is also possible to transactionally apply configurations or export their values to a buffer or print them.
To persist Configuration Values, it is possible to store them in non-volatile storages.
Any mechanism of security (
access control
,encryption
of configurations) isnot
directly in the scope of the Registry but in the Configuration Managers and the specific implementations of theConfiguration Schemas
andStorages
.The graphic below shows an example of two
Configuration Namespaces
(SYS and APP).The
APP
namespace contains a application specificMy app
Configuration Schema and theSYS
namespace specifies aWLAN
and aLED Strip
Configuration Schema.The application
My app
uses the customMy app
Configuration Schema to expose custom Configuration Parameters to the RIOT Registry and the driversWS2812
,SK6812
andUCS1903
contain instances ofthe
LED Strip
Configuration Schema to expose common LED Strip Configuration Parameters.Also, there are two Storages available:
MTD
andVFS
.The
MTD
Storage internally uses the RIOTMTD
driver and theVFS
Storage internally uses the RIOTVFS
module.Components
The RIOT Registry is split into multiple components as can be seen in the graphic below:
Registry Core
This component holds the most basic functionality of the RIOT Registry.
It allows to
set
andget
Configuration Values, transactionallycommit
them to make the changes come into effect andexport
all Configuration Parameters that exist in a givenConfiguration Namespace
,Configuration Schema
orConfiguration Group
.Furthermore it is possible to
add
Configuration Namespaces
orConfiguration Schema Instances
.Registry Namespace
The
Configuration Namespaces
such asSYS
orAPP
and their respectiveConfiguration Schemas
are not part of the Registry itself.It is possible to
add
customConfiguration Namespaces
depending on the given needs.Storage
The
Storage
component provides an interface toload
Configuration Values from a persistentStorage
implementation or tosave
the current Registry configuration to it.Registry Storage
The implementations of a Storage such as
VFS
orMTD
are not part of the Registry itself and can be switched out with implementations that are most suitable to the given needs.Integer Path
The
int_path
component provides helper functions that convert a path of up to 4 integer values to the respective pointer of aConfiguration Namespace
,Configuration Schema
,Configuration Schema Instance
,Configuration Group
orConfiguration Parameter
and the other way around.The structure of an integer configuration path is the following:
namespace_id
/schema_id
/instance_id
/group_id | parameter_id
For example:
String Path
The
string_path
component provies helper functions that convert a string path to the respective pointer of aConfiguration Namespace
,Configuration Schema
,Configuration Schema Instance
,Configuration Group
orConfiguration Parameter
and the other way around.The structure of a string configuration path is the following:
namespace_name
/schema_name
/instance_id
(/group_name
)* /parameter_name
.The amount of path items is flexible, so the path could only consist of the
namespace_name
or only thenamespace_name
and theschema_name
and so on.For example:\
API
The graphic below shows the API of the RIOT Registry.
The top shows the Core API to manage Configuration Parameters.
On the right-hand side are functions to
set
andget
Configuration Parameters, transactionallycommit
them andexport
them to a buffer or terminal.On the left-hand side are setup functions to
add
Configuration Namespaces
andConfiguration Schema Instances
to the Registry.The bottom shows the storage API to manage the persistance of Configuration Parameters.
The left-hand side shows functions to
load”
andsave
Configuration Parameters to and from the persistent Storage.The right-hand side shows functions to
add
Storage Sources
(for reading) and toset
aStorage Destination
(for writing).The Registry can have multiple
Storage Sources
, but always only oneStorage Destination
.This allows to migrate from an old
Storage
to a new one.The functionality of these functions is explained in the following paragraphs.
Core API
Get
A Configuration Value can be retrieved using the
registry_get
function.The function takes the
Configuration Schema Instance
, theConfiguration Parameter
and aregistry_value_t
pointer (to return the value) as its arguments.Set
A Configuration Value can be set using the
registry_set
function.The function takes the
Configuration Schema Instance
, theConfiguration Parameter
, avoid*
buffer and the buffer size as its arguments.The buffer must contain the value in its correct c-type.
If the Registry expects a
u8
, but au16
is provided, the operation will fail.Furthermore the registry can specify constraints like
minimum
andmaximum
values and an array ofallowed
orforbidden
values.If these constraints are not fulfilled, then the operation will fail as well.
Commit
Once the value(s) of one or multiple
Configuration Parameter(s)
are changed by theregistry_set
function, they still need to be committed, so that the new values are taken into effect.Configuration Parameter(s) can be committed using the
registry_commit
function.In this case the Registry provides multiple commit functions to allow committing in varying degrees.
The provided functions are
registry_commit
, this function commits everyConfiguration Parameter
currently available in the Registry,registry_commit_namespace
, this function commits everyConfiguration Parameter
within the givenConfiguration Namespace
,registry_commit_schema
, this function commits everyConfiguration Parameter
within the givenConfiguration Schema
,registry_commit_instance
, this function commits everyConfiguration Parameter
within the givenConfiguration Schema Instance
,registry_commit_group
, this function commits everyConfiguration Parameter
within the givenConfiguration Group
andregistry_commit_parameter
, this function commits only a singleConfiguration Parameter
.When a
Configuration Parameter
is committed, it will be passed on to thecommit_cb
handler of theConfiguration Schema Instance
, provided by the Driver / Module that needs runtime configuration.This way the Driver / Module gets notified, when the
Configuration Parameter
has been committed and can apply the changes accordingly.Export
Some times it is convenient to have a way to see what
Configuration Namespaces
,Configuration Schemas
,Configuration Schema Instances
,Configuration Groups
orConfiguration Parameters
are available within our current RIOT Registry deployment.To get this information there is the
registry
The RIOT Registry allows exporting these objects using the
registry_export
function.In this case the Registry provides multiple export functions to allow exporting in varying degrees.
The provided functions are
registry_export
, this function exports everyConfiguration Object
currently available in the Registry,registry_export_namespace
, this function exports everyConfiguration Object
within the givenConfiguration Namespace
,registry_export_schema
, this function exports everyConfiguration Object
within the givenConfiguration Schema
,registry_export_instance
, this function exports everyConfiguration Object
within the givenConfiguration Schema Instance
,registry_export_group
, this function exports everyConfiguration Object
within the givenConfiguration Group
andregistry_export_parameter
, this function exports only a singleConfiguration Parameter
.When a
Configuration Parameter
is exported, it will be passed on to theexport_cb
handler provided as an argument of eachregistry_export*
function.This way inside of the
export_cb
function we can process the result and for example print all availableConfiguration Parameter
to the console.Add Namespaces to the Registry
To be able to use
Configuration Schemas
and theirParameters
etc. it is necessary to add aConfiguration Namespace
that holds the requiredConfiguration Schemas
to the Registry.This is possible using the
REGISTRY_ADD_NAMESPACE
macro, providing the name of theConfiguration Namespace
and a pointer to aregistry_namespace_t
object as arguments.Add Configuration Schema Instances to the Registry
To implement runtime configuration functionality into a Module / Driver, it is necessary to add a
Configuration Schema Instance
of the neededConfiguration Schema
to the Registry.This is possible using the
registry_add_schema_instance
function, providing theConfiguration Schema
and theConfiguration Schema Instance
as arguments.Storage API
Load from Storage
It is often needed to load Configuration Parameters from a non-volatile
Storage
device.For example when a device restarts after a shutdown.
This is possible using the
registry_load
function.Internally the
registry_load
function calls theload
callback of a registeredStorage Source
.That function takes a
registry_storage_instance_t
and aload_cb_t
as its arguments.The
the registry_storage_instance_t
contains data such as the mount point.the
load_cb_t
is used to tell the Registry about foundConfiguration Values
.This
load_cb_t
function takes a pointer to aConfiguration Schema Instance
a pointer to aConfiguration Parameter
, a valuevoid *
buffer and its size as arguments.Inside its
load
function, theStorage Source
searches its persistent storage device forConfiguration Values
.If a
Configuration Value
is found, theStorage Source
calls theload_cb_t
function and provides the necessary arguments.Save to Storage
To save
Configuration Values
to a non-volatileStorage
device, the Registry provides multiple functions:The function
registry_save
saves all availableConfiguration Parameters
within the RIOT Registry to the storage.The function
registry_save_namespace
saves all availableConfiguration Parameters
within the specifiedConfiguration Namespace
to the storage.The function
registry_save_schema
saves all availableConfiguration Parameters
within the specifiedConfiguration Schema
to the storage.The function
registry_save_instance
saves all availableConfiguration Parameters
within the specifiedConfiguration Schema Instance
to the storage.The function
registry_save_group
saves all availableConfiguration Parameters
within the specifiedConfiguration Group
to the storage.The function
registry_save_parameter
saves a single providedConfiguration Parameters
to the storage.Internally these functions call the
save
handler of theStorage Destination
for eachConfiguration Parameter
that has to be saved to storage.The
save
handler of theStorage Destination
takes aStorage Instance
providing data such as the mount point, aConfiguration Schema Instance
, aConfiguration Parameter
and aConfiguration Value
as its arguments.These values provide the same information base that is necessary to
load
them using theload_cb_t
as explained in the previous section.The way how the
Storage
stores these values internally is not specified.Add Storage Sources
To be able to load configurations from
Storage
it is necessary to add aStorage Source
that can readConfiguration Values
from a persistent storage device.This is possible using the
REGISTRY_ADD_STORAGE_SOURCE
macro, providing a pointer to aregistry_storage_instance_t
object as its argument.Set the Storage Destination
To be able to write configurations to
Storage
it is necessary to add aStorage Destination
that can writeConfiguration Values
to a persistent storage device.This is possible using the
REGISTRY_SET_STORAGE_DESTINATION
macro, providing a pointer to aregistry_storage_instance_t
object as its argument.Comparison to Apache Mynewt Config
While originally our work on the RIOT Registry was heavily inspired by Apache Mynewt Config, it has since evolved to provide features such as
Type Safety
, makeString Paths
optional, introduceInteger Paths
and provide a more modularPointer-Based API
.The table below shows the difference between Apache Mynewt Config and the proposed RIOT Registry.
The Idea here is
not
to talk down Apache Mynewt Config, as its simplicity of course has its own advantages, but to point out more clearly how our solution differs to it.External Configuration Managers
CLI
The only available Configuration Manager in this PR is a CLI.
It can be tried in the example under
examples/registry
.The CLI uses int paths separated by
/
.E.g.:
0/0/0/0
.CoAP API
The idea is to use the
registry_int
and/orregistry_path
module to provide a simple CoAP API.Get
The get function could be mapped to the CoAP GET function.
Set
The set function could be mapped to the CoAP PUT function.
Commit
This is more tricky.
Possible solutions could be to provide a
/commit
suffix or prefix at the end or beginning of a path, to tell the registry, that this is a commit operation.Export
The export function could be implemented using a CoAP GET function using
/export
as a suffix or prefix of the path.CoAP would then return one response containing all the requested objects for example structured using CBOR.
LwM2M
A LwM2M integration would require to write a mapping between each LwM2M Object to the respective RIOT Registry Configuration Schemas.
In this case every LwM2M set operation would immediately trigger a
registry_commit
as LwM2M does not provide acommit
operation.This is not a drawback, as LwM2M allows to set multiple values at the same time, thus covering the same use-case as the
registry_commit
function.Testing procedure
Testing commands unter tests/unittests:
Issues/PRs references
See also #10622
See also #19557