stages: add org.osbuild.nm.conf
New stage to create NetworkManager configuration files. Currently only a subset of settings are supported. Include a simple stage test case for it.
This commit is contained in:
parent
152b14a0d6
commit
6f7382b885
6 changed files with 1388 additions and 0 deletions
204
stages/org.osbuild.nm.conf
Executable file
204
stages/org.osbuild.nm.conf
Executable file
|
|
@ -0,0 +1,204 @@
|
|||
#!/usr/bin/python3
|
||||
"""
|
||||
Create NetworkManager configuration files.
|
||||
|
||||
This stages allows to create NetworkManager configuration files in the
|
||||
location specified via `path` with the `settings` provided. Only a
|
||||
subset of the all options are currently supported.
|
||||
|
||||
The stage will use `true` and `false` for boolean values and `,` as
|
||||
list separator.
|
||||
|
||||
NetworkManger looks in the following locations for configuration files:
|
||||
- /etc/NetworkManager/NetworkManager.conf
|
||||
- /etc/NetworkManager/conf.d/name.conf
|
||||
- /usr/lib/NetworkManager/conf.d/name.conf,
|
||||
|
||||
and also in the following locations that very likely should not be used:
|
||||
- /run/NetworkManager/conf.d/name.conf
|
||||
-/var/lib/NetworkManager/NetworkManager-intern.conf
|
||||
"""
|
||||
|
||||
import configparser
|
||||
import os
|
||||
import sys
|
||||
|
||||
import osbuild.api
|
||||
|
||||
|
||||
SCHEMA = r"""
|
||||
"definitions": {
|
||||
"device-list": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"str-array": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"managed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"wifi.scan-rand-mac-address": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"global-dns-domain": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["servers"],
|
||||
"properties": {
|
||||
"servers": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["path", "settings"],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"settings": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"main": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"no-auto-default": {
|
||||
"$ref": "#/definitions/device-list"
|
||||
},
|
||||
"plugins": {
|
||||
"$ref": "#/definitions/str-array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["config"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"config": {
|
||||
"$ref": "#/definitions/device"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"global-dns-domain": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "config"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"config": {
|
||||
"$ref": "#/definitions/global-dns-domain"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"keyfile": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"unmanaged-devices": {
|
||||
"$ref": "#/definitions/device-list"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def make_value(value):
|
||||
if isinstance(value, list):
|
||||
val = ",".join(value)
|
||||
elif isinstance(value, bool):
|
||||
val = "true" if value else "false"
|
||||
else:
|
||||
val = str(value)
|
||||
return val
|
||||
|
||||
|
||||
def make_section(cfg, name, settings):
|
||||
if not cfg.has_section(name):
|
||||
cfg.add_section(name)
|
||||
|
||||
for key, value in settings.items():
|
||||
val = make_value(value)
|
||||
cfg.set(name, key, val)
|
||||
|
||||
|
||||
def make_named_section(cfg, name: str, section):
|
||||
|
||||
config = section["config"]
|
||||
suffix = section.get("name")
|
||||
if suffix:
|
||||
name = f"{name}-{suffix}"
|
||||
|
||||
make_section(cfg, name, config)
|
||||
|
||||
|
||||
def main(tree, options):
|
||||
path = options["path"]
|
||||
settings = options["settings"]
|
||||
|
||||
cfgfile = os.path.join(tree, path.lstrip("/"))
|
||||
|
||||
cfgpath = os.path.dirname(cfgfile)
|
||||
os.makedirs(cfgpath, exist_ok=True)
|
||||
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.optionxform = lambda o: o
|
||||
|
||||
for name, items in settings.items():
|
||||
|
||||
if isinstance(items, dict):
|
||||
make_section(cfg, name, items)
|
||||
elif isinstance(items, list):
|
||||
for item in items:
|
||||
make_named_section(cfg, name, item)
|
||||
else:
|
||||
raise ValueError(f"Invalid section type: {type(items)}")
|
||||
|
||||
with open(cfgfile, "w") as f:
|
||||
os.fchmod(f.fileno(), 0o600)
|
||||
cfg.write(f, space_around_delimiters=False)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = osbuild.api.arguments()
|
||||
r = main(args["tree"], args["options"])
|
||||
sys.exit(r)
|
||||
Loading…
Add table
Add a link
Reference in a new issue