libvirt/scripts/merge-systemd-units.py
Andrea Bolognani b1aeca5db0 systemd: Support merging multiple units
In order to further deduplicate the contents of the various unit
files, we need to be able to merge multiple additional units
into the initial one.

Luckily the merge logic is in no way constrained to working with
just two units, so achieving this is pretty much just a matter
of lifting the existing limitation on the number of arguments
that the script accepts.

As a special case, it's now also possible to call the script
with just the base unit as argument. No merging will be performed
in that case, obviously, but we'll still go through the basic
validation and cleanup steps.

This also fixes a bug in the check for the number of arguments:
sys.argv also contains the name of the script, so we should have
checked that its size was at least 3. The check is now written in
a way that's less prone to misunderstandings.

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2023-10-25 11:42:19 +02:00

100 lines
1.9 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright (C) 2023 Red Hat, Inc.
# SPDX-License-Identifier: LGPL-2.1-or-later
import sys
SECTIONS = [
"[Unit]",
"[Service]",
"[Socket]",
"[Install]",
]
def parse_unit(unit_path):
unit = {}
current_section = "[Invalid]"
with open(unit_path) as f:
for line in f:
line = line.strip()
if line == "":
continue
if line[0] == "[" and line[-1] == "]":
if line not in SECTIONS:
print("Unknown section {}".format(line))
sys.exit(1)
current_section = line
continue
if current_section not in unit:
unit[current_section] = []
unit[current_section].append(line)
if "[Invalid]" in unit:
print("Contents found outside of any section")
sys.exit(1)
return unit
def format_unit(unit):
lines = []
for section in SECTIONS:
if section not in unit:
continue
lines.append(section)
for line in unit[section]:
lines.append(line)
lines.append("")
return "\n".join(lines)
def merge_units(base, extra):
merged = {}
for section in SECTIONS:
if section in extra and section not in base:
print("Section {} in extra but not in base".format(section))
sys.exit(1)
if section not in base:
continue
merged[section] = base[section]
if section not in extra:
continue
merged[section].extend(extra[section])
return merged
prog = sys.argv[0]
args = sys.argv[1:]
if len(args) < 1:
print("usage: {} BASE [EXTRA]...".format(prog))
sys.exit(1)
merged = parse_unit(args[0])
for arg in args[1:]:
extra = parse_unit(arg)
merged = merge_units(merged, extra)
sys.stdout.write(format_unit(merged))