summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Märdian <slyon@ubuntu.com>2022-08-18 11:54:23 +0200
committerLukas Märdian <slyon@ubuntu.com>2022-08-18 11:54:23 +0200
commit5d8f5f9a5dfdaf610140a1a7d4d7d53c309035e6 (patch)
treea3e5ebcf59b26d665b51ed68a12032cdb7c3563a
parent75a6ca7b316d3cb098f18b5b892fcfcbb65a5700 (diff)
parentcc826f7ca3d4362578c1f60a149d75792ca02705 (diff)
Merge tag 'ubuntu/0.105-0ubuntu1' into debian/unstable
netplan.io Debian release 0.105-0ubuntu1
-rw-r--r--.github/workflows/autopkgtest.yml20
-rw-r--r--.github/workflows/build-abi.yml (renamed from .github/workflows/build.yml)17
-rw-r--r--.github/workflows/check-coverage.yml26
-rw-r--r--.github/workflows/snapd.patch9
-rw-r--r--CONTRIBUTING2
-rw-r--r--Makefile18
-rw-r--r--README.md16
-rw-r--r--abi-compat/README.md17
-rw-r--r--abi-compat/jammy_0.105.xml4360
-rw-r--r--abi-compat/suppressions.abignore9
-rw-r--r--dbus/meson.build22
-rw-r--r--debian/changelog32
-rw-r--r--debian/control1
-rw-r--r--debian/libnetplan0.symbols19
-rw-r--r--debian/patches/0002-cli-apply-fix-potential-race-with-rename-creation-of.patch173
-rw-r--r--debian/patches/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch534
-rw-r--r--debian/patches/0004-tests-ethernets-fix-autopkgtest-with-alternating-def.patch22
-rw-r--r--debian/patches/0005-nm-fix-rendering-of-password-for-unknown-passthrough.patch115
-rw-r--r--debian/patches/autopkgtest-fixes.patch38
-rw-r--r--debian/patches/ovs-timeout.patch590
-rw-r--r--debian/patches/series6
-rw-r--r--debian/tests/control1
-rw-r--r--doc/.gitignore2
-rw-r--r--doc/Makefile63
-rw-r--r--doc/README.md24
-rw-r--r--doc/conf.py54
-rw-r--r--doc/dbus-config.md (renamed from examples/dbus_config_scenario.txt)34
-rw-r--r--doc/examples.md630
-rw-r--r--doc/explanation.md9
-rw-r--r--doc/howto.md6
l---------doc/index.md1
-rw-r--r--doc/manpage-header.md1
-rw-r--r--doc/meson.build26
-rw-r--r--doc/netplan-apply.md2
-rw-r--r--doc/netplan-yaml.md1713
l---------[-rw-r--r--]doc/netplan.md1530
-rw-r--r--doc/reference.md29
-rw-r--r--doc/requirements.txt7
-rw-r--r--doc/spelling_wordlist.txt76
-rw-r--r--doc/tutorials.md15
-rw-r--r--examples/infiniband.yaml9
-rw-r--r--examples/meson.build6
-rw-r--r--examples/vrf.yaml18
-rw-r--r--examples/vxlan.yaml40
-rw-r--r--examples/wireless.yaml1
-rwxr-xr-xfeatures_h_generator.sh6
-rwxr-xr-xfeatures_py_generator.sh7
-rw-r--r--include/meson.build2
-rw-r--r--include/netplan.h32
-rw-r--r--include/parse.h7
-rw-r--r--meson.build125
-rw-r--r--netplan/cli/commands/apply.py62
-rw-r--r--netplan/cli/commands/ip.py35
-rw-r--r--netplan/cli/commands/set.py177
-rw-r--r--netplan/cli/commands/try_command.py32
-rw-r--r--netplan/cli/ovs.py17
-rw-r--r--netplan/cli/sriov.py114
-rw-r--r--netplan/cli/utils.py63
-rw-r--r--netplan/configmanager.py244
-rw-r--r--netplan/libnetplan.py281
-rw-r--r--netplan/meson.build35
-rw-r--r--rpm/netplan.spec198
-rw-r--r--src/abi.h382
-rw-r--r--src/abi_compat.c27
-rw-r--r--src/dbus.c13
-rw-r--r--src/error.c5
-rw-r--r--src/generate.c6
-rw-r--r--src/meson.build46
-rw-r--r--src/names.c23
-rw-r--r--src/names.h36
-rw-r--r--src/netplan.c263
-rw-r--r--src/networkd.c202
-rw-r--r--src/nm.c296
-rw-r--r--src/openvswitch.c2
-rw-r--r--src/parse-nm.c12
-rw-r--r--src/parse.c661
-rw-r--r--src/sriov.c21
-rw-r--r--src/types.c121
-rw-r--r--src/types.h359
-rw-r--r--src/util-internal.h14
-rw-r--r--src/util.c219
-rw-r--r--src/validation.c85
-rw-r--r--src/validation.h3
-rw-r--r--src/yaml-helpers.h4
-rwxr-xr-xtests/cli.py129
-rw-r--r--tests/dbus/test_dbus.py5
-rw-r--r--tests/generator/base.py24
-rw-r--r--tests/generator/test_auth.py18
-rw-r--r--tests/generator/test_bonds.py16
-rw-r--r--tests/generator/test_bridges.py37
-rw-r--r--tests/generator/test_common.py48
-rw-r--r--tests/generator/test_errors.py12
-rw-r--r--tests/generator/test_ethernets.py160
-rw-r--r--tests/generator/test_modems.py30
-rw-r--r--tests/generator/test_ovs.py40
-rw-r--r--tests/generator/test_passthrough.py28
-rw-r--r--tests/generator/test_routing.py2
-rw-r--r--tests/generator/test_tunnels.py280
-rw-r--r--tests/generator/test_vlans.py28
-rw-r--r--tests/generator/test_vrfs.py188
-rw-r--r--tests/generator/test_wifis.py59
-rw-r--r--tests/integration/base.py30
-rw-r--r--tests/integration/bonds.py8
-rw-r--r--tests/integration/bridges.py4
-rw-r--r--tests/integration/ethernets.py71
-rw-r--r--tests/integration/ovs.py140
-rw-r--r--tests/integration/regressions.py35
-rw-r--r--tests/integration/routing.py52
-rwxr-xr-xtests/integration/run.py6
-rw-r--r--tests/integration/scenarios.py6
-rw-r--r--tests/integration/tunnels.py132
-rw-r--r--tests/integration/vlans.py4
-rw-r--r--tests/integration/wifi.py31
-rw-r--r--tests/parser/base.py5
-rw-r--r--tests/parser/test_keyfile.py131
-rw-r--r--tests/test_cli_get_set.py256
-rw-r--r--tests/test_cli_units.py38
-rw-r--r--tests/test_configmanager.py158
-rw-r--r--tests/test_libnetplan.py232
-rw-r--r--tests/test_ovs.py56
-rw-r--r--tests/test_sriov.py12
-rw-r--r--tests/test_utils.py96
-rwxr-xr-xtests/validate_docs.sh10
123 files changed, 12360 insertions, 4837 deletions
diff --git a/.github/workflows/autopkgtest.yml b/.github/workflows/autopkgtest.yml
index 0f74783..fba590f 100644
--- a/.github/workflows/autopkgtest.yml
+++ b/.github/workflows/autopkgtest.yml
@@ -12,7 +12,7 @@ on:
jobs:
lxd-ubuntu-jammy:
# The type of runner that the job will run on
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
@@ -20,20 +20,32 @@ jobs:
- uses: actions/checkout@v2
- run: |
git fetch --unshallow --tags
+ # Install openvswitch-switch to make the OVS integration tests work
+ # Install linux-modules-extra-azure to provide the 'vrf' kernel module,
+ # it's needed (will be auto-loaded) by routing.test_vrf_basic
- name: Install dependencies
run: |
sudo sed -i '/deb-src/s/^# //' /etc/apt/sources.list
sudo apt update
- sudo apt install autopkgtest ubuntu-dev-tools devscripts openvswitch-switch
+ sudo apt install autopkgtest ubuntu-dev-tools devscripts openvswitch-switch linux-modules-extra-$(uname -r)
sudo snap install lxd
sudo lxd init --auto --storage-backend=dir
+ # work around LP: #1878225 as fallback
+ - name: Preparing autopkgtest-build-lxd
+ run: |
+ # Fix Docker blocking LXD networking:
+ # https://discuss.linuxcontainers.org/t/9953/4
+ sudo iptables -I DOCKER-USER -j ACCEPT
+ sudo patch /usr/bin/autopkgtest-build-lxd .github/workflows/snapd.patch
+ sudo autopkgtest-build-lxd ubuntu-daily:jammy
- name: Prepare test
run: |
pull-lp-source netplan.io
cp -r netplan.io-*/debian .
rm -r debian/patches/ # clear any distro patches
+ # temporary until 'ethtool' is a proper test dependency
+ sed -i 's/wpasupplicant,/wpasupplicant, ethtool,/g' debian/tests/control
dch -v $(git describe --tags) "Autopkgtest CI testing (Jammy)"
- sudo autopkgtest-build-lxd ubuntu-daily:jammy
- name: Run autopkgtest (incl. build)
run: |
- sudo autopkgtest . -U --env=DPKG_GENSYMBOLS_CHECK_LEVEL=0 -- lxd autopkgtest/ubuntu/jammy/amd64
+ sudo autopkgtest . -U --env=DPKG_GENSYMBOLS_CHECK_LEVEL=0 --env=DEB_BUILD_OPTIONS=nocheck -- lxd autopkgtest/ubuntu/jammy/amd64
diff --git a/.github/workflows/build.yml b/.github/workflows/build-abi.yml
index 2346d27..4aed327 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build-abi.yml
@@ -1,4 +1,4 @@
-name: Build
+name: Build & ABI compatibility
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the main branch
@@ -13,7 +13,7 @@ jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
@@ -21,13 +21,24 @@ jobs:
- uses: actions/checkout@v2
# Installs the build dependencies
+ # Always include phased updates (LP: #1979244)
- name: Install build depends
run: |
+ echo "APT::Get::Always-Include-Phased-Updates \"true\";" | sudo tee /etc/apt/apt.conf.d/90phased-updates
sudo sed -i '/deb-src/s/^# //' /etc/apt/sources.list
sudo apt update
#sudo apt install lcov python3-coverage curl
+ sudo apt install abigail-tools meson
sudo apt build-dep netplan.io
# Runs the build
+ # TODO: move to meson build (on Jammy), once available
- name: Run build
- run: make
+ run: |
+ meson setup _build --prefix=/usr
+ meson compile -C _build
+
+ # Abigail ABI checker
+ - name: Check ABI compatibility
+ run: |
+ abidiff abi-compat/jammy_0.105.xml _build/src/libnetplan.so.0.0 --headers-dir2 include/ --header-file2 src/abi.h --suppressions abi-compat/suppressions.abignore --no-added-syms
diff --git a/.github/workflows/check-coverage.yml b/.github/workflows/check-coverage.yml
index faaa75c..3a3dd7e 100644
--- a/.github/workflows/check-coverage.yml
+++ b/.github/workflows/check-coverage.yml
@@ -13,29 +13,35 @@ jobs:
# This workflow contains a single job called "test-and-coverage"
test-and-coverage:
# The type of runner that the job will run on
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- # Installs the build dependencies
+ # Installs the build dependencies, simulating a TTY/PTY via 'unbuffer' to
+ # to fix test_terminal.py on GA (https://github.com/actions/runner/issues/241)
+ # Always include phased updates (LP: #1979244)
- name: Install build depends
run: |
+ echo "APT::Get::Always-Include-Phased-Updates \"true\";" | sudo tee /etc/apt/apt.conf.d/90phased-updates
sudo sed -i '/deb-src/s/^# //' /etc/apt/sources.list
sudo apt update
- sudo apt install lcov python3-coverage curl
+ sudo apt install python3-coverage curl meson gcovr expect
sudo apt build-dep netplan.io
# Runs the unit tests with coverage
- name: Run unit tests
- run: make coverage
+ run: |
+ meson setup _build --prefix=/usr -Db_coverage=true
+ meson compile -C _build
+ unbuffer meson test -C _build --verbose
# Checks the coverage diff to the main branch
- - name: Upload coverage to Codecov
- uses: codecov/codecov-action@v1
- with:
- name: check-coverage
- fail_ci_if_error: true
- verbose: true
+ #- name: Upload coverage to Codecov
+ # uses: codecov/codecov-action@v1
+ # with:
+ # name: check-coverage
+ # fail_ci_if_error: true
+ # verbose: true
diff --git a/.github/workflows/snapd.patch b/.github/workflows/snapd.patch
new file mode 100644
index 0000000..437e1f7
--- /dev/null
+++ b/.github/workflows/snapd.patch
@@ -0,0 +1,9 @@
+@@ -70,6 +70,8 @@
+
+ sleep 5
+ if lxc exec "$CONTAINER" -- systemctl mask serial-getty@getty.service; then
++ lxc exec "$CONTAINER" -- systemctl mask snapd.service
++ lxc exec "$CONTAINER" -- systemctl mask snapd.seeded.service
+ lxc exec "$CONTAINER" -- reboot
+ fi
+
diff --git a/CONTRIBUTING b/CONTRIBUTING
index 9712d67..0b3e767 100644
--- a/CONTRIBUTING
+++ b/CONTRIBUTING
@@ -51,6 +51,6 @@ used in the code. We do insist on proper indentation (4 spaces), but we will
not block good features and bug fixes on purely style issues. Please exercise
your best judgement: if it looks odd or too clever to you, chances are it will
look odd or too clever to code reviewers. In that case, you may be asked for
-some styles changes in a pull request. Similary, if you see code that you
+some styles changes in a pull request. Similarly, if you see code that you
find hard to understand, we do encourage that you submit pull requests that
help make the code easier to understand and maintain.
diff --git a/Makefile b/Makefile
index 0368e41..7b28cd7 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ BUILDFLAGS = \
-g \
-fPIC \
-std=c99 \
- -D_XOPEN_SOURCE=500 \
+ -D_XOPEN_SOURCE=700 \
-DSBINDIR=\"$(SBINDIR)\" \
-I${CURDIR}/include \
-Wall \
@@ -52,7 +52,7 @@ NOSETESTS3 ?= $(shell command -v nosetests-3 || command -v nosetests3 || echo tr
default: netplan/_features.py generate netplan-dbus dbus/io.netplan.Netplan.service doc/netplan.html doc/netplan.5 doc/netplan-generate.8 doc/netplan-apply.8 doc/netplan-try.8 doc/netplan-dbus.8 doc/netplan-get.8 doc/netplan-set.8
-%.o: src/%.c
+%.o: src/%.c src/_features.h
$(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -c $^ `pkg-config --cflags --libs glib-2.0 gio-2.0 yaml-0.1 uuid`
libnetplan.so.$(NETPLAN_SOVER): $(SRCS) abicompat.lds
@@ -62,7 +62,7 @@ libnetplan.so.$(NETPLAN_SOVER): $(SRCS) abicompat.lds
generate: libnetplan.so.$(NETPLAN_SOVER) generate.o
$(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(filter-out $<,$^) -L. -lnetplan `pkg-config --cflags --libs glib-2.0 gio-2.0 yaml-0.1 uuid`
-netplan-dbus: libnetplan.so.$(NETPLAN_SOVER) src/_features.h dbus.o
+netplan-dbus: libnetplan.so.$(NETPLAN_SOVER) dbus.o
$(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(filter-out $<,$(patsubst %.h,,$^)) -L. -lnetplan `pkg-config --cflags --libs libsystemd glib-2.0 gio-2.0 yaml-0.1 uuid`
src/_features.h: src/[^_]*.[hc]
@@ -77,7 +77,7 @@ netplan/_features.py: src/[^_]*.[hc]
echo "]" >> $@
clean:
- rm -f netplan/_features.py src/_features.h
+ rm -f netplan/_features.py src/_features.h src/_features.h.gch
rm -f generate doc/*.html doc/*.[1-9]
rm -f *.o *.so*
rm -f netplan-dbus dbus/*.service
@@ -86,8 +86,8 @@ clean:
find . | grep -E "(__pycache__|\.pyc)" | xargs rm -rf
check: default linting
- tests/cli.py
- LD_LIBRARY_PATH=. $(NOSETESTS3) -v --with-coverage
+ PYTHONPATH=. LD_LIBRARY_PATH=. tests/cli.py
+ PYTHONPATH=. LD_LIBRARY_PATH=. $(NOSETESTS3) -v --with-coverage
tests/validate_docs.sh
linting:
@@ -152,10 +152,10 @@ install: default
%.html: %.md
pandoc -s --metadata title="Netplan reference" --toc -o $@ $<
-doc/netplan.5: doc/manpage-header.md doc/netplan.md doc/manpage-footer.md
- pandoc -s -o $@ $^
+doc/netplan.5: doc/manpage-header.md doc/netplan-yaml.md doc/manpage-footer.md
+ pandoc -s -o $@ --from=markdown-smart $^
%.8: %.md
- pandoc -s -o $@ $^
+ pandoc -s -o $@ --from=markdown-smart $^
.PHONY: clean
diff --git a/README.md b/README.md
index 31d422d..6051ace 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,8 @@
# netplan - Backend-agnostic network configuration in YAML
-[![Build](https://github.com/canonical/netplan/workflows/Build/badge.svg?branch=master)](https://github.com/canonical/netplan/actions?query=branch%3Amaster+workflow%3ABuild)
-[![Codecov](https://codecov.io/gh/canonical/netplan/branch/master/graph/badge.svg)](https://codecov.io/gh/canonical/netplan)
+[![Build+ABI](https://github.com/canonical/netplan/workflows/Build%20&%20ABI%20compatibility/badge.svg?branch=main)](https://github.com/canonical/netplan/actions/workflows/build-abi.yml?query=branch%3Amain)
+[![Test+Coverage](https://github.com/canonical/netplan/workflows/Unit%20tests%20&%20Coverage/badge.svg?branch=main)](https://github.com/canonical/netplan/actions/workflows/check-coverage.yml?query=branch%3Amain)
+[![CI](https://github.com/canonical/netplan/workflows/Autopkgtest%20CI/badge.svg?branch=main)](https://github.com/canonical/netplan/actions/workflows/autopkgtest.yml?query=branch%3Amain)
# Website
@@ -12,7 +13,16 @@ http://netplan.io
An overview of the architecture can be found at [netplan.io/design](https://netplan.io/design)
-The full documentation for netplan is available in the [doc/netplan.md file](../master/doc/netplan.md)
+The full documentation for netplan is available in the [doc/](../main/doc/) directory.
+
+# Build using Meson
+
+Steps to build netplan using the [Meson](https://mesonbuild.com) build system inside the `build/` directory:
+
+* meson setup build --prefix=/usr [-Db_coverage=true]
+* meson compile -C build
+* meson test -C build --verbose [TEST_NAME]
+* meson install -C build --destdir ../tmproot
# Bug reports
diff --git a/abi-compat/README.md b/abi-compat/README.md
new file mode 100644
index 0000000..7be64a8
--- /dev/null
+++ b/abi-compat/README.md
@@ -0,0 +1,17 @@
+# Netplan's ABI checker
+We're using "abigail" (abigail-tools) to validate libnetplan's ABI.
+
+## HowTo create a ABI reference
+The `abidw` tool can be used to generate an ABI XML like this:
+```
+meson setup build --prefix=/usr
+meson compile -C build
+abidw build/src/libnetplan.so.0.0 --headers-dir include/ --header-file src/abi.h > abi-compat/jammy_0.105.xml
+```
+
+## HowTo compare a ABI
+The `abidiff` tool can be used to compare a new library ABI to an existing XML
+reference like this (also, see .github/workflows/build-abi.yml):
+```
+abidiff abi-compat/jammy_0.105.xml build/src/libnetplan.so.0.0 --headers-dir2 include/ --header-file2 src/abi.h --suppressions abi-compat/suppressions.abignore --no-added-syms
+```
diff --git a/abi-compat/jammy_0.105.xml b/abi-compat/jammy_0.105.xml
new file mode 100644
index 0000000..a435f75
--- /dev/null
+++ b/abi-compat/jammy_0.105.xml
@@ -0,0 +1,4360 @@
+<abi-corpus version='2.0' path='build/src/libnetplan.so.0.0' architecture='elf-amd-x86_64' soname='libnetplan.so.0.0'>
+ <elf-needed>
+ <dependency name='libglib-2.0.so.0'/>
+ <dependency name='libgio-2.0.so.0'/>
+ <dependency name='libgobject-2.0.so.0'/>
+ <dependency name='libyaml-0.so.2'/>
+ <dependency name='libuuid.so.1'/>
+ <dependency name='libc.so.6'/>
+ </elf-needed>
+ <elf-function-symbols>
+ <elf-symbol name='_netplan_iter_defs_per_devtype_free' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_iter_defs_per_devtype_init' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_iter_defs_per_devtype_next' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_netdef_get_critical' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_netdef_get_sriov_link' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_netdef_get_sriov_vlan_filter' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_netdef_get_vlan_id' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_netdef_get_vlan_link' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_netdef_id' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_netdef_is_trivial_compound_itf' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_netdef_pertype_iter_free' type='func-type' binding='global-binding' visibility='default-visibility' alias='_netplan_iter_defs_per_devtype_free' is-defined='yes'/>
+ <elf-symbol name='_netplan_netdef_pertype_iter_next' type='func-type' binding='global-binding' visibility='default-visibility' alias='_netplan_iter_defs_per_devtype_next' is-defined='yes'/>
+ <elf-symbol name='_netplan_state_get_vf_count_for_def' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_netplan_state_new_netdef_pertype_iter' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='_write_netplan_conf' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='find_yaml_glob' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='g_string_free_to_file' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='get_global_network' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='get_unspecified_address' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='is_wireguard_key' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_backend_name' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_clear_netdefs' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_def_type_name' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_delete_connection' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_finish_parse' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_generate' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_get_filename_by_id' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_get_global_backend' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_get_id_from_nm_filename' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_get_backend' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_get_delay_virtual_functions_rebind' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_get_embedded_switch_mode' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_get_filename' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_get_filepath' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_get_id' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_get_set_name' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_get_type' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_has_match' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_match_interface' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_write_network_file' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_write_networkd' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_write_nm' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_write_ovs' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_netdef_write_yaml' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_networkd_cleanup' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_nm_cleanup' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_ovs_cleanup' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parse_keyfile' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parse_yaml' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parser_clear' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parser_load_keyfile' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parser_load_nullable_fields' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parser_load_yaml' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parser_load_yaml_from_fd' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parser_load_yaml_hierarchy' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parser_new' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_parser_reset' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_sriov_cleanup' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_clear' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_dump_yaml' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_finish_nm_write' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_finish_ovs_write' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_finish_sriov_write' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_get_backend' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_get_netdef' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_get_netdefs_size' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_import_parser_results' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_new' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_reset' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_update_yaml_hierarchy' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_state_write_yaml_file' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_util_create_yaml_patch' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_util_dump_yaml_subtree' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='process_input_file' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='process_yaml_hierarchy' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='safe_mkdir_p_dir' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='systemd_escape' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='tunnel_mode_to_string' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='unlink_glob' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='wifi_get_freq24' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='wifi_get_freq5' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='write_netplan_conf' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='write_netplan_conf_full' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ </elf-function-symbols>
+ <elf-variable-symbols>
+ <elf-symbol name='NETPLAN_OPTIONAL_ADDRESS_TYPES' size='96' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='NETPLAN_WIFI_WOWLAN_TYPES' size='160' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='global_backend' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='global_state' size='176' type='object-type' binding='global-binding' visibility='default-visibility' alias='netdefs' is-defined='yes'/>
+ <elf-symbol name='netdefs' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netdefs_ordered' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='netplan_backend_to_name' size='32' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='ovs_settings_global' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='wifi_frequency_24' size='8' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='wifi_frequency_5' size='8' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ </elf-variable-symbols>
+ <abi-instr address-size='64' path='../src/abi_compat.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <type-decl name='char' size-in-bits='8' id='type-id-1'/>
+ <array-type-def dimensions='1' type-id='type-id-1' size-in-bits='8' id='type-id-2'>
+ <subrange length='1' type-id='type-id-3' id='type-id-4'/>
+ </array-type-def>
+ <array-type-def dimensions='1' type-id='type-id-1' size-in-bits='160' id='type-id-5'>
+ <subrange length='20' type-id='type-id-3' id='type-id-6'/>
+ </array-type-def>
+ <class-decl name='_GData' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-7'/>
+ <class-decl name='_GHashTable' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-8'/>
+ <class-decl name='_IO_codecvt' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-9'/>
+ <class-decl name='_IO_marker' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-10'/>
+ <class-decl name='_IO_wide_data' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-11'/>
+ <class-decl name='netdef_pertype_iter' size-in-bits='448' is-struct='yes' visibility='default' filepath='../src/util.c' line='635' column='1' id='type-id-12'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='type' type-id='type-id-13' visibility='default' filepath='../src/util.c' line='636' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='iter' type-id='type-id-14' visibility='default' filepath='../src/util.c' line='637' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='np_state' type-id='type-id-15' visibility='default' filepath='../src/util.c' line='638' column='1'/>
+ </data-member>
+ </class-decl>
+ <type-decl name='int' size-in-bits='32' id='type-id-16'/>
+ <type-decl name='long int' size-in-bits='64' id='type-id-17'/>
+ <type-decl name='signed char' size-in-bits='8' id='type-id-18'/>
+ <type-decl name='unnamed-enum-underlying-type-32' is-anonymous='yes' size-in-bits='32' alignment-in-bits='32' id='type-id-19'/>
+ <type-decl name='unsigned char' size-in-bits='8' id='type-id-20'/>
+ <array-type-def dimensions='1' type-id='type-id-20' size-in-bits='128' id='type-id-21'>
+ <subrange length='16' type-id='type-id-3' id='type-id-22'/>
+ </array-type-def>
+ <type-decl name='unsigned int' size-in-bits='32' id='type-id-23'/>
+ <type-decl name='unsigned long int' size-in-bits='64' id='type-id-3'/>
+ <type-decl name='unsigned short int' size-in-bits='16' id='type-id-24'/>
+ <type-decl name='variadic parameter type' id='type-id-25'/>
+ <type-decl name='void' id='type-id-26'/>
+ <typedef-decl name='NetplanNetDefinition' type-id='type-id-27' filepath='../include/netplan.h' line='33' column='1' id='type-id-28'/>
+ <typedef-decl name='NetplanState' type-id='type-id-29' filepath='../include/netplan.h' line='34' column='1' id='type-id-30'/>
+ <enum-decl name='NetplanBackend' naming-typedef-id='type-id-31' filepath='../include/netplan.h' line='36' column='1' id='type-id-32'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_BACKEND_NONE' value='0'/>
+ <enumerator name='NETPLAN_BACKEND_NETWORKD' value='1'/>
+ <enumerator name='NETPLAN_BACKEND_NM' value='2'/>
+ <enumerator name='NETPLAN_BACKEND_OVS' value='3'/>
+ <enumerator name='NETPLAN_BACKEND_MAX_' value='4'/>
+ </enum-decl>
+ <typedef-decl name='NetplanBackend' type-id='type-id-32' filepath='../include/netplan.h' line='42' column='1' id='type-id-31'/>
+ <enum-decl name='NetplanDefType' naming-typedef-id='type-id-13' filepath='../include/parse.h' line='26' column='1' id='type-id-33'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_DEF_TYPE_NONE' value='0'/>
+ <enumerator name='NETPLAN_DEF_TYPE_ETHERNET' value='1'/>
+ <enumerator name='NETPLAN_DEF_TYPE_WIFI' value='2'/>
+ <enumerator name='NETPLAN_DEF_TYPE_MODEM' value='3'/>
+ <enumerator name='NETPLAN_DEF_TYPE_VIRTUAL' value='4'/>
+ <enumerator name='NETPLAN_DEF_TYPE_BRIDGE' value='4'/>
+ <enumerator name='NETPLAN_DEF_TYPE_BOND' value='5'/>
+ <enumerator name='NETPLAN_DEF_TYPE_VLAN' value='6'/>
+ <enumerator name='NETPLAN_DEF_TYPE_TUNNEL' value='7'/>
+ <enumerator name='NETPLAN_DEF_TYPE_PORT' value='8'/>
+ <enumerator name='NETPLAN_DEF_TYPE_VRF' value='9'/>
+ <enumerator name='NETPLAN_DEF_TYPE_NM' value='10'/>
+ <enumerator name='NETPLAN_DEF_TYPE_MAX_' value='11'/>
+ </enum-decl>
+ <typedef-decl name='NetplanDefType' type-id='type-id-33' filepath='../include/parse.h' line='43' column='1' id='type-id-13'/>
+ <typedef-decl name='NetplanParser' type-id='type-id-34' filepath='../include/parse.h' line='45' column='1' id='type-id-35'/>
+ <typedef-decl name='NetplanFlags' type-id='type-id-16' filepath='../src/abi.h' line='23' column='1' id='type-id-36'/>
+ <enum-decl name='NetplanOptionalAddressFlag' naming-typedef-id='type-id-37' filepath='../src/abi.h' line='27' column='1' id='type-id-38'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_OPTIONAL_IPV4_LL' value='1'/>
+ <enumerator name='NETPLAN_OPTIONAL_IPV6_RA' value='2'/>
+ <enumerator name='NETPLAN_OPTIONAL_DHCP4' value='4'/>
+ <enumerator name='NETPLAN_OPTIONAL_DHCP6' value='8'/>
+ <enumerator name='NETPLAN_OPTIONAL_STATIC' value='16'/>
+ </enum-decl>
+ <typedef-decl name='NetplanOptionalAddressFlag' type-id='type-id-38' filepath='../src/abi.h' line='33' column='1' id='type-id-37'/>
+ <class-decl name='dhcp_overrides' size-in-bits='384' is-struct='yes' visibility='default' filepath='../src/abi.h' line='36' column='1' id='type-id-39'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='use_dns' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='37' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='use_ntp' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='38' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='send_hostname' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='39' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='96'>
+ <var-decl name='use_hostname' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='40' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='use_mtu' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='41' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='160'>
+ <var-decl name='use_routes' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='42' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='use_domains' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='43' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='hostname' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='44' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='metric' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='45' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanDHCPOverrides' type-id='type-id-39' filepath='../src/abi.h' line='46' column='1' id='type-id-43'/>
+ <enum-decl name='NetplanRAMode' naming-typedef-id='type-id-44' filepath='../src/abi.h' line='48' column='1' id='type-id-45'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_RA_MODE_KERNEL' value='0'/>
+ <enumerator name='NETPLAN_RA_MODE_ENABLED' value='1'/>
+ <enumerator name='NETPLAN_RA_MODE_DISABLED' value='2'/>
+ </enum-decl>
+ <typedef-decl name='NetplanRAMode' type-id='type-id-45' filepath='../src/abi.h' line='52' column='1' id='type-id-44'/>
+ <enum-decl name='NetplanInfinibandMode' naming-typedef-id='type-id-46' filepath='../src/abi.h' line='54' column='1' id='type-id-47'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_IB_MODE_KERNEL' value='0'/>
+ <enumerator name='NETPLAN_IB_MODE_DATAGRAM' value='1'/>
+ <enumerator name='NETPLAN_IB_MODE_CONNECTED' value='2'/>
+ <enumerator name='NETPLAN_IB_MODE_MAX_' value='3'/>
+ </enum-decl>
+ <typedef-decl name='NetplanInfinibandMode' type-id='type-id-47' filepath='../src/abi.h' line='60' column='1' id='type-id-46'/>
+ <enum-decl name='NetplanWifiWowlanFlag' naming-typedef-id='type-id-48' filepath='../src/abi.h' line='62' column='1' id='type-id-49'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_WIFI_WOWLAN_DEFAULT' value='1'/>
+ <enumerator name='NETPLAN_WIFI_WOWLAN_ANY' value='2'/>
+ <enumerator name='NETPLAN_WIFI_WOWLAN_DISCONNECT' value='4'/>
+ <enumerator name='NETPLAN_WIFI_WOWLAN_MAGIC' value='8'/>
+ <enumerator name='NETPLAN_WIFI_WOWLAN_GTK_REKEY_FAILURE' value='16'/>
+ <enumerator name='NETPLAN_WIFI_WOWLAN_EAP_IDENTITY_REQ' value='32'/>
+ <enumerator name='NETPLAN_WIFI_WOWLAN_4WAY_HANDSHAKE' value='64'/>
+ <enumerator name='NETPLAN_WIFI_WOWLAN_RFKILL_RELEASE' value='128'/>
+ <enumerator name='NETPLAN_WIFI_WOWLAN_TCP' value='256'/>
+ </enum-decl>
+ <typedef-decl name='NetplanWifiWowlanFlag' type-id='type-id-49' filepath='../src/abi.h' line='72' column='1' id='type-id-48'/>
+ <enum-decl name='NetplanTunnelMode' naming-typedef-id='type-id-50' filepath='../src/abi.h' line='83' column='1' id='type-id-51'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_UNKNOWN' value='0'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_IPIP' value='1'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_GRE' value='2'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_SIT' value='3'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_ISATAP' value='4'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_VTI' value='5'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_IP6IP6' value='6'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_IPIP6' value='7'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_IP6GRE' value='8'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_VTI6' value='9'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_VXLAN' value='10'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_GRETAP' value='101'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_IP6GRETAP' value='102'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_WIREGUARD' value='103'/>
+ <enumerator name='NETPLAN_TUNNEL_MODE_MAX_' value='104'/>
+ </enum-decl>
+ <typedef-decl name='NetplanTunnelMode' type-id='type-id-51' filepath='../src/abi.h' line='102' column='1' id='type-id-50'/>
+ <enum-decl name='NetplanAuthKeyManagementType' naming-typedef-id='type-id-52' filepath='../src/abi.h' line='104' column='1' id='type-id-53'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_AUTH_KEY_MANAGEMENT_NONE' value='0'/>
+ <enumerator name='NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK' value='1'/>
+ <enumerator name='NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP' value='2'/>
+ <enumerator name='NETPLAN_AUTH_KEY_MANAGEMENT_8021X' value='3'/>
+ <enumerator name='NETPLAN_AUTH_KEY_MANAGEMENT_MAX' value='4'/>
+ </enum-decl>
+ <typedef-decl name='NetplanAuthKeyManagementType' type-id='type-id-53' filepath='../src/abi.h' line='110' column='1' id='type-id-52'/>
+ <enum-decl name='NetplanAuthEAPMethod' naming-typedef-id='type-id-54' filepath='../src/abi.h' line='112' column='1' id='type-id-55'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_AUTH_EAP_NONE' value='0'/>
+ <enumerator name='NETPLAN_AUTH_EAP_TLS' value='1'/>
+ <enumerator name='NETPLAN_AUTH_EAP_PEAP' value='2'/>
+ <enumerator name='NETPLAN_AUTH_EAP_TTLS' value='3'/>
+ <enumerator name='NETPLAN_AUTH_EAP_METHOD_MAX' value='4'/>
+ </enum-decl>
+ <typedef-decl name='NetplanAuthEAPMethod' type-id='type-id-55' filepath='../src/abi.h' line='118' column='1' id='type-id-54'/>
+ <class-decl name='authentication_settings' size-in-bits='576' is-struct='yes' visibility='default' filepath='../src/abi.h' line='120' column='1' id='type-id-56'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='key_management' type-id='type-id-52' visibility='default' filepath='../src/abi.h' line='121' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='eap_method' type-id='type-id-54' visibility='default' filepath='../src/abi.h' line='122' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='identity' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='123' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='anonymous_identity' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='124' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='password' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='125' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='ca_certificate' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='126' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='client_certificate' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='127' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='client_key' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='128' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='client_key_password' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='129' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='phase2_auth' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='130' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanAuthenticationSettings' type-id='type-id-56' filepath='../src/abi.h' line='131' column='1' id='type-id-57'/>
+ <class-decl name='ovs_controller' size-in-bits='128' is-struct='yes' visibility='default' filepath='../src/abi.h' line='133' column='1' id='type-id-58'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='connection_mode' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='134' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='addresses' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='135' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanOVSController' type-id='type-id-58' filepath='../src/abi.h' line='136' column='1' id='type-id-60'/>
+ <class-decl name='ovs_settings' size-in-bits='1152' is-struct='yes' visibility='default' filepath='../src/abi.h' line='138' column='1' id='type-id-61'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='external_ids' type-id='type-id-62' visibility='default' filepath='../src/abi.h' line='139' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='other_config' type-id='type-id-62' visibility='default' filepath='../src/abi.h' line='140' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='lacp' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='141' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='fail_mode' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='142' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='mcast_snooping' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='143' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='protocols' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='144' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='rstp' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='145' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='controller' type-id='type-id-60' visibility='default' filepath='../src/abi.h' line='146' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='576'>
+ <var-decl name='ssl' type-id='type-id-57' visibility='default' filepath='../src/abi.h' line='147' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanOVSSettings' type-id='type-id-61' filepath='../src/abi.h' line='148' column='1' id='type-id-63'/>
+ <union-decl name='NetplanBackendSettings' size-in-bits='320' naming-typedef-id='type-id-64' visibility='default' filepath='../src/abi.h' line='150' column='1' id='type-id-65'>
+ <data-member access='public'>
+ <var-decl name='nm' type-id='type-id-66' visibility='default' filepath='../src/abi.h' line='157' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='networkd' type-id='type-id-67' visibility='default' filepath='../src/abi.h' line='160' column='1'/>
+ </data-member>
+ </union-decl>
+ <class-decl name='NetplanNMSettings' size-in-bits='320' is-struct='yes' visibility='default' filepath='../src/abi.h' line='151' column='1' id='type-id-66'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='name' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='152' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='uuid' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='153' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='stable_id' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='154' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='device' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='155' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='passthrough' type-id='type-id-68' visibility='default' filepath='../src/abi.h' line='156' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='NetplanNetworkdSettings' size-in-bits='64' is-struct='yes' visibility='default' filepath='../src/abi.h' line='158' column='1' id='type-id-67'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='unit' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='159' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanBackendSettings' type-id='type-id-65' filepath='../src/abi.h' line='161' column='1' id='type-id-64'/>
+ <enum-decl name='NetplanTristate' naming-typedef-id='type-id-69' filepath='../src/abi.h' line='164' column='1' id='type-id-70'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_TRISTATE_UNSET' value='-1'/>
+ <enumerator name='NETPLAN_TRISTATE_FALSE' value='0'/>
+ <enumerator name='NETPLAN_TRISTATE_TRUE' value='1'/>
+ </enum-decl>
+ <typedef-decl name='NetplanTristate' type-id='type-id-70' filepath='../src/abi.h' line='182' column='1' id='type-id-69'/>
+ <typedef-decl name='NetplanVxlan' type-id='type-id-71' filepath='../src/abi.h' line='184' column='1' id='type-id-72'/>
+ <class-decl name='netplan_net_definition' size-in-bits='9152' is-struct='yes' visibility='default' filepath='../src/abi.h' line='189' column='1' id='type-id-27'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='type' type-id='type-id-13' visibility='default' filepath='../src/abi.h' line='190' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='backend' type-id='type-id-31' visibility='default' filepath='../src/abi.h' line='191' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='id' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='192' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='uuid' type-id='type-id-73' visibility='default' filepath='../src/abi.h' line='194' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='optional' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='197' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='288'>
+ <var-decl name='optional_addresses' type-id='type-id-37' visibility='default' filepath='../src/abi.h' line='198' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='critical' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='199' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='352'>
+ <var-decl name='dhcp4' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='202' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='dhcp6' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='203' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='dhcp_identifier' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='204' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='dhcp4_overrides' type-id='type-id-43' visibility='default' filepath='../src/abi.h' line='205' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='896'>
+ <var-decl name='dhcp6_overrides' type-id='type-id-43' visibility='default' filepath='../src/abi.h' line='206' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1280'>
+ <var-decl name='accept_ra' type-id='type-id-44' visibility='default' filepath='../src/abi.h' line='207' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1344'>
+ <var-decl name='ip4_addresses' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='208' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1408'>
+ <var-decl name='ip6_addresses' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='209' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1472'>
+ <var-decl name='address_options' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='210' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1536'>
+ <var-decl name='ip6_privacy' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='211' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1568'>
+ <var-decl name='ip6_addr_gen_mode' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='212' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1600'>
+ <var-decl name='ip6_addr_gen_token' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='213' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1664'>
+ <var-decl name='gateway4' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='214' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1728'>
+ <var-decl name='gateway6' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='215' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1792'>
+ <var-decl name='ip4_nameservers' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='216' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1856'>
+ <var-decl name='ip6_nameservers' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='217' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1920'>
+ <var-decl name='search_domains' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='218' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1984'>
+ <var-decl name='routes' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='219' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2048'>
+ <var-decl name='ip_rules' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='220' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2112'>
+ <var-decl name='wireguard_peers' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='221' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2176'>
+ <var-decl name='linklocal' type-id='type-id-74' visibility='default' filepath='../src/abi.h' line='225' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2240'>
+ <var-decl name='bridge' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='228' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2304'>
+ <var-decl name='bond' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='229' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2368'>
+ <var-decl name='peer' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='232' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2432'>
+ <var-decl name='vlan_id' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='235' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2496'>
+ <var-decl name='vlan_link' type-id='type-id-75' visibility='default' filepath='../src/abi.h' line='236' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2560'>
+ <var-decl name='has_vlans' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='237' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2624'>
+ <var-decl name='set_mac' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='240' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2688'>
+ <var-decl name='mtubytes' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='243' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2720'>
+ <var-decl name='ipv6_mtubytes' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='246' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2752'>
+ <var-decl name='set_name' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='249' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2816'>
+ <var-decl name='match' type-id='type-id-76' visibility='default' filepath='../src/abi.h' line='255' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3008'>
+ <var-decl name='has_match' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='256' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3040'>
+ <var-decl name='wake_on_lan' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='257' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3072'>
+ <var-decl name='wowlan' type-id='type-id-48' visibility='default' filepath='../src/abi.h' line='258' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3104'>
+ <var-decl name='emit_lldp' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='259' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3136'>
+ <var-decl name='access_points' type-id='type-id-62' visibility='default' filepath='../src/abi.h' line='262' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3200'>
+ <var-decl name='bond_params' type-id='type-id-77' visibility='default' filepath='../src/abi.h' line='286' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='4416'>
+ <var-decl name='modem_params' type-id='type-id-78' visibility='default' filepath='../src/abi.h' line='300' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='5056'>
+ <var-decl name='bridge_params' type-id='type-id-79' visibility='default' filepath='../src/abi.h' line='311' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='5440'>
+ <var-decl name='custom_bridging' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='312' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='5504'>
+ <var-decl name='tunnel' type-id='type-id-80' visibility='default' filepath='../src/abi.h' line='323' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='5952'>
+ <var-decl name='auth' type-id='type-id-57' visibility='default' filepath='../src/abi.h' line='325' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='6528'>
+ <var-decl name='has_auth' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='326' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='6592'>
+ <var-decl name='sriov_link' type-id='type-id-81' visibility='default' filepath='../src/abi.h' line='330' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='6656'>
+ <var-decl name='sriov_vlan_filter' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='331' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='6688'>
+ <var-decl name='sriov_explicit_vf_count' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='332' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='6720'>
+ <var-decl name='ovs_settings' type-id='type-id-63' visibility='default' filepath='../src/abi.h' line='336' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='7872'>
+ <var-decl name='backend_settings' type-id='type-id-64' visibility='default' filepath='../src/abi.h' line='338' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8192'>
+ <var-decl name='filepath' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='340' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8256'>
+ <var-decl name='tunnel_ttl' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='342' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8320'>
+ <var-decl name='activation_mode' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='345' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8384'>
+ <var-decl name='ignore_carrier' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='348' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8416'>
+ <var-decl name='receive_checksum_offload' type-id='type-id-69' visibility='default' filepath='../src/abi.h' line='351' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8448'>
+ <var-decl name='transmit_checksum_offload' type-id='type-id-69' visibility='default' filepath='../src/abi.h' line='352' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8480'>
+ <var-decl name='tcp_segmentation_offload' type-id='type-id-69' visibility='default' filepath='../src/abi.h' line='353' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8512'>
+ <var-decl name='tcp6_segmentation_offload' type-id='type-id-69' visibility='default' filepath='../src/abi.h' line='354' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8544'>
+ <var-decl name='generic_segmentation_offload' type-id='type-id-69' visibility='default' filepath='../src/abi.h' line='355' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8576'>
+ <var-decl name='generic_receive_offload' type-id='type-id-69' visibility='default' filepath='../src/abi.h' line='356' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8608'>
+ <var-decl name='large_receive_offload' type-id='type-id-69' visibility='default' filepath='../src/abi.h' line='357' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8640'>
+ <var-decl name='_private' type-id='type-id-82' visibility='default' filepath='../src/abi.h' line='359' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8704'>
+ <var-decl name='embedded_switch_mode' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='362' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8768'>
+ <var-decl name='sriov_delay_virtual_functions_rebind' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='363' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8800'>
+ <var-decl name='ib_mode' type-id='type-id-46' visibility='default' filepath='../src/abi.h' line='366' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8832'>
+ <var-decl name='regulatory_domain' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='369' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8896'>
+ <var-decl name='vrf_link' type-id='type-id-75' visibility='default' filepath='../src/abi.h' line='373' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8960'>
+ <var-decl name='vrf_table' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='374' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='8992'>
+ <var-decl name='bridge_neigh_suppress' type-id='type-id-69' visibility='default' filepath='../src/abi.h' line='376' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='9024'>
+ <var-decl name='has_vxlans' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='380' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='9088'>
+ <var-decl name='vxlan' type-id='type-id-83' visibility='default' filepath='../src/abi.h' line='381' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__' size-in-bits='64' is-struct='yes' is-anonymous='yes' visibility='default' filepath='../src/abi.h' line='222' column='1' id='type-id-74'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='ipv4' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='223' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='ipv6' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='224' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__1' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='../src/abi.h' line='250' column='1' id='type-id-76'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='driver' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='252' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='mac' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='253' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='original_name' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='254' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__2' size-in-bits='1216' is-struct='yes' is-anonymous='yes' visibility='default' filepath='../src/abi.h' line='264' column='1' id='type-id-77'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='mode' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='265' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='lacp_rate' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='266' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='monitor_interval' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='267' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='min_links' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='268' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='transmit_hash_policy' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='269' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='selection_logic' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='270' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='all_slaves_active' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='271' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='arp_interval' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='272' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='arp_ip_targets' type-id='type-id-59' visibility='default' filepath='../src/abi.h' line='273' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='576'>
+ <var-decl name='arp_validate' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='274' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='640'>
+ <var-decl name='arp_all_targets' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='275' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='704'>
+ <var-decl name='up_delay' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='276' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='768'>
+ <var-decl name='down_delay' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='277' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='832'>
+ <var-decl name='fail_over_mac_policy' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='278' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='896'>
+ <var-decl name='gratuitous_arp' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='279' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='928'>
+ <var-decl name='packets_per_slave' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='281' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='960'>
+ <var-decl name='primary_reselect_policy' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='282' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1024'>
+ <var-decl name='resend_igmp' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='283' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1088'>
+ <var-decl name='learn_interval' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='284' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1152'>
+ <var-decl name='primary_slave' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='285' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__3' size-in-bits='640' is-struct='yes' is-anonymous='yes' visibility='default' filepath='../src/abi.h' line='289' column='1' id='type-id-78'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='apn' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='290' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='auto_config' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='291' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='device_id' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='292' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='network_id' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='293' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='number' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='294' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='password' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='295' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='pin' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='296' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='sim_id' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='297' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='sim_operator_id' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='298' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='576'>
+ <var-decl name='username' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='299' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__4' size-in-bits='384' is-struct='yes' is-anonymous='yes' visibility='default' filepath='../src/abi.h' line='302' column='1' id='type-id-79'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='ageing_time' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='303' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='priority' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='304' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='96'>
+ <var-decl name='port_priority' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='305' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='forward_delay' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='306' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='hello_time' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='307' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='max_age' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='308' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='path_cost' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='309' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='352'>
+ <var-decl name='stp' type-id='type-id-40' visibility='default' filepath='../src/abi.h' line='310' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__5' size-in-bits='448' is-struct='yes' is-anonymous='yes' visibility='default' filepath='../src/abi.h' line='314' column='1' id='type-id-80'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='mode' type-id='type-id-50' visibility='default' filepath='../src/abi.h' line='315' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='local_ip' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='316' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='remote_ip' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='317' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='input_key' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='318' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='output_key' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='319' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='private_key' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='320' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='fwmark' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='321' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='416'>
+ <var-decl name='port' type-id='type-id-42' visibility='default' filepath='../src/abi.h' line='322' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='private_netdef_data' size-in-bits='64' is-struct='yes' visibility='default' filepath='../src/types.h' line='70' column='1' id='type-id-84'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='dirty_fields' type-id='type-id-62' visibility='default' filepath='../src/types.h' line='71' column='1'/>
+ </data-member>
+ </class-decl>
+ <enum-decl name='NetplanWifiMode' naming-typedef-id='type-id-85' filepath='../src/types.h' line='74' column='1' id='type-id-86'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_WIFI_MODE_INFRASTRUCTURE' value='0'/>
+ <enumerator name='NETPLAN_WIFI_MODE_ADHOC' value='1'/>
+ <enumerator name='NETPLAN_WIFI_MODE_AP' value='2'/>
+ <enumerator name='NETPLAN_WIFI_MODE_OTHER' value='3'/>
+ <enumerator name='NETPLAN_WIFI_MODE_MAX_' value='4'/>
+ </enum-decl>
+ <typedef-decl name='NetplanWifiMode' type-id='type-id-86' filepath='../src/types.h' line='80' column='1' id='type-id-85'/>
+ <class-decl name='NetplanWireguardPeer' size-in-bits='320' is-struct='yes' naming-typedef-id='type-id-87' visibility='default' filepath='../src/types.h' line='82' column='1' id='type-id-88'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='endpoint' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='83' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='public_key' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='84' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='preshared_key' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='85' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='allowed_ips' type-id='type-id-59' visibility='default' filepath='../src/types.h' line='86' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='keepalive' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='87' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanWireguardPeer' type-id='type-id-88' filepath='../src/types.h' line='88' column='1' id='type-id-87'/>
+ <enum-decl name='NetplanWifiBand' naming-typedef-id='type-id-89' filepath='../src/types.h' line='90' column='1' id='type-id-90'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_WIFI_BAND_DEFAULT' value='0'/>
+ <enumerator name='NETPLAN_WIFI_BAND_5' value='1'/>
+ <enumerator name='NETPLAN_WIFI_BAND_24' value='2'/>
+ </enum-decl>
+ <typedef-decl name='NetplanWifiBand' type-id='type-id-90' filepath='../src/types.h' line='94' column='1' id='type-id-89'/>
+ <class-decl name='NetplanAddressOptions' size-in-bits='192' is-struct='yes' naming-typedef-id='type-id-91' visibility='default' filepath='../src/types.h' line='96' column='1' id='type-id-92'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='address' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='97' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='lifetime' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='98' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='label' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='99' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanAddressOptions' type-id='type-id-92' filepath='../src/types.h' line='100' column='1' id='type-id-91'/>
+ <class-decl name='NetplanWifiAccessPoint' size-in-bits='1280' is-struct='yes' naming-typedef-id='type-id-93' visibility='default' filepath='../src/types.h' line='102' column='1' id='type-id-94'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='mode' type-id='type-id-85' visibility='default' filepath='../src/types.h' line='103' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='ssid' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='104' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='band' type-id='type-id-89' visibility='default' filepath='../src/types.h' line='105' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='bssid' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='106' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='hidden' type-id='type-id-40' visibility='default' filepath='../src/types.h' line='107' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='288'>
+ <var-decl name='channel' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='108' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='auth' type-id='type-id-57' visibility='default' filepath='../src/types.h' line='110' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='896'>
+ <var-decl name='has_auth' type-id='type-id-40' visibility='default' filepath='../src/types.h' line='111' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='960'>
+ <var-decl name='backend_settings' type-id='type-id-64' visibility='default' filepath='../src/types.h' line='113' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanWifiAccessPoint' type-id='type-id-94' filepath='../src/types.h' line='114' column='1' id='type-id-93'/>
+ <class-decl name='NetplanIPRoute' size-in-bits='640' is-struct='yes' naming-typedef-id='type-id-95' visibility='default' filepath='../src/types.h' line='116' column='1' id='type-id-96'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='family' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='117' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='type' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='118' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='scope' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='119' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='table' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='120' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='from' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='122' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='to' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='123' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='via' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='124' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='onlink' type-id='type-id-40' visibility='default' filepath='../src/types.h' line='126' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='480'>
+ <var-decl name='metric' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='130' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='mtubytes' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='132' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='544'>
+ <var-decl name='congestion_window' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='133' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='576'>
+ <var-decl name='advertised_receive_window' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='134' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanIPRoute' type-id='type-id-96' filepath='../src/types.h' line='135' column='1' id='type-id-95'/>
+ <class-decl name='NetplanIPRule' size-in-bits='320' is-struct='yes' naming-typedef-id='type-id-97' visibility='default' filepath='../src/types.h' line='137' column='1' id='type-id-98'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='family' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='138' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='from' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='140' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='to' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='141' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='table' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='144' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='224'>
+ <var-decl name='priority' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='145' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='fwmark' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='147' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='288'>
+ <var-decl name='tos' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='149' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='NetplanIPRule' type-id='type-id-98' filepath='../src/types.h' line='150' column='1' id='type-id-97'/>
+ <class-decl name='netplan_vxlan' size-in-bits='576' is-struct='yes' visibility='default' filepath='../src/types.h' line='152' column='1' id='type-id-71'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='link' type-id='type-id-75' visibility='default' filepath='../src/types.h' line='153' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='vni' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='154' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='96'>
+ <var-decl name='ageing' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='155' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='limit' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='156' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='160'>
+ <var-decl name='tos' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='157' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='flow_label' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='158' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='224'>
+ <var-decl name='source_port_min' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='159' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='source_port_max' type-id='type-id-42' visibility='default' filepath='../src/types.h' line='160' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='288'>
+ <var-decl name='mac_learning' type-id='type-id-40' visibility='default' filepath='../src/types.h' line='161' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='arp_proxy' type-id='type-id-40' visibility='default' filepath='../src/types.h' line='162' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='352'>
+ <var-decl name='short_circuit' type-id='type-id-40' visibility='default' filepath='../src/types.h' line='163' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='independent' type-id='type-id-40' visibility='default' filepath='../src/types.h' line='164' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='416'>
+ <var-decl name='notifications' type-id='type-id-36' visibility='default' filepath='../src/types.h' line='165' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='checksums' type-id='type-id-36' visibility='default' filepath='../src/types.h' line='166' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='480'>
+ <var-decl name='extensions' type-id='type-id-36' visibility='default' filepath='../src/types.h' line='167' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='do_not_fragment' type-id='type-id-69' visibility='default' filepath='../src/types.h' line='168' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='netplan_state' size-in-bits='1408' is-struct='yes' visibility='default' filepath='../src/types.h' line='171' column='1' id='type-id-29'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='netdefs' type-id='type-id-62' visibility='default' filepath='../src/types.h' line='177' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='netdefs_ordered' type-id='type-id-99' visibility='default' filepath='../src/types.h' line='178' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='backend' type-id='type-id-31' visibility='default' filepath='../src/types.h' line='179' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='ovs_settings' type-id='type-id-63' visibility='default' filepath='../src/types.h' line='180' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1344'>
+ <var-decl name='sources' type-id='type-id-62' visibility='default' filepath='../src/types.h' line='184' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='netplan_parser' size-in-bits='3136' is-struct='yes' visibility='default' filepath='../src/types.h' line='187' column='1' id='type-id-34'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='doc' type-id='type-id-100' visibility='default' filepath='../src/types.h' line='188' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='832'>
+ <var-decl name='parsed_defs' type-id='type-id-62' visibility='default' filepath='../src/types.h' line='191' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='896'>
+ <var-decl name='ordered' type-id='type-id-99' visibility='default' filepath='../src/types.h' line='194' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='960'>
+ <var-decl name='global_backend' type-id='type-id-31' visibility='default' filepath='../src/types.h' line='195' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1024'>
+ <var-decl name='global_ovs_settings' type-id='type-id-63' visibility='default' filepath='../src/types.h' line='196' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2176'>
+ <var-decl name='sources' type-id='type-id-62' visibility='default' filepath='../src/types.h' line='199' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2240'>
+ <var-decl name='current' type-id='type-id-101' visibility='default' filepath='../src/types.h' line='220' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2880'>
+ <var-decl name='missing_id' type-id='type-id-62' visibility='default' filepath='../src/types.h' line='230' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2944'>
+ <var-decl name='ids_in_file' type-id='type-id-62' visibility='default' filepath='../src/types.h' line='238' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3008'>
+ <var-decl name='missing_ids_found' type-id='type-id-16' visibility='default' filepath='../src/types.h' line='239' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3072'>
+ <var-decl name='null_fields' type-id='type-id-62' visibility='default' filepath='../src/types.h' line='242' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__13' size-in-bits='640' is-struct='yes' is-anonymous='yes' visibility='default' filepath='../src/types.h' line='202' column='1' id='type-id-101'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='netdef' type-id='type-id-75' visibility='default' filepath='../src/types.h' line='204' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='auth' type-id='type-id-102' visibility='default' filepath='../src/types.h' line='205' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='access_point' type-id='type-id-103' visibility='default' filepath='../src/types.h' line='208' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='wireguard_peer' type-id='type-id-104' visibility='default' filepath='../src/types.h' line='209' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='addr_options' type-id='type-id-105' visibility='default' filepath='../src/types.h' line='210' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='route' type-id='type-id-106' visibility='default' filepath='../src/types.h' line='211' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='ip_rule' type-id='type-id-107' visibility='default' filepath='../src/types.h' line='212' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='vxlan' type-id='type-id-83' visibility='default' filepath='../src/types.h' line='213' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='filepath' type-id='type-id-108' visibility='default' filepath='../src/types.h' line='214' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='576'>
+ <var-decl name='backend' type-id='type-id-31' visibility='default' filepath='../src/types.h' line='219' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GArray' type-id='type-id-109' filepath='/usr/include/glib-2.0/glib/garray.h' line='37' column='1' id='type-id-110'/>
+ <class-decl name='_GArray' size-in-bits='128' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/glib/garray.h' line='41' column='1' id='type-id-109'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='data' type-id='type-id-111' visibility='default' filepath='/usr/include/glib-2.0/glib/garray.h' line='43' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='len' type-id='type-id-42' visibility='default' filepath='/usr/include/glib-2.0/glib/garray.h' line='44' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GData' type-id='type-id-7' filepath='/usr/include/glib-2.0/glib/gdataset.h' line='36' column='1' id='type-id-112'/>
+ <typedef-decl name='GError' type-id='type-id-113' filepath='/usr/include/glib-2.0/glib/gerror.h' line='41' column='1' id='type-id-114'/>
+ <class-decl name='_GError' size-in-bits='128' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/glib/gerror.h' line='43' column='1' id='type-id-113'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='domain' type-id='type-id-115' visibility='default' filepath='/usr/include/glib-2.0/glib/gerror.h' line='45' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='code' type-id='type-id-116' visibility='default' filepath='/usr/include/glib-2.0/glib/gerror.h' line='46' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='message' type-id='type-id-111' visibility='default' filepath='/usr/include/glib-2.0/glib/gerror.h' line='47' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GHashTable' type-id='type-id-8' filepath='/usr/include/glib-2.0/glib/ghash.h' line='37' column='1' id='type-id-117'/>
+ <typedef-decl name='GList' type-id='type-id-118' filepath='/usr/include/glib-2.0/glib/glist.h' line='37' column='1' id='type-id-119'/>
+ <class-decl name='_GList' size-in-bits='192' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/glib/glist.h' line='39' column='1' id='type-id-118'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='data' type-id='type-id-120' visibility='default' filepath='/usr/include/glib-2.0/glib/glist.h' line='41' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='next' type-id='type-id-99' visibility='default' filepath='/usr/include/glib-2.0/glib/glist.h' line='42' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='prev' type-id='type-id-99' visibility='default' filepath='/usr/include/glib-2.0/glib/glist.h' line='43' column='1'/>
+ </data-member>
+ </class-decl>
+ <enum-decl name='GLogLevelFlags' naming-typedef-id='type-id-121' filepath='/usr/include/glib-2.0/glib/gmessages.h' line='54' column='1' id='type-id-122'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='G_LOG_FLAG_RECURSION' value='1'/>
+ <enumerator name='G_LOG_FLAG_FATAL' value='2'/>
+ <enumerator name='G_LOG_LEVEL_ERROR' value='4'/>
+ <enumerator name='G_LOG_LEVEL_CRITICAL' value='8'/>
+ <enumerator name='G_LOG_LEVEL_WARNING' value='16'/>
+ <enumerator name='G_LOG_LEVEL_MESSAGE' value='32'/>
+ <enumerator name='G_LOG_LEVEL_INFO' value='64'/>
+ <enumerator name='G_LOG_LEVEL_DEBUG' value='128'/>
+ <enumerator name='G_LOG_LEVEL_MASK' value='-4'/>
+ </enum-decl>
+ <typedef-decl name='GLogLevelFlags' type-id='type-id-122' filepath='/usr/include/glib-2.0/glib/gmessages.h' line='68' column='1' id='type-id-121'/>
+ <typedef-decl name='GQuark' type-id='type-id-123' filepath='/usr/include/glib-2.0/glib/gquark.h' line='36' column='1' id='type-id-115'/>
+ <typedef-decl name='gchar' type-id='type-id-1' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='46' column='1' id='type-id-124'/>
+ <typedef-decl name='gint' type-id='type-id-16' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='49' column='1' id='type-id-116'/>
+ <typedef-decl name='gboolean' type-id='type-id-116' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='50' column='1' id='type-id-40'/>
+ <typedef-decl name='guint' type-id='type-id-23' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='55' column='1' id='type-id-42'/>
+ <typedef-decl name='gpointer' type-id='type-id-125' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='103' column='1' id='type-id-120'/>
+ <typedef-decl name='gconstpointer' type-id='type-id-125' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='104' column='1' id='type-id-126'/>
+ <typedef-decl name='uuid_t' type-id='type-id-21' filepath='/usr/include/uuid/uuid.h' line='44' column='1' id='type-id-73'/>
+ <typedef-decl name='__off_t' type-id='type-id-17' filepath='/usr/include/x86_64-linux-gnu/bits/types.h' line='152' column='1' id='type-id-127'/>
+ <typedef-decl name='__off64_t' type-id='type-id-17' filepath='/usr/include/x86_64-linux-gnu/bits/types.h' line='153' column='1' id='type-id-128'/>
+ <typedef-decl name='FILE' type-id='type-id-129' filepath='/usr/include/x86_64-linux-gnu/bits/types/FILE.h' line='7' column='1' id='type-id-130'/>
+ <typedef-decl name='_IO_lock_t' type-id='type-id-26' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='43' column='1' id='type-id-131'/>
+ <class-decl name='_IO_FILE' size-in-bits='1728' is-struct='yes' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='49' column='1' id='type-id-129'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='_flags' type-id='type-id-16' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='51' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='_IO_read_ptr' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='54' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='_IO_read_end' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='55' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='_IO_read_base' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='56' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='_IO_write_base' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='57' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='_IO_write_ptr' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='58' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='_IO_write_end' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='59' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='_IO_buf_base' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='60' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='_IO_buf_end' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='61' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='576'>
+ <var-decl name='_IO_save_base' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='64' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='640'>
+ <var-decl name='_IO_backup_base' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='65' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='704'>
+ <var-decl name='_IO_save_end' type-id='type-id-41' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='66' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='768'>
+ <var-decl name='_markers' type-id='type-id-132' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='68' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='832'>
+ <var-decl name='_chain' type-id='type-id-133' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='70' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='896'>
+ <var-decl name='_fileno' type-id='type-id-16' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='72' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='928'>
+ <var-decl name='_flags2' type-id='type-id-16' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='73' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='960'>
+ <var-decl name='_old_offset' type-id='type-id-127' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='74' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1024'>
+ <var-decl name='_cur_column' type-id='type-id-24' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='77' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1040'>
+ <var-decl name='_vtable_offset' type-id='type-id-18' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='78' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1048'>
+ <var-decl name='_shortbuf' type-id='type-id-2' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='79' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1088'>
+ <var-decl name='_lock' type-id='type-id-134' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='81' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1152'>
+ <var-decl name='_offset' type-id='type-id-128' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='89' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1216'>
+ <var-decl name='_codecvt' type-id='type-id-135' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='91' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1280'>
+ <var-decl name='_wide_data' type-id='type-id-136' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='92' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1344'>
+ <var-decl name='_freeres_list' type-id='type-id-133' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='93' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1408'>
+ <var-decl name='_freeres_buf' type-id='type-id-125' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='94' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1472'>
+ <var-decl name='__pad5' type-id='type-id-137' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='95' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1536'>
+ <var-decl name='_mode' type-id='type-id-16' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='96' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1568'>
+ <var-decl name='_unused2' type-id='type-id-5' visibility='default' filepath='/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h' line='98' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_char_t' type-id='type-id-20' filepath='/usr/include/yaml.h' line='80' column='1' id='type-id-138'/>
+ <class-decl name='yaml_version_directive_s' size-in-bits='64' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='83' column='1' id='type-id-139'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='major' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='85' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='minor' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='87' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_version_directive_t' type-id='type-id-139' filepath='/usr/include/yaml.h' line='88' column='1' id='type-id-140'/>
+ <class-decl name='yaml_tag_directive_s' size-in-bits='128' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='91' column='1' id='type-id-141'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='handle' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='93' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='prefix' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='95' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_tag_directive_t' type-id='type-id-141' filepath='/usr/include/yaml.h' line='96' column='1' id='type-id-143'/>
+ <class-decl name='yaml_mark_s' size-in-bits='192' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='147' column='1' id='type-id-144'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='index' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='149' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='line' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='152' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='column' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='155' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_mark_t' type-id='type-id-144' filepath='/usr/include/yaml.h' line='156' column='1' id='type-id-145'/>
+ <enum-decl name='yaml_scalar_style_e' filepath='/usr/include/yaml.h' line='166' column='1' id='type-id-146'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_ANY_SCALAR_STYLE' value='0'/>
+ <enumerator name='YAML_PLAIN_SCALAR_STYLE' value='1'/>
+ <enumerator name='YAML_SINGLE_QUOTED_SCALAR_STYLE' value='2'/>
+ <enumerator name='YAML_DOUBLE_QUOTED_SCALAR_STYLE' value='3'/>
+ <enumerator name='YAML_LITERAL_SCALAR_STYLE' value='4'/>
+ <enumerator name='YAML_FOLDED_SCALAR_STYLE' value='5'/>
+ </enum-decl>
+ <typedef-decl name='yaml_scalar_style_t' type-id='type-id-146' filepath='/usr/include/yaml.h' line='182' column='1' id='type-id-147'/>
+ <enum-decl name='yaml_sequence_style_e' filepath='/usr/include/yaml.h' line='185' column='1' id='type-id-148'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_ANY_SEQUENCE_STYLE' value='0'/>
+ <enumerator name='YAML_BLOCK_SEQUENCE_STYLE' value='1'/>
+ <enumerator name='YAML_FLOW_SEQUENCE_STYLE' value='2'/>
+ </enum-decl>
+ <typedef-decl name='yaml_sequence_style_t' type-id='type-id-148' filepath='/usr/include/yaml.h' line='193' column='1' id='type-id-149'/>
+ <enum-decl name='yaml_mapping_style_e' filepath='/usr/include/yaml.h' line='196' column='1' id='type-id-150'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_ANY_MAPPING_STYLE' value='0'/>
+ <enumerator name='YAML_BLOCK_MAPPING_STYLE' value='1'/>
+ <enumerator name='YAML_FLOW_MAPPING_STYLE' value='2'/>
+ </enum-decl>
+ <typedef-decl name='yaml_mapping_style_t' type-id='type-id-150' filepath='/usr/include/yaml.h' line='205' column='1' id='type-id-151'/>
+ <enum-decl name='yaml_node_type_e' filepath='/usr/include/yaml.h' line='692' column='1' id='type-id-152'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_NO_NODE' value='0'/>
+ <enumerator name='YAML_SCALAR_NODE' value='1'/>
+ <enumerator name='YAML_SEQUENCE_NODE' value='2'/>
+ <enumerator name='YAML_MAPPING_NODE' value='3'/>
+ </enum-decl>
+ <typedef-decl name='yaml_node_type_t' type-id='type-id-152' filepath='/usr/include/yaml.h' line='702' column='1' id='type-id-153'/>
+ <typedef-decl name='yaml_node_t' type-id='type-id-154' filepath='/usr/include/yaml.h' line='705' column='1' id='type-id-155'/>
+ <typedef-decl name='yaml_node_item_t' type-id='type-id-16' filepath='/usr/include/yaml.h' line='708' column='1' id='type-id-156'/>
+ <class-decl name='yaml_node_pair_s' size-in-bits='64' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='711' column='1' id='type-id-157'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='key' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='713' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='value' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='715' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_node_pair_t' type-id='type-id-157' filepath='/usr/include/yaml.h' line='716' column='1' id='type-id-158'/>
+ <class-decl name='yaml_node_s' size-in-bits='768' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='719' column='1' id='type-id-154'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='type' type-id='type-id-153' visibility='default' filepath='/usr/include/yaml.h' line='722' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='tag' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='725' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='data' type-id='type-id-159' visibility='default' filepath='/usr/include/yaml.h' line='770' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='start_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='773' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='576'>
+ <var-decl name='end_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='775' column='1'/>
+ </data-member>
+ </class-decl>
+ <union-decl name='__anonymous_union__' size-in-bits='256' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='728' column='1' id='type-id-159'>
+ <data-member access='public'>
+ <var-decl name='scalar' type-id='type-id-160' visibility='default' filepath='/usr/include/yaml.h' line='738' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='sequence' type-id='type-id-161' visibility='default' filepath='/usr/include/yaml.h' line='753' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='mapping' type-id='type-id-162' visibility='default' filepath='/usr/include/yaml.h' line='768' column='1'/>
+ </data-member>
+ </union-decl>
+ <class-decl name='__anonymous_struct__7' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='731' column='1' id='type-id-160'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='value' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='733' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='length' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='735' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='style' type-id='type-id-147' visibility='default' filepath='/usr/include/yaml.h' line='737' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__8' size-in-bits='256' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='741' column='1' id='type-id-161'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='items' type-id='type-id-163' visibility='default' filepath='/usr/include/yaml.h' line='750' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='style' type-id='type-id-149' visibility='default' filepath='/usr/include/yaml.h' line='752' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__9' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='743' column='1' id='type-id-163'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-164' visibility='default' filepath='/usr/include/yaml.h' line='745' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-164' visibility='default' filepath='/usr/include/yaml.h' line='747' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-164' visibility='default' filepath='/usr/include/yaml.h' line='749' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__10' size-in-bits='256' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='756' column='1' id='type-id-162'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='pairs' type-id='type-id-165' visibility='default' filepath='/usr/include/yaml.h' line='765' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='style' type-id='type-id-151' visibility='default' filepath='/usr/include/yaml.h' line='767' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__11' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='758' column='1' id='type-id-165'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-166' visibility='default' filepath='/usr/include/yaml.h' line='760' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-166' visibility='default' filepath='/usr/include/yaml.h' line='762' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-166' visibility='default' filepath='/usr/include/yaml.h' line='764' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='yaml_document_s' size-in-bits='832' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='780' column='1' id='type-id-167'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='nodes' type-id='type-id-168' visibility='default' filepath='/usr/include/yaml.h' line='790' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='version_directive' type-id='type-id-169' visibility='default' filepath='/usr/include/yaml.h' line='793' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='tag_directives' type-id='type-id-170' visibility='default' filepath='/usr/include/yaml.h' line='801' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='start_implicit' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='804' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='416'>
+ <var-decl name='end_implicit' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='806' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='start_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='809' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='640'>
+ <var-decl name='end_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='811' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__6' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='783' column='1' id='type-id-168'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-171' visibility='default' filepath='/usr/include/yaml.h' line='785' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-171' visibility='default' filepath='/usr/include/yaml.h' line='787' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-171' visibility='default' filepath='/usr/include/yaml.h' line='789' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__12' size-in-bits='128' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='796' column='1' id='type-id-170'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-172' visibility='default' filepath='/usr/include/yaml.h' line='798' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-172' visibility='default' filepath='/usr/include/yaml.h' line='800' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_document_t' type-id='type-id-167' filepath='/usr/include/yaml.h' line='813' column='1' id='type-id-100'/>
+ <typedef-decl name='size_t' type-id='type-id-3' filepath='/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h' line='209' column='1' id='type-id-137'/>
+ <typedef-decl name='guint32' type-id='type-id-23' filepath='/usr/lib/x86_64-linux-gnu/glib-2.0/include/glibconfig.h' line='54' column='1' id='type-id-123'/>
+ <pointer-type-def type-id='type-id-130' size-in-bits='64' id='type-id-173'/>
+ <pointer-type-def type-id='type-id-110' size-in-bits='64' id='type-id-59'/>
+ <pointer-type-def type-id='type-id-112' size-in-bits='64' id='type-id-68'/>
+ <pointer-type-def type-id='type-id-114' size-in-bits='64' id='type-id-174'/>
+ <pointer-type-def type-id='type-id-174' size-in-bits='64' id='type-id-175'/>
+ <pointer-type-def type-id='type-id-117' size-in-bits='64' id='type-id-62'/>
+ <pointer-type-def type-id='type-id-119' size-in-bits='64' id='type-id-99'/>
+ <pointer-type-def type-id='type-id-91' size-in-bits='64' id='type-id-105'/>
+ <pointer-type-def type-id='type-id-57' size-in-bits='64' id='type-id-102'/>
+ <pointer-type-def type-id='type-id-95' size-in-bits='64' id='type-id-106'/>
+ <pointer-type-def type-id='type-id-97' size-in-bits='64' id='type-id-107'/>
+ <pointer-type-def type-id='type-id-28' size-in-bits='64' id='type-id-75'/>
+ <pointer-type-def type-id='type-id-35' size-in-bits='64' id='type-id-176'/>
+ <pointer-type-def type-id='type-id-176' size-in-bits='64' id='type-id-177'/>
+ <pointer-type-def type-id='type-id-30' size-in-bits='64' id='type-id-15'/>
+ <pointer-type-def type-id='type-id-15' size-in-bits='64' id='type-id-178'/>
+ <pointer-type-def type-id='type-id-72' size-in-bits='64' id='type-id-83'/>
+ <pointer-type-def type-id='type-id-93' size-in-bits='64' id='type-id-103'/>
+ <pointer-type-def type-id='type-id-87' size-in-bits='64' id='type-id-104'/>
+ <pointer-type-def type-id='type-id-129' size-in-bits='64' id='type-id-133'/>
+ <pointer-type-def type-id='type-id-9' size-in-bits='64' id='type-id-135'/>
+ <pointer-type-def type-id='type-id-131' size-in-bits='64' id='type-id-134'/>
+ <pointer-type-def type-id='type-id-10' size-in-bits='64' id='type-id-132'/>
+ <pointer-type-def type-id='type-id-11' size-in-bits='64' id='type-id-136'/>
+ <pointer-type-def type-id='type-id-1' size-in-bits='64' id='type-id-41'/>
+ <qualified-type-def type-id='type-id-28' const='yes' id='type-id-179'/>
+ <pointer-type-def type-id='type-id-179' size-in-bits='64' id='type-id-180'/>
+ <qualified-type-def type-id='type-id-30' const='yes' id='type-id-181'/>
+ <pointer-type-def type-id='type-id-181' size-in-bits='64' id='type-id-182'/>
+ <qualified-type-def type-id='type-id-1' const='yes' id='type-id-183'/>
+ <pointer-type-def type-id='type-id-183' size-in-bits='64' id='type-id-108'/>
+ <qualified-type-def type-id='type-id-124' const='yes' id='type-id-184'/>
+ <pointer-type-def type-id='type-id-184' size-in-bits='64' id='type-id-185'/>
+ <pointer-type-def type-id='type-id-124' size-in-bits='64' id='type-id-111'/>
+ <pointer-type-def type-id='type-id-12' size-in-bits='64' id='type-id-186'/>
+ <pointer-type-def type-id='type-id-27' size-in-bits='64' id='type-id-81'/>
+ <pointer-type-def type-id='type-id-84' size-in-bits='64' id='type-id-82'/>
+ <pointer-type-def type-id='type-id-26' size-in-bits='64' id='type-id-125'/>
+ <pointer-type-def type-id='type-id-138' size-in-bits='64' id='type-id-142'/>
+ <pointer-type-def type-id='type-id-156' size-in-bits='64' id='type-id-164'/>
+ <pointer-type-def type-id='type-id-158' size-in-bits='64' id='type-id-166'/>
+ <pointer-type-def type-id='type-id-155' size-in-bits='64' id='type-id-171'/>
+ <pointer-type-def type-id='type-id-143' size-in-bits='64' id='type-id-172'/>
+ <pointer-type-def type-id='type-id-140' size-in-bits='64' id='type-id-169'/>
+ <class-decl name='_GData' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-7'/>
+ <class-decl name='_GHashTable' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-8'/>
+ <class-decl name='_IO_codecvt' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-9'/>
+ <class-decl name='_IO_marker' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-10'/>
+ <class-decl name='_IO_wide_data' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-11'/>
+ <function-decl name='netplan_state_new' mangled-name='netplan_state_new' filepath='../include/netplan.h' line='45' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_new'>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-15'/>
+ </function-decl>
+ <function-decl name='netplan_state_reset' mangled-name='netplan_state_reset' filepath='../include/netplan.h' line='48' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_reset'>
+ <parameter type-id='type-id-15'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='netplan_state_clear' mangled-name='netplan_state_clear' filepath='../include/netplan.h' line='51' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_clear'>
+ <parameter type-id='type-id-178'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='netplan_state_get_backend' mangled-name='netplan_state_get_backend' filepath='../include/netplan.h' line='54' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_get_backend'>
+ <parameter type-id='type-id-182'/>
+ <return type-id='type-id-31'/>
+ </function-decl>
+ <function-decl name='netplan_state_get_netdefs_size' mangled-name='netplan_state_get_netdefs_size' filepath='../include/netplan.h' line='57' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_get_netdefs_size'>
+ <parameter type-id='type-id-182'/>
+ <return type-id='type-id-42'/>
+ </function-decl>
+ <function-decl name='netplan_state_get_netdef' mangled-name='netplan_state_get_netdef' filepath='../include/netplan.h' line='60' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_get_netdef'>
+ <parameter type-id='type-id-182'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-75'/>
+ </function-decl>
+ <function-decl name='netplan_state_dump_yaml' mangled-name='netplan_state_dump_yaml' filepath='../include/netplan.h' line='86' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_dump_yaml'>
+ <parameter type-id='type-id-182'/>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_write_yaml' mangled-name='netplan_netdef_write_yaml' filepath='../include/netplan.h' line='92' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_write_yaml'>
+ <parameter type-id='type-id-182'/>
+ <parameter type-id='type-id-180'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_parser_load_keyfile' mangled-name='netplan_parser_load_keyfile' filepath='../include/parse-nm.h' line='26' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parser_load_keyfile'>
+ <parameter type-id='type-id-176'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_parser_new' mangled-name='netplan_parser_new' filepath='../include/parse.h' line='52' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parser_new'>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-176'/>
+ </function-decl>
+ <function-decl name='netplan_parser_reset' mangled-name='netplan_parser_reset' filepath='../include/parse.h' line='55' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parser_reset'>
+ <parameter type-id='type-id-176'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='netplan_parser_clear' mangled-name='netplan_parser_clear' filepath='../include/parse.h' line='58' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parser_clear'>
+ <parameter type-id='type-id-177'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='netplan_parser_load_yaml' mangled-name='netplan_parser_load_yaml' filepath='../include/parse.h' line='61' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parser_load_yaml'>
+ <parameter type-id='type-id-176'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_state_import_parser_results' mangled-name='netplan_state_import_parser_results' filepath='../include/parse.h' line='64' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_import_parser_results'>
+ <parameter type-id='type-id-15'/>
+ <parameter type-id='type-id-176'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <var-decl name='global_state' type-id='type-id-30' mangled-name='global_state' visibility='default' filepath='../src/abi_compat.c' line='64' column='1' elf-symbol-id='global_state'/>
+ <function-decl name='netplan_get_global_backend' mangled-name='netplan_get_global_backend' filepath='../src/abi_compat.c' line='68' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_get_global_backend'>
+ <return type-id='type-id-31'/>
+ </function-decl>
+ <function-decl name='netplan_clear_netdefs' mangled-name='netplan_clear_netdefs' filepath='../src/abi_compat.c' line='79' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_clear_netdefs'>
+ <return type-id='type-id-42'/>
+ </function-decl>
+ <function-decl name='netplan_parse_yaml' mangled-name='netplan_parse_yaml' filepath='../src/abi_compat.c' line='89' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parse_yaml'>
+ <parameter type-id='type-id-108' name='filename' filepath='../src/abi_compat.c' line='89' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/abi_compat.c' line='89' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_finish_parse' mangled-name='netplan_finish_parse' filepath='../src/abi_compat.c' line='98' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_finish_parse'>
+ <parameter type-id='type-id-175' name='error' filepath='../src/abi_compat.c' line='98' column='1'/>
+ <return type-id='type-id-62'/>
+ </function-decl>
+ <function-decl name='write_netplan_conf' mangled-name='write_netplan_conf' filepath='../src/abi_compat.c' line='112' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='write_netplan_conf'>
+ <parameter type-id='type-id-180' name='def' filepath='../src/abi_compat.c' line='112' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/abi_compat.c' line='112' column='1'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='write_netplan_conf_full' mangled-name='write_netplan_conf_full' filepath='../src/abi_compat.c' line='124' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='write_netplan_conf_full'>
+ <parameter type-id='type-id-108' name='file_hint' filepath='../src/abi_compat.c' line='124' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/abi_compat.c' line='124' column='1'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='netplan_parse_keyfile' mangled-name='netplan_parse_keyfile' filepath='../src/abi_compat.c' line='138' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parse_keyfile'>
+ <parameter type-id='type-id-108' name='filename' filepath='../src/abi_compat.c' line='138' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/abi_compat.c' line='138' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='process_input_file' mangled-name='process_input_file' filepath='../src/abi_compat.c' line='144' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='process_input_file'>
+ <parameter type-id='type-id-108' name='f' filepath='../src/abi_compat.c' line='144' column='1'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='process_yaml_hierarchy' mangled-name='process_yaml_hierarchy' filepath='../src/abi_compat.c' line='156' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='process_yaml_hierarchy'>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/abi_compat.c' line='156' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='_write_netplan_conf' mangled-name='_write_netplan_conf' filepath='../src/abi_compat.c' line='171' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_write_netplan_conf'>
+ <parameter type-id='type-id-108' name='netdef_id' filepath='../src/abi_compat.c' line='171' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/abi_compat.c' line='171' column='1'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='netplan_get_filename_by_id' mangled-name='netplan_get_filename_by_id' filepath='../src/abi_compat.c' line='190' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_get_filename_by_id'>
+ <parameter type-id='type-id-108' name='netdef_id' filepath='../src/abi_compat.c' line='190' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/abi_compat.c' line='190' column='1'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='_netplan_state_new_netdef_pertype_iter' mangled-name='_netplan_state_new_netdef_pertype_iter' filepath='../src/abi_compat.c' line='212' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_state_new_netdef_pertype_iter'>
+ <parameter type-id='type-id-15'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-186'/>
+ </function-decl>
+ <function-decl name='_netplan_iter_defs_per_devtype_init' mangled-name='_netplan_iter_defs_per_devtype_init' filepath='../src/abi_compat.c' line='215' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_iter_defs_per_devtype_init'>
+ <parameter type-id='type-id-108' name='devtype' filepath='../src/abi_compat.c' line='215' column='1'/>
+ <return type-id='type-id-186'/>
+ </function-decl>
+ <function-decl name='_netplan_netdef_id' mangled-name='_netplan_netdef_id' filepath='../src/abi_compat.c' line='221' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_netdef_id'>
+ <parameter type-id='type-id-75' name='netdef' filepath='../src/abi_compat.c' line='221' column='1'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_get_filename' mangled-name='netplan_netdef_get_filename' filepath='../src/abi_compat.c' line='227' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_get_filename'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/abi_compat.c' line='227' column='1'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_state_has_nondefault_globals' filepath='../src/types.h' line='282' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-182'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_parser_load_yaml_hierarchy' mangled-name='netplan_parser_load_yaml_hierarchy' filepath='../src/util-internal.h' line='84' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parser_load_yaml_hierarchy'>
+ <parameter type-id='type-id-176'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_build_path' filepath='/usr/include/glib-2.0/glib/gfileutils.h' line='165' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_hash_table_lookup' filepath='/usr/include/glib-2.0/glib/ghash.h' line='95' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-120'/>
+ </function-decl>
+ <function-decl name='g_free' filepath='/usr/include/glib-2.0/glib/gmem.h' line='72' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_log' filepath='/usr/include/glib-2.0/glib/gmessages.h' line='103' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-121'/>
+ <parameter type-id='type-id-185'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_fprintf' filepath='/usr/include/glib-2.0/glib/gprintf.h' line='31' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-173'/>
+ <parameter type-id='type-id-185'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-116'/>
+ </function-decl>
+ <function-decl name='g_strdup' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='217' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_assertion_message_expr' filepath='/usr/include/glib-2.0/glib/gtestutils.h' line='540' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='exit' filepath='/usr/include/stdlib.h' line='624' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='close' filepath='/usr/include/unistd.h' line='358' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/error.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <class-decl name='_GBufferedInputStreamPrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-187'/>
+ <class-decl name='_GCancellablePrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-188'/>
+ <class-decl name='_GDataInputStreamPrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-189'/>
+ <class-decl name='_GFile' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-190'/>
+ <class-decl name='_GFileInputStreamPrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-191'/>
+ <class-decl name='_GInputStreamPrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-192'/>
+ <class-decl name='__va_list_tag' size-in-bits='192' is-struct='yes' visibility='default' id='type-id-193'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='gp_offset' type-id='type-id-23' visibility='default'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='fp_offset' type-id='type-id-23' visibility='default'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='overflow_arg_area' type-id='type-id-125' visibility='default'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='reg_save_area' type-id='type-id-125' visibility='default'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GBufferedInputStreamPrivate' type-id='type-id-187' filepath='/usr/include/glib-2.0/gio/gbufferedinputstream.h' line='45' column='1' id='type-id-194'/>
+ <class-decl name='_GBufferedInputStream' size-in-bits='384' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/gio/gbufferedinputstream.h' line='47' column='1' id='type-id-195'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='parent_instance' type-id='type-id-196' visibility='default' filepath='/usr/include/glib-2.0/gio/gbufferedinputstream.h' line='49' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='priv' type-id='type-id-197' visibility='default' filepath='/usr/include/glib-2.0/gio/gbufferedinputstream.h' line='52' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GCancellablePrivate' type-id='type-id-188' filepath='/usr/include/glib-2.0/gio/gcancellable.h' line='45' column='1' id='type-id-198'/>
+ <class-decl name='_GCancellable' size-in-bits='256' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/gio/gcancellable.h' line='47' column='1' id='type-id-199'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='parent_instance' type-id='type-id-200' visibility='default' filepath='/usr/include/glib-2.0/gio/gcancellable.h' line='49' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='priv' type-id='type-id-201' visibility='default' filepath='/usr/include/glib-2.0/gio/gcancellable.h' line='52' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GDataInputStreamPrivate' type-id='type-id-189' filepath='/usr/include/glib-2.0/gio/gdatainputstream.h' line='46' column='1' id='type-id-202'/>
+ <class-decl name='_GDataInputStream' size-in-bits='448' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/gio/gdatainputstream.h' line='48' column='1' id='type-id-203'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='parent_instance' type-id='type-id-204' visibility='default' filepath='/usr/include/glib-2.0/gio/gdatainputstream.h' line='50' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='priv' type-id='type-id-205' visibility='default' filepath='/usr/include/glib-2.0/gio/gdatainputstream.h' line='53' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GFileInputStreamPrivate' type-id='type-id-191' filepath='/usr/include/glib-2.0/gio/gfileinputstream.h' line='48' column='1' id='type-id-206'/>
+ <class-decl name='_GFileInputStream' size-in-bits='320' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/gio/gfileinputstream.h' line='50' column='1' id='type-id-207'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='parent_instance' type-id='type-id-208' visibility='default' filepath='/usr/include/glib-2.0/gio/gfileinputstream.h' line='52' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='priv' type-id='type-id-209' visibility='default' filepath='/usr/include/glib-2.0/gio/gfileinputstream.h' line='55' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='_GFilterInputStream' size-in-bits='320' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/gio/gfilterinputstream.h' line='46' column='1' id='type-id-210'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='parent_instance' type-id='type-id-208' visibility='default' filepath='/usr/include/glib-2.0/gio/gfilterinputstream.h' line='48' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='base_stream' type-id='type-id-211' visibility='default' filepath='/usr/include/glib-2.0/gio/gfilterinputstream.h' line='51' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GInputStreamPrivate' type-id='type-id-192' filepath='/usr/include/glib-2.0/gio/ginputstream.h' line='45' column='1' id='type-id-212'/>
+ <class-decl name='_GInputStream' size-in-bits='256' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/gio/ginputstream.h' line='47' column='1' id='type-id-213'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='parent_instance' type-id='type-id-200' visibility='default' filepath='/usr/include/glib-2.0/gio/ginputstream.h' line='49' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='priv' type-id='type-id-214' visibility='default' filepath='/usr/include/glib-2.0/gio/ginputstream.h' line='52' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GBufferedInputStream' type-id='type-id-195' filepath='/usr/include/glib-2.0/gio/giotypes.h' line='36' column='1' id='type-id-204'/>
+ <typedef-decl name='GCancellable' type-id='type-id-199' filepath='/usr/include/glib-2.0/gio/giotypes.h' line='38' column='1' id='type-id-215'/>
+ <typedef-decl name='GDataInputStream' type-id='type-id-203' filepath='/usr/include/glib-2.0/gio/giotypes.h' line='44' column='1' id='type-id-216'/>
+ <typedef-decl name='GFilterInputStream' type-id='type-id-210' filepath='/usr/include/glib-2.0/gio/giotypes.h' line='74' column='1' id='type-id-196'/>
+ <typedef-decl name='GFile' type-id='type-id-190' filepath='/usr/include/glib-2.0/gio/giotypes.h' line='84' column='1' id='type-id-217'/>
+ <typedef-decl name='GFileInputStream' type-id='type-id-207' filepath='/usr/include/glib-2.0/gio/giotypes.h' line='96' column='1' id='type-id-218'/>
+ <typedef-decl name='GInputStream' type-id='type-id-213' filepath='/usr/include/glib-2.0/gio/giotypes.h' line='108' column='1' id='type-id-208'/>
+ <typedef-decl name='GString' type-id='type-id-219' filepath='/usr/include/glib-2.0/glib/gstring.h' line='39' column='1' id='type-id-220'/>
+ <class-decl name='_GString' size-in-bits='192' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/glib/gstring.h' line='41' column='1' id='type-id-219'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='str' type-id='type-id-111' visibility='default' filepath='/usr/include/glib-2.0/glib/gstring.h' line='43' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='len' type-id='type-id-221' visibility='default' filepath='/usr/include/glib-2.0/glib/gstring.h' line='44' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='allocated_len' type-id='type-id-221' visibility='default' filepath='/usr/include/glib-2.0/glib/gstring.h' line='45' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GObject' type-id='type-id-222' filepath='/usr/include/glib-2.0/gobject/gobject.h' line='190' column='1' id='type-id-200'/>
+ <class-decl name='_GObject' size-in-bits='192' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/gobject/gobject.h' line='265' column='1' id='type-id-222'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='g_type_instance' type-id='type-id-223' visibility='default' filepath='/usr/include/glib-2.0/gobject/gobject.h' line='267' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='ref_count' type-id='type-id-42' visibility='default' filepath='/usr/include/glib-2.0/gobject/gobject.h' line='270' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='qdata' type-id='type-id-68' visibility='default' filepath='/usr/include/glib-2.0/gobject/gobject.h' line='271' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GType' type-id='type-id-221' filepath='/usr/include/glib-2.0/gobject/gtype.h' line='400' column='1' id='type-id-224'/>
+ <typedef-decl name='GTypeClass' type-id='type-id-225' filepath='/usr/include/glib-2.0/gobject/gtype.h' line='407' column='1' id='type-id-226'/>
+ <typedef-decl name='GTypeInstance' type-id='type-id-227' filepath='/usr/include/glib-2.0/gobject/gtype.h' line='409' column='1' id='type-id-223'/>
+ <class-decl name='_GTypeClass' size-in-bits='64' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/gobject/gtype.h' line='424' column='1' id='type-id-225'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='g_type' type-id='type-id-224' visibility='default' filepath='/usr/include/glib-2.0/gobject/gtype.h' line='427' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='_GTypeInstance' size-in-bits='64' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/gobject/gtype.h' line='434' column='1' id='type-id-227'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='g_class' type-id='type-id-228' visibility='default' filepath='/usr/include/glib-2.0/gobject/gtype.h' line='437' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='gsize' type-id='type-id-3' filepath='/usr/lib/x86_64-linux-gnu/glib-2.0/include/glibconfig.h' line='80' column='1' id='type-id-221'/>
+ <pointer-type-def type-id='type-id-194' size-in-bits='64' id='type-id-197'/>
+ <pointer-type-def type-id='type-id-215' size-in-bits='64' id='type-id-229'/>
+ <pointer-type-def type-id='type-id-198' size-in-bits='64' id='type-id-201'/>
+ <pointer-type-def type-id='type-id-216' size-in-bits='64' id='type-id-230'/>
+ <pointer-type-def type-id='type-id-202' size-in-bits='64' id='type-id-205'/>
+ <pointer-type-def type-id='type-id-217' size-in-bits='64' id='type-id-231'/>
+ <pointer-type-def type-id='type-id-218' size-in-bits='64' id='type-id-232'/>
+ <pointer-type-def type-id='type-id-206' size-in-bits='64' id='type-id-209'/>
+ <pointer-type-def type-id='type-id-208' size-in-bits='64' id='type-id-211'/>
+ <pointer-type-def type-id='type-id-212' size-in-bits='64' id='type-id-214'/>
+ <pointer-type-def type-id='type-id-220' size-in-bits='64' id='type-id-233'/>
+ <pointer-type-def type-id='type-id-226' size-in-bits='64' id='type-id-228'/>
+ <pointer-type-def type-id='type-id-223' size-in-bits='64' id='type-id-234'/>
+ <pointer-type-def type-id='type-id-193' size-in-bits='64' id='type-id-235'/>
+ <pointer-type-def type-id='type-id-111' size-in-bits='64' id='type-id-236'/>
+ <pointer-type-def type-id='type-id-221' size-in-bits='64' id='type-id-237'/>
+ <class-decl name='_GBufferedInputStreamPrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-187'/>
+ <class-decl name='_GCancellablePrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-188'/>
+ <class-decl name='_GDataInputStreamPrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-189'/>
+ <class-decl name='_GFile' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-190'/>
+ <class-decl name='_GFileInputStreamPrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-191'/>
+ <class-decl name='_GInputStreamPrivate' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-192'/>
+ <function-decl name='g_data_input_stream_new' filepath='/usr/include/glib-2.0/gio/gdatainputstream.h' line='72' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-211'/>
+ <return type-id='type-id-230'/>
+ </function-decl>
+ <function-decl name='g_data_input_stream_read_line' filepath='/usr/include/glib-2.0/gio/gdatainputstream.h' line='113' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-230'/>
+ <parameter type-id='type-id-237'/>
+ <parameter type-id='type-id-229'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-41'/>
+ </function-decl>
+ <function-decl name='g_file_new_for_path' filepath='/usr/include/glib-2.0/gio/gfile.h' line='608' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-231'/>
+ </function-decl>
+ <function-decl name='g_file_read' filepath='/usr/include/glib-2.0/gio/gfile.h' line='671' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-231'/>
+ <parameter type-id='type-id-229'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-232'/>
+ </function-decl>
+ <function-decl name='g_input_stream_get_type' filepath='/usr/include/glib-2.0/gio/ginputstream.h' line='113' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <return type-id='type-id-224'/>
+ </function-decl>
+ <function-decl name='g_set_error' filepath='/usr/include/glib-2.0/glib/gerror.h' line='219' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-175'/>
+ <parameter type-id='type-id-115'/>
+ <parameter type-id='type-id-116'/>
+ <parameter type-id='type-id-185'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_markup_error_quark' filepath='/usr/include/glib-2.0/glib/gmarkup.h' line='73' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <return type-id='type-id-115'/>
+ </function-decl>
+ <function-decl name='g_vasprintf' filepath='/usr/include/glib-2.0/glib/gprintf.h' line='51' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-236'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-235'/>
+ <return type-id='type-id-116'/>
+ </function-decl>
+ <function-decl name='g_string_sized_new' filepath='/usr/include/glib-2.0/glib/gstring.h' line='54' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-221'/>
+ <return type-id='type-id-233'/>
+ </function-decl>
+ <function-decl name='g_string_free' filepath='/usr/include/glib-2.0/glib/gstring.h' line='56' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-233'/>
+ <parameter type-id='type-id-40'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_string_append_printf' filepath='/usr/include/glib-2.0/glib/gstring.h' line='154' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-233'/>
+ <parameter type-id='type-id-185'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_object_unref' filepath='/usr/include/glib-2.0/gobject/gobject.h' line='523' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_type_check_instance_cast' filepath='/usr/include/glib-2.0/gobject/gtype.h' line='2422' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-234'/>
+ <parameter type-id='type-id-224'/>
+ <return type-id='type-id-234'/>
+ </function-decl>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/names.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <array-type-def dimensions='1' type-id='type-id-238' size-in-bits='256' id='type-id-239'>
+ <subrange length='4' type-id='type-id-3' id='type-id-240'/>
+ </array-type-def>
+ <qualified-type-def type-id='type-id-108' const='yes' id='type-id-238'/>
+ <var-decl name='netplan_backend_to_str' type-id='type-id-239' mangled-name='netplan_backend_to_name' visibility='default' filepath='../src/names.c' line='25' column='1' elf-symbol-id='netplan_backend_to_name'/>
+ <function-decl name='netplan_backend_name' mangled-name='netplan_backend_name' filepath='../src/names.c' line='117' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_backend_name'>
+ <parameter type-id='type-id-31' name='val' filepath='../src/names.c' line='117' column='1'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_def_type_name' mangled-name='netplan_def_type_name' filepath='../src/names.c' line='118' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_def_type_name'>
+ <parameter type-id='type-id-13' name='val' filepath='../src/names.c' line='118' column='1'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_tunnel_mode_name' mangled-name='tunnel_mode_to_string' filepath='../src/names.c' line='121' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='tunnel_mode_to_string'>
+ <parameter type-id='type-id-50' name='val' filepath='../src/names.c' line='121' column='1'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='g_strcmp0' filepath='/usr/include/glib-2.0/glib/gtestutils.h' line='239' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/netplan.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <enum-decl name='NetplanAddrGenMode' naming-typedef-id='type-id-241' filepath='../src/types.h' line='30' column='1' id='type-id-242'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='NETPLAN_ADDRGEN_DEFAULT' value='0'/>
+ <enumerator name='NETPLAN_ADDRGEN_EUI64' value='1'/>
+ <enumerator name='NETPLAN_ADDRGEN_STABLEPRIVACY' value='2'/>
+ <enumerator name='NETPLAN_ADDRGEN_MAX' value='3'/>
+ </enum-decl>
+ <typedef-decl name='NetplanAddrGenMode' type-id='type-id-242' filepath='../src/types.h' line='35' column='1' id='type-id-241'/>
+ <typedef-decl name='GDataForeachFunc' type-id='type-id-243' filepath='/usr/include/glib-2.0/glib/gdataset.h' line='38' column='1' id='type-id-244'/>
+ <typedef-decl name='GHashTableIter' type-id='type-id-245' filepath='/usr/include/glib-2.0/glib/ghash.h' line='43' column='1' id='type-id-14'/>
+ <class-decl name='_GHashTableIter' size-in-bits='320' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/glib/ghash.h' line='45' column='1' id='type-id-245'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='dummy1' type-id='type-id-120' visibility='default' filepath='/usr/include/glib-2.0/glib/ghash.h' line='48' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='dummy2' type-id='type-id-120' visibility='default' filepath='/usr/include/glib-2.0/glib/ghash.h' line='49' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='dummy3' type-id='type-id-120' visibility='default' filepath='/usr/include/glib-2.0/glib/ghash.h' line='50' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='dummy4' type-id='type-id-16' visibility='default' filepath='/usr/include/glib-2.0/glib/ghash.h' line='51' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='224'>
+ <var-decl name='dummy5' type-id='type-id-40' visibility='default' filepath='/usr/include/glib-2.0/glib/ghash.h' line='52' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='dummy6' type-id='type-id-120' visibility='default' filepath='/usr/include/glib-2.0/glib/ghash.h' line='53' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GCompareFunc' type-id='type-id-246' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='106' column='1' id='type-id-247'/>
+ <typedef-decl name='GEqualFunc' type-id='type-id-248' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='111' column='1' id='type-id-249'/>
+ <typedef-decl name='GDestroyNotify' type-id='type-id-250' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='113' column='1' id='type-id-251'/>
+ <typedef-decl name='GHashFunc' type-id='type-id-252' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='116' column='1' id='type-id-253'/>
+ <enum-decl name='yaml_encoding_e' filepath='/usr/include/yaml.h' line='99' column='1' id='type-id-254'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_ANY_ENCODING' value='0'/>
+ <enumerator name='YAML_UTF8_ENCODING' value='1'/>
+ <enumerator name='YAML_UTF16LE_ENCODING' value='2'/>
+ <enumerator name='YAML_UTF16BE_ENCODING' value='3'/>
+ </enum-decl>
+ <typedef-decl name='yaml_encoding_t' type-id='type-id-254' filepath='/usr/include/yaml.h' line='108' column='1' id='type-id-255'/>
+ <enum-decl name='yaml_break_e' filepath='/usr/include/yaml.h' line='112' column='1' id='type-id-256'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_ANY_BREAK' value='0'/>
+ <enumerator name='YAML_CR_BREAK' value='1'/>
+ <enumerator name='YAML_LN_BREAK' value='2'/>
+ <enumerator name='YAML_CRLN_BREAK' value='3'/>
+ </enum-decl>
+ <typedef-decl name='yaml_break_t' type-id='type-id-256' filepath='/usr/include/yaml.h' line='121' column='1' id='type-id-257'/>
+ <enum-decl name='yaml_error_type_e' filepath='/usr/include/yaml.h' line='124' column='1' id='type-id-258'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_NO_ERROR' value='0'/>
+ <enumerator name='YAML_MEMORY_ERROR' value='1'/>
+ <enumerator name='YAML_READER_ERROR' value='2'/>
+ <enumerator name='YAML_SCANNER_ERROR' value='3'/>
+ <enumerator name='YAML_PARSER_ERROR' value='4'/>
+ <enumerator name='YAML_COMPOSER_ERROR' value='5'/>
+ <enumerator name='YAML_WRITER_ERROR' value='6'/>
+ <enumerator name='YAML_EMITTER_ERROR' value='7'/>
+ </enum-decl>
+ <typedef-decl name='yaml_error_type_t' type-id='type-id-258' filepath='/usr/include/yaml.h' line='144' column='1' id='type-id-259'/>
+ <enum-decl name='yaml_event_type_e' filepath='/usr/include/yaml.h' line='355' column='1' id='type-id-260'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_NO_EVENT' value='0'/>
+ <enumerator name='YAML_STREAM_START_EVENT' value='1'/>
+ <enumerator name='YAML_STREAM_END_EVENT' value='2'/>
+ <enumerator name='YAML_DOCUMENT_START_EVENT' value='3'/>
+ <enumerator name='YAML_DOCUMENT_END_EVENT' value='4'/>
+ <enumerator name='YAML_ALIAS_EVENT' value='5'/>
+ <enumerator name='YAML_SCALAR_EVENT' value='6'/>
+ <enumerator name='YAML_SEQUENCE_START_EVENT' value='7'/>
+ <enumerator name='YAML_SEQUENCE_END_EVENT' value='8'/>
+ <enumerator name='YAML_MAPPING_START_EVENT' value='9'/>
+ <enumerator name='YAML_MAPPING_END_EVENT' value='10'/>
+ </enum-decl>
+ <typedef-decl name='yaml_event_type_t' type-id='type-id-260' filepath='/usr/include/yaml.h' line='383' column='1' id='type-id-261'/>
+ <class-decl name='yaml_event_s' size-in-bits='832' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='386' column='1' id='type-id-262'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='type' type-id='type-id-261' visibility='default' filepath='/usr/include/yaml.h' line='389' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='data' type-id='type-id-263' visibility='default' filepath='/usr/include/yaml.h' line='471' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='start_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='474' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='640'>
+ <var-decl name='end_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='476' column='1'/>
+ </data-member>
+ </class-decl>
+ <union-decl name='__anonymous_union__1' size-in-bits='384' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='392' column='1' id='type-id-263'>
+ <data-member access='public'>
+ <var-decl name='stream_start' type-id='type-id-264' visibility='default' filepath='/usr/include/yaml.h' line='398' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='document_start' type-id='type-id-265' visibility='default' filepath='/usr/include/yaml.h' line='415' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='document_end' type-id='type-id-266' visibility='default' filepath='/usr/include/yaml.h' line='421' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='alias' type-id='type-id-267' visibility='default' filepath='/usr/include/yaml.h' line='427' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='scalar' type-id='type-id-268' visibility='default' filepath='/usr/include/yaml.h' line='445' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='sequence_start' type-id='type-id-269' visibility='default' filepath='/usr/include/yaml.h' line='457' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='mapping_start' type-id='type-id-270' visibility='default' filepath='/usr/include/yaml.h' line='469' column='1'/>
+ </data-member>
+ </union-decl>
+ <class-decl name='__anonymous_struct__5' size-in-bits='32' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='395' column='1' id='type-id-264'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='encoding' type-id='type-id-255' visibility='default' filepath='/usr/include/yaml.h' line='397' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__6' size-in-bits='256' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='401' column='1' id='type-id-265'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='version_directive' type-id='type-id-169' visibility='default' filepath='/usr/include/yaml.h' line='403' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='tag_directives' type-id='type-id-170' visibility='default' filepath='/usr/include/yaml.h' line='411' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='implicit' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='414' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__8' size-in-bits='32' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='418' column='1' id='type-id-266'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='implicit' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='420' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__9' size-in-bits='64' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='424' column='1' id='type-id-267'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='anchor' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='426' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__10' size-in-bits='384' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='430' column='1' id='type-id-268'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='anchor' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='432' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='tag' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='434' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='value' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='436' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='length' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='438' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='plain_implicit' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='440' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='288'>
+ <var-decl name='quoted_implicit' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='442' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='style' type-id='type-id-147' visibility='default' filepath='/usr/include/yaml.h' line='444' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__11' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='448' column='1' id='type-id-269'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='anchor' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='450' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='tag' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='452' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='implicit' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='454' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='160'>
+ <var-decl name='style' type-id='type-id-149' visibility='default' filepath='/usr/include/yaml.h' line='456' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__12' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='460' column='1' id='type-id-270'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='anchor' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='462' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='tag' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='464' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='implicit' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='466' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='160'>
+ <var-decl name='style' type-id='type-id-151' visibility='default' filepath='/usr/include/yaml.h' line='468' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_event_t' type-id='type-id-262' filepath='/usr/include/yaml.h' line='478' column='1' id='type-id-271'/>
+ <typedef-decl name='yaml_write_handler_t' type-id='type-id-272' filepath='/usr/include/yaml.h' line='1478' column='1' id='type-id-273'/>
+ <enum-decl name='yaml_emitter_state_e' filepath='/usr/include/yaml.h' line='1481' column='1' id='type-id-274'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_EMIT_STREAM_START_STATE' value='0'/>
+ <enumerator name='YAML_EMIT_FIRST_DOCUMENT_START_STATE' value='1'/>
+ <enumerator name='YAML_EMIT_DOCUMENT_START_STATE' value='2'/>
+ <enumerator name='YAML_EMIT_DOCUMENT_CONTENT_STATE' value='3'/>
+ <enumerator name='YAML_EMIT_DOCUMENT_END_STATE' value='4'/>
+ <enumerator name='YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE' value='5'/>
+ <enumerator name='YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE' value='6'/>
+ <enumerator name='YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE' value='7'/>
+ <enumerator name='YAML_EMIT_FLOW_MAPPING_KEY_STATE' value='8'/>
+ <enumerator name='YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE' value='9'/>
+ <enumerator name='YAML_EMIT_FLOW_MAPPING_VALUE_STATE' value='10'/>
+ <enumerator name='YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE' value='11'/>
+ <enumerator name='YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE' value='12'/>
+ <enumerator name='YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE' value='13'/>
+ <enumerator name='YAML_EMIT_BLOCK_MAPPING_KEY_STATE' value='14'/>
+ <enumerator name='YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE' value='15'/>
+ <enumerator name='YAML_EMIT_BLOCK_MAPPING_VALUE_STATE' value='16'/>
+ <enumerator name='YAML_EMIT_END_STATE' value='17'/>
+ </enum-decl>
+ <typedef-decl name='yaml_emitter_state_t' type-id='type-id-274' filepath='/usr/include/yaml.h' line='1518' column='1' id='type-id-275'/>
+ <class-decl name='yaml_anchors_s' size-in-bits='96' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='1523' column='1' id='type-id-276'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='references' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1525' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='anchor' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1527' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='serialized' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1529' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_anchors_t' type-id='type-id-276' filepath='/usr/include/yaml.h' line='1530' column='1' id='type-id-277'/>
+ <class-decl name='yaml_emitter_s' size-in-bits='3456' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='1539' column='1' id='type-id-278'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='error' type-id='type-id-259' visibility='default' filepath='/usr/include/yaml.h' line='1547' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='problem' type-id='type-id-108' visibility='default' filepath='/usr/include/yaml.h' line='1549' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='write_handler' type-id='type-id-279' visibility='default' filepath='/usr/include/yaml.h' line='1561' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='write_handler_data' type-id='type-id-125' visibility='default' filepath='/usr/include/yaml.h' line='1564' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='output' type-id='type-id-280' visibility='default' filepath='/usr/include/yaml.h' line='1580' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='buffer' type-id='type-id-281' visibility='default' filepath='/usr/include/yaml.h' line='1592' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='704'>
+ <var-decl name='raw_buffer' type-id='type-id-282' visibility='default' filepath='/usr/include/yaml.h' line='1604' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='960'>
+ <var-decl name='encoding' type-id='type-id-255' visibility='default' filepath='/usr/include/yaml.h' line='1607' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='992'>
+ <var-decl name='canonical' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1619' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1024'>
+ <var-decl name='best_indent' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1621' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1056'>
+ <var-decl name='best_width' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1623' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1088'>
+ <var-decl name='unicode' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1625' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1120'>
+ <var-decl name='line_break' type-id='type-id-257' visibility='default' filepath='/usr/include/yaml.h' line='1627' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1152'>
+ <var-decl name='states' type-id='type-id-283' visibility='default' filepath='/usr/include/yaml.h' line='1637' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1344'>
+ <var-decl name='state' type-id='type-id-275' visibility='default' filepath='/usr/include/yaml.h' line='1640' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1408'>
+ <var-decl name='events' type-id='type-id-284' visibility='default' filepath='/usr/include/yaml.h' line='1652' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1664'>
+ <var-decl name='indents' type-id='type-id-285' visibility='default' filepath='/usr/include/yaml.h' line='1662' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1856'>
+ <var-decl name='tag_directives' type-id='type-id-286' visibility='default' filepath='/usr/include/yaml.h' line='1672' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2048'>
+ <var-decl name='indent' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1675' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2080'>
+ <var-decl name='flow_level' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1678' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2112'>
+ <var-decl name='root_context' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1681' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2144'>
+ <var-decl name='sequence_context' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1683' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2176'>
+ <var-decl name='mapping_context' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1685' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2208'>
+ <var-decl name='simple_key_context' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1687' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2240'>
+ <var-decl name='line' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1690' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2272'>
+ <var-decl name='column' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1692' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2304'>
+ <var-decl name='whitespace' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1694' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2336'>
+ <var-decl name='indention' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1696' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2368'>
+ <var-decl name='open_ended' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1698' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2432'>
+ <var-decl name='anchor_data' type-id='type-id-287' visibility='default' filepath='/usr/include/yaml.h' line='1708' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2624'>
+ <var-decl name='tag_data' type-id='type-id-288' visibility='default' filepath='/usr/include/yaml.h' line='1720' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2880'>
+ <var-decl name='scalar_data' type-id='type-id-289' visibility='default' filepath='/usr/include/yaml.h' line='1740' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3200'>
+ <var-decl name='opened' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1752' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3232'>
+ <var-decl name='closed' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1754' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3264'>
+ <var-decl name='anchors' type-id='type-id-290' visibility='default' filepath='/usr/include/yaml.h' line='1757' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3328'>
+ <var-decl name='last_anchor_id' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1760' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3392'>
+ <var-decl name='document' type-id='type-id-291' visibility='default' filepath='/usr/include/yaml.h' line='1763' column='1'/>
+ </data-member>
+ </class-decl>
+ <union-decl name='__anonymous_union__' size-in-bits='192' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1567' column='1' id='type-id-280'>
+ <data-member access='public'>
+ <var-decl name='string' type-id='type-id-292' visibility='default' filepath='/usr/include/yaml.h' line='1576' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='file' type-id='type-id-173' visibility='default' filepath='/usr/include/yaml.h' line='1579' column='1'/>
+ </data-member>
+ </union-decl>
+ <class-decl name='__anonymous_struct__' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1569' column='1' id='type-id-292'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='buffer' type-id='type-id-293' visibility='default' filepath='/usr/include/yaml.h' line='1571' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='size' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1573' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='size_written' type-id='type-id-294' visibility='default' filepath='/usr/include/yaml.h' line='1575' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__1' size-in-bits='256' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1583' column='1' id='type-id-281'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='1585' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='1587' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='pointer' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='1589' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='last' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='1591' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__2' size-in-bits='256' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1595' column='1' id='type-id-282'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-293' visibility='default' filepath='/usr/include/yaml.h' line='1597' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-293' visibility='default' filepath='/usr/include/yaml.h' line='1599' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='pointer' type-id='type-id-293' visibility='default' filepath='/usr/include/yaml.h' line='1601' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='last' type-id='type-id-293' visibility='default' filepath='/usr/include/yaml.h' line='1603' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__3' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1630' column='1' id='type-id-283'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-295' visibility='default' filepath='/usr/include/yaml.h' line='1632' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-295' visibility='default' filepath='/usr/include/yaml.h' line='1634' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-295' visibility='default' filepath='/usr/include/yaml.h' line='1636' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__4' size-in-bits='256' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1643' column='1' id='type-id-284'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-296' visibility='default' filepath='/usr/include/yaml.h' line='1645' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-296' visibility='default' filepath='/usr/include/yaml.h' line='1647' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='head' type-id='type-id-296' visibility='default' filepath='/usr/include/yaml.h' line='1649' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='tail' type-id='type-id-296' visibility='default' filepath='/usr/include/yaml.h' line='1651' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__13' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1655' column='1' id='type-id-285'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-297' visibility='default' filepath='/usr/include/yaml.h' line='1657' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-297' visibility='default' filepath='/usr/include/yaml.h' line='1659' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-297' visibility='default' filepath='/usr/include/yaml.h' line='1661' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__14' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1665' column='1' id='type-id-286'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-172' visibility='default' filepath='/usr/include/yaml.h' line='1667' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-172' visibility='default' filepath='/usr/include/yaml.h' line='1669' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-172' visibility='default' filepath='/usr/include/yaml.h' line='1671' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__15' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1701' column='1' id='type-id-287'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='anchor' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='1703' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='anchor_length' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1705' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='alias' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1707' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__16' size-in-bits='256' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1711' column='1' id='type-id-288'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='handle' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='1713' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='handle_length' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1715' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='suffix' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='1717' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='suffix_length' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1719' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__17' size-in-bits='320' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1723' column='1' id='type-id-289'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='value' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='1725' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='length' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1727' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='multiline' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1729' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='160'>
+ <var-decl name='flow_plain_allowed' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1731' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='block_plain_allowed' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1733' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='224'>
+ <var-decl name='single_quoted_allowed' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1735' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='block_allowed' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1737' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='288'>
+ <var-decl name='style' type-id='type-id-147' visibility='default' filepath='/usr/include/yaml.h' line='1739' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_emitter_t' type-id='type-id-278' filepath='/usr/include/yaml.h' line='1769' column='1' id='type-id-298'/>
+ <pointer-type-def type-id='type-id-68' size-in-bits='64' id='type-id-299'/>
+ <pointer-type-def type-id='type-id-14' size-in-bits='64' id='type-id-300'/>
+ <qualified-type-def type-id='type-id-63' const='yes' id='type-id-301'/>
+ <pointer-type-def type-id='type-id-301' size-in-bits='64' id='type-id-302'/>
+ <qualified-type-def type-id='type-id-108' restrict='yes' id='type-id-303'/>
+ <pointer-type-def type-id='type-id-120' size-in-bits='64' id='type-id-304'/>
+ <pointer-type-def type-id='type-id-16' size-in-bits='64' id='type-id-297'/>
+ <pointer-type-def type-id='type-id-137' size-in-bits='64' id='type-id-294'/>
+ <pointer-type-def type-id='type-id-305' size-in-bits='64' id='type-id-248'/>
+ <pointer-type-def type-id='type-id-306' size-in-bits='64' id='type-id-246'/>
+ <pointer-type-def type-id='type-id-307' size-in-bits='64' id='type-id-252'/>
+ <pointer-type-def type-id='type-id-20' size-in-bits='64' id='type-id-293'/>
+ <pointer-type-def type-id='type-id-308' size-in-bits='64' id='type-id-243'/>
+ <pointer-type-def type-id='type-id-309' size-in-bits='64' id='type-id-250'/>
+ <pointer-type-def type-id='type-id-277' size-in-bits='64' id='type-id-290'/>
+ <pointer-type-def type-id='type-id-100' size-in-bits='64' id='type-id-291'/>
+ <pointer-type-def type-id='type-id-275' size-in-bits='64' id='type-id-295'/>
+ <pointer-type-def type-id='type-id-298' size-in-bits='64' id='type-id-310'/>
+ <pointer-type-def type-id='type-id-271' size-in-bits='64' id='type-id-296'/>
+ <pointer-type-def type-id='type-id-273' size-in-bits='64' id='type-id-279'/>
+ <function-decl name='netplan_auth_key_management_type_name' filepath='../src/names.h' line='30' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-52'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_auth_eap_method_name' filepath='../src/names.h' line='33' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-54'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_addr_gen_mode_name' filepath='../src/names.h' line='39' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-241'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_wifi_mode_name' filepath='../src/names.h' line='42' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-85'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_infiniband_mode_name' filepath='../src/names.h' line='45' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-46'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_vxlan_notification_name' filepath='../src/names.h' line='48' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_vxlan_checksum_name' filepath='../src/names.h' line='51' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_vxlan_extension_name' filepath='../src/names.h' line='54' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_state_write_yaml_file' mangled-name='netplan_state_write_yaml_file' filepath='../src/netplan.c' line='1090' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_write_yaml_file'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/netplan.c' line='1090' column='1'/>
+ <parameter type-id='type-id-108' name='filename' filepath='../src/netplan.c' line='1090' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/netplan.c' line='1090' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/netplan.c' line='1090' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_state_update_yaml_hierarchy' mangled-name='netplan_state_update_yaml_hierarchy' filepath='../src/netplan.c' line='1163' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_update_yaml_hierarchy'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/netplan.c' line='1163' column='1'/>
+ <parameter type-id='type-id-108' name='default_filename' filepath='../src/netplan.c' line='1163' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/netplan.c' line='1163' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/netplan.c' line='1163' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='get_default_backend_for_type' filepath='../src/util-internal.h' line='75' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-31'/>
+ <parameter type-id='type-id-13'/>
+ <return type-id='type-id-31'/>
+ </function-decl>
+ <function-decl name='has_openvswitch' filepath='../src/util-internal.h' line='93' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-302'/>
+ <parameter type-id='type-id-31'/>
+ <parameter type-id='type-id-62'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='complex_object_is_dirty' filepath='../src/util-internal.h' line='99' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-180'/>
+ <parameter type-id='type-id-125'/>
+ <parameter type-id='type-id-137'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='__errno_location' filepath='/usr/include/errno.h' line='37' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <return type-id='type-id-297'/>
+ </function-decl>
+ <function-decl name='g_array_new' filepath='/usr/include/glib-2.0/glib/garray.h' line='70' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-40'/>
+ <parameter type-id='type-id-40'/>
+ <parameter type-id='type-id-42'/>
+ <return type-id='type-id-59'/>
+ </function-decl>
+ <function-decl name='g_array_free' filepath='/usr/include/glib-2.0/glib/garray.h' line='84' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-59'/>
+ <parameter type-id='type-id-40'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_array_append_vals' filepath='/usr/include/glib-2.0/glib/garray.h' line='93' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-59'/>
+ <parameter type-id='type-id-126'/>
+ <parameter type-id='type-id-42'/>
+ <return type-id='type-id-59'/>
+ </function-decl>
+ <function-decl name='g_datalist_foreach' filepath='/usr/include/glib-2.0/glib/gdataset.h' line='76' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-299'/>
+ <parameter type-id='type-id-244'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_file_error_quark' filepath='/usr/include/glib-2.0/glib/gfileutils.h' line='109' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <return type-id='type-id-115'/>
+ </function-decl>
+ <function-decl name='g_hash_table_new_full' filepath='/usr/include/glib-2.0/glib/ghash.h' line='60' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-253'/>
+ <parameter type-id='type-id-249'/>
+ <parameter type-id='type-id-251'/>
+ <parameter type-id='type-id-251'/>
+ <return type-id='type-id-62'/>
+ </function-decl>
+ <function-decl name='g_hash_table_destroy' filepath='/usr/include/glib-2.0/glib/ghash.h' line='67' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_hash_table_insert' filepath='/usr/include/glib-2.0/glib/ghash.h' line='69' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-120'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_hash_table_steal_extended' filepath='/usr/include/glib-2.0/glib/ghash.h' line='88' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-126'/>
+ <parameter type-id='type-id-304'/>
+ <parameter type-id='type-id-304'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_hash_table_contains' filepath='/usr/include/glib-2.0/glib/ghash.h' line='98' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_hash_table_size' filepath='/usr/include/glib-2.0/glib/ghash.h' line='122' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <return type-id='type-id-42'/>
+ </function-decl>
+ <function-decl name='g_hash_table_iter_init' filepath='/usr/include/glib-2.0/glib/ghash.h' line='132' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-300'/>
+ <parameter type-id='type-id-62'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_hash_table_iter_next' filepath='/usr/include/glib-2.0/glib/ghash.h' line='135' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-300'/>
+ <parameter type-id='type-id-304'/>
+ <parameter type-id='type-id-304'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_hash_table_iter_remove' filepath='/usr/include/glib-2.0/glib/ghash.h' line='141' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-300'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_str_equal' filepath='/usr/include/glib-2.0/glib/ghash.h' line='159' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-126'/>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_str_hash' filepath='/usr/include/glib-2.0/glib/ghash.h' line='162' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-42'/>
+ </function-decl>
+ <function-decl name='g_list_free' filepath='/usr/include/glib-2.0/glib/glist.h' line='51' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-99'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_list_append' filepath='/usr/include/glib-2.0/glib/glist.h' line='59' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-99'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-99'/>
+ </function-decl>
+ <function-decl name='g_list_find_custom' filepath='/usr/include/glib-2.0/glib/glist.h' line='120' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-99'/>
+ <parameter type-id='type-id-126'/>
+ <parameter type-id='type-id-247'/>
+ <return type-id='type-id-99'/>
+ </function-decl>
+ <function-decl name='g_quark_to_string' filepath='/usr/include/glib-2.0/glib/gquark.h' line='47' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-115'/>
+ <return type-id='type-id-185'/>
+ </function-decl>
+ <function-decl name='g_strdup_printf' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='219' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_strconcat' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='231' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_strsplit' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='275' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-116'/>
+ <return type-id='type-id-236'/>
+ </function-decl>
+ <function-decl name='g_strfreev' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='286' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-236'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='rename' filepath='/usr/include/stdio.h' line='154' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='fclose' filepath='/usr/include/stdio.h' line='178' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-173'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='fdopen' filepath='/usr/include/stdio.h' line='293' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-173'/>
+ </function-decl>
+ <function-decl name='strchr' filepath='/usr/include/string.h' line='246' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-41'/>
+ </function-decl>
+ <function-decl name='strlen' filepath='/usr/include/string.h' line='407' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-137'/>
+ </function-decl>
+ <function-decl name='strerror' filepath='/usr/include/string.h' line='419' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-41'/>
+ </function-decl>
+ <function-decl name='dup' filepath='/usr/include/unistd.h' line='552' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='unlink' filepath='/usr/include/unistd.h' line='858' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_stream_start_event_initialize' filepath='/usr/include/yaml.h' line='490' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-296'/>
+ <parameter type-id='type-id-255'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_stream_end_event_initialize' filepath='/usr/include/yaml.h' line='502' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-296'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_document_start_event_initialize' filepath='/usr/include/yaml.h' line='524' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-296'/>
+ <parameter type-id='type-id-169'/>
+ <parameter type-id='type-id-172'/>
+ <parameter type-id='type-id-172'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_document_end_event_initialize' filepath='/usr/include/yaml.h' line='543' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-296'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_scalar_event_initialize' filepath='/usr/include/yaml.h' line='580' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-296'/>
+ <parameter type-id='type-id-142'/>
+ <parameter type-id='type-id-142'/>
+ <parameter type-id='type-id-142'/>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-147'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_sequence_start_event_initialize' filepath='/usr/include/yaml.h' line='603' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-296'/>
+ <parameter type-id='type-id-142'/>
+ <parameter type-id='type-id-142'/>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-149'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_sequence_end_event_initialize' filepath='/usr/include/yaml.h' line='616' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-296'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_mapping_start_event_initialize' filepath='/usr/include/yaml.h' line='635' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-296'/>
+ <parameter type-id='type-id-142'/>
+ <parameter type-id='type-id-142'/>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-151'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_mapping_end_event_initialize' filepath='/usr/include/yaml.h' line='648' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-296'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_emitter_initialize' filepath='/usr/include/yaml.h' line='1783' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-310'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_emitter_delete' filepath='/usr/include/yaml.h' line='1792' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-310'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='yaml_emitter_set_output_file' filepath='/usr/include/yaml.h' line='1824' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-310'/>
+ <parameter type-id='type-id-173'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='yaml_emitter_emit' filepath='/usr/include/yaml.h' line='1915' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-310'/>
+ <parameter type-id='type-id-296'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-type size-in-bits='64' id='type-id-272'>
+ <parameter type-id='type-id-125'/>
+ <parameter type-id='type-id-293'/>
+ <parameter type-id='type-id-137'/>
+ <return type-id='type-id-16'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-305'>
+ <parameter type-id='type-id-126'/>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-40'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-306'>
+ <parameter type-id='type-id-126'/>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-116'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-307'>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-42'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-308'>
+ <parameter type-id='type-id-115'/>
+ <parameter type-id='type-id-120'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-26'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-309'>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-26'/>
+ </function-type>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/networkd.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <typedef-decl name='GHFunc' type-id='type-id-311' filepath='/usr/include/glib-2.0/glib/gtypes.h' line='117' column='1' id='type-id-312'/>
+ <typedef-decl name='__mode_t' type-id='type-id-23' filepath='/usr/include/x86_64-linux-gnu/bits/types.h' line='150' column='1' id='type-id-313'/>
+ <typedef-decl name='guint64' type-id='type-id-3' filepath='/usr/lib/x86_64-linux-gnu/glib-2.0/include/glibconfig.h' line='64' column='1' id='type-id-314'/>
+ <typedef-decl name='gssize' type-id='type-id-17' filepath='/usr/lib/x86_64-linux-gnu/glib-2.0/include/glibconfig.h' line='79' column='1' id='type-id-315'/>
+ <qualified-type-def type-id='type-id-24' const='yes' id='type-id-316'/>
+ <pointer-type-def type-id='type-id-316' size-in-bits='64' id='type-id-317'/>
+ <pointer-type-def type-id='type-id-317' size-in-bits='64' id='type-id-318'/>
+ <pointer-type-def type-id='type-id-40' size-in-bits='64' id='type-id-319'/>
+ <pointer-type-def type-id='type-id-320' size-in-bits='64' id='type-id-311'/>
+ <function-decl name='netplan_netdef_write_network_file' mangled-name='netplan_netdef_write_network_file' filepath='../src/networkd.c' line='710' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_write_network_file'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/networkd.c' line='711' column='1'/>
+ <parameter type-id='type-id-180' name='def' filepath='../src/networkd.c' line='712' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/networkd.c' line='713' column='1'/>
+ <parameter type-id='type-id-108' name='path' filepath='../src/networkd.c' line='714' column='1'/>
+ <parameter type-id='type-id-319' name='has_been_written' filepath='../src/networkd.c' line='715' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/networkd.c' line='716' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_write_networkd' mangled-name='netplan_netdef_write_networkd' filepath='../src/networkd.c' line='1254' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_write_networkd'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/networkd.c' line='1255' column='1'/>
+ <parameter type-id='type-id-180' name='def' filepath='../src/networkd.c' line='1256' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/networkd.c' line='1257' column='1'/>
+ <parameter type-id='type-id-319' name='has_been_written' filepath='../src/networkd.c' line='1258' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/networkd.c' line='1259' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_networkd_cleanup' mangled-name='netplan_networkd_cleanup' filepath='../src/networkd.c' line='1321' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_networkd_cleanup'>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/networkd.c' line='1321' column='1'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='safe_mkdir_p_dir' mangled-name='safe_mkdir_p_dir' filepath='../src/util-internal.h' line='37' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='safe_mkdir_p_dir'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_string_free_to_file' mangled-name='g_string_free_to_file' filepath='../src/util-internal.h' line='40' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='g_string_free_to_file'>
+ <parameter type-id='type-id-233'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='unlink_glob' mangled-name='unlink_glob' filepath='../src/util-internal.h' line='43' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='unlink_glob'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='get_global_network' mangled-name='get_global_network' filepath='../src/util-internal.h' line='49' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='get_global_network'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='wifi_get_freq24' mangled-name='wifi_get_freq24' filepath='../src/util-internal.h' line='55' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='wifi_get_freq24'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='wifi_get_freq5' mangled-name='wifi_get_freq5' filepath='../src/util-internal.h' line='58' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='wifi_get_freq5'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='systemd_escape' mangled-name='systemd_escape' filepath='../src/util-internal.h' line='61' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='systemd_escape'>
+ <parameter type-id='type-id-41'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='is_multicast_address' filepath='../src/util-internal.h' line='102' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='is_wireguard_key' mangled-name='is_wireguard_key' filepath='../src/validation.h' line='31' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='is_wireguard_key'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='__ctype_b_loc' filepath='/usr/include/ctype.h' line='79' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <return type-id='type-id-318'/>
+ </function-decl>
+ <function-decl name='g_hash_table_foreach' filepath='/usr/include/glib-2.0/glib/ghash.h' line='106' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-312'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_ascii_strtoull' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='153' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-236'/>
+ <parameter type-id='type-id-42'/>
+ <return type-id='type-id-314'/>
+ </function-decl>
+ <function-decl name='g_strjoin' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='234' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_string_new' filepath='/usr/include/glib-2.0/glib/gstring.h' line='49' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-233'/>
+ </function-decl>
+ <function-decl name='g_string_append' filepath='/usr/include/glib-2.0/glib/gstring.h' line='80' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-233'/>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-233'/>
+ </function-decl>
+ <function-decl name='g_string_insert_c' filepath='/usr/include/glib-2.0/glib/gstring.h' line='110' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-233'/>
+ <parameter type-id='type-id-315'/>
+ <parameter type-id='type-id-124'/>
+ <return type-id='type-id-233'/>
+ </function-decl>
+ <function-decl name='g_string_overwrite' filepath='/usr/include/glib-2.0/glib/gstring.h' line='118' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-233'/>
+ <parameter type-id='type-id-221'/>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-233'/>
+ </function-decl>
+ <function-decl name='g_string_printf' filepath='/usr/include/glib-2.0/glib/gstring.h' line='145' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-233'/>
+ <parameter type-id='type-id-185'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='strncmp' filepath='/usr/include/string.h' line='159' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-137'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='symlink' filepath='/usr/include/unistd.h' line='832' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='umask' filepath='/usr/include/x86_64-linux-gnu/sys/stat.h' line='380' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-313'/>
+ <return type-id='type-id-313'/>
+ </function-decl>
+ <function-type size-in-bits='64' id='type-id-320'>
+ <parameter type-id='type-id-120'/>
+ <parameter type-id='type-id-120'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-26'/>
+ </function-type>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/nm.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <class-decl name='_GKeyFile' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-321'/>
+ <typedef-decl name='GKeyFile' type-id='type-id-321' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='48' column='1' id='type-id-322'/>
+ <pointer-type-def type-id='type-id-322' size-in-bits='64' id='type-id-323'/>
+ <qualified-type-def type-id='type-id-185' const='yes' id='type-id-324'/>
+ <pointer-type-def type-id='type-id-324' size-in-bits='64' id='type-id-325'/>
+ <qualified-type-def type-id='type-id-20' const='yes' id='type-id-326'/>
+ <pointer-type-def type-id='type-id-326' size-in-bits='64' id='type-id-327'/>
+ <class-decl name='_GKeyFile' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-321'/>
+ <function-decl name='netplan_netdef_write_nm' mangled-name='netplan_netdef_write_nm' filepath='../src/nm.c' line='943' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_write_nm'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/nm.c' line='944' column='1'/>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/nm.c' line='945' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/nm.c' line='946' column='1'/>
+ <parameter type-id='type-id-319' name='has_been_written' filepath='../src/nm.c' line='947' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/nm.c' line='948' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_state_finish_nm_write' mangled-name='netplan_state_finish_nm_write' filepath='../src/nm.c' line='985' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_finish_nm_write'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/nm.c' line='986' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/nm.c' line='987' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/nm.c' line='988' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_nm_cleanup' mangled-name='netplan_nm_cleanup' filepath='../src/nm.c' line='1093' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_nm_cleanup'>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/nm.c' line='1093' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_datalist_get_data' filepath='/usr/include/glib-2.0/glib/gdataset.h' line='119' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-299'/>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-120'/>
+ </function-decl>
+ <function-decl name='g_key_file_new' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='58' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <return type-id='type-id-323'/>
+ </function-decl>
+ <function-decl name='g_key_file_unref' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='62' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_key_file_save_to_file' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='102' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_key_file_has_key' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='119' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_key_file_set_value' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='129' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_key_file_get_string' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='134' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_key_file_set_string' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='139' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_key_file_set_boolean' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='166' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-40'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_key_file_set_integer' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='176' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-116'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_key_file_set_uint64' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='196' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-314'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_key_file_set_string_list' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='217' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-325'/>
+ <parameter type-id='type-id-221'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_key_file_set_comment' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='273' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_key_file_remove_key' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='290' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_ascii_strcasecmp' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='184' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-116'/>
+ </function-decl>
+ <function-decl name='g_strjoinv' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='283' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-236'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_strv_length' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='290' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-236'/>
+ <return type-id='type-id-42'/>
+ </function-decl>
+ <function-decl name='g_string_ascii_down' filepath='/usr/include/glib-2.0/glib/gstring.h' line='136' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-233'/>
+ <return type-id='type-id-233'/>
+ </function-decl>
+ <function-decl name='g_uri_escape_string' filepath='/usr/include/glib-2.0/glib/guri.h' line='401' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-40'/>
+ <return type-id='type-id-41'/>
+ </function-decl>
+ <function-decl name='strpbrk' filepath='/usr/include/string.h' line='323' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-41'/>
+ </function-decl>
+ <function-decl name='uuid_generate' filepath='/usr/include/uuid/uuid.h' line='91' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-293'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='uuid_is_null' filepath='/usr/include/uuid/uuid.h' line='100' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-327'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='uuid_unparse' filepath='/usr/include/uuid/uuid.h' line='107' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-327'/>
+ <parameter type-id='type-id-41'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/openvswitch.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <function-decl name='netplan_netdef_write_ovs' mangled-name='netplan_netdef_write_ovs' filepath='../src/openvswitch.c' line='320' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_write_ovs'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/openvswitch.c' line='320' column='1'/>
+ <parameter type-id='type-id-180' name='def' filepath='../src/openvswitch.c' line='320' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/openvswitch.c' line='320' column='1'/>
+ <parameter type-id='type-id-319' name='has_been_written' filepath='../src/openvswitch.c' line='320' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/openvswitch.c' line='320' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_state_finish_ovs_write' mangled-name='netplan_state_finish_ovs_write' filepath='../src/openvswitch.c' line='461' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_finish_ovs_write'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/openvswitch.c' line='461' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/openvswitch.c' line='461' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/openvswitch.c' line='461' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_ovs_cleanup' mangled-name='netplan_ovs_cleanup' filepath='../src/openvswitch.c' line='507' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_ovs_cleanup'>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/openvswitch.c' line='507' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_str_has_prefix' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='141' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_string_erase' filepath='/usr/include/glib-2.0/glib/gstring.h' line='127' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-233'/>
+ <parameter type-id='type-id-315'/>
+ <parameter type-id='type-id-315'/>
+ <return type-id='type-id-233'/>
+ </function-decl>
+ <function-decl name='strcmp' filepath='/usr/include/string.h' line='156' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/parse-nm.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <enum-decl name='GKeyFileFlags' naming-typedef-id='type-id-328' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='51' column='1' id='type-id-329'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='G_KEY_FILE_NONE' value='0'/>
+ <enumerator name='G_KEY_FILE_KEEP_COMMENTS' value='1'/>
+ <enumerator name='G_KEY_FILE_KEEP_TRANSLATIONS' value='2'/>
+ </enum-decl>
+ <typedef-decl name='GKeyFileFlags' type-id='type-id-329' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='55' column='1' id='type-id-328'/>
+ <function-decl name='netplan_get_id_from_nm_filename' mangled-name='netplan_get_id_from_nm_filename' filepath='../include/util.h' line='30' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_get_id_from_nm_filename'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='get_unspecified_address' mangled-name='get_unspecified_address' filepath='../src/util-internal.h' line='52' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='get_unspecified_address'>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_new' filepath='../src/util-internal.h' line='78' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-176'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-13'/>
+ <parameter type-id='type-id-31'/>
+ <return type-id='type-id-75'/>
+ </function-decl>
+ <function-decl name='g_key_file_free' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='64' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_key_file_load_from_file' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='69' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-328'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_key_file_get_groups' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='108' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-237'/>
+ <return type-id='type-id-236'/>
+ </function-decl>
+ <function-decl name='g_key_file_get_keys' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='111' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-237'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-236'/>
+ </function-decl>
+ <function-decl name='g_key_file_has_group' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='116' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_key_file_get_boolean' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='161' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_key_file_get_integer' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='171' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-116'/>
+ </function-decl>
+ <function-decl name='g_key_file_get_uint64' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='191' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-314'/>
+ </function-decl>
+ <function-decl name='g_key_file_get_string_list' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='211' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-237'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-236'/>
+ </function-decl>
+ <function-decl name='g_key_file_remove_group' filepath='/usr/include/glib-2.0/glib/gkeyfile.h' line='295' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-323'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='strtoul' filepath='/usr/include/stdlib.h' line='181' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-303'/>
+ <parameter type-id='type-id-330'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-3'/>
+ </function-decl>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/parse.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <array-type-def dimensions='1' type-id='type-id-331' size-in-bits='768' id='type-id-332'>
+ <subrange length='6' type-id='type-id-3' id='type-id-333'/>
+ </array-type-def>
+ <array-type-def dimensions='1' type-id='type-id-331' size-in-bits='infinite' id='type-id-334'>
+ <subrange length='infinite' id='type-id-335'/>
+ </array-type-def>
+ <array-type-def dimensions='1' type-id='type-id-336' size-in-bits='1280' id='type-id-337'>
+ <subrange length='10' type-id='type-id-3' id='type-id-338'/>
+ </array-type-def>
+ <array-type-def dimensions='1' type-id='type-id-336' size-in-bits='infinite' id='type-id-339'>
+ <subrange length='infinite' id='type-id-335'/>
+ </array-type-def>
+ <class-decl name='re_dfa_t' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-340'/>
+ <class-decl name='NetplanWifiWowlanType' size-in-bits='128' is-struct='yes' visibility='default' filepath='../src/abi.h' line='74' column='1' id='type-id-336'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='name' type-id='type-id-41' visibility='default' filepath='../src/abi.h' line='75' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='flag' type-id='type-id-48' visibility='default' filepath='../src/abi.h' line='76' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='NetplanOptionalAddressType' size-in-bits='128' is-struct='yes' visibility='default' filepath='../src/types.h' line='37' column='1' id='type-id-331'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='name' type-id='type-id-41' visibility='default' filepath='../src/types.h' line='38' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='flag' type-id='type-id-37' visibility='default' filepath='../src/types.h' line='39' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='GHRFunc' type-id='type-id-341' filepath='/usr/include/glib-2.0/glib/ghash.h' line='39' column='1' id='type-id-342'/>
+ <typedef-decl name='__re_long_size_t' type-id='type-id-3' filepath='/usr/include/regex.h' line='56' column='1' id='type-id-343'/>
+ <typedef-decl name='reg_syntax_t' type-id='type-id-3' filepath='/usr/include/regex.h' line='72' column='1' id='type-id-344'/>
+ <class-decl name='re_pattern_buffer' size-in-bits='512' is-struct='yes' visibility='default' filepath='/usr/include/regex.h' line='413' column='1' id='type-id-345'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='__buffer' type-id='type-id-346' visibility='default' filepath='/usr/include/regex.h' line='417' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='__allocated' type-id='type-id-343' visibility='default' filepath='/usr/include/regex.h' line='420' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='__used' type-id='type-id-343' visibility='default' filepath='/usr/include/regex.h' line='423' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='__syntax' type-id='type-id-344' visibility='default' filepath='/usr/include/regex.h' line='426' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='__fastmap' type-id='type-id-41' visibility='default' filepath='/usr/include/regex.h' line='431' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='__translate' type-id='type-id-293' visibility='default' filepath='/usr/include/regex.h' line='437' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='re_nsub' type-id='type-id-137' visibility='default' filepath='/usr/include/regex.h' line='440' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='__can_be_null' type-id='type-id-23' visibility='default' filepath='/usr/include/regex.h' line='446' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='449'>
+ <var-decl name='__regs_allocated' type-id='type-id-23' visibility='default' filepath='/usr/include/regex.h' line='457' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='451'>
+ <var-decl name='__fastmap_accurate' type-id='type-id-23' visibility='default' filepath='/usr/include/regex.h' line='461' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='452'>
+ <var-decl name='__no_sub' type-id='type-id-23' visibility='default' filepath='/usr/include/regex.h' line='465' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='453'>
+ <var-decl name='__not_bol' type-id='type-id-23' visibility='default' filepath='/usr/include/regex.h' line='469' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='454'>
+ <var-decl name='__not_eol' type-id='type-id-23' visibility='default' filepath='/usr/include/regex.h' line='472' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='455'>
+ <var-decl name='__newline_anchor' type-id='type-id-23' visibility='default' filepath='/usr/include/regex.h' line='475' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='regex_t' type-id='type-id-345' filepath='/usr/include/regex.h' line='478' column='1' id='type-id-347'/>
+ <typedef-decl name='regoff_t' type-id='type-id-16' filepath='/usr/include/regex.h' line='490' column='1' id='type-id-348'/>
+ <class-decl name='regmatch_t' size-in-bits='64' is-struct='yes' naming-typedef-id='type-id-349' visibility='default' filepath='/usr/include/regex.h' line='517' column='1' id='type-id-350'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='rm_so' type-id='type-id-348' visibility='default' filepath='/usr/include/regex.h' line='519' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='rm_eo' type-id='type-id-348' visibility='default' filepath='/usr/include/regex.h' line='520' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='regmatch_t' type-id='type-id-350' filepath='/usr/include/regex.h' line='521' column='1' id='type-id-349'/>
+ <enum-decl name='yaml_token_type_e' filepath='/usr/include/yaml.h' line='215' column='1' id='type-id-351'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_NO_TOKEN' value='0'/>
+ <enumerator name='YAML_STREAM_START_TOKEN' value='1'/>
+ <enumerator name='YAML_STREAM_END_TOKEN' value='2'/>
+ <enumerator name='YAML_VERSION_DIRECTIVE_TOKEN' value='3'/>
+ <enumerator name='YAML_TAG_DIRECTIVE_TOKEN' value='4'/>
+ <enumerator name='YAML_DOCUMENT_START_TOKEN' value='5'/>
+ <enumerator name='YAML_DOCUMENT_END_TOKEN' value='6'/>
+ <enumerator name='YAML_BLOCK_SEQUENCE_START_TOKEN' value='7'/>
+ <enumerator name='YAML_BLOCK_MAPPING_START_TOKEN' value='8'/>
+ <enumerator name='YAML_BLOCK_END_TOKEN' value='9'/>
+ <enumerator name='YAML_FLOW_SEQUENCE_START_TOKEN' value='10'/>
+ <enumerator name='YAML_FLOW_SEQUENCE_END_TOKEN' value='11'/>
+ <enumerator name='YAML_FLOW_MAPPING_START_TOKEN' value='12'/>
+ <enumerator name='YAML_FLOW_MAPPING_END_TOKEN' value='13'/>
+ <enumerator name='YAML_BLOCK_ENTRY_TOKEN' value='14'/>
+ <enumerator name='YAML_FLOW_ENTRY_TOKEN' value='15'/>
+ <enumerator name='YAML_KEY_TOKEN' value='16'/>
+ <enumerator name='YAML_VALUE_TOKEN' value='17'/>
+ <enumerator name='YAML_ALIAS_TOKEN' value='18'/>
+ <enumerator name='YAML_ANCHOR_TOKEN' value='19'/>
+ <enumerator name='YAML_TAG_TOKEN' value='20'/>
+ <enumerator name='YAML_SCALAR_TOKEN' value='21'/>
+ </enum-decl>
+ <typedef-decl name='yaml_token_type_t' type-id='type-id-351' filepath='/usr/include/yaml.h' line='266' column='1' id='type-id-352'/>
+ <class-decl name='yaml_token_s' size-in-bits='640' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='269' column='1' id='type-id-353'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='type' type-id='type-id-352' visibility='default' filepath='/usr/include/yaml.h' line='272' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='data' type-id='type-id-354' visibility='default' filepath='/usr/include/yaml.h' line='329' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='start_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='332' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='end_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='334' column='1'/>
+ </data-member>
+ </class-decl>
+ <union-decl name='__anonymous_union__1' size-in-bits='192' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='275' column='1' id='type-id-354'>
+ <data-member access='public'>
+ <var-decl name='stream_start' type-id='type-id-264' visibility='default' filepath='/usr/include/yaml.h' line='281' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='alias' type-id='type-id-355' visibility='default' filepath='/usr/include/yaml.h' line='287' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='anchor' type-id='type-id-355' visibility='default' filepath='/usr/include/yaml.h' line='293' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='tag' type-id='type-id-356' visibility='default' filepath='/usr/include/yaml.h' line='301' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='scalar' type-id='type-id-160' visibility='default' filepath='/usr/include/yaml.h' line='311' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='version_directive' type-id='type-id-357' visibility='default' filepath='/usr/include/yaml.h' line='319' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='tag_directive' type-id='type-id-358' visibility='default' filepath='/usr/include/yaml.h' line='327' column='1'/>
+ </data-member>
+ </union-decl>
+ <class-decl name='__anonymous_struct__5' size-in-bits='64' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='284' column='1' id='type-id-355'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='value' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='286' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__7' size-in-bits='128' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='296' column='1' id='type-id-356'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='handle' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='298' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='suffix' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='300' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__9' size-in-bits='64' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='314' column='1' id='type-id-357'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='major' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='316' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='minor' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='318' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__10' size-in-bits='128' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='322' column='1' id='type-id-358'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='handle' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='324' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='prefix' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='326' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_token_t' type-id='type-id-353' filepath='/usr/include/yaml.h' line='336' column='1' id='type-id-359'/>
+ <typedef-decl name='yaml_read_handler_t' type-id='type-id-360' filepath='/usr/include/yaml.h' line='988' column='1' id='type-id-361'/>
+ <class-decl name='yaml_simple_key_s' size-in-bits='320' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='995' column='1' id='type-id-362'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='possible' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='997' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='32'>
+ <var-decl name='required' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1000' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='token_number' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1003' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='1006' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_simple_key_t' type-id='type-id-362' filepath='/usr/include/yaml.h' line='1007' column='1' id='type-id-363'/>
+ <enum-decl name='yaml_parser_state_e' filepath='/usr/include/yaml.h' line='1012' column='1' id='type-id-364'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='YAML_PARSE_STREAM_START_STATE' value='0'/>
+ <enumerator name='YAML_PARSE_IMPLICIT_DOCUMENT_START_STATE' value='1'/>
+ <enumerator name='YAML_PARSE_DOCUMENT_START_STATE' value='2'/>
+ <enumerator name='YAML_PARSE_DOCUMENT_CONTENT_STATE' value='3'/>
+ <enumerator name='YAML_PARSE_DOCUMENT_END_STATE' value='4'/>
+ <enumerator name='YAML_PARSE_BLOCK_NODE_STATE' value='5'/>
+ <enumerator name='YAML_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE' value='6'/>
+ <enumerator name='YAML_PARSE_FLOW_NODE_STATE' value='7'/>
+ <enumerator name='YAML_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE' value='8'/>
+ <enumerator name='YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE' value='9'/>
+ <enumerator name='YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE' value='10'/>
+ <enumerator name='YAML_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE' value='11'/>
+ <enumerator name='YAML_PARSE_BLOCK_MAPPING_KEY_STATE' value='12'/>
+ <enumerator name='YAML_PARSE_BLOCK_MAPPING_VALUE_STATE' value='13'/>
+ <enumerator name='YAML_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE' value='14'/>
+ <enumerator name='YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE' value='15'/>
+ <enumerator name='YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE' value='16'/>
+ <enumerator name='YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE' value='17'/>
+ <enumerator name='YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE' value='18'/>
+ <enumerator name='YAML_PARSE_FLOW_MAPPING_FIRST_KEY_STATE' value='19'/>
+ <enumerator name='YAML_PARSE_FLOW_MAPPING_KEY_STATE' value='20'/>
+ <enumerator name='YAML_PARSE_FLOW_MAPPING_VALUE_STATE' value='21'/>
+ <enumerator name='YAML_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE' value='22'/>
+ <enumerator name='YAML_PARSE_END_STATE' value='23'/>
+ </enum-decl>
+ <typedef-decl name='yaml_parser_state_t' type-id='type-id-364' filepath='/usr/include/yaml.h' line='1061' column='1' id='type-id-365'/>
+ <class-decl name='yaml_alias_data_s' size-in-bits='320' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='1067' column='1' id='type-id-366'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='anchor' type-id='type-id-142' visibility='default' filepath='/usr/include/yaml.h' line='1069' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='index' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1071' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='1073' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_alias_data_t' type-id='type-id-366' filepath='/usr/include/yaml.h' line='1074' column='1' id='type-id-367'/>
+ <class-decl name='yaml_parser_s' size-in-bits='3840' is-struct='yes' visibility='default' filepath='/usr/include/yaml.h' line='1083' column='1' id='type-id-368'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='error' type-id='type-id-259' visibility='default' filepath='/usr/include/yaml.h' line='1091' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='problem' type-id='type-id-108' visibility='default' filepath='/usr/include/yaml.h' line='1093' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='problem_offset' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1095' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='problem_value' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1097' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='problem_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='1099' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='context' type-id='type-id-108' visibility='default' filepath='/usr/include/yaml.h' line='1101' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='context_mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='1103' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='704'>
+ <var-decl name='read_handler' type-id='type-id-369' visibility='default' filepath='/usr/include/yaml.h' line='1115' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='768'>
+ <var-decl name='read_handler_data' type-id='type-id-125' visibility='default' filepath='/usr/include/yaml.h' line='1118' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='832'>
+ <var-decl name='input' type-id='type-id-370' visibility='default' filepath='/usr/include/yaml.h' line='1134' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1024'>
+ <var-decl name='eof' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1137' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1088'>
+ <var-decl name='buffer' type-id='type-id-281' visibility='default' filepath='/usr/include/yaml.h' line='1149' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1344'>
+ <var-decl name='unread' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1152' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1408'>
+ <var-decl name='raw_buffer' type-id='type-id-282' visibility='default' filepath='/usr/include/yaml.h' line='1164' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1664'>
+ <var-decl name='encoding' type-id='type-id-255' visibility='default' filepath='/usr/include/yaml.h' line='1167' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1728'>
+ <var-decl name='offset' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1170' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1792'>
+ <var-decl name='mark' type-id='type-id-145' visibility='default' filepath='/usr/include/yaml.h' line='1173' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='1984'>
+ <var-decl name='stream_start_produced' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1185' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2016'>
+ <var-decl name='stream_end_produced' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1188' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2048'>
+ <var-decl name='flow_level' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1191' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2112'>
+ <var-decl name='tokens' type-id='type-id-371' visibility='default' filepath='/usr/include/yaml.h' line='1203' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2368'>
+ <var-decl name='tokens_parsed' type-id='type-id-137' visibility='default' filepath='/usr/include/yaml.h' line='1206' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2432'>
+ <var-decl name='token_available' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1209' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2496'>
+ <var-decl name='indents' type-id='type-id-285' visibility='default' filepath='/usr/include/yaml.h' line='1219' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2688'>
+ <var-decl name='indent' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1222' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2720'>
+ <var-decl name='simple_key_allowed' type-id='type-id-16' visibility='default' filepath='/usr/include/yaml.h' line='1225' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2752'>
+ <var-decl name='simple_keys' type-id='type-id-372' visibility='default' filepath='/usr/include/yaml.h' line='1235' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='2944'>
+ <var-decl name='states' type-id='type-id-373' visibility='default' filepath='/usr/include/yaml.h' line='1254' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3136'>
+ <var-decl name='state' type-id='type-id-365' visibility='default' filepath='/usr/include/yaml.h' line='1257' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3200'>
+ <var-decl name='marks' type-id='type-id-374' visibility='default' filepath='/usr/include/yaml.h' line='1267' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3392'>
+ <var-decl name='tag_directives' type-id='type-id-286' visibility='default' filepath='/usr/include/yaml.h' line='1277' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3584'>
+ <var-decl name='aliases' type-id='type-id-375' visibility='default' filepath='/usr/include/yaml.h' line='1296' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='3776'>
+ <var-decl name='document' type-id='type-id-291' visibility='default' filepath='/usr/include/yaml.h' line='1299' column='1'/>
+ </data-member>
+ </class-decl>
+ <union-decl name='__anonymous_union__' size-in-bits='192' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1121' column='1' id='type-id-370'>
+ <data-member access='public'>
+ <var-decl name='string' type-id='type-id-376' visibility='default' filepath='/usr/include/yaml.h' line='1130' column='1'/>
+ </data-member>
+ <data-member access='public'>
+ <var-decl name='file' type-id='type-id-173' visibility='default' filepath='/usr/include/yaml.h' line='1133' column='1'/>
+ </data-member>
+ </union-decl>
+ <class-decl name='__anonymous_struct__' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1123' column='1' id='type-id-376'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-327' visibility='default' filepath='/usr/include/yaml.h' line='1125' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-327' visibility='default' filepath='/usr/include/yaml.h' line='1127' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='current' type-id='type-id-327' visibility='default' filepath='/usr/include/yaml.h' line='1129' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__3' size-in-bits='256' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1194' column='1' id='type-id-371'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-377' visibility='default' filepath='/usr/include/yaml.h' line='1196' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-377' visibility='default' filepath='/usr/include/yaml.h' line='1198' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='head' type-id='type-id-377' visibility='default' filepath='/usr/include/yaml.h' line='1200' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='tail' type-id='type-id-377' visibility='default' filepath='/usr/include/yaml.h' line='1202' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__12' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1228' column='1' id='type-id-372'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-378' visibility='default' filepath='/usr/include/yaml.h' line='1230' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-378' visibility='default' filepath='/usr/include/yaml.h' line='1232' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-378' visibility='default' filepath='/usr/include/yaml.h' line='1234' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__13' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1247' column='1' id='type-id-373'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-379' visibility='default' filepath='/usr/include/yaml.h' line='1249' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-379' visibility='default' filepath='/usr/include/yaml.h' line='1251' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-379' visibility='default' filepath='/usr/include/yaml.h' line='1253' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__14' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1260' column='1' id='type-id-374'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-380' visibility='default' filepath='/usr/include/yaml.h' line='1262' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-380' visibility='default' filepath='/usr/include/yaml.h' line='1264' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-380' visibility='default' filepath='/usr/include/yaml.h' line='1266' column='1'/>
+ </data-member>
+ </class-decl>
+ <class-decl name='__anonymous_struct__16' size-in-bits='192' is-struct='yes' is-anonymous='yes' visibility='default' filepath='/usr/include/yaml.h' line='1289' column='1' id='type-id-375'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='start' type-id='type-id-381' visibility='default' filepath='/usr/include/yaml.h' line='1291' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='end' type-id='type-id-381' visibility='default' filepath='/usr/include/yaml.h' line='1293' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='top' type-id='type-id-381' visibility='default' filepath='/usr/include/yaml.h' line='1295' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='yaml_parser_t' type-id='type-id-368' filepath='/usr/include/yaml.h' line='1305' column='1' id='type-id-382'/>
+ <pointer-type-def type-id='type-id-105' size-in-bits='64' id='type-id-383'/>
+ <pointer-type-def type-id='type-id-106' size-in-bits='64' id='type-id-384'/>
+ <pointer-type-def type-id='type-id-107' size-in-bits='64' id='type-id-385'/>
+ <pointer-type-def type-id='type-id-63' size-in-bits='64' id='type-id-386'/>
+ <pointer-type-def type-id='type-id-103' size-in-bits='64' id='type-id-387'/>
+ <pointer-type-def type-id='type-id-104' size-in-bits='64' id='type-id-388'/>
+ <pointer-type-def type-id='type-id-41' size-in-bits='64' id='type-id-389'/>
+ <qualified-type-def type-id='type-id-389' restrict='yes' id='type-id-330'/>
+ <qualified-type-def type-id='type-id-35' const='yes' id='type-id-390'/>
+ <pointer-type-def type-id='type-id-390' size-in-bits='64' id='type-id-391'/>
+ <qualified-type-def type-id='type-id-347' const='yes' id='type-id-392'/>
+ <pointer-type-def type-id='type-id-392' size-in-bits='64' id='type-id-393'/>
+ <qualified-type-def type-id='type-id-393' restrict='yes' id='type-id-394'/>
+ <qualified-type-def type-id='type-id-155' const='yes' id='type-id-395'/>
+ <pointer-type-def type-id='type-id-395' size-in-bits='64' id='type-id-396'/>
+ <qualified-type-def type-id='type-id-382' const='yes' id='type-id-397'/>
+ <pointer-type-def type-id='type-id-397' size-in-bits='64' id='type-id-398'/>
+ <pointer-type-def type-id='type-id-340' size-in-bits='64' id='type-id-346'/>
+ <pointer-type-def type-id='type-id-347' size-in-bits='64' id='type-id-399'/>
+ <qualified-type-def type-id='type-id-399' restrict='yes' id='type-id-400'/>
+ <pointer-type-def type-id='type-id-349' size-in-bits='64' id='type-id-401'/>
+ <qualified-type-def type-id='type-id-401' restrict='yes' id='type-id-402'/>
+ <pointer-type-def type-id='type-id-403' size-in-bits='64' id='type-id-341'/>
+ <pointer-type-def type-id='type-id-367' size-in-bits='64' id='type-id-381'/>
+ <pointer-type-def type-id='type-id-145' size-in-bits='64' id='type-id-380'/>
+ <pointer-type-def type-id='type-id-365' size-in-bits='64' id='type-id-379'/>
+ <pointer-type-def type-id='type-id-382' size-in-bits='64' id='type-id-404'/>
+ <pointer-type-def type-id='type-id-361' size-in-bits='64' id='type-id-369'/>
+ <pointer-type-def type-id='type-id-363' size-in-bits='64' id='type-id-378'/>
+ <pointer-type-def type-id='type-id-359' size-in-bits='64' id='type-id-377'/>
+ <class-decl name='re_dfa_t' is-struct='yes' visibility='default' is-declaration-only='yes' id='type-id-340'/>
+ <function-decl name='parser_error' filepath='../src/error.h' line='29' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-398'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='yaml_error' filepath='../src/error.h' line='32' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-391'/>
+ <parameter type-id='type-id-396'/>
+ <parameter type-id='type-id-175'/>
+ <parameter type-id='type-id-108'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_parser_load_yaml_from_fd' mangled-name='netplan_parser_load_yaml_from_fd' filepath='../src/parse.c' line='3137' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parser_load_yaml_from_fd'>
+ <parameter type-id='type-id-176' name='npp' filepath='../src/parse.c' line='3137' column='1'/>
+ <parameter type-id='type-id-16' name='fd' filepath='../src/parse.c' line='3137' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/parse.c' line='3137' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_parser_load_nullable_fields' mangled-name='netplan_parser_load_nullable_fields' filepath='../src/parse.c' line='3367' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_parser_load_nullable_fields'>
+ <parameter type-id='type-id-176' name='npp' filepath='../src/parse.c' line='3367' column='1'/>
+ <parameter type-id='type-id-16' name='input_fd' filepath='../src/parse.c' line='3367' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/parse.c' line='3367' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <var-decl name='NETPLAN_OPTIONAL_ADDRESS_TYPES' type-id='type-id-334' mangled-name='NETPLAN_OPTIONAL_ADDRESS_TYPES' visibility='default' filepath='../src/types.h' line='61' column='1' elf-symbol-id='NETPLAN_OPTIONAL_ADDRESS_TYPES'/>
+ <var-decl name='NETPLAN_WIFI_WOWLAN_TYPES' type-id='type-id-339' mangled-name='NETPLAN_WIFI_WOWLAN_TYPES' visibility='default' filepath='../src/types.h' line='63' column='1' elf-symbol-id='NETPLAN_WIFI_WOWLAN_TYPES'/>
+ <function-decl name='reset_netdef' filepath='../src/types.h' line='255' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-75'/>
+ <parameter type-id='type-id-13'/>
+ <parameter type-id='type-id-31'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='reset_ip_rule' filepath='../src/types.h' line='258' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-107'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='reset_ovs_settings' filepath='../src/types.h' line='261' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-386'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='reset_vxlan' filepath='../src/types.h' line='264' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-83'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='access_point_clear' filepath='../src/types.h' line='267' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-387'/>
+ <parameter type-id='type-id-31'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='wireguard_peer_clear' filepath='../src/types.h' line='270' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-388'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='address_options_clear' filepath='../src/types.h' line='273' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-383'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='ip_rule_clear' filepath='../src/types.h' line='276' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-385'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='route_clear' filepath='../src/types.h' line='279' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-384'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='mark_data_as_dirty' filepath='../src/util-internal.h' line='69' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-176'/>
+ <parameter type-id='type-id-125'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='is_ip4_address' filepath='../src/validation.h' line='25' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='is_ip6_address' filepath='../src/validation.h' line='26' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='is_hostname' filepath='../src/validation.h' line='27' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='validate_ovs_target' filepath='../src/validation.h' line='28' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-40'/>
+ <parameter type-id='type-id-111'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='validate_netdef_grammar' filepath='../src/validation.h' line='34' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-391'/>
+ <parameter type-id='type-id-75'/>
+ <parameter type-id='type-id-171'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='validate_backend_rules' filepath='../src/validation.h' line='37' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-391'/>
+ <parameter type-id='type-id-75'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='validate_sriov_rules' filepath='../src/validation.h' line='40' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-391'/>
+ <parameter type-id='type-id-75'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='validate_default_route_consistency' filepath='../src/validation.h' line='43' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-391'/>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='adopt_and_validate_vrf_routes' filepath='../src/validation.h' line='46' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-391'/>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_datalist_init' filepath='/usr/include/glib-2.0/glib/gdataset.h' line='45' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-299'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_datalist_id_set_data_full' filepath='/usr/include/glib-2.0/glib/gdataset.h' line='52' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-299'/>
+ <parameter type-id='type-id-115'/>
+ <parameter type-id='type-id-120'/>
+ <parameter type-id='type-id-251'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_clear_error' filepath='/usr/include/glib-2.0/glib/gerror.h' line='239' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_hash_table_new' filepath='/usr/include/glib-2.0/glib/ghash.h' line='57' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-253'/>
+ <parameter type-id='type-id-249'/>
+ <return type-id='type-id-62'/>
+ </function-decl>
+ <function-decl name='g_hash_table_add' filepath='/usr/include/glib-2.0/glib/ghash.h' line='77' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_hash_table_remove' filepath='/usr/include/glib-2.0/glib/ghash.h' line='80' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_hash_table_lookup_extended' filepath='/usr/include/glib-2.0/glib/ghash.h' line='101' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-126'/>
+ <parameter type-id='type-id-304'/>
+ <parameter type-id='type-id-304'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_hash_table_foreach_steal' filepath='/usr/include/glib-2.0/glib/ghash.h' line='118' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <parameter type-id='type-id-342'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-42'/>
+ </function-decl>
+ <function-decl name='g_list_free_full' filepath='/usr/include/glib-2.0/glib/glist.h' line='56' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-99'/>
+ <parameter type-id='type-id-251'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_list_prepend' filepath='/usr/include/glib-2.0/glib/glist.h' line='62' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-99'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-99'/>
+ </function-decl>
+ <function-decl name='g_list_concat' filepath='/usr/include/glib-2.0/glib/glist.h' line='86' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-99'/>
+ <parameter type-id='type-id-99'/>
+ <return type-id='type-id-99'/>
+ </function-decl>
+ <function-decl name='g_list_length' filepath='/usr/include/glib-2.0/glib/glist.h' line='134' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-99'/>
+ <return type-id='type-id-42'/>
+ </function-decl>
+ <function-decl name='g_malloc0_n' filepath='/usr/include/glib-2.0/glib/gmem.h' line='97' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-221'/>
+ <parameter type-id='type-id-221'/>
+ <return type-id='type-id-120'/>
+ </function-decl>
+ <function-decl name='g_quark_from_string' filepath='/usr/include/glib-2.0/glib/gquark.h' line='45' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-115'/>
+ </function-decl>
+ <function-decl name='g_strerror' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='112' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-116'/>
+ <return type-id='type-id-185'/>
+ </function-decl>
+ <function-decl name='g_strrstr' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='130' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_assertion_message_cmpstr' filepath='/usr/include/glib-2.0/glib/gtestutils.h' line='546' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='regcomp' filepath='/usr/include/regex.h' line='675' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-400'/>
+ <parameter type-id='type-id-303'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='regexec' filepath='/usr/include/regex.h' line='679' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-394'/>
+ <parameter type-id='type-id-303'/>
+ <parameter type-id='type-id-137'/>
+ <parameter type-id='type-id-402'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='strtol' filepath='/usr/include/stdlib.h' line='177' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-303'/>
+ <parameter type-id='type-id-330'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-17'/>
+ </function-decl>
+ <function-decl name='memset' filepath='/usr/include/string.h' line='61' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-125'/>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-137'/>
+ <return type-id='type-id-125'/>
+ </function-decl>
+ <function-decl name='strrchr' filepath='/usr/include/string.h' line='273' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-41'/>
+ </function-decl>
+ <function-decl name='yaml_document_delete' filepath='/usr/include/yaml.h' line='847' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-291'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='yaml_document_get_node' filepath='/usr/include/yaml.h' line='862' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-291'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-171'/>
+ </function-decl>
+ <function-decl name='yaml_document_get_root_node' filepath='/usr/include/yaml.h' line='881' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-291'/>
+ <return type-id='type-id-171'/>
+ </function-decl>
+ <function-decl name='yaml_parser_initialize' filepath='/usr/include/yaml.h' line='1319' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-404'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_parser_delete' filepath='/usr/include/yaml.h' line='1328' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-404'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='yaml_parser_set_input_file' filepath='/usr/include/yaml.h' line='1357' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-404'/>
+ <parameter type-id='type-id-173'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='yaml_parser_load' filepath='/usr/include/yaml.h' line='1453' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-404'/>
+ <parameter type-id='type-id-291'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-type size-in-bits='64' id='type-id-360'>
+ <parameter type-id='type-id-125'/>
+ <parameter type-id='type-id-293'/>
+ <parameter type-id='type-id-137'/>
+ <parameter type-id='type-id-294'/>
+ <return type-id='type-id-16'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-403'>
+ <parameter type-id='type-id-120'/>
+ <parameter type-id='type-id-120'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-40'/>
+ </function-type>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/sriov.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <function-decl name='netplan_state_finish_sriov_write' mangled-name='netplan_state_finish_sriov_write' filepath='../src/sriov.c' line='68' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_state_finish_sriov_write'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/sriov.c' line='68' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/sriov.c' line='68' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/sriov.c' line='68' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_sriov_cleanup' mangled-name='netplan_sriov_cleanup' filepath='../src/sriov.c' line='118' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_sriov_cleanup'>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/sriov.c' line='118' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='_netplan_state_get_vf_count_for_def' mangled-name='_netplan_state_get_vf_count_for_def' filepath='../src/sriov.c' line='127' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_state_get_vf_count_for_def'>
+ <parameter type-id='type-id-182' name='np_state' filepath='../src/sriov.c' line='127' column='1'/>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/sriov.c' line='127' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/sriov.c' line='127' column='1'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='g_string_truncate' filepath='/usr/include/glib-2.0/glib/gstring.h' line='69' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-233'/>
+ <parameter type-id='type-id-221'/>
+ <return type-id='type-id-233'/>
+ </function-decl>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/types.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <typedef-decl name='__ssize_t' type-id='type-id-17' filepath='/usr/include/x86_64-linux-gnu/bits/types.h' line='194' column='1' id='type-id-405'/>
+ <typedef-decl name='ssize_t' type-id='type-id-405' filepath='/usr/include/x86_64-linux-gnu/sys/types.h' line='108' column='1' id='type-id-406'/>
+ <function-decl name='netplan_netdef_get_filepath' mangled-name='netplan_netdef_get_filepath' filepath='../src/types.c' line='483' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_get_filepath'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='483' column='1'/>
+ <parameter type-id='type-id-41' name='out_buffer' filepath='../src/types.c' line='483' column='1'/>
+ <parameter type-id='type-id-137' name='out_buf_size' filepath='../src/types.c' line='483' column='1'/>
+ <return type-id='type-id-406'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_get_backend' mangled-name='netplan_netdef_get_backend' filepath='../src/types.c' line='490' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_get_backend'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='490' column='1'/>
+ <return type-id='type-id-31'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_get_type' mangled-name='netplan_netdef_get_type' filepath='../src/types.c' line='496' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_get_type'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='496' column='1'/>
+ <return type-id='type-id-13'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_get_id' mangled-name='netplan_netdef_get_id' filepath='../src/types.c' line='502' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_get_id'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='502' column='1'/>
+ <parameter type-id='type-id-41' name='out_buffer' filepath='../src/types.c' line='502' column='1'/>
+ <parameter type-id='type-id-137' name='out_buf_size' filepath='../src/types.c' line='502' column='1'/>
+ <return type-id='type-id-406'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_get_embedded_switch_mode' mangled-name='netplan_netdef_get_embedded_switch_mode' filepath='../src/types.c' line='516' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_get_embedded_switch_mode'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='516' column='1'/>
+ <return type-id='type-id-108'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_get_delay_virtual_functions_rebind' mangled-name='netplan_netdef_get_delay_virtual_functions_rebind' filepath='../src/types.c' line='523' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_get_delay_virtual_functions_rebind'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='523' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_has_match' mangled-name='netplan_netdef_has_match' filepath='../src/types.c' line='530' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_has_match'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='530' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='_netplan_netdef_get_sriov_link' mangled-name='_netplan_netdef_get_sriov_link' filepath='../src/types.c' line='536' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_netdef_get_sriov_link'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='536' column='1'/>
+ <return type-id='type-id-75'/>
+ </function-decl>
+ <function-decl name='_netplan_netdef_get_sriov_vlan_filter' mangled-name='_netplan_netdef_get_sriov_vlan_filter' filepath='../src/types.c' line='542' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_netdef_get_sriov_vlan_filter'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='542' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='_netplan_netdef_get_vlan_link' mangled-name='_netplan_netdef_get_vlan_link' filepath='../src/types.c' line='547' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_netdef_get_vlan_link'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='547' column='1'/>
+ <return type-id='type-id-75'/>
+ </function-decl>
+ <function-decl name='_netplan_netdef_get_vlan_id' mangled-name='_netplan_netdef_get_vlan_id' filepath='../src/types.c' line='553' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_netdef_get_vlan_id'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='553' column='1'/>
+ <return type-id='type-id-42'/>
+ </function-decl>
+ <function-decl name='_netplan_netdef_get_critical' mangled-name='_netplan_netdef_get_critical' filepath='../src/types.c' line='559' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_netdef_get_critical'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='559' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='_netplan_netdef_is_trivial_compound_itf' mangled-name='_netplan_netdef_is_trivial_compound_itf' filepath='../src/types.c' line='565' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_netdef_is_trivial_compound_itf'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/types.c' line='565' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_copy_string' filepath='../src/util-internal.h' line='96' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-41'/>
+ <parameter type-id='type-id-137'/>
+ <return type-id='type-id-406'/>
+ </function-decl>
+ <function-decl name='g_datalist_clear' filepath='/usr/include/glib-2.0/glib/gdataset.h' line='47' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-299'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/util.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <typedef-decl name='GSpawnChildSetupFunc' type-id='type-id-250' filepath='/usr/include/glib-2.0/glib/gspawn.h' line='138' column='1' id='type-id-407'/>
+ <enum-decl name='GSpawnFlags' naming-typedef-id='type-id-408' filepath='/usr/include/glib-2.0/glib/gspawn.h' line='169' column='1' id='type-id-409'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='G_SPAWN_DEFAULT' value='0'/>
+ <enumerator name='G_SPAWN_LEAVE_DESCRIPTORS_OPEN' value='1'/>
+ <enumerator name='G_SPAWN_DO_NOT_REAP_CHILD' value='2'/>
+ <enumerator name='G_SPAWN_SEARCH_PATH' value='4'/>
+ <enumerator name='G_SPAWN_STDOUT_TO_DEV_NULL' value='8'/>
+ <enumerator name='G_SPAWN_STDERR_TO_DEV_NULL' value='16'/>
+ <enumerator name='G_SPAWN_CHILD_INHERITS_STDIN' value='32'/>
+ <enumerator name='G_SPAWN_FILE_AND_ARGV_ZERO' value='64'/>
+ <enumerator name='G_SPAWN_SEARCH_PATH_FROM_ENVP' value='128'/>
+ <enumerator name='G_SPAWN_CLOEXEC_PIPES' value='256'/>
+ </enum-decl>
+ <typedef-decl name='GSpawnFlags' type-id='type-id-409' filepath='/usr/include/glib-2.0/glib/gspawn.h' line='182' column='1' id='type-id-408'/>
+ <class-decl name='glob_t' size-in-bits='576' is-struct='yes' naming-typedef-id='type-id-410' visibility='default' filepath='/usr/include/glob.h' line='82' column='1' id='type-id-411'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='gl_pathc' type-id='type-id-137' visibility='default' filepath='/usr/include/glob.h' line='84' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='gl_pathv' type-id='type-id-389' visibility='default' filepath='/usr/include/glob.h' line='85' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='128'>
+ <var-decl name='gl_offs' type-id='type-id-137' visibility='default' filepath='/usr/include/glob.h' line='86' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='192'>
+ <var-decl name='gl_flags' type-id='type-id-16' visibility='default' filepath='/usr/include/glob.h' line='87' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='256'>
+ <var-decl name='gl_closedir' type-id='type-id-412' visibility='default' filepath='/usr/include/glob.h' line='91' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='320'>
+ <var-decl name='gl_readdir' type-id='type-id-413' visibility='default' filepath='/usr/include/glob.h' line='95' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='384'>
+ <var-decl name='gl_opendir' type-id='type-id-414' visibility='default' filepath='/usr/include/glob.h' line='97' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='448'>
+ <var-decl name='gl_lstat' type-id='type-id-415' visibility='default' filepath='/usr/include/glob.h' line='102' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='512'>
+ <var-decl name='gl_stat' type-id='type-id-415' visibility='default' filepath='/usr/include/glob.h' line='103' column='1'/>
+ </data-member>
+ </class-decl>
+ <typedef-decl name='glob_t' type-id='type-id-411' filepath='/usr/include/glob.h' line='105' column='1' id='type-id-410'/>
+ <typedef-decl name='uint32_t' type-id='type-id-416' filepath='/usr/include/x86_64-linux-gnu/bits/stdint-uintn.h' line='26' column='1' id='type-id-417'/>
+ <typedef-decl name='__uint32_t' type-id='type-id-23' filepath='/usr/include/x86_64-linux-gnu/bits/types.h' line='42' column='1' id='type-id-416'/>
+ <qualified-type-def type-id='type-id-41' restrict='yes' id='type-id-418'/>
+ <pointer-type-def type-id='type-id-116' size-in-bits='64' id='type-id-419'/>
+ <pointer-type-def type-id='type-id-410' size-in-bits='64' id='type-id-420'/>
+ <qualified-type-def type-id='type-id-420' restrict='yes' id='type-id-421'/>
+ <pointer-type-def type-id='type-id-422' size-in-bits='64' id='type-id-415'/>
+ <pointer-type-def type-id='type-id-423' size-in-bits='64' id='type-id-424'/>
+ <pointer-type-def type-id='type-id-425' size-in-bits='64' id='type-id-412'/>
+ <pointer-type-def type-id='type-id-426' size-in-bits='64' id='type-id-414'/>
+ <pointer-type-def type-id='type-id-427' size-in-bits='64' id='type-id-413'/>
+ <qualified-type-def type-id='type-id-125' restrict='yes' id='type-id-428'/>
+ <function-decl name='netplan_def_type_from_name' filepath='../src/names.h' line='57' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-13'/>
+ </function-decl>
+ <var-decl name='wifi_frequency_24' type-id='type-id-62' mangled-name='wifi_frequency_24' visibility='default' filepath='../src/util-internal.h' line='31' column='1' elf-symbol-id='wifi_frequency_24'/>
+ <var-decl name='wifi_frequency_5' type-id='type-id-62' mangled-name='wifi_frequency_5' visibility='default' filepath='../src/util-internal.h' line='34' column='1' elf-symbol-id='wifi_frequency_5'/>
+ <function-decl name='find_yaml_glob' mangled-name='find_yaml_glob' filepath='../src/util.c' line='112' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='find_yaml_glob'>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/util.c' line='112' column='1'/>
+ <parameter type-id='type-id-420' name='out_glob' filepath='../src/util.c' line='112' column='1'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='netplan_util_create_yaml_patch' mangled-name='netplan_util_create_yaml_patch' filepath='../src/util.c' line='142' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_util_create_yaml_patch'>
+ <parameter type-id='type-id-108' name='conf_obj_path' filepath='../src/util.c' line='142' column='1'/>
+ <parameter type-id='type-id-108' name='obj_payload' filepath='../src/util.c' line='142' column='1'/>
+ <parameter type-id='type-id-16' name='output_fd' filepath='../src/util.c' line='142' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/util.c' line='142' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_util_dump_yaml_subtree' mangled-name='netplan_util_dump_yaml_subtree' filepath='../src/util.c' line='311' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_util_dump_yaml_subtree'>
+ <parameter type-id='type-id-108' name='prefix' filepath='../src/util.c' line='311' column='1'/>
+ <parameter type-id='type-id-16' name='input_fd' filepath='../src/util.c' line='311' column='1'/>
+ <parameter type-id='type-id-16' name='output_fd' filepath='../src/util.c' line='311' column='1'/>
+ <parameter type-id='type-id-175' name='error' filepath='../src/util.c' line='311' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_delete_connection' mangled-name='netplan_delete_connection' filepath='../src/util.c' line='485' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_delete_connection'>
+ <parameter type-id='type-id-108' name='id' filepath='../src/util.c' line='485' column='1'/>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/util.c' line='485' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_generate' mangled-name='netplan_generate' filepath='../src/util.c' line='540' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_generate'>
+ <parameter type-id='type-id-108' name='rootdir' filepath='../src/util.c' line='540' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='_netplan_netdef_pertype_iter_next' mangled-name='_netplan_netdef_pertype_iter_next' filepath='../src/util.c' line='655' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_netdef_pertype_iter_next'>
+ <parameter type-id='type-id-186' name='it' filepath='../src/util.c' line='655' column='1'/>
+ <return type-id='type-id-75'/>
+ </function-decl>
+ <function-decl name='_netplan_netdef_pertype_iter_free' mangled-name='_netplan_netdef_pertype_iter_free' filepath='../src/util.c' line='671' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_netplan_netdef_pertype_iter_free'>
+ <parameter type-id='type-id-186' name='it' filepath='../src/util.c' line='671' column='1'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_match_interface' mangled-name='netplan_netdef_match_interface' filepath='../src/util.c' line='750' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_match_interface'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/util.c' line='750' column='1'/>
+ <parameter type-id='type-id-108' name='name' filepath='../src/util.c' line='750' column='1'/>
+ <parameter type-id='type-id-108' name='mac' filepath='../src/util.c' line='750' column='1'/>
+ <parameter type-id='type-id-108' name='driver_name' filepath='../src/util.c' line='750' column='1'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='netplan_netdef_get_set_name' mangled-name='netplan_netdef_get_set_name' filepath='../src/util.c' line='785' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='netplan_netdef_get_set_name'>
+ <parameter type-id='type-id-180' name='netdef' filepath='../src/util.c' line='785' column='1'/>
+ <parameter type-id='type-id-41' name='out_buf' filepath='../src/util.c' line='785' column='1'/>
+ <parameter type-id='type-id-137' name='out_size' filepath='../src/util.c' line='785' column='1'/>
+ <return type-id='type-id-406'/>
+ </function-decl>
+ <function-decl name='inet_pton' filepath='/usr/include/arpa/inet.h' line='58' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-16'/>
+ <parameter type-id='type-id-303'/>
+ <parameter type-id='type-id-428'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='fnmatch' filepath='/usr/include/fnmatch.h' line='56' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='g_error_free' filepath='/usr/include/glib-2.0/glib/gerror.h' line='206' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-174'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_file_set_contents' filepath='/usr/include/glib-2.0/glib/gfileutils.h' line='123' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-315'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_mkdir_with_parents' filepath='/usr/include/glib-2.0/glib/gfileutils.h' line='182' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-116'/>
+ <return type-id='type-id-116'/>
+ </function-decl>
+ <function-decl name='g_path_get_basename' filepath='/usr/include/glib-2.0/glib/gfileutils.h' line='211' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_path_get_dirname' filepath='/usr/include/glib-2.0/glib/gfileutils.h' line='213' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_hash_table_get_keys' filepath='/usr/include/glib-2.0/glib/ghash.h' line='124' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <return type-id='type-id-99'/>
+ </function-decl>
+ <function-decl name='g_hash_table_unref' filepath='/usr/include/glib-2.0/glib/ghash.h' line='151' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-62'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_direct_hash' filepath='/usr/include/glib-2.0/glib/ghash.h' line='183' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-42'/>
+ </function-decl>
+ <function-decl name='g_direct_equal' filepath='/usr/include/glib-2.0/glib/ghash.h' line='185' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-126'/>
+ <parameter type-id='type-id-126'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_list_sort' filepath='/usr/include/glib-2.0/glib/glist.h' line='140' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-99'/>
+ <parameter type-id='type-id-247'/>
+ <return type-id='type-id-99'/>
+ </function-decl>
+ <function-decl name='g_malloc0' filepath='/usr/include/glib-2.0/glib/gmem.h' line='81' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-221'/>
+ <return type-id='type-id-120'/>
+ </function-decl>
+ <function-decl name='g_spawn_sync' filepath='/usr/include/glib-2.0/glib/gspawn.h' line='254' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-236'/>
+ <parameter type-id='type-id-236'/>
+ <parameter type-id='type-id-408'/>
+ <parameter type-id='type-id-407'/>
+ <parameter type-id='type-id-120'/>
+ <parameter type-id='type-id-236'/>
+ <parameter type-id='type-id-236'/>
+ <parameter type-id='type-id-419'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_spawn_check_wait_status' filepath='/usr/include/glib-2.0/glib/gspawn.h' line='276' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-116'/>
+ <parameter type-id='type-id-175'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_strchug' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='176' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-111'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_strchomp' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='179' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-111'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_strndup' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='225' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-221'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='g_strsplit_set' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='279' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-116'/>
+ <return type-id='type-id-236'/>
+ </function-decl>
+ <function-decl name='ntohl' filepath='/usr/include/netinet/in.h' line='382' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-417'/>
+ <return type-id='type-id-417'/>
+ </function-decl>
+ <function-decl name='fflush' filepath='/usr/include/stdio.h' line='230' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-173'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='fseek' filepath='/usr/include/stdio.h' line='713' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-173'/>
+ <parameter type-id='type-id-17'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='getenv' filepath='/usr/include/stdlib.h' line='641' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-41'/>
+ </function-decl>
+ <function-decl name='stpncpy' filepath='/usr/include/string.h' line='499' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-418'/>
+ <parameter type-id='type-id-303'/>
+ <parameter type-id='type-id-137'/>
+ <return type-id='type-id-41'/>
+ </function-decl>
+ <function-decl name='yaml_parser_set_input_string' filepath='/usr/include/yaml.h' line='1343' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-404'/>
+ <parameter type-id='type-id-327'/>
+ <parameter type-id='type-id-137'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='yaml_parser_parse' filepath='/usr/include/yaml.h' line='1428' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-404'/>
+ <parameter type-id='type-id-296'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='yaml_emitter_flush' filepath='/usr/include/yaml.h' line='1969' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-310'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-type size-in-bits='64' id='type-id-422'>
+ <parameter type-id='type-id-303'/>
+ <parameter type-id='type-id-428'/>
+ <return type-id='type-id-16'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-423'>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-16'/>
+ <return type-id='type-id-16'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-425'>
+ <parameter type-id='type-id-125'/>
+ <return type-id='type-id-26'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-426'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-125'/>
+ </function-type>
+ <function-type size-in-bits='64' id='type-id-427'>
+ <parameter type-id='type-id-125'/>
+ <return type-id='type-id-125'/>
+ </function-type>
+ </abi-instr>
+ <abi-instr address-size='64' path='../src/validation.c' comp-dir-path='/home/lukas/canonical/netplan/build' language='LANG_C99'>
+ <enum-decl name='GFileTest' naming-typedef-id='type-id-429' filepath='/usr/include/glib-2.0/glib/gfileutils.h' line='67' column='1' id='type-id-430'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='G_FILE_TEST_IS_REGULAR' value='1'/>
+ <enumerator name='G_FILE_TEST_IS_SYMLINK' value='2'/>
+ <enumerator name='G_FILE_TEST_IS_DIR' value='4'/>
+ <enumerator name='G_FILE_TEST_IS_EXECUTABLE' value='8'/>
+ <enumerator name='G_FILE_TEST_EXISTS' value='16'/>
+ </enum-decl>
+ <typedef-decl name='GFileTest' type-id='type-id-430' filepath='/usr/include/glib-2.0/glib/gfileutils.h' line='73' column='1' id='type-id-429'/>
+ <enum-decl name='GRegexCompileFlags' naming-typedef-id='type-id-431' filepath='/usr/include/glib-2.0/glib/gregex.h' line='297' column='1' id='type-id-432'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='G_REGEX_CASELESS' value='1'/>
+ <enumerator name='G_REGEX_MULTILINE' value='2'/>
+ <enumerator name='G_REGEX_DOTALL' value='4'/>
+ <enumerator name='G_REGEX_EXTENDED' value='8'/>
+ <enumerator name='G_REGEX_ANCHORED' value='16'/>
+ <enumerator name='G_REGEX_DOLLAR_ENDONLY' value='32'/>
+ <enumerator name='G_REGEX_UNGREEDY' value='512'/>
+ <enumerator name='G_REGEX_RAW' value='2048'/>
+ <enumerator name='G_REGEX_NO_AUTO_CAPTURE' value='4096'/>
+ <enumerator name='G_REGEX_OPTIMIZE' value='8192'/>
+ <enumerator name='G_REGEX_FIRSTLINE' value='262144'/>
+ <enumerator name='G_REGEX_DUPNAMES' value='524288'/>
+ <enumerator name='G_REGEX_NEWLINE_CR' value='1048576'/>
+ <enumerator name='G_REGEX_NEWLINE_LF' value='2097152'/>
+ <enumerator name='G_REGEX_NEWLINE_CRLF' value='3145728'/>
+ <enumerator name='G_REGEX_NEWLINE_ANYCRLF' value='5242880'/>
+ <enumerator name='G_REGEX_BSR_ANYCRLF' value='8388608'/>
+ <enumerator name='G_REGEX_JAVASCRIPT_COMPAT' value='33554432'/>
+ </enum-decl>
+ <typedef-decl name='GRegexCompileFlags' type-id='type-id-432' filepath='/usr/include/glib-2.0/glib/gregex.h' line='316' column='1' id='type-id-431'/>
+ <enum-decl name='GRegexMatchFlags' naming-typedef-id='type-id-433' filepath='/usr/include/glib-2.0/glib/gregex.h' line='387' column='1' id='type-id-434'>
+ <underlying-type type-id='type-id-19'/>
+ <enumerator name='G_REGEX_MATCH_ANCHORED' value='16'/>
+ <enumerator name='G_REGEX_MATCH_NOTBOL' value='128'/>
+ <enumerator name='G_REGEX_MATCH_NOTEOL' value='256'/>
+ <enumerator name='G_REGEX_MATCH_NOTEMPTY' value='1024'/>
+ <enumerator name='G_REGEX_MATCH_PARTIAL' value='32768'/>
+ <enumerator name='G_REGEX_MATCH_NEWLINE_CR' value='1048576'/>
+ <enumerator name='G_REGEX_MATCH_NEWLINE_LF' value='2097152'/>
+ <enumerator name='G_REGEX_MATCH_NEWLINE_CRLF' value='3145728'/>
+ <enumerator name='G_REGEX_MATCH_NEWLINE_ANY' value='4194304'/>
+ <enumerator name='G_REGEX_MATCH_NEWLINE_ANYCRLF' value='5242880'/>
+ <enumerator name='G_REGEX_MATCH_BSR_ANYCRLF' value='8388608'/>
+ <enumerator name='G_REGEX_MATCH_BSR_ANY' value='16777216'/>
+ <enumerator name='G_REGEX_MATCH_PARTIAL_SOFT' value='32768'/>
+ <enumerator name='G_REGEX_MATCH_PARTIAL_HARD' value='134217728'/>
+ <enumerator name='G_REGEX_MATCH_NOTEMPTY_ATSTART' value='268435456'/>
+ </enum-decl>
+ <typedef-decl name='GRegexMatchFlags' type-id='type-id-434' filepath='/usr/include/glib-2.0/glib/gregex.h' line='403' column='1' id='type-id-433'/>
+ <typedef-decl name='GSList' type-id='type-id-435' filepath='/usr/include/glib-2.0/glib/gslist.h' line='37' column='1' id='type-id-436'/>
+ <class-decl name='_GSList' size-in-bits='128' is-struct='yes' visibility='default' filepath='/usr/include/glib-2.0/glib/gslist.h' line='39' column='1' id='type-id-435'>
+ <data-member access='public' layout-offset-in-bits='0'>
+ <var-decl name='data' type-id='type-id-120' visibility='default' filepath='/usr/include/glib-2.0/glib/gslist.h' line='41' column='1'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='64'>
+ <var-decl name='next' type-id='type-id-437' visibility='default' filepath='/usr/include/glib-2.0/glib/gslist.h' line='42' column='1'/>
+ </data-member>
+ </class-decl>
+ <pointer-type-def type-id='type-id-436' size-in-bits='64' id='type-id-437'/>
+ <function-decl name='g_file_test' filepath='/usr/include/glib-2.0/glib/gfileutils.h' line='115' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-429'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_malloc' filepath='/usr/include/glib-2.0/glib/gmem.h' line='79' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-221'/>
+ <return type-id='type-id-120'/>
+ </function-decl>
+ <function-decl name='g_regex_match_simple' filepath='/usr/include/glib-2.0/glib/gregex.h' line='482' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-431'/>
+ <parameter type-id='type-id-433'/>
+ <return type-id='type-id-40'/>
+ </function-decl>
+ <function-decl name='g_slist_free_full' filepath='/usr/include/glib-2.0/glib/gslist.h' line='55' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-437'/>
+ <parameter type-id='type-id-251'/>
+ <return type-id='type-id-26'/>
+ </function-decl>
+ <function-decl name='g_slist_prepend' filepath='/usr/include/glib-2.0/glib/gslist.h' line='61' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-437'/>
+ <parameter type-id='type-id-120'/>
+ <return type-id='type-id-437'/>
+ </function-decl>
+ <function-decl name='g_ascii_strup' filepath='/usr/include/glib-2.0/glib/gstrfuncs.h' line='194' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-185'/>
+ <parameter type-id='type-id-315'/>
+ <return type-id='type-id-111'/>
+ </function-decl>
+ <function-decl name='snprintf' filepath='/usr/include/stdio.h' line='378' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-41'/>
+ <parameter type-id='type-id-137'/>
+ <parameter type-id='type-id-108'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='atoi' filepath='/usr/include/stdlib.h' line='105' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-108'/>
+ <return type-id='type-id-16'/>
+ </function-decl>
+ <function-decl name='strncpy' filepath='/usr/include/string.h' line='144' column='1' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='type-id-41'/>
+ <parameter type-id='type-id-108'/>
+ <parameter type-id='type-id-137'/>
+ <return type-id='type-id-41'/>
+ </function-decl>
+ </abi-instr>
+</abi-corpus>
diff --git a/abi-compat/suppressions.abignore b/abi-compat/suppressions.abignore
new file mode 100644
index 0000000..d248137
--- /dev/null
+++ b/abi-compat/suppressions.abignore
@@ -0,0 +1,9 @@
+# Ignore this type/file/function/variable during ABI checks
+# passed to abidiff using the --suppressions parameter
+#
+# Documentation about the syntax can be found here:
+# https://sourceware.org/libabigail/manual/libabigail-concepts.html#suppression-specifications
+
+[suppress_variable]
+ name = global_state
+ type_name = NetplanState
diff --git a/dbus/meson.build b/dbus/meson.build
new file mode 100644
index 0000000..68a6ce3
--- /dev/null
+++ b/dbus/meson.build
@@ -0,0 +1,22 @@
+executable(
+ 'netplan-dbus',
+ '../src/dbus.c',
+ include_directories: inc,
+ link_with: libnetplan,
+ dependencies: [libsystemd, glib, gio, yaml, uuid],
+ install_dir: join_paths(get_option('libexecdir'), 'netplan'),
+ install: true)
+
+install_data(
+ 'io.netplan.Netplan.conf',
+ install_dir: join_paths(get_option('datadir'), 'dbus-1', 'systemd.d'))
+
+conf_data = configuration_data()
+conf_data.set('ROOTLIBEXECDIR', join_paths(get_option('prefix'), get_option('libexecdir')))
+configure_file(
+ input: 'io.netplan.Netplan.service.in',
+ output: 'io.netplan.Netplan.service',
+ configuration: conf_data,
+ install: true,
+ install_dir: join_paths(get_option('datadir'), 'dbus-1', 'system-services'))
+
diff --git a/debian/changelog b/debian/changelog
index 3377fbe..a362838 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,35 @@
+netplan.io (0.105-1) unstable; urgency=medium
+
+ * Merge new upstream release 0.105 (from tag ubuntu/0.105-0ubuntu1)
+ - Add support for VXLAN tunnels (#288), LP: #1764716
+ - Add support for VRF devices (#285), LP: #1773522
+ - Add support for InfiniBand (IPoIB) (#283), LP: #1848471
+ - Allow key configuration for GRE tunnels (#274), LP: #1966476
+ - Allow setting the regulatory domain (#281), LP: #1951586
+ - Documentation improvements & restructuring (#287)
+ - Add meson build system (#268)
+ - Add abigail ABI compatibility checker (#269)
+ - Update of Fedora RPM spec (#264)
+ - CI improvements (#265, #282)
+ - Netplan `set` uses the consolidated libnetplan YAML parser (#254)
+ - Refactor ConfigManager to use the libnetplan YAML parser (#255)
+ - New `netplan_netdef_get_filepath` API (#275)
+ - Improve NetworkManager device management logic (#276), LP: #1951653
+ Bug fixes:
+ - Fix `apply` netdev rename/create race condition (#260), LP: #1962095
+ - Fix `try` timeout (#271), LP: #1967084
+ - Fix infinite timeouts in ovs-vsctl (#266), Closes: #1000137
+ - Fix offload options using tristate setting (#270), LP: #1956264
+ - Fix rendering of NetworkManager passthrough WPA (#279), LP: #1972800
+ - Fix CLI crash on LibNetplanException (#286)
+ - Fix NetworkManager internal DHCP client lease lookup (#284), LP: #1979674
+ * Update symbols file for 0.105
+ * d/patches/: Drop patches, applied upstream
+ * d/p/autopkgtest-fixes.patch: Drop test quirks (PR#276)
+ * d/control, d/tests/control: suggest/add iw for setting a regulatory domain
+
+ -- Lukas Märdian <luk@slyon.de> Thu, 18 Aug 2022 11:43:30 +0200
+
netplan.io (0.104-2) unstable; urgency=medium
[ Lukas Märdian ]
diff --git a/debian/control b/debian/control
index 45be39e..dd915c8 100644
--- a/debian/control
+++ b/debian/control
@@ -47,6 +47,7 @@ Depends:
Suggests:
network-manager | wpasupplicant,
openvswitch-switch,
+ iw,
Conflicts: netplan
Breaks: nplan (<< 0.34~)
Replaces: nplan (<< 0.34~)
diff --git a/debian/libnetplan0.symbols b/debian/libnetplan0.symbols
index 29d903d..daafb73 100644
--- a/debian/libnetplan0.symbols
+++ b/debian/libnetplan0.symbols
@@ -5,9 +5,16 @@ libnetplan.so.0.0 libnetplan0 #MINVER#
_netplan_iter_defs_per_devtype_free@Base 0.104
_netplan_iter_defs_per_devtype_init@Base 0.104
_netplan_iter_defs_per_devtype_next@Base 0.104
+ _netplan_netdef_get_critical@Base 0.105
+ _netplan_netdef_get_sriov_link@Base 0.105
+ _netplan_netdef_get_sriov_vlan_filter@Base 0.105
+ _netplan_netdef_get_vlan_id@Base 0.105
+ _netplan_netdef_get_vlan_link@Base 0.105
_netplan_netdef_id@Base 0.104
+ _netplan_netdef_is_trivial_compound_itf@Base 0.105
_netplan_netdef_pertype_iter_free@Base 0.104
_netplan_netdef_pertype_iter_next@Base 0.104
+ _netplan_state_get_vf_count_for_def@Base 0.105
_netplan_state_new_netdef_pertype_iter@Base 0.104
_write_netplan_conf@Base 0.102
find_yaml_glob@Base 0.101
@@ -22,16 +29,23 @@ libnetplan.so.0.0 libnetplan0 #MINVER#
netplan_backend_name@Base 0.104
netplan_backend_to_name@Base 0.104
netplan_clear_netdefs@Base 0.101
+ netplan_def_type_name@Base 0.105
netplan_delete_connection@Base 0.102
netplan_finish_parse@Base 0.99
netplan_generate@Base 0.102
netplan_get_filename_by_id@Base 0.102
netplan_get_global_backend@Base 0.99
netplan_get_id_from_nm_filename@Base 0.102
+ netplan_netdef_get_backend@Base 0.105
netplan_netdef_get_delay_virtual_functions_rebind@Base 0.104
netplan_netdef_get_embedded_switch_mode@Base 0.104
netplan_netdef_get_filename@Base 0.104
+ netplan_netdef_get_filepath@Base 0.105
netplan_netdef_get_id@Base 0.104
+ netplan_netdef_get_set_name@Base 0.105
+ netplan_netdef_get_type@Base 0.105
+ netplan_netdef_has_match@Base 0.105
+ netplan_netdef_match_interface@Base 0.105
netplan_netdef_write_network_file@Base 0.104
netplan_netdef_write_networkd@Base 0.104
netplan_netdef_write_nm@Base 0.104
@@ -44,7 +58,9 @@ libnetplan.so.0.0 libnetplan0 #MINVER#
netplan_parse_yaml@Base 0.99
netplan_parser_clear@Base 0.104
netplan_parser_load_keyfile@Base 0.104
+ netplan_parser_load_nullable_fields@Base 0.105
netplan_parser_load_yaml@Base 0.104
+ netplan_parser_load_yaml_from_fd@Base 0.105
netplan_parser_load_yaml_hierarchy@Base 0.104
netplan_parser_new@Base 0.104
netplan_parser_reset@Base 0.104
@@ -60,6 +76,9 @@ libnetplan.so.0.0 libnetplan0 #MINVER#
netplan_state_import_parser_results@Base 0.104
netplan_state_new@Base 0.104
netplan_state_reset@Base 0.104
+ netplan_state_update_yaml_hierarchy@Base 0.105
+ netplan_state_write_yaml_file@Base 0.105
+ netplan_util_create_yaml_patch@Base 0.105
netplan_util_dump_yaml_subtree@Base 0.104
ovs_settings_global@Base 0.100
process_input_file@Base 0.102
diff --git a/debian/patches/0002-cli-apply-fix-potential-race-with-rename-creation-of.patch b/debian/patches/0002-cli-apply-fix-potential-race-with-rename-creation-of.patch
deleted file mode 100644
index 46ec5a4..0000000
--- a/debian/patches/0002-cli-apply-fix-potential-race-with-rename-creation-of.patch
+++ /dev/null
@@ -1,173 +0,0 @@
-From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com>
-Date: Thu, 10 Mar 2022 09:41:58 +0100
-Subject: cli:apply: fix potential race with rename/creation of netdevs and
- start networkd if off (LP: #1962095) (#260)
-
-Calling networkctl_reload() before networkd_interfaces() makes sure that newly created netdevs will show up in the interface list.
-
-Making use of the index number (instead of interface name) ensures that renamed interfaces are properly reconfigured even if they changed their name.
-
-Starting networkd if not active is making sure we can process network changes even if systemd-networkd was stopped before (e.g. by subiquity), see LP#1962095
-
-COMMITS:
-* tests:integration:base: increase debugging output
-* cli:apply: fix potential race with rename/creation of netdevs
-Calling networkctl_reload() before networkd_interfaces() makes sure that newly created netdevs will show up in the interface list.
-Making use of the index number (instead of interface name) ensures that renamed interfaces are properly reconfigured even if they changed their name.
-* tests:regressions: handle networkd inactive fallback (LP: #1962095)
-* cli:apply: start networkd if stopped (LP: #1962095)
-* tests:bonds: do not try to match on MAC while changing it at the same time
-When a MAC address is set for a bond, networkd will set the same MAC address to
-the enslaved interfaces. Therefore we cannot match on the original MAC for the
-ethernet device, as networkd will not find/manage that interface otherwise.
-https://systemd.network/systemd.netdev.html#MACAddress=
----
- netplan/cli/commands/apply.py | 8 +++++++-
- netplan/cli/utils.py | 7 +++++--
- tests/integration/base.py | 7 ++++++-
- tests/integration/bonds.py | 4 +---
- tests/integration/regressions.py | 16 ++++++++++++++++
- tests/test_utils.py | 18 +++++++++++++-----
- 6 files changed, 48 insertions(+), 12 deletions(-)
-
-diff --git a/netplan/cli/commands/apply.py b/netplan/cli/commands/apply.py
-index d481184..b36662a 100644
---- a/netplan/cli/commands/apply.py
-+++ b/netplan/cli/commands/apply.py
-@@ -252,7 +252,13 @@ class NetplanApply(utils.NetplanCommand):
- if not f.endswith('/' + OVS_CLEANUP_SERVICE)]
- # Run 'systemctl start' command synchronously, to avoid race conditions
- # with 'oneshot' systemd service units, e.g. netplan-ovs-*.service.
-- utils.networkctl_reconfigure(utils.networkd_interfaces())
-+ try:
-+ utils.networkctl_reload()
-+ utils.networkctl_reconfigure(utils.networkd_interfaces())
-+ except subprocess.CalledProcessError:
-+ # (re-)start systemd-networkd if it is not running, yet
-+ logging.warning('Falling back to a hard restart of systemd-networkd.service')
-+ utils.systemctl('restart', ['systemd-networkd.service'], sync=True)
- # 1st: execute OVS cleanup, to avoid races while applying OVS config
- utils.systemctl('start', [OVS_CLEANUP_SERVICE], sync=True)
- # 2nd: start all other services
-diff --git a/netplan/cli/utils.py b/netplan/cli/utils.py
-index a05b42b..4fb1dad 100644
---- a/netplan/cli/utils.py
-+++ b/netplan/cli/utils.py
-@@ -95,12 +95,15 @@ def networkd_interfaces():
- for line in out.splitlines():
- s = line.strip().split(' ')
- if s[0].isnumeric() and s[-1] not in ['unmanaged', 'linger']:
-- interfaces.add(s[1])
-+ interfaces.add(s[0])
- return interfaces
-
-
--def networkctl_reconfigure(interfaces):
-+def networkctl_reload():
- subprocess.check_call(['networkctl', 'reload'])
-+
-+
-+def networkctl_reconfigure(interfaces):
- if len(interfaces) >= 1:
- subprocess.check_call(['networkctl', 'reconfigure'] + list(interfaces))
-
-diff --git a/tests/integration/base.py b/tests/integration/base.py
-index fffcaa7..10094dd 100644
---- a/tests/integration/base.py
-+++ b/tests/integration/base.py
-@@ -294,7 +294,12 @@ class IntegrationTestsBase(unittest.TestCase):
- cmd = ['netplan', 'apply']
- if state_dir:
- cmd = cmd + ['--state', state_dir]
-- out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, universal_newlines=True)
-+ out = ''
-+ try:
-+ out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, universal_newlines=True)
-+ except subprocess.CalledProcessError as e:
-+ self.assertTrue(False, 'netplan apply failed: {}'.format(e.output))
-+
- if 'Run \'systemctl daemon-reload\' to reload units.' in out:
- self.fail('systemd units changed without reload')
- # start NM so that we can verify that it does not manage anything
-diff --git a/tests/integration/bonds.py b/tests/integration/bonds.py
-index 763f7e5..2e3773e 100644
---- a/tests/integration/bonds.py
-+++ b/tests/integration/bonds.py
-@@ -315,14 +315,12 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
- ethbn:
- match:
- name: %(ec)s
-- macaddress: %(ec_mac)s
- bonds:
- mybond:
- interfaces: [ethbn]
- macaddress: 00:01:02:03:04:05
- dhcp4: yes''' % {'r': self.backend,
-- 'ec': self.dev_e_client,
-- 'ec_mac': self.dev_e_client_mac})
-+ 'ec': self.dev_e_client})
- self.generate_and_settle([self.dev_e_client, self.state_dhcp4('mybond')])
- self.assert_iface_up(self.dev_e_client, ['master mybond'], ['inet '])
- self.assert_iface_up('mybond', ['inet 192.168.5.[0-9]+/24', '00:01:02:03:04:05'])
-diff --git a/tests/integration/regressions.py b/tests/integration/regressions.py
-index 40aeeac..7d8cabb 100644
---- a/tests/integration/regressions.py
-+++ b/tests/integration/regressions.py
-@@ -118,6 +118,22 @@ r'Press ENTER before the timeout to accept the new configuration\n\n\n'
- r'(Changes will revert in \d+ seconds\n)+'
- r'Reverting\.')
-
-+ def test_apply_networkd_inactive_lp1962095(self):
-+ self.setup_eth(None)
-+ with open(self.config, 'w') as f:
-+ f.write('''network:
-+ ethernets:
-+ %(ec)s:
-+ dhcp4: true
-+ %(e2c)s:
-+ dhcp4: true
-+ version: 2''' % {'r': self.backend, 'ec': self.dev_e_client, 'e2c': self.dev_e2_client})
-+ # stop networkd to simulate the failure case
-+ subprocess.check_call(['systemctl', 'stop', 'systemd-networkd.service', 'systemd-networkd.socket'])
-+ self.generate_and_settle([self.state_dhcp4(self.dev_e_client), self.state_dhcp4(self.dev_e2_client)])
-+ self.assert_iface_up(self.dev_e_client, ['inet 192.168.5.[0-9]+/24'])
-+ self.assert_iface_up(self.dev_e2_client, ['inet 192.168.6.[0-9]+/24'])
-+
-
- @unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
-diff --git a/tests/test_utils.py b/tests/test_utils.py
-index b958d58..bd035a9 100644
---- a/tests/test_utils.py
-+++ b/tests/test_utils.py
-@@ -215,17 +215,25 @@ class TestUtils(unittest.TestCase):
- 174 wwan0 wwan off linger''')
- res = utils.networkd_interfaces()
- self.assertEquals(self.mock_networkctl.calls(), [['networkctl', '--no-pager', '--no-legend']])
-- self.assertIn('wlan0', res)
-- self.assertIn('ens3', res)
-+ self.assertIn('2', res)
-+ self.assertIn('3', res)
-+
-+ def test_networkctl_reload(self):
-+ self.mock_networkctl = MockCmd('networkctl')
-+ path_env = os.environ['PATH']
-+ os.environ['PATH'] = os.path.dirname(self.mock_networkctl.path) + os.pathsep + path_env
-+ utils.networkctl_reload()
-+ self.assertEquals(self.mock_networkctl.calls(), [
-+ ['networkctl', 'reload']
-+ ])
-
- def test_networkctl_reconfigure(self):
- self.mock_networkctl = MockCmd('networkctl')
- path_env = os.environ['PATH']
- os.environ['PATH'] = os.path.dirname(self.mock_networkctl.path) + os.pathsep + path_env
-- utils.networkctl_reconfigure(['eth0', 'eth1'])
-+ utils.networkctl_reconfigure(['3', '5'])
- self.assertEquals(self.mock_networkctl.calls(), [
-- ['networkctl', 'reload'],
-- ['networkctl', 'reconfigure', 'eth0', 'eth1']
-+ ['networkctl', 'reconfigure', '3', '5']
- ])
-
- def test_is_nm_snap_enabled(self):
diff --git a/debian/patches/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch b/debian/patches/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch
deleted file mode 100644
index 62af06c..0000000
--- a/debian/patches/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch
+++ /dev/null
@@ -1,534 +0,0 @@
-From: Nicolas Bock <nicolas.bock@canonical.com>
-Date: Thu, 12 May 2022 01:18:36 -0600
-Subject: Add tristate type for offload options (LP: #1956264) (#270)
-MIME-Version: 1.0
-Content-Type: text/plain; charset="utf-8"
-Content-Transfer-Encoding: 8bit
-
-Closes: https://bugs.launchpad.net/netplan/+bug/1956264
-
-COMMITS:
-* Add tristate type for offload options
-* Clarify name matching issues for `networkd`
-* Fix tests for offload
-* cli: apply: properly change udev/.link offloading settings
-* Re-used existing offloading fields, they are ABI compatible
-Size:
-Both enum and gboolean reduce to integer, so they are of same size.
-Content:
-An old consumer looking at these will interpret UNSET as if it was TRUE,
-which is the kernel's default (=UNSET) value.
-* CI: quirk to add ethtool test dependency
-* tests: ethernets: link offloading validation
-* doc: be more specific with the offloading docs
-
-Co-authored-by: Lukas Märdian <slyon@ubuntu.com>
----
- doc/netplan.md | 36 ++++++++++++------------
- netplan/cli/commands/apply.py | 11 +++++++-
- src/netplan.c | 21 +++++++++-----
- src/networkd.c | 49 ++++++++++++++++++--------------
- src/parse.c | 55 +++++++++++++++++++++++++++++++-----
- src/types.c | 8 ++++++
- src/types.h | 35 ++++++++++++++++++-----
- tests/generator/test_ethernets.py | 34 +++++++++++++++-------
- tests/integration/ethernets.py | 59 +++++++++++++++++++++++++++++++++++++++
- 9 files changed, 237 insertions(+), 71 deletions(-)
-
-diff --git a/doc/netplan.md b/doc/netplan.md
-index e9b4e92..373e05d 100644
---- a/doc/netplan.md
-+++ b/doc/netplan.md
-@@ -79,6 +79,10 @@ Virtual devices
-
- ## Common properties for physical device types
-
-+**Note:** Some options will not work reliably for devices matched by name only
-+and rendered by networkd, due to interactions with device renaming in udev.
-+Match devices by MAC when setting options like: ``wakeonlan`` or ``*-offload``.
-+
- ``match`` (mapping)
-
- : This selects a subset of available physical devices by various hardware
-@@ -139,54 +143,50 @@ Virtual devices
-
- : Enable wake on LAN. Off by default.
-
-- **Note:** This will not work reliably for devices matched by name
-- only and rendered by networkd, due to interactions with device
-- renaming in udev. Match devices by MAC when setting wake on LAN.
--
- ``emit-lldp`` (bool) – since **0.99**
-
- : (networkd backend only) Whether to emit LLDP packets. Off by default.
-
- ``receive-checksum-offload`` (bool) – since **0.104**
-
--: (networkd backend only) If set to true, the hardware offload for
-- checksumming of ingress network packets is enabled. When unset,
-+: (networkd backend only) If set to true (false), the hardware offload for
-+ checksumming of ingress network packets is enabled (disabled). When unset,
- the kernel's default will be used.
-
- ``transmit-checksum-offload`` (bool) – since **0.104**
-
--: (networkd backend only) If set to true, the hardware offload for
-- checksumming of egress network packets is enabled. When unset,
-+: (networkd backend only) If set to true (false), the hardware offload for
-+ checksumming of egress network packets is enabled (disabled). When unset,
- the kernel's default will be used.
-
- ``tcp-segmentation-offload`` (bool) – since **0.104**
-
--: (networkd backend only) If set to true, the TCP Segmentation
-- Offload (TSO) is enabled. When unset, the kernel's default will
-+: (networkd backend only) If set to true (false), the TCP Segmentation
-+ Offload (TSO) is enabled (disabled). When unset, the kernel's default will
- be used.
-
- ``tcp6-segmentation-offload`` (bool) – since **0.104**
-
--: (networkd backend only) If set to true, the TCP6 Segmentation
-- Offload (tx-tcp6-segmentation) is enabled. When unset, the
-+: (networkd backend only) If set to true (false), the TCP6 Segmentation
-+ Offload (tx-tcp6-segmentation) is enabled (disabled). When unset, the
- kernel's default will be used.
-
- ``generic-segmentation-offload`` (bool) – since **0.104**
-
--: (networkd backend only) If set to true, the Generic Segmentation
-- Offload (GSO) is enabled. When unset, the kernel's default will
-+: (networkd backend only) If set to true (false), the Generic Segmentation
-+ Offload (GSO) is enabled (disabled). When unset, the kernel's default will
- be used.
-
- ``generic-receive-offload`` (bool) – since **0.104**
-
--: (networkd backend only) If set to true, the Generic Receive
-- Offload (GRO) is enabled. When unset, the kernel's default will
-+: (networkd backend only) If set to true (false), the Generic Receive
-+ Offload (GRO) is enabled (disabled). When unset, the kernel's default will
- be used.
-
- ``large-receive-offload`` (bool) – since **0.104**
-
--: (networkd backend only) If set to true, the Generic Receive
-- Offload (GRO) is enabled. When unset, the kernel's default will
-+: (networkd backend only) If set to true (false), the Large Receive Offload
-+ (LRO) is enabled (disabled). When unset, the kernel's default will
- be used.
-
- ``openvswitch`` (mapping) – since **0.100**
-diff --git a/netplan/cli/commands/apply.py b/netplan/cli/commands/apply.py
-index b36662a..9d4511f 100644
---- a/netplan/cli/commands/apply.py
-+++ b/netplan/cli/commands/apply.py
-@@ -221,13 +221,22 @@ class NetplanApply(utils.NetplanCommand):
- '/sys/class/net/' + device],
- stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL)
-+ subprocess.check_call(['udevadm', 'test',
-+ '/sys/class/net/' + device],
-+ stdout=subprocess.DEVNULL,
-+ stderr=subprocess.DEVNULL)
- except subprocess.CalledProcessError:
- logging.debug('Ignoring device without syspath: %s', device)
-
-+ devices_after_udev = netifaces.interfaces()
- # apply some more changes manually
- for iface, settings in changes.items():
- # rename non-critical network interfaces
-- if settings.get('name'):
-+ new_name = settings.get('name')
-+ if new_name:
-+ if iface in devices and new_name in devices_after_udev:
-+ logging.debug('Interface rename {} -> {} already happened.'.format(iface, new_name))
-+ continue # re-name already happened via 'udevadm test'
- # bring down the interface, using its current (matched) interface name
- subprocess.check_call(['ip', 'link', 'set', 'dev', iface, 'down'],
- stdout=subprocess.DEVNULL,
-diff --git a/src/netplan.c b/src/netplan.c
-index 7b387b4..d1a27a6 100644
---- a/src/netplan.c
-+++ b/src/netplan.c
-@@ -752,13 +752,20 @@ _serialize_yaml(
- YAML_BOOL_TRUE(def, event, emitter, "wakeonlan", def->wake_on_lan);
-
- /* Offload options */
-- YAML_BOOL_TRUE(def, event, emitter, "receive-checksum-offload", def->receive_checksum_offload);
-- YAML_BOOL_TRUE(def, event, emitter, "transmit-checksum-offload", def->transmit_checksum_offload);
-- YAML_BOOL_TRUE(def, event, emitter, "tcp-segmentation-offload", def->tcp_segmentation_offload);
-- YAML_BOOL_TRUE(def, event, emitter, "tcp6-segmentation-offload", def->tcp6_segmentation_offload);
-- YAML_BOOL_TRUE(def, event, emitter, "generic-segmentation-offload", def->generic_segmentation_offload);
-- YAML_BOOL_TRUE(def, event, emitter, "generic-receive-offload", def->generic_receive_offload);
-- YAML_BOOL_TRUE(def, event, emitter, "large-receive-offload", def->large_receive_offload);
-+ if (def->receive_checksum_offload != NETPLAN_TRISTATE_UNSET)
-+ YAML_BOOL_TRUE(def, event, emitter, "receive-checksum-offload", def->receive_checksum_offload);
-+ if (def->transmit_checksum_offload != NETPLAN_TRISTATE_UNSET)
-+ YAML_BOOL_TRUE(def, event, emitter, "transmit-checksum-offload", def->transmit_checksum_offload);
-+ if (def->tcp_segmentation_offload != NETPLAN_TRISTATE_UNSET)
-+ YAML_BOOL_TRUE(def, event, emitter, "tcp-segmentation-offload", def->tcp_segmentation_offload);
-+ if (def->tcp6_segmentation_offload != NETPLAN_TRISTATE_UNSET)
-+ YAML_BOOL_TRUE(def, event, emitter, "tcp6-segmentation-offload", def->tcp6_segmentation_offload);
-+ if (def->generic_segmentation_offload != NETPLAN_TRISTATE_UNSET)
-+ YAML_BOOL_TRUE(def, event, emitter, "generic-segmentation-offload", def->generic_segmentation_offload);
-+ if (def->generic_receive_offload != NETPLAN_TRISTATE_UNSET)
-+ YAML_BOOL_TRUE(def, event, emitter, "generic-receive-offload", def->generic_receive_offload);
-+ if (def->large_receive_offload != NETPLAN_TRISTATE_UNSET)
-+ YAML_BOOL_TRUE(def, event, emitter, "large-receive-offload", def->large_receive_offload);
-
- if (def->wowlan && def->wowlan != NETPLAN_WIFI_WOWLAN_DEFAULT) {
- YAML_SCALAR_PLAIN(event, emitter, "wakeonwlan");
-diff --git a/src/networkd.c b/src/networkd.c
-index 6d26047..62c87ce 100644
---- a/src/networkd.c
-+++ b/src/networkd.c
-@@ -243,13 +243,13 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
- if (!def->set_name &&
- !def->wake_on_lan &&
- !def->mtubytes &&
-- !def->receive_checksum_offload &&
-- !def->transmit_checksum_offload &&
-- !def->tcp_segmentation_offload &&
-- !def->tcp6_segmentation_offload &&
-- !def->generic_segmentation_offload &&
-- !def->generic_receive_offload &&
-- !def->large_receive_offload)
-+ (def->receive_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
-+ (def->transmit_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
-+ (def->tcp_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
-+ (def->tcp6_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
-+ (def->generic_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
-+ (def->generic_receive_offload == NETPLAN_TRISTATE_UNSET) &&
-+ (def->large_receive_offload == NETPLAN_TRISTATE_UNSET))
- return;
-
- /* build file contents */
-@@ -265,26 +265,33 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
- g_string_append_printf(s, "MTUBytes=%u\n", def->mtubytes);
-
- /* Offload options */
-- if (def->receive_checksum_offload)
-- g_string_append_printf(s, "ReceiveChecksumOffload=%u\n", def->receive_checksum_offload);
-+ if (def->receive_checksum_offload != NETPLAN_TRISTATE_UNSET)
-+ g_string_append_printf(s, "ReceiveChecksumOffload=%s\n",
-+ (def->receive_checksum_offload ? "true" : "false"));
-
-- if (def->transmit_checksum_offload)
-- g_string_append_printf(s, "TransmitChecksumOffload=%u\n", def->transmit_checksum_offload);
-+ if (def->transmit_checksum_offload != NETPLAN_TRISTATE_UNSET)
-+ g_string_append_printf(s, "TransmitChecksumOffload=%s\n",
-+ (def->transmit_checksum_offload ? "true" : "false"));
-
-- if (def->tcp_segmentation_offload)
-- g_string_append_printf(s, "TCPSegmentationOffload=%u\n", def->tcp_segmentation_offload);
-+ if (def->tcp_segmentation_offload != NETPLAN_TRISTATE_UNSET)
-+ g_string_append_printf(s, "TCPSegmentationOffload=%s\n",
-+ (def->tcp_segmentation_offload ? "true" : "false"));
-
-- if (def->tcp6_segmentation_offload)
-- g_string_append_printf(s, "TCP6SegmentationOffload=%u\n", def->tcp6_segmentation_offload);
-+ if (def->tcp6_segmentation_offload != NETPLAN_TRISTATE_UNSET)
-+ g_string_append_printf(s, "TCP6SegmentationOffload=%s\n",
-+ (def->tcp6_segmentation_offload ? "true" : "false"));
-
-- if (def->generic_segmentation_offload)
-- g_string_append_printf(s, "GenericSegmentationOffload=%u\n", def->generic_segmentation_offload);
-+ if (def->generic_segmentation_offload != NETPLAN_TRISTATE_UNSET)
-+ g_string_append_printf(s, "GenericSegmentationOffload=%s\n",
-+ (def->generic_segmentation_offload ? "true" : "false"));
-
-- if (def->generic_receive_offload)
-- g_string_append_printf(s, "GenericReceiveOffload=%u\n", def->generic_receive_offload);
-+ if (def->generic_receive_offload != NETPLAN_TRISTATE_UNSET)
-+ g_string_append_printf(s, "GenericReceiveOffload=%s\n",
-+ (def->generic_receive_offload ? "true" : "false"));
-
-- if (def->large_receive_offload)
-- g_string_append_printf(s, "LargeReceiveOffload=%u\n", def->large_receive_offload);
-+ if (def->large_receive_offload != NETPLAN_TRISTATE_UNSET)
-+ g_string_append_printf(s, "LargeReceiveOffload=%s\n",
-+ (def->large_receive_offload ? "true" : "false"));
-
- orig_umask = umask(022);
- g_string_free_to_file(s, rootdir, path, ".link");
-diff --git a/src/parse.c b/src/parse.c
-index 350c508..0cb07d2 100644
---- a/src/parse.c
-+++ b/src/parse.c
-@@ -370,6 +370,37 @@ handle_generic_bool(NetplanParser* npp, yaml_node_t* node, void* entryptr, const
- return TRUE;
- }
-
-+/*
-+ * Handler for setting a HashTable field from a mapping node, inside a given struct
-+ * @entryptr: pointer to the beginning of the to-be-modified data structure
-+ * @data: offset into entryptr struct where the boolean field to write is located
-+ */
-+static gboolean
-+handle_generic_tristate(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
-+{
-+ g_assert(entryptr);
-+ NetplanTristate v;
-+ guint offset = GPOINTER_TO_UINT(data);
-+ NetplanTristate* dest = ((void*) entryptr + offset);
-+
-+ if (g_ascii_strcasecmp(scalar(node), "true") == 0 ||
-+ g_ascii_strcasecmp(scalar(node), "on") == 0 ||
-+ g_ascii_strcasecmp(scalar(node), "yes") == 0 ||
-+ g_ascii_strcasecmp(scalar(node), "y") == 0)
-+ v = NETPLAN_TRISTATE_TRUE;
-+ else if (g_ascii_strcasecmp(scalar(node), "false") == 0 ||
-+ g_ascii_strcasecmp(scalar(node), "off") == 0 ||
-+ g_ascii_strcasecmp(scalar(node), "no") == 0 ||
-+ g_ascii_strcasecmp(scalar(node), "n") == 0)
-+ v = NETPLAN_TRISTATE_FALSE;
-+ else
-+ return yaml_error(npp, node, error, "invalid boolean value '%s'", scalar(node));
-+
-+ *dest = v;
-+ mark_data_as_dirty(npp, dest);
-+ return TRUE;
-+}
-+
- /*
- * Handler for setting a HashTable field from a mapping node, inside a given struct
- * @entryptr: pointer to the beginning of the to-be-modified data structure
-@@ -516,6 +547,16 @@ handle_netdef_bool(NetplanParser* npp, yaml_node_t* node, const void* data, GErr
- return handle_generic_bool(npp, node, npp->current.netdef, data, error);
- }
-
-+/**
-+ * Generic handler for tri-state settings that can bei "UNSET", "TRUE", or "FALSE".
-+ * @data: offset into NetplanNetDefinition where the guint field to write is located
-+ */
-+static gboolean
-+handle_netdef_tristate(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
-+{
-+ return handle_generic_tristate(npp, node, npp->current.netdef, data, error);
-+}
-+
- /**
- * Generic handler for setting a npp->current.netdef guint field from a scalar node
- * @data: offset into NetplanNetDefinition where the guint field to write is located
-@@ -2356,13 +2397,13 @@ static const mapping_entry_handler dhcp6_overrides_handlers[] = {
- {"wakeonlan", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(wake_on_lan)}, \
- {"wakeonwlan", YAML_SEQUENCE_NODE, {.generic=handle_wowlan}, netdef_offset(wowlan)}, \
- {"emit-lldp", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(emit_lldp)}, \
-- {"receive-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(receive_checksum_offload)}, \
-- {"transmit-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(transmit_checksum_offload)}, \
-- {"tcp-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(tcp_segmentation_offload)}, \
-- {"tcp6-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(tcp6_segmentation_offload)}, \
-- {"generic-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(generic_segmentation_offload)}, \
-- {"generic-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(generic_receive_offload)}, \
-- {"large-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(large_receive_offload)}
-+ {"receive-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(receive_checksum_offload)}, \
-+ {"transmit-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(transmit_checksum_offload)}, \
-+ {"tcp-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(tcp_segmentation_offload)}, \
-+ {"tcp6-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(tcp6_segmentation_offload)}, \
-+ {"generic-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(generic_segmentation_offload)}, \
-+ {"generic-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(generic_receive_offload)}, \
-+ {"large-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(large_receive_offload)}
-
- static const mapping_entry_handler ethernet_def_handlers[] = {
- COMMON_LINK_HANDLERS,
-diff --git a/src/types.c b/src/types.c
-index eb9f780..00c2b0f 100644
---- a/src/types.c
-+++ b/src/types.c
-@@ -335,6 +335,14 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
-
- reset_private_netdef_data(netdef->_private);
- FREE_AND_NULLIFY(netdef->_private);
-+
-+ netdef->receive_checksum_offload = NETPLAN_TRISTATE_UNSET;
-+ netdef->transmit_checksum_offload = NETPLAN_TRISTATE_UNSET;
-+ netdef->tcp_segmentation_offload = NETPLAN_TRISTATE_UNSET;
-+ netdef->tcp6_segmentation_offload = NETPLAN_TRISTATE_UNSET;
-+ netdef->generic_segmentation_offload = NETPLAN_TRISTATE_UNSET;
-+ netdef->generic_receive_offload = NETPLAN_TRISTATE_UNSET;
-+ netdef->large_receive_offload = NETPLAN_TRISTATE_UNSET;
- }
-
- static void
-diff --git a/src/types.h b/src/types.h
-index 27a23fc..710b1f1 100644
---- a/src/types.h
-+++ b/src/types.h
-@@ -171,6 +171,27 @@ typedef union {
- } networkd;
- } NetplanBackendSettings;
-
-+typedef enum
-+{
-+ /**
-+ * @brief Tristate enum type
-+ *
-+ * This type defines a boolean which can be unset, i.e.
-+ * this type has three states. The enum is ordered so
-+ * that
-+ *
-+ * UNSET -> -1
-+ * FALSE -> 0
-+ * TRUE -> 1
-+ *
-+ * And the integer values can be used directly when
-+ * converting to string.
-+ */
-+ NETPLAN_TRISTATE_UNSET = -1, /* -1 */
-+ NETPLAN_TRISTATE_FALSE, /* 0 */
-+ NETPLAN_TRISTATE_TRUE, /* 1 */
-+} NetplanTristate;
-+
- struct netplan_net_definition {
- NetplanDefType type;
- NetplanBackend backend;
-@@ -333,13 +354,13 @@ struct netplan_net_definition {
- gboolean ignore_carrier;
-
- /* offload options */
-- gboolean receive_checksum_offload;
-- gboolean transmit_checksum_offload;
-- gboolean tcp_segmentation_offload;
-- gboolean tcp6_segmentation_offload;
-- gboolean generic_segmentation_offload;
-- gboolean generic_receive_offload;
-- gboolean large_receive_offload;
-+ NetplanTristate receive_checksum_offload;
-+ NetplanTristate transmit_checksum_offload;
-+ NetplanTristate tcp_segmentation_offload;
-+ NetplanTristate tcp6_segmentation_offload;
-+ NetplanTristate generic_segmentation_offload;
-+ NetplanTristate generic_receive_offload;
-+ NetplanTristate large_receive_offload;
-
- struct private_netdef_data* _private;
-
-diff --git a/tests/generator/test_ethernets.py b/tests/generator/test_ethernets.py
-index 46bf764..e81941b 100644
---- a/tests/generator/test_ethernets.py
-+++ b/tests/generator/test_ethernets.py
-@@ -772,11 +772,11 @@ method=ignore
- ethernets:
- eth1:
- receive-checksum-offload: true
-- transmit-checksum-offload: true
-+ transmit-checksum-offload: off
- tcp-segmentation-offload: true
-- tcp6-segmentation-offload: true
-+ tcp6-segmentation-offload: false
- generic-segmentation-offload: true
-- generic-receive-offload: true
-+ generic-receive-offload: no
- large-receive-offload: true''')
-
- self.assert_networkd({'eth1.link': '''[Match]
-@@ -784,13 +784,13 @@ OriginalName=eth1
-
- [Link]
- WakeOnLan=off
--ReceiveChecksumOffload=1
--TransmitChecksumOffload=1
--TCPSegmentationOffload=1
--TCP6SegmentationOffload=1
--GenericSegmentationOffload=1
--GenericReceiveOffload=1
--LargeReceiveOffload=1
-+ReceiveChecksumOffload=true
-+TransmitChecksumOffload=false
-+TCPSegmentationOffload=true
-+TCP6SegmentationOffload=false
-+GenericSegmentationOffload=true
-+GenericReceiveOffload=false
-+LargeReceiveOffload=true
- ''',
- 'eth1.network': '''[Match]
- Name=eth1
-@@ -799,3 +799,17 @@ Name=eth1
- LinkLocalAddressing=ipv6
- '''})
- self.assert_networkd_udev(None)
-+
-+ def test_offload_invalid(self):
-+ err = self.generate('''network:
-+ version: 2
-+ ethernets:
-+ eth1:
-+ generic-receive-offload: n
-+ receive-checksum-offload: true
-+ tcp-segmentation-offload: true
-+ tcp6-segmentation-offload: false
-+ generic-segmentation-offload: true
-+ transmit-checksum-offload: xx
-+ large-receive-offload: true''', expect_fail=True)
-+ self.assertIn('invalid boolean value \'xx\'', err)
-diff --git a/tests/integration/ethernets.py b/tests/integration/ethernets.py
-index 865c0d4..06ac069 100644
---- a/tests/integration/ethernets.py
-+++ b/tests/integration/ethernets.py
-@@ -236,6 +236,65 @@ class _CommonTests():
- self.assert_iface_up('iface1', ['inet 10.10.10.11'])
- self.assert_iface_up('iface2', ['inet 10.10.10.22'])
-
-+ def test_link_offloading(self):
-+ self.setup_eth(None, False)
-+ # check kernel defaults
-+ out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
-+ self.assertIn(b'rx-checksumming: on', out)
-+ self.assertIn(b'tx-checksumming: on', out)
-+ self.assertIn(b'tcp-segmentation-offload: on', out)
-+ self.assertIn(b'tx-tcp6-segmentation: on', out)
-+ self.assertIn(b'generic-segmentation-offload: on', out)
-+ self.assertIn(b'generic-receive-offload: off', out) # off by default
-+ # validate turning off
-+ with open(self.config, 'w') as f:
-+ f.write('''network:
-+ renderer: %(r)s
-+ ethernets:
-+ %(ec)s:
-+ addresses: [10.10.10.22/24]
-+ receive-checksum-offload: off
-+ transmit-checksum-offload: off
-+ tcp-segmentation-offload: off
-+ tcp6-segmentation-offload: off
-+ generic-segmentation-offload: off
-+ generic-receive-offload: off
-+ #large-receive-offload: off # not possible on veth
-+''' % {'r': self.backend, 'ec': self.dev_e_client})
-+ self.generate_and_settle([self.dev_e_client])
-+ self.assert_iface_up(self.dev_e_client, ['inet 10.10.10.22'])
-+ out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
-+ self.assertIn(b'rx-checksumming: off', out)
-+ self.assertIn(b'tx-checksumming: off', out)
-+ self.assertIn(b'tcp-segmentation-offload: off', out)
-+ self.assertIn(b'tx-tcp6-segmentation: off', out)
-+ self.assertIn(b'generic-segmentation-offload: off', out)
-+ self.assertIn(b'generic-receive-offload: off', out)
-+ # validate turning on
-+ with open(self.config, 'w') as f:
-+ f.write('''network:
-+ renderer: %(r)s
-+ ethernets:
-+ %(ec)s:
-+ addresses: [10.10.10.22/24]
-+ receive-checksum-offload: true
-+ transmit-checksum-offload: true
-+ tcp-segmentation-offload: true
-+ tcp6-segmentation-offload: true
-+ generic-segmentation-offload: true
-+ generic-receive-offload: true
-+ #large-receive-offload: true # not possible on veth
-+''' % {'r': self.backend, 'ec': self.dev_e_client})
-+ self.generate_and_settle([self.dev_e_client])
-+ self.assert_iface_up(self.dev_e_client, ['inet 10.10.10.22'])
-+ out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
-+ self.assertIn(b'rx-checksumming: on', out)
-+ self.assertIn(b'tx-checksumming: on', out)
-+ self.assertIn(b'tcp-segmentation-offload: on', out)
-+ self.assertIn(b'tx-tcp6-segmentation: on', out)
-+ self.assertIn(b'generic-segmentation-offload: on', out)
-+ self.assertIn(b'generic-receive-offload: on', out)
-+
-
- @unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
diff --git a/debian/patches/0004-tests-ethernets-fix-autopkgtest-with-alternating-def.patch b/debian/patches/0004-tests-ethernets-fix-autopkgtest-with-alternating-def.patch
deleted file mode 100644
index 6ecaead..0000000
--- a/debian/patches/0004-tests-ethernets-fix-autopkgtest-with-alternating-def.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com>
-Date: Thu, 19 May 2022 12:43:11 +0200
-Subject: tests:ethernets: fix autopkgtest with alternating default value
-
----
- tests/integration/ethernets.py | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/tests/integration/ethernets.py b/tests/integration/ethernets.py
-index 06ac069..f2fcc4e 100644
---- a/tests/integration/ethernets.py
-+++ b/tests/integration/ethernets.py
-@@ -245,7 +245,8 @@ class _CommonTests():
- self.assertIn(b'tcp-segmentation-offload: on', out)
- self.assertIn(b'tx-tcp6-segmentation: on', out)
- self.assertIn(b'generic-segmentation-offload: on', out)
-- self.assertIn(b'generic-receive-offload: off', out) # off by default
-+ # enabled for armhf on autopkgtest.u.c but 'off' elsewhere
-+ # self.assertIn(b'generic-receive-offload: off', out)
- # validate turning off
- with open(self.config, 'w') as f:
- f.write('''network:
diff --git a/debian/patches/0005-nm-fix-rendering-of-password-for-unknown-passthrough.patch b/debian/patches/0005-nm-fix-rendering-of-password-for-unknown-passthrough.patch
deleted file mode 100644
index 1f63e09..0000000
--- a/debian/patches/0005-nm-fix-rendering-of-password-for-unknown-passthrough.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com>
-Date: Thu, 19 May 2022 12:42:11 +0200
-Subject: nm: fix rendering of password for unknown/passthrough WPA types
- (#279)
-
-The NetworkManager backend should not take a shortcut and skip rendering the password, in case of an unknown WPA type, as that might be overwritten by a passthrough value.
-
-LP: #1972800
----
- src/nm.c | 15 ++++++-------
- tests/parser/test_keyfile.py | 51 ++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 57 insertions(+), 9 deletions(-)
-
-diff --git a/src/nm.c b/src/nm.c
-index 319a80b..b00a21c 100644
---- a/src/nm.c
-+++ b/src/nm.c
-@@ -431,9 +431,6 @@ write_tunnel_params(const NetplanNetDefinition* def, GKeyFile *kf)
- static void
- write_dot1x_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *kf)
- {
-- if (auth->eap_method == NETPLAN_AUTH_EAP_NONE)
-- return;
--
- switch (auth->eap_method) {
- case NETPLAN_AUTH_EAP_TLS:
- g_key_file_set_string(kf, "802-1x", "eap", "tls");
-@@ -468,14 +465,11 @@ write_dot1x_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile
- static void
- write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *kf)
- {
-- if (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_NONE)
-- return;
--
- switch (auth->key_management) {
-+ case NETPLAN_AUTH_KEY_MANAGEMENT_NONE:
-+ break;
- case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK:
- g_key_file_set_string(kf, "wifi-security", "key-mgmt", "wpa-psk");
-- if (auth->password)
-- g_key_file_set_string(kf, "wifi-security", "psk", auth->password);
- break;
- case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP:
- g_key_file_set_string(kf, "wifi-security", "key-mgmt", "wpa-eap");
-@@ -486,7 +480,10 @@ write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *
- default: break; // LCOV_EXCL_LINE
- }
-
-- write_dot1x_auth_parameters(auth, kf);
-+ if (auth->eap_method != NETPLAN_AUTH_EAP_NONE)
-+ write_dot1x_auth_parameters(auth, kf);
-+ else if (auth->password)
-+ g_key_file_set_string(kf, "wifi-security", "psk", auth->password);
- }
-
- static void
-diff --git a/tests/parser/test_keyfile.py b/tests/parser/test_keyfile.py
-index 809cfd8..7822bbe 100644
---- a/tests/parser/test_keyfile.py
-+++ b/tests/parser/test_keyfile.py
-@@ -1242,3 +1242,54 @@ method=auto
- passthrough:
- ipv6.ip6-privacy: "-1"
- '''.format(UUID, UUID)})
-+
-+ def test_keyfile_wpa3_sae(self):
-+ self.generate_from_keyfile('''[connection]
-+id=test2
-+uuid={}
-+type=wifi
-+interface-name=wlan0
-+
-+[wifi]
-+mode=infrastructure
-+ssid=ubuntu-wpa2-wpa3-mixed
-+
-+[wifi-security]
-+key-mgmt=sae
-+psk=test1234
-+
-+[ipv4]
-+method=auto
-+
-+[ipv6]
-+addr-gen-mode=stable-privacy
-+method=auto
-+
-+[proxy]
-+'''.format(UUID))
-+ self.assert_netplan({UUID: '''network:
-+ version: 2
-+ wifis:
-+ NM-{}:
-+ renderer: NetworkManager
-+ match:
-+ name: "wlan0"
-+ dhcp4: true
-+ dhcp6: true
-+ ipv6-address-generation: "stable-privacy"
-+ access-points:
-+ "ubuntu-wpa2-wpa3-mixed":
-+ auth:
-+ key-management: "none"
-+ password: "test1234"
-+ networkmanager:
-+ uuid: "ff9d6ebc-226d-4f82-a485-b7ff83b9607f"
-+ name: "test2"
-+ passthrough:
-+ wifi-security.key-mgmt: "sae"
-+ ipv6.ip6-privacy: "-1"
-+ proxy._: ""
-+ networkmanager:
-+ uuid: "{}"
-+ name: "test2"
-+'''.format(UUID, UUID)})
diff --git a/debian/patches/autopkgtest-fixes.patch b/debian/patches/autopkgtest-fixes.patch
deleted file mode 100644
index b61a214..0000000
--- a/debian/patches/autopkgtest-fixes.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-diff --git a/tests/integration/base.py b/tests/integration/base.py
-index 5042bf4..93ee722 100644
---- a/tests/integration/base.py
-+++ b/tests/integration/base.py
-@@ -75,7 +75,7 @@ class IntegrationTestsBase(unittest.TestCase):
-
- os.makedirs('/etc/NetworkManager/conf.d', exist_ok=True)
- with open('/etc/NetworkManager/conf.d/99-test-ignore.conf', 'w') as f:
-- f.write('[keyfile]\nunmanaged-devices+=interface-name:eth0,interface-name:en*,interface-name:veth42,interface-name:veth43')
-+ f.write('[keyfile]\nunmanaged-devices+=interface-name:en*,eth0,veth42,veth43,nptestsrv')
- subprocess.check_call(['netplan', 'apply'])
- subprocess.call(['/lib/systemd/systemd-networkd-wait-online', '--quiet', '--timeout=30'])
-
-@@ -144,12 +144,6 @@ class IntegrationTestsBase(unittest.TestCase):
- universal_newlines=True)
- klass.dev_e2_client_mac = out.split()[2]
-
-- os.makedirs('/run/NetworkManager/conf.d', exist_ok=True)
--
-- # work around https://launchpad.net/bugs/1615044
-- with open('/run/NetworkManager/conf.d/11-globally-managed-devices.conf', 'w') as f:
-- f.write('[keyfile]\nunmanaged-devices=')
--
- @classmethod
- def shutdown_devices(klass):
- '''Remove test devices'''
-@@ -440,9 +434,10 @@ class IntegrationTestsWifi(IntegrationTestsBase):
- klass.dev_w_ap = devs[0]
- klass.dev_w_client = devs[1]
-
-+ os.makedirs('/run/NetworkManager/conf.d', exist_ok=True)
- # don't let NM trample over our fake AP
- with open('/run/NetworkManager/conf.d/test-blacklist.conf', 'w') as f:
-- f.write('[main]\nplugins=keyfile\n[keyfile]\nunmanaged-devices+=nptestsrv,%s\n' % klass.dev_w_ap)
-+ f.write('[main]\nplugins=keyfile\n[keyfile]\nunmanaged-devices+=%s\n' % klass.dev_w_ap)
-
- @classmethod
- def shutdown_devices(klass):
diff --git a/debian/patches/ovs-timeout.patch b/debian/patches/ovs-timeout.patch
deleted file mode 100644
index 66bf694..0000000
--- a/debian/patches/ovs-timeout.patch
+++ /dev/null
@@ -1,590 +0,0 @@
-From 62f0a75e8e69149578bef1007cc808810b3440d3 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Lukas=20M=C3=A4rdian?= <slyon@ubuntu.com>
-Date: Wed, 11 May 2022 18:11:20 +0200
-Subject: [PATCH] Fix infinite timeouts in ovs-vsctl (Closes: #1000137) (#266)
-
-* ovs: time out OVS commands after a while
-
-* tests: time out ovs-vsctl commands
-
-ovs-vsctl will hang forever if the host is not OVS enabled, blocking the tests
----
- src/openvswitch.c | 2 +-
- tests/generator/base.py | 4 +-
- tests/generator/test_ovs.py | 29 +++++++++
- tests/integration/ovs.py | 117 ++++++++++++++++++------------------
- 4 files changed, 91 insertions(+), 61 deletions(-)
-
---- a/src/openvswitch.c
-+++ b/src/openvswitch.c
-@@ -59,7 +59,7 @@ write_ovs_systemd_unit(const char* id, c
- g_string_append_printf(s, "After=netplan-ovs-%s.service\n", dependency);
- }
-
-- g_string_append(s, "\n[Service]\nType=oneshot\n");
-+ g_string_append(s, "\n[Service]\nType=oneshot\nTimeoutStartSec=10s\n");
- g_string_append(s, cmds->str);
-
- g_string_free_to_file(s, rootdir, path, NULL);
---- a/tests/generator/base.py
-+++ b/tests/generator/base.py
-@@ -66,9 +66,9 @@ standalone\nExecStart=/usr/bin/ovs-vsctl
- Bridge %(iface)s external-ids:netplan/mcast_snooping_enable=false\nExecStart=/usr/bin/ovs-vsctl set Bridge %(iface)s \
- rstp_enable=false\nExecStart=/usr/bin/ovs-vsctl set Bridge %(iface)s external-ids:netplan/rstp_enable=false\n'
- OVS_BR_EMPTY = _OVS_BASE + 'After=netplan-ovs-cleanup.service\nBefore=network.target\nWants=network.target\n\n[Service]\n\
--Type=oneshot\nExecStart=/usr/bin/ovs-vsctl --may-exist add-br %(iface)s\n' + OVS_BR_DEFAULT
-+Type=oneshot\nTimeoutStartSec=10s\nExecStart=/usr/bin/ovs-vsctl --may-exist add-br %(iface)s\n' + OVS_BR_DEFAULT
- OVS_CLEANUP = _OVS_BASE + 'ConditionFileIsExecutable=/usr/bin/ovs-vsctl\nBefore=network.target\nWants=network.target\n\n\
--[Service]\nType=oneshot\nExecStart=/usr/sbin/netplan apply --only-ovs-cleanup\n'
-+[Service]\nType=oneshot\nTimeoutStartSec=10s\nExecStart=/usr/sbin/netplan apply --only-ovs-cleanup\n'
- UDEV_MAC_RULE = 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="%s", ATTR{address}=="%s", NAME="%s"\n'
- UDEV_NO_MAC_RULE = 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="%s", NAME="%s"\n'
- UDEV_SRIOV_RULE = 'ACTION=="add", SUBSYSTEM=="net", ATTRS{sriov_totalvfs}=="?*", RUN+="/usr/sbin/netplan apply --sriov-only"\n'
---- a/tests/generator/test_ovs.py
-+++ b/tests/generator/test_ovs.py
-@@ -50,6 +50,7 @@ class TestOpenVSwitch(TestBase):
- self.assert_ovs({'ovs0.service': OVS_VIRTUAL % {'iface': 'ovs0', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br ovs0
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port ovs0 eth1
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port ovs0 eth0
-@@ -60,6 +61,7 @@ After=netplan-ovs-ovs0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl set Interface eth0 external-ids:iface-id=myhostname
- ExecStart=/usr/bin/ovs-vsctl set Interface eth0 external-ids:netplan/external-ids/iface-id=myhostname
- ExecStart=/usr/bin/ovs-vsctl set Interface eth0 other-config:disable-in-band=true
-@@ -71,6 +73,7 @@ After=netplan-ovs-ovs0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl set Interface eth1 other-config:disable-in-band=false
- ExecStart=/usr/bin/ovs-vsctl set Interface eth1 external-ids:netplan/other-config/disable-in-band=false
- '''},
-@@ -109,6 +112,7 @@ ExecStart=/usr/bin/ovs-vsctl set Interfa
- self.assert_ovs({'global.service': OVS_VIRTUAL % {'iface': 'global', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:iface-id=myhostname
- ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:netplan/external-ids/iface-id=myhostname
- ExecStart=/usr/bin/ovs-vsctl set open_vswitch . other-config:disable-in-band=true
-@@ -129,6 +133,7 @@ ExecStart=/usr/bin/ovs-vsctl set open_vs
- self.assert_ovs({'ovs0.service': OVS_VIRTUAL % {'iface': 'ovs0', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br ovs0
- ''' + OVS_BR_DEFAULT % {'iface': 'ovs0'} + '''\
- ExecStart=/usr/bin/ovs-vsctl set Bridge ovs0 protocols=OpenFlow10,OpenFlow11,OpenFlow12
-@@ -185,6 +190,7 @@ After=netplan-ovs-br0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
-@@ -253,6 +259,7 @@ After=netplan-ovs-br0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=active
-@@ -318,6 +325,7 @@ After=netplan-ovs-br0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
-@@ -357,6 +365,7 @@ After=netplan-ovs-br0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
-@@ -408,6 +417,7 @@ ExecStart=/usr/bin/ovs-vsctl set Port bo
- '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 eth1
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 eth2
-@@ -432,6 +442,7 @@ ExecStart=/usr/bin/ovs-vsctl --may-exist
- self.assert_ovs({'br0.service': OVS_VIRTUAL % {'iface': 'br0', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
- ''' + OVS_BR_DEFAULT % {'iface': 'br0'} + '''\
- ExecStart=/usr/bin/ovs-vsctl set Bridge br0 external-ids:iface-id=myhostname
-@@ -462,6 +473,7 @@ ExecStart=/usr/bin/ovs-vsctl set Bridge
- '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 eth1
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 eth2
-@@ -521,6 +533,7 @@ ExecStart=/usr/bin/ovs-vsctl set Bridge
- '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
- ''' + OVS_BR_DEFAULT % {'iface': 'br0'} + '''\
- ExecStart=/usr/bin/ovs-vsctl set Bridge br0 protocols=OpenFlow10,OpenFlow11,OpenFlow15
-@@ -570,6 +583,7 @@ ExecStart=/usr/bin/ovs-vsctl set Bridge
- '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
- ''' + OVS_BR_DEFAULT % {'iface': 'br0'} + '''\
- ExecStart=/usr/bin/ovs-vsctl set-controller br0 ptcp: ptcp:1337 ptcp:1337:[fe80::1234%eth0] pssl:1337:[fe80::1] ssl:10.10.10.1 \
-@@ -583,6 +597,7 @@ ExecStart=/usr/bin/ovs-vsctl set Control
- 'global.service': OVS_VIRTUAL % {'iface': 'global', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl set-ssl /key/path /some/path /another/path
- ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:netplan/global/set-ssl=/key/path,/some/path,/another/path
- '''},
-@@ -680,6 +695,7 @@ ExecStart=/usr/bin/ovs-vsctl set open_vs
- self.assert_ovs({'global.service': OVS_VIRTUAL % {'iface': 'global', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl set-ssl /key/path /some/path /another/path
- ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:netplan/global/set-ssl=/key/path,/some/path,/another/path
- '''},
-@@ -784,6 +800,7 @@ After=netplan-ovs-br0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
-@@ -832,6 +849,7 @@ Bond=bond0
- 'br1.service': OVS_VIRTUAL % {'iface': 'br1', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br1
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br1 patchx -- set Interface patchx type=patch options:peer=patchy
- ''' + OVS_BR_DEFAULT % {'iface': 'br1'}},
-@@ -841,6 +859,7 @@ After=netplan-ovs-br0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 patchy eth0 -- set Interface patchy type=patch options:peer=patchx
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
- ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
-@@ -852,6 +871,7 @@ After=netplan-ovs-br1.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl set Port patchx external-ids:netplan=true
- '''},
- 'patchy.service': OVS_VIRTUAL % {'iface': 'patchy', 'extra':
-@@ -860,6 +880,7 @@ After=netplan-ovs-bond0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl set Interface patchy external-ids:netplan=true
- '''},
- 'cleanup.service': OVS_CLEANUP % {'iface': 'cleanup'}})
-@@ -887,12 +908,14 @@ ExecStart=/usr/bin/ovs-vsctl set Interfa
- self.assert_ovs({'br0.service': OVS_VIRTUAL % {'iface': 'br0', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 patch0-1 -- set Interface patch0-1 type=patch options:peer=patch1-0
- ''' + OVS_BR_DEFAULT % {'iface': 'br0'}},
- 'br1.service': OVS_VIRTUAL % {'iface': 'br1', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br1
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br1 patch1-0 -- set Interface patch1-0 type=patch options:peer=patch0-1
- ''' + OVS_BR_DEFAULT % {'iface': 'br1'}},
-@@ -902,6 +925,7 @@ After=netplan-ovs-br0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl set Port patch0-1 external-ids:netplan=true
- '''},
- 'patch1-0.service': OVS_VIRTUAL % {'iface': 'patch1-0', 'extra':
-@@ -910,6 +934,7 @@ After=netplan-ovs-br1.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl set Port patch1-0 external-ids:netplan=true
- '''},
- 'cleanup.service': OVS_CLEANUP % {'iface': 'cleanup'}})
-@@ -934,6 +959,7 @@ ExecStart=/usr/bin/ovs-vsctl set Port pa
- self.assert_ovs({'br0.service': OVS_VIRTUAL % {'iface': 'br0', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
- ''' + OVS_BR_DEFAULT % {'iface': 'br0'}},
- 'br0.100.service': OVS_VIRTUAL % {'iface': 'br0.100', 'extra':
-@@ -942,6 +968,7 @@ After=netplan-ovs-br0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0.100 br0 100
- ExecStart=/usr/bin/ovs-vsctl set Interface br0.100 external-ids:netplan=true
- '''},
-@@ -971,6 +998,7 @@ After=netplan-ovs-br0.service
-
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0.100 br0 100
- ExecStart=/usr/bin/ovs-vsctl set Interface br0.100 external-ids:netplan=true
- '''},
-@@ -1007,6 +1035,7 @@ ExecStart=/usr/bin/ovs-vsctl set Interfa
- self.assert_ovs({'ovs-br.service': OVS_VIRTUAL % {'iface': 'ovs-br', 'extra': '''
- [Service]
- Type=oneshot
-+TimeoutStartSec=10s
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-br ovs-br
- ExecStart=/usr/bin/ovs-vsctl --may-exist add-port ovs-br non-ovs-bond
- ''' + OVS_BR_DEFAULT % {'iface': 'ovs-br'}},
---- a/tests/integration/ovs.py
-+++ b/tests/integration/ovs.py
-@@ -31,8 +31,8 @@ class _CommonTests():
-
- def _collect_ovs_settings(self, bridge0):
- d = {}
-- d['show'] = subprocess.check_output(['ovs-vsctl', 'show'])
-- d['ssl'] = subprocess.check_output(['ovs-vsctl', 'get-ssl'])
-+ d['show'] = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
-+ d['ssl'] = subprocess.check_output(['ovs-vsctl', '-t', '5', 'get-ssl'])
- # Get external-ids
- for tbl in ('Open_vSwitch', 'Controller', 'Bridge', 'Port', 'Interface'):
- cols = 'name,external-ids'
-@@ -40,37 +40,37 @@ class _CommonTests():
- cols = 'external-ids'
- elif tbl == 'Controller':
- cols = '_uuid,external-ids'
-- d['external-ids-%s' % tbl] = subprocess.check_output(['ovs-vsctl', '--columns=%s' % cols, '-f', 'csv', '-d',
-- 'bare', '--no-headings', 'list', tbl])
-+ d['external-ids-%s' % tbl] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=%s' % cols, '-f', 'csv',
-+ '-d', 'bare', '--no-headings', 'list', tbl])
- # Get other-config
- for tbl in ('Open_vSwitch', 'Bridge', 'Port', 'Interface'):
- cols = 'name,other-config'
- if tbl == 'Open_vSwitch':
- cols = 'other-config'
-- d['other-config-%s' % tbl] = subprocess.check_output(['ovs-vsctl', '--columns=%s' % cols, '-f', 'csv', '-d',
-- 'bare', '--no-headings', 'list', tbl])
-+ d['other-config-%s' % tbl] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=%s' % cols, '-f', 'csv',
-+ '-d', 'bare', '--no-headings', 'list', tbl])
- # Get bond settings
- for col in ('bond_mode', 'lacp'):
-- d['%s-Bond' % col] = subprocess.check_output(['ovs-vsctl', '--columns=name,%s' % col, '-f', 'csv', '-d', 'bare',
-- '--no-headings', 'list', 'Port'])
-+ d['%s-Bond' % col] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=name,%s' % col, '-f', 'csv',
-+ '-d', 'bare', '--no-headings', 'list', 'Port'])
- # Get bridge settings
-- d['set-fail-mode-Bridge'] = subprocess.check_output(['ovs-vsctl', 'get-fail-mode', bridge0])
-+ d['set-fail-mode-Bridge'] = subprocess.check_output(['ovs-vsctl', '-t', '5', 'get-fail-mode', bridge0])
- for col in ('mcast_snooping_enable', 'rstp_enable', 'protocols'):
-- d['%s-Bridge' % col] = subprocess.check_output(['ovs-vsctl', '--columns=name,%s' % col, '-f', 'csv', '-d', 'bare',
-- '--no-headings', 'list', 'Bridge'])
-+ d['%s-Bridge' % col] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=name,%s' % col, '-f', 'csv',
-+ '-d', 'bare', '--no-headings', 'list', 'Bridge'])
- # Get controller settings
-- d['set-controller-Bridge'] = subprocess.check_output(['ovs-vsctl', 'get-controller', bridge0])
-+ d['set-controller-Bridge'] = subprocess.check_output(['ovs-vsctl', '-t', '5', 'get-controller', bridge0])
- for col in ('connection_mode',):
-- d['%s-Controller' % col] = subprocess.check_output(['ovs-vsctl', '--columns=_uuid,%s' % col, '-f', 'csv', '-d',
-- 'bare', '--no-headings', 'list', 'Controller'])
-+ d['%s-Controller' % col] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=_uuid,%s' % col, '-f', 'csv',
-+ '-d', 'bare', '--no-headings', 'list', 'Controller'])
- return d
-
- def test_cleanup_interfaces(self):
- self.setup_eth(None, False)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs0'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs1'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch0-1'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch1-0'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs0'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs1'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch0-1'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch1-0'])
- with open(self.config, 'w') as f:
- f.write('''network:
- openvswitch:
-@@ -81,7 +81,7 @@ class _CommonTests():
- ovs1: {interfaces: [patch1-0]}''')
- self.generate_and_settle(['ovs0', 'ovs1'])
- # Basic verification that the bridges/ports/interfaces are there in OVS
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertIn(b' Bridge ovs0', out)
- self.assertIn(b' Port patch0-1', out)
- self.assertIn(b' Interface patch0-1', out)
-@@ -94,7 +94,7 @@ class _CommonTests():
- %(ec)s: {addresses: ['1.2.3.4/24']}''' % {'ec': self.dev_e_client})
- self.generate_and_settle([self.dev_e_client])
- # Verify that the netplan=true tagged bridges/ports have been cleaned up
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertNotIn(b'Bridge ovs0', out)
- self.assertNotIn(b'Port patch0-1', out)
- self.assertNotIn(b'Interface patch0-1', out)
-@@ -105,11 +105,11 @@ class _CommonTests():
-
- def test_cleanup_patch_ports(self):
- self.setup_eth(None, False)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs0'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs1'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch0-1'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patchy'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'bond0'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs0'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs1'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch0-1'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patchy'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'bond0'])
- with open(self.config, 'w') as f:
- f.write('''network:
- ethernets:
-@@ -122,7 +122,7 @@ class _CommonTests():
- ovs0: {interfaces: [patch0-1, bond0]}''' % {'ec': self.dev_e_client})
- self.generate_and_settle([self.dev_e_client, 'ovs0'])
- # Basic verification that the bridges/ports/interfaces are there in OVS
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertIn(b' Bridge ovs0', out)
- self.assertIn(b' Port patch0-1\n Interface patch0-1\n type: patch', out)
- self.assertIn(b' Port bond0', out)
-@@ -141,7 +141,7 @@ class _CommonTests():
- self.generate_and_settle([self.dev_e_client, 'ovs1'])
- # Verify that the netplan=true tagged patch ports have been cleaned up
- # even though the containing bond0 port still exists (with new patch ports)
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertIn(b' Bridge ovs1', out)
- self.assertIn(b' Port patchy\n Interface patchy\n type: patch', out)
- self.assertIn(b' Port bond0', out)
-@@ -155,9 +155,9 @@ class _CommonTests():
-
- def test_bridge_vlan(self):
- self.setup_eth(None, True)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-%s' % self.dev_e_client])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-data'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-%s.100' % self.dev_e_client])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-%s' % self.dev_e_client])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-data'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-%s.100' % self.dev_e_client])
- with open(self.config, 'w') as f:
- f.write('''network:
- version: 2
-@@ -183,7 +183,7 @@ class _CommonTests():
- 'br-data',
- 'br-eth42.100'])
- # Basic verification that the interfaces/ports are set up in OVS
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertIn(b' Bridge br-%b' % self.dev_e_client.encode(), out)
- self.assertIn(b''' Port %(ec)b
- Interface %(ec)b''' % {b'ec': self.dev_e_client.encode()}, out)
-@@ -196,16 +196,16 @@ class _CommonTests():
- ['inet 192.168.5.[0-9]+/16', 'mtu 9000']) # from DHCP
- self.assert_iface('br-data', ['inet 192.168.20.1/16'])
- self.assert_iface(self.dev_e_client, ['mtu 9000', 'master ovs-system'])
-- self.assertIn(b'100', subprocess.check_output(['ovs-vsctl', 'br-to-vlan',
-+ self.assertIn(b'100', subprocess.check_output(['ovs-vsctl', '-t', '5', 'br-to-vlan',
- 'br-%s.100' % self.dev_e_client]))
- self.assertIn(b'br-%b' % self.dev_e_client.encode(), subprocess.check_output(
-- ['ovs-vsctl', 'br-to-parent', 'br-%s.100' % self.dev_e_client]))
-+ ['ovs-vsctl', '-t', '5', 'br-to-parent', 'br-%s.100' % self.dev_e_client]))
- self.assertIn(b'br-%b' % self.dev_e_client.encode(), out)
-
- def test_bridge_vlan_deletion(self):
- self.setup_eth(None, True)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-%s' % self.dev_e_client])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-%s.100' % self.dev_e_client])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-%s' % self.dev_e_client])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-%s.100' % self.dev_e_client])
- with open(self.config, 'w') as f:
- f.write('''network:
- version: 2
-@@ -228,13 +228,13 @@ class _CommonTests():
- 'br-eth42.100'])
-
- # Basic verification that the underlying bridge and vlan interface are configured
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertIn(b' Bridge br-%b' % self.dev_e_client.encode(), out)
- self.assertIn(b''' Port br-%(ec)b.100
- tag: 100
- Interface br-%(ec)b.100
- type: internal''' % {b'ec': self.dev_e_client.encode()}, out)
-- self.assertIn(b'100', subprocess.check_output(['ovs-vsctl', 'br-to-vlan',
-+ self.assertIn(b'100', subprocess.check_output(['ovs-vsctl', '-t', '5', 'br-to-vlan',
- 'br-%s.100' % self.dev_e_client]))
-
- # Write a network configuration that has the .100 vlan interface removed
-@@ -253,14 +253,14 @@ class _CommonTests():
- self.generate_and_settle([self.dev_e_client, self.state_dhcp4('br-eth42')])
-
- # Check that the underlying bridge is still present but the vlan interface is now absent
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertIn(b' Bridge br-%b' % self.dev_e_client.encode(), out)
- self.assertNotIn(b'Port br-%(ec)b.100' % {b'ec': self.dev_e_client.encode()}, out)
-
- def test_bridge_base(self):
- self.setup_eth(None, False)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovsbr'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', 'del-ssl'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovsbr'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', 'del-ssl'])
- with open(self.config, 'w') as f:
- f.write('''network:
- ethernets:
-@@ -282,7 +282,7 @@ class _CommonTests():
- ''' % {'ec': self.dev_e_client, 'e2c': self.dev_e2_client})
- self.generate_and_settle([self.dev_e_client, self.dev_e2_client, 'ovsbr'])
- # Basic verification that the interfaces/ports are in OVS
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertIn(b' Bridge ovsbr', out)
- self.assertIn(b' Controller "tcp:127.0.0.1"', out)
- self.assertIn(b' Controller "pssl:1337:[::1]"', out)
-@@ -291,15 +291,15 @@ class _CommonTests():
- self.assertIn(b' Port %(ec)b\n Interface %(ec)b' % {b'ec': self.dev_e_client.encode()}, out)
- self.assertIn(b' Port %(e2c)b\n Interface %(e2c)b' % {b'e2c': self.dev_e2_client.encode()}, out)
- # Verify the bridge was tagged 'netplan:true' correctly
-- out = subprocess.check_output(['ovs-vsctl', '--columns=name,external-ids', '-f', 'csv', '-d', 'bare',
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=name,external-ids', '-f', 'csv', '-d', 'bare',
- 'list', 'Bridge', 'ovsbr'])
- self.assertIn(b'netplan=true', out)
- self.assert_iface('ovsbr', ['inet 192.170.1.1/24'])
-
- def test_bond_base(self):
- self.setup_eth(None, False)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovsbr'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'mybond'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovsbr'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'mybond'])
- with open(self.config, 'w') as f:
- f.write('''network:
- ethernets:
-@@ -318,13 +318,14 @@ class _CommonTests():
- interfaces: [mybond]''' % {'ec': self.dev_e_client, 'e2c': self.dev_e2_client})
- self.generate_and_settle([self.dev_e_client, self.dev_e2_client, 'ovsbr'])
- # Basic verification that the interfaces/ports are in OVS
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertIn(b' Bridge ovsbr', out)
- self.assertIn(b' Port mybond', out)
- self.assertIn(b' Interface %b' % self.dev_e_client.encode(), out)
- self.assertIn(b' Interface %b' % self.dev_e2_client.encode(), out)
- # Verify the bond was tagged 'netplan:true' correctly
-- out = subprocess.check_output(['ovs-vsctl', '--columns=name,external-ids', '-f', 'csv', '-d', 'bare', 'list', 'Port'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=name,external-ids', '-f', 'csv',
-+ '-d', 'bare', 'list', 'Port'])
- self.assertIn(b'mybond,netplan=true', out)
- # Verify bond params
- out = subprocess.check_output(['ovs-appctl', 'bond/show', 'mybond'])
-@@ -337,10 +338,10 @@ class _CommonTests():
-
- def test_bridge_patch_ports(self):
- self.setup_eth(None)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br0'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br1'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch0-1'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch1-0'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br0'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br1'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch0-1'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch1-0'])
- with open(self.config, 'w') as f:
- f.write('''network:
- openvswitch:
-@@ -355,7 +356,7 @@ class _CommonTests():
- interfaces: [patch1-0]''')
- self.generate_and_settle(['br0', 'br1'])
- # Basic verification that the interfaces/ports are set up in OVS
-- out = subprocess.check_output(['ovs-vsctl', 'show'])
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
- self.assertIn(b' Bridge br0', out)
- self.assertIn(b''' Port patch0-1
- Interface patch0-1
-@@ -371,7 +372,7 @@ class _CommonTests():
-
- def test_bridge_non_ovs_bond(self):
- self.setup_eth(None, False)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs-br'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs-br'])
- self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'non-ovs-bond'])
- with open(self.config, 'w') as f:
- f.write('''network:
-@@ -388,7 +389,7 @@ class _CommonTests():
- openvswitch: {}''' % {'ec': self.dev_e_client, 'e2c': self.dev_e2_client})
- self.generate_and_settle([self.dev_e_client, self.dev_e2_client, 'ovs-br', 'non-ovs-bond'])
- # Basic verification that the interfaces/ports are set up in OVS
-- out = subprocess.check_output(['ovs-vsctl', 'show'], universal_newlines=True)
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'], universal_newlines=True)
- self.assertIn(' Bridge ovs-br', out)
- self.assertIn(''' Port non-ovs-bond
- Interface non-ovs-bond''', out)
-@@ -401,7 +402,7 @@ class _CommonTests():
-
- def test_vlan_maas(self):
- self.setup_eth(None, False)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs0'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs0'])
- self.addCleanup(subprocess.call, ['ip', 'link', 'delete', '%s.21' % self.dev_e_client], stderr=subprocess.DEVNULL)
- with open(self.config, 'w') as f:
- f.write('''network:
-@@ -434,7 +435,7 @@ class _CommonTests():
- mtu: 1500''' % {'ec': self.dev_e_client})
- self.generate_and_settle([self.dev_e_client, 'ovs0', 'eth42.21'])
- # Basic verification that the interfaces/ports are set up in OVS
-- out = subprocess.check_output(['ovs-vsctl', 'show'], universal_newlines=True)
-+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'], universal_newlines=True)
- self.assertIn(' Bridge ovs0', out)
- self.assertIn(''' Port %(ec)s.21
- Interface %(ec)s.21''' % {'ec': self.dev_e_client}, out)
-@@ -466,9 +467,9 @@ class _CommonTests():
-
- def test_settings_tag_cleanup(self):
- self.setup_eth(None, False)
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs0'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs1'])
-- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'bond0'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs0'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs1'])
-+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'bond0'])
- with open(self.config, 'w') as f:
- f.write('''network:
- version: 2
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index ca456a8..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1,6 +0,0 @@
-ovs-timeout.patch
-autopkgtest-fixes.patch
-0002-cli-apply-fix-potential-race-with-rename-creation-of.patch
-0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch
-0004-tests-ethernets-fix-autopkgtest-with-alternating-def.patch
-0005-nm-fix-rendering-of-password-for-unknown-passthrough.patch
diff --git a/debian/tests/control b/debian/tests/control
index 2131fb8..1bb15fc 100644
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -95,6 +95,7 @@ Depends: @,
libnm0,
python3-gi,
gir1.2-nm-1.0,
+ iw,
Restrictions: allow-stderr, needs-root, isolation-container, breaks-testbed, flaky
Features: test-name=wifi
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000..61b33cc
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,2 @@
+_build
+/*env*/
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..9371f95
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,63 @@
+# Minimal makefile for Sphinx documentation
+# This is for development purposes only, e.g. to run a local Sphinx
+# development environment. Usage:
+#
+# Setup: Run `make install` from doc/ to setup a Python virtual env
+# Devel: Run `make run` from doc/ to get a live preview of your changes
+# Test: Run `make spelling` from doc/ to validate your spelling
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = .
+BUILDDIR = _build
+
+VENV = env/bin/activate
+PORT = 8090
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+install:
+ @echo "... setting up virtualenv"
+ python3 -m venv env
+ . $(VENV); pip install --upgrade -r requirements.txt
+ @echo "\n" \
+ "--------------------------------------------------------------- \n" \
+ "* watch, build and serve the documentation: make run \n" \
+ "* check spelling: make spelling \n" \
+ "\n" \
+ "enchant must be installed in order for pyenchant (and therefore \n" \
+ "spelling checks) to work. \n" \
+ "--------------------------------------------------------------- \n"
+
+clean:
+ -rm -rf _build/*
+
+run:
+ . $(VENV); sphinx-autobuild $(ALLSPHINXOPTS) --ignore ".git/*" --ignore "*.scss" . -b dirhtml -a _build/html --host 0.0.0.0 --port $(PORT)
+
+test:
+ . $(VENV); $(SPHINXBUILD) -b html . _build/html
+
+html:
+ . $(VENV); $(SPHINXBUILD) -b dirhtml . _build/html
+
+spelling:
+ . $(VENV); $(SPHINXBUILD) -b spelling $(ALLSPHINXOPTS) . _build/spelling
+ @echo
+ @echo "Check finished. Wrong words can be found in " \
+ "_build/spelling/output.txt."
+
+quickstart:
+ . $(VENV); sphinx-quickstart
+
+.PHONY: help install clean run Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000..5252390
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,24 @@
+# Netplan Documentation
+
+```{toctree}
+---
+maxdepth: 2
+---
+reference
+howto
+tutorials
+explanation
+```
+
+## Project and community
+
+* **Reporting bugs**: We want to know about the problems so we can fix them.
+* **Code changes**: The code is open and we are open to accepting changes to
+ it. So, don’t worry about maintaining a new fork, and instead, let's work
+ together.
+
+### If you want to get involved
+* Visit the website at [netplan.io](https://netplan.io)
+* Join the [community forum](https://askubuntu.com/questions/tagged/netplan)
+* Report a bug on [Launchpad](https://bugs.launchpad.net/netplan/+filebug)
+* Contribute on [GitHub](https://github.com/canonical/netplan)
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..5980810
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,54 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'Netplan'
+copyright = '2022, Netplan team'
+author = 'Netplan team'
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinx_design', 'myst_parser']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'env']
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'furo'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# -- Options for MyST --------------------------------------------------------
+myst_title_to_header = True
diff --git a/examples/dbus_config_scenario.txt b/doc/dbus-config.md
index d1ec15e..5de354e 100644
--- a/examples/dbus_config_scenario.txt
+++ b/doc/dbus-config.md
@@ -1,41 +1,57 @@
-# Example interaction with Netplan's DBus config API
+# How to use DBus config API
-## Copy the current state from /{etc,run,lib}/netplan/*.yaml by creating a new config object
+Copy the current state from `/{etc,run,lib}/netplan/*.yaml` by creating a new config object
+```
$ busctl call io.netplan.Netplan /io/netplan/Netplan io.netplan.Netplan Config
o "/io/netplan/Netplan/config/ULJIU0"
+```
-## Read the merged YAML configuration
+Read the merged YAML configuration
+```
$ busctl call io.netplan.Netplan /io/netplan/Netplan/config/ULJIU0 io.netplan.Netplan.Config Get
s "network:\n ethernets:\n eth0:\n dhcp4: true\n renderer: networkd\n version: 2\n"
+```
-## Write a new config snippet into 70-snapd.yaml
+Write a new config snippet into `70-snapd.yaml`
+```
$ busctl call io.netplan.Netplan /io/netplan/Netplan/config/ULJIU0 io.netplan.Netplan.Config Set ss "ethernets.eth0={dhcp4: false, dhcp6: true}" "70-snapd"
b true
+```
-## Check the newly written configuration
+Check the newly written configuration
+```
$ busctl call io.netplan.Netplan /io/netplan/Netplan/config/ULJIU0 io.netplan.Netplan.Config Get
s "network:\n ethernets:\n eth0:\n dhcp4: false\n dhcp6: true\n renderer: networkd\n version: 2\n"
+```
-## Try to apply the current config object's state
+Try to apply the current config object's state
+```
$ busctl call io.netplan.Netplan /io/netplan/Netplan/config/ULJIU0 io.netplan.Netplan.Config Try u 20
b true
+```
-## Accept the Try() state within the 20 seconds timeout, if not it will be auto-rejected
+Accept the Try() state within the 20 seconds timeout, if not it will be auto-rejected
+```
$ busctl call io.netplan.Netplan /io/netplan/Netplan/config/ULJIU0 io.netplan.Netplan.Config Apply
b true
[SIGNAL] io.netplan.Netplan /io/netplan/Netplan/config/ULJIU0 io.netplan.Netplan.Config Changed() is triggered
[OBJECT] io.netplan.Netplan /io/netplan/Netplan/config/ULJIU0 is removed from the bus
+```
-## Create a new config object and get the merged YAML config
+Create a new config object and get the merged YAML config
+```
$ busctl call io.netplan.Netplan /io/netplan/Netplan io.netplan.Netplan Config
o "/io/netplan/Netplan/config/KC0IU0
$ busctl call io.netplan.Netplan /io/netplan/Netplan/config/KC0IU0 io.netplan.Netplan.Config Get
s "network:\n ethernets:\n eth0:\n dhcp4: false\n dhcp6: true\n renderer: networkd\n version: 2\n"
+```
-## Reject that config object again
+Reject that config object again
+```
$ busctl call io.netplan.Netplan /io/netplan/Netplan/config/KC0IU0 io.netplan.Netplan.Config Cancel
b true
[SIGNAL] io.netplan.Netplan /io/netplan/Netplan/config/KC0IU0 io.netplan.Netplan.Config Changed() is triggered
[OBJECT] io.netplan.Netplan /io/netplan/Netplan/config/KC0IU0 is removed from the bus
+```
diff --git a/doc/examples.md b/doc/examples.md
new file mode 100644
index 0000000..ae65e6e
--- /dev/null
+++ b/doc/examples.md
@@ -0,0 +1,630 @@
+# Examples
+
+Below are a collection of example netplan configurations for common scenarios.
+If you see a scenario missing or have one to contribute, please file a bug
+against this documentation with the example.
+
+To configure netplan, save configuration files under `/etc/netplan/` with a
+`.yaml` extension (e.g. `/etc/netplan/config.yaml`), then run
+`sudo netplan apply`. This command parses and applies the configuration to the
+system. Configuration written to disk under `/etc/netplan/` will persist between
+reboots.
+
+Also, see [/examples](https://github.com/canonical/netplan/tree/main/examples)
+on GitHub.
+
+## Using DHCP and static addressing
+
+To let the interface named `enp3s0` get an address via DHCP, create a YAML file with the following:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ enp3s0:
+ dhcp4: true
+```
+
+To instead set a static IP address, use the addresses key, which takes a list of (IPv4 or IPv6), addresses along with the subnet prefix length (e.g. /24). DNS information can be provided as well, and the gateway can be defined via a default route:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ enp3s0:
+ addresses:
+ - 10.10.10.2/24
+ nameservers:
+ search: [mydomain, otherdomain]
+ addresses: [10.10.10.1, 1.1.1.1]
+ routes:
+ - to: default
+ via: 10.10.10.1
+```
+
+## Connecting multiple interfaces with DHCP
+
+Many systems now include more than one network interface. Servers will commonly need to connect to multiple networks, and may require that traffic to the Internet goes through a specific interface despite all of them providing a valid gateway.
+
+One can achieve the exact routing desired over DHCP by specifying a metric for the routes retrieved over DHCP, which will ensure some routes are preferred over others. In this example, 'enred' is preferred over 'engreen', as it has a lower route metric:
+
+```yaml
+network:
+ version: 2
+ ethernets:
+ enred:
+ dhcp4: yes
+ dhcp4-overrides:
+ route-metric: 100
+ engreen:
+ dhcp4: yes
+ dhcp4-overrides:
+ route-metric: 200
+```
+
+## Connecting to an open wireless network
+
+Netplan easily supports connecting to an open wireless network (one that is not secured by a password), only requiring that the access point is defined:
+
+```yaml
+network:
+ version: 2
+ wifis:
+ wl0:
+ access-points:
+ opennetwork: {}
+ dhcp4: yes
+```
+
+## Connecting to a WPA Personal wireless network
+
+Wireless devices use the 'wifis' key and share the same configuration options with wired ethernet devices. The wireless access point name and password should also be specified:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ wifis:
+ wlp2s0b1:
+ dhcp4: no
+ dhcp6: no
+ addresses: [192.168.0.21/24]
+ nameservers:
+ addresses: [192.168.0.1, 8.8.8.8]
+ access-points:
+ "network_ssid_name":
+ password: "**********"
+ routes:
+ - to: default
+ via: 192.168.0.1
+```
+
+## Connecting to WPA Enterprise wireless networks
+
+It is also common to find wireless networks secured using WPA or WPA2 Enterprise, which requires additional authentication parameters.
+
+For example, if the network is secured using WPA-EAP and TTLS:
+
+```yaml
+network:
+ version: 2
+ wifis:
+ wl0:
+ access-points:
+ workplace:
+ auth:
+ key-management: eap
+ method: ttls
+ anonymous-identity: "@internal.example.com"
+ identity: "joe@internal.example.com"
+ password: "v3ryS3kr1t"
+ dhcp4: yes
+```
+
+Or, if the network is secured using WPA-EAP and TLS:
+
+```yaml
+network:
+ version: 2
+ wifis:
+ wl0:
+ access-points:
+ university:
+ auth:
+ key-management: eap
+ method: tls
+ anonymous-identity: "@cust.example.com"
+ identity: "cert-joe@cust.example.com"
+ ca-certificate: /etc/ssl/cust-cacrt.pem
+ client-certificate: /etc/ssl/cust-crt.pem
+ client-key: /etc/ssl/cust-key.pem
+ client-key-password: "d3cryptPr1v4t3K3y"
+ dhcp4: yes
+```
+
+Many different modes of encryption are supported. See the [Netplan reference](/reference) page.
+
+## Using multiple addresses on a single interface
+
+The addresses key can take a list of addresses to assign to an interface:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ enp3s0:
+ addresses:
+ - 10.100.1.37/24
+ - 10.100.1.38/24:
+ label: "enp3s0:0"
+ - 10.100.1.39/24:
+ label: "enp3s0:some-label"
+ routes:
+ - to: default
+ via: 10.100.1.1
+```
+
+## Using multiple addresses with multiple gateways
+
+Similar to the example above, interfaces with multiple addresses can be
+configured with multiple gateways, and static DNS nameservers (Google DNS for
+this example):
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ enp3s0:
+ addresses:
+ - 10.0.0.10/24
+ - 11.0.0.11/24
+ nameservers:
+ addresses:
+ - 8.8.8.8
+ - 8.8.4.4
+ routes:
+ - to: default
+ via: 10.0.0.1
+ metric: 200
+ - to: default
+ via: 11.0.0.1
+ metric: 300
+```
+
+We configure individual routes to default (or 0.0.0.0/0) using the address of the gateway for the subnet. The `metric` value should be adjusted so the routing happens as expected.
+
+DHCP can be used to receive one of the IP addresses for the interface. In this case, the default route for that address will be automatically configured with a `metric` value of 100.
+
+## Using Network Manager as a renderer
+
+Netplan supports both networkd and Network Manager as backends. You can specify which network backend should be used to configure particular devices by using the `renderer` key. You can also delegate all configuration of the network to Network Manager itself by specifying only the `renderer` key:
+
+```yaml
+network:
+ version: 2
+ renderer: NetworkManager
+```
+
+## Configuring interface bonding
+
+Bonding is configured by declaring a bond interface with a list of physical interfaces and a bonding mode. Below is an example of an active-backup bond that uses DHCP to obtain an address:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ bonds:
+ bond0:
+ dhcp4: yes
+ interfaces:
+ - enp3s0
+ - enp4s0
+ parameters:
+ mode: active-backup
+ primary: enp3s0
+```
+
+Below is an example of a system acting as a router with various bonded interfaces and different types. Note the 'optional: true' key declarations that allow booting to occur without waiting for those interfaces to activate fully.
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ enp1s0:
+ dhcp4: no
+ enp2s0:
+ dhcp4: no
+ enp3s0:
+ dhcp4: no
+ optional: true
+ enp4s0:
+ dhcp4: no
+ optional: true
+ enp5s0:
+ dhcp4: no
+ optional: true
+ enp6s0:
+ dhcp4: no
+ optional: true
+ bonds:
+ bond-lan:
+ interfaces: [enp2s0, enp3s0]
+ addresses: [192.168.93.2/24]
+ parameters:
+ mode: 802.3ad
+ mii-monitor-interval: 1
+ bond-wan:
+ interfaces: [enp1s0, enp4s0]
+ addresses: [192.168.1.252/24]
+ nameservers:
+ search: [local]
+ addresses: [8.8.8.8, 8.8.4.4]
+ parameters:
+ mode: active-backup
+ mii-monitor-interval: 1
+ gratuitious-arp: 5
+ routes:
+ - to: default
+ via: 192.168.1.1
+ bond-conntrack:
+ interfaces: [enp5s0, enp6s0]
+ addresses: [192.168.254.2/24]
+ parameters:
+ mode: balance-rr
+ mii-monitor-interval: 1
+```
+
+## Configuring network bridges
+
+To create a very simple bridge consisting of a single device that uses DHCP, write:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ enp3s0:
+ dhcp4: no
+ bridges:
+ br0:
+ dhcp4: yes
+ interfaces:
+ - enp3s0
+```
+
+A more complex example, to get libvirtd to use a specific bridge with a tagged vlan, while continuing to provide an untagged interface as well would involve:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ enp0s25:
+ dhcp4: true
+ bridges:
+ br0:
+ addresses: [ 10.3.99.25/24 ]
+ interfaces: [ vlan15 ]
+ vlans:
+ vlan15:
+ accept-ra: no
+ id: 15
+ link: enp0s25
+```
+
+Then libvirtd would be configured to use this bridge by adding the following content to a new XML file under `/etc/libvirtd/qemu/networks/`. The name of the bridge in the &lt;bridge&gt; tag as well as in &lt;name&gt; need to match the name of the bridge device configured using netplan:
+
+```xml
+<network>
+ <name>br0</name>
+ <bridge name='br0'/>
+ <forward mode="bridge"/>
+</network>
+```
+
+## Attaching VLANs to network interfaces
+
+To configure multiple VLANs with renamed interfaces:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ mainif:
+ match:
+ macaddress: "de:ad:be:ef:ca:fe"
+ set-name: mainif
+ addresses: [ "10.3.0.5/23" ]
+ nameservers:
+ addresses: [ "8.8.8.8", "8.8.4.4" ]
+ search: [ example.com ]
+ routes:
+ - to: default
+ via: 10.3.0.1
+ vlans:
+ vlan15:
+ id: 15
+ link: mainif
+ addresses: [ "10.3.99.5/24" ]
+ vlan10:
+ id: 10
+ link: mainif
+ addresses: [ "10.3.98.5/24" ]
+ nameservers:
+ addresses: [ "127.0.0.1" ]
+ search: [ domain1.example.com, domain2.example.com ]
+```
+
+## Reaching a directly connected gateway
+
+This allows setting up a default route, or any route, using the "on-link" keyword where the gateway is an IP address that is directly connected to the network even if the address does not match the subnet configured on the interface.
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ ens3:
+ addresses: [ "10.10.10.1/24" ]
+ routes:
+ - to: default # or 0.0.0.0/0
+ via: 9.9.9.9
+ on-link: true
+```
+
+For IPv6 the config would be very similar, with the notable difference being an additional scope: link host route to the router's address required:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ ens3:
+ addresses: [ "2001:cafe:face:beef::dead:dead/64" ]
+ routes:
+ - to: "2001:cafe:face::1/128"
+ scope: link
+ - to: default # or "::/0"
+ via: "2001:cafe:face::1"
+ on-link: true
+```
+
+## Configuring source routing
+
+Route tables can be added to particular interfaces to allow routing between two networks:
+
+In the example below, ens3 is on the 192.168.3.0/24 network and ens5 is on the 192.168.5.0/24 network. This enables clients on either network to connect to the other and allow the response to come from the correct interface.
+
+Furthermore, the default route is still assigned to ens5 allowing any other traffic to go through it.
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ ens3:
+ addresses:
+ - 192.168.3.30/24
+ dhcp4: no
+ routes:
+ - to: 192.168.3.0/24
+ via: 192.168.3.1
+ table: 101
+ routing-policy:
+ - from: 192.168.3.0/24
+ table: 101
+ ens5:
+ addresses:
+ - 192.168.5.24/24
+ dhcp4: no
+ routes:
+ - to: default
+ via: 192.168.5.1
+ - to: 192.168.5.0/24
+ via: 192.168.5.1
+ table: 102
+ routing-policy:
+ - from: 192.168.5.0/24
+ table: 102
+```
+
+## Configuring a loopback interface
+
+Networkd does not allow creating new loopback devices, but a user can add new addresses to the standard loopback interface, lo, in order to have it considered a valid address on the machine as well as for custom routing:
+
+```yaml
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ lo:
+ addresses: [ "127.0.0.1/8", "::1/128", "7.7.7.7/32" ]
+```
+
+## Integration with a Windows DHCP Server
+
+For networks where DHCP is provided by a Windows Server using the dhcp-identifier key allows for interoperability:
+
+```yaml
+network:
+ version: 2
+ ethernets:
+ enp3s0:
+ dhcp4: yes
+ dhcp-identifier: mac
+```
+
+## Connecting an IP tunnel
+
+Tunnels allow an administrator to extend networks across the Internet by configuring two endpoints that will connect a special tunnel interface and do the routing required. Netplan supports SIT, GRE, IP-in-IP (ipip, ipip6, ip6ip6), IP6GRE, VTI and VTI6 tunnels.
+
+A common use of tunnels is to enable IPv6 connectivity on networks that only support IPv4. The example below show how such a tunnel might be configured.
+
+Here, 1.1.1.1 is the client's own IP address; 2.2.2.2 is the remote server's IPv4 address, "2001:dead:beef::2/64" is the client's IPv6 address as defined by the tunnel, and "2001:dead:beef::1" is the remote server's IPv6 address.
+
+Finally, "2001:cafe:face::1/64" is an address for the client within the routed IPv6 prefix:
+
+```yaml
+network:
+ version: 2
+ ethernets:
+ eth0:
+ addresses:
+ - 1.1.1.1/24
+ - "2001:cafe:face::1/64"
+ routes:
+ - to: default
+ via: 1.1.1.254
+ tunnels:
+ he-ipv6:
+ mode: sit
+ remote: 2.2.2.2
+ local: 1.1.1.1
+ addresses:
+ - "2001:dead:beef::2/64"
+ routes:
+ - to: default
+ via: "2001:dead:beef::1"
+```
+
+## Configuring SR-IOV Virtual Functions
+
+For SR-IOV network cards, it is possible to dynamically allocate Virtual Function interfaces for every configured Physical Function. In netplan, a VF is defined by having a link: property pointing to the parent PF.
+
+```yaml
+network:
+ version: 2
+ ethernets:
+ eno1:
+ mtu: 9000
+ enp1s16f1:
+ link: eno1
+ addresses : [ "10.15.98.25/24" ]
+ vf1:
+ match:
+ name: enp1s16f[2-3]
+ link: eno1
+ addresses : [ "10.15.99.25/24" ]
+```
+
+## Complex example
+This is a complex example which shows most available features
+
+```yaml
+ network:
+ version: 2
+ # if specified, can only realistically have that value, as networkd cannot
+ # render wifi/3G.
+ renderer: NetworkManager
+ vrfs:
+ mgmt-vrf:
+ table: 10
+ interfaces:
+ - id1
+ routes:
+ - to: default
+ via: 192.168.24.254
+ metric: 100
+ ethernets:
+ lo:
+ addresses:
+ - 172.16.20.20/32
+ link-local: []
+ # opaque ID for physical interfaces, only referred to by other stanzas
+ id0:
+ match:
+ macaddress: 00:11:22:33:44:55
+ wakeonlan: true
+ dhcp4: true
+ addresses:
+ - 192.168.14.2/24
+ - 192.168.14.3/24
+ - "2001:1::1/64"
+ nameservers:
+ search: [foo.local, bar.local]
+ addresses: [8.8.8.8]
+ routes:
+ - to: default
+ via: 192.168.14.1
+ - to: default
+ via: "2001:1::2"
+ - to: 0.0.0.0/0
+ via: 11.0.0.1
+ table: 70
+ on-link: true
+ metric: 3
+ routing-policy:
+ - to: 10.0.0.0/8
+ from: 192.168.14.2/24
+ table: 70
+ priority: 100
+ - to: 20.0.0.0/8
+ from: 192.168.14.3/24
+ table: 70
+ priority: 50
+ # only networkd can render on-link routes and routing policies
+ renderer: networkd
+ id1:
+ match:
+ macaddress: 00:11:22:33:44:56
+ wakeonlan: true
+ dhcp4: true
+ addresses:
+ - 192.168.24.2/24
+ lom:
+ match:
+ driver: ixgbe
+ # you are responsible for setting tight enough match rules
+ # that only match one device if you use set-name
+ set-name: lom1
+ dhcp6: true
+ switchports:
+ # all cards on second PCI bus unconfigured by
+ # themselves, will be added to br0 below
+ match:
+ name: enp2*
+ mtu: 1280
+ wifis:
+ all-wlans:
+ # useful on a system where you know there is
+ # only ever going to be one device
+ match: {}
+ access-points:
+ "Joe's home":
+ # mode defaults to "infrastructure" (client)
+ password: "s3kr1t"
+ # this creates an AP on wlp1s0 using hostapd
+ # no match rules, thus the ID is the interface name
+ wlp1s0:
+ access-points:
+ "guest":
+ mode: ap
+ # no WPA config implies default of open
+ bridges:
+ # the key name is the name for virtual (created) interfaces
+ # no match: and set-name: allowed
+ br0:
+ # IDs of the components; switchports expands into multiple interfaces
+ interfaces: [wlp1s0, switchports]
+ dhcp4: true
+ br20:
+ interfaces: [vxlan20]
+ tunnels:
+ vxlan20:
+ mode: vxlan
+ link: lo
+ id: 20
+ mtu: 8950
+ accept-ra: no
+ neigh-suppress: true
+ link-local: []
+ mac-learning: false
+ port: 4789
+ local: 172.16.20.20
+```
diff --git a/doc/explanation.md b/doc/explanation.md
new file mode 100644
index 0000000..c076d91
--- /dev/null
+++ b/doc/explanation.md
@@ -0,0 +1,9 @@
+# Explanation
+
+## Design
+* [Netplan Design](https://netplan.io/design)
+ – Network configuration abstraction via systemd-generator
+
+## FAQs
+* [Netplan FAQs](https://netplan.io/faq)
+ – Find answers to common questions \ No newline at end of file
diff --git a/doc/howto.md b/doc/howto.md
new file mode 100644
index 0000000..d9d7d35
--- /dev/null
+++ b/doc/howto.md
@@ -0,0 +1,6 @@
+# How-to guides
+
+```{toctree}
+examples
+dbus-config
+```
diff --git a/doc/index.md b/doc/index.md
new file mode 120000
index 0000000..42061c0
--- /dev/null
+++ b/doc/index.md
@@ -0,0 +1 @@
+README.md \ No newline at end of file
diff --git a/doc/manpage-header.md b/doc/manpage-header.md
index 0f5231e..b6f138f 100644
--- a/doc/manpage-header.md
+++ b/doc/manpage-header.md
@@ -4,6 +4,7 @@ section: 5
author:
- Mathieu Trudel-Lapierre (<cyphermox@ubuntu.com>)
- Martin Pitt (<martin.pitt@ubuntu.com>)
+- Lukas Märdian (<slyon@ubuntu.com>)
...
# NAME
diff --git a/doc/meson.build b/doc/meson.build
new file mode 100644
index 0000000..53b1723
--- /dev/null
+++ b/doc/meson.build
@@ -0,0 +1,26 @@
+if pandoc.found()
+ custom_target(
+ input: ['manpage-header.md', 'netplan-yaml.md', 'manpage-footer.md'],
+ output: 'netplan.5',
+ command: [pandoc, '-s', '-o', '@OUTPUT@', '--from=markdown-smart', '@INPUT@'],
+ install: true,
+ install_dir: join_paths(get_option('mandir'), 'man5'))
+ custom_target(
+ input: 'netplan-yaml.md',
+ output: 'netplan.html',
+ command: [pandoc, '-s', '--metadata', 'title="Netplan reference"', '--toc', '-o', '@OUTPUT@', '@INPUT@'],
+ install: true,
+ install_dir: join_paths(get_option('datadir'), 'doc', 'netplan'))
+ foreach doc : ['netplan-apply', 'netplan-dbus', 'netplan-generate', 'netplan-get', 'netplan-set', 'netplan-try']
+ markdown = files(doc + '.md')
+ manpage = doc + '.8'
+ custom_target(
+ input: markdown,
+ output: manpage,
+ command: [pandoc, '-s', '-o', '@OUTPUT@', '--from=markdown-smart', '@INPUT@'],
+ install: true,
+ install_dir: join_paths(get_option('mandir'), 'man8'))
+ endforeach
+else
+ warning('Program "pandoc" not found! Cannot generate documentation/man pages')
+endif
diff --git a/doc/netplan-apply.md b/doc/netplan-apply.md
index 7811f04..b8c7de1 100644
--- a/doc/netplan-apply.md
+++ b/doc/netplan-apply.md
@@ -50,7 +50,7 @@ see **netplan**(5).
**netplan apply** will not remove virtual devices such as bridges and bonds
that have been created, even if they are no longer described in the netplan
configuration. That is due to the fact that netplan operates statelessly and
-is not aware of the previously defined virtal devices.
+is not aware of the previously defined virtual devices.
This can be resolved by manually removing the virtual device (for example
``ip link delete dev bond0``) and then running **netplan apply**, by rebooting,
diff --git a/doc/netplan-yaml.md b/doc/netplan-yaml.md
new file mode 100644
index 0000000..2f4e38d
--- /dev/null
+++ b/doc/netplan-yaml.md
@@ -0,0 +1,1713 @@
+---
+title: "YAML configuration"
+---
+
+## Introduction
+Distribution installers, cloud instantiation, image builds for particular
+devices, or any other way to deploy an operating system put its desired
+network configuration into YAML configuration file(s). During
+early boot, the netplan "network renderer" runs which reads
+`/{lib,etc,run}/netplan/*.yaml` and writes configuration to `/run` to hand
+off control of devices to the specified networking daemon.
+
+ - Configured devices get handled by systemd-networkd by default,
+ unless explicitly marked as managed by a specific renderer (NetworkManager)
+ - Devices not covered by the network config do not get touched at all.
+ - Usable in initramfs (few dependencies and fast)
+ - No persistent generated config, only original YAML config
+ - Parser supports multiple config files to allow applications like libvirt or
+ lxd to package up expected network config (`virbr0`, `lxdbr0`), or to change
+ the global default policy to use NetworkManager for everything.
+ - Retains the flexibility to change backends/policy later or adjust to
+ removing NetworkManager, as generated configuration is ephemeral.
+
+## General structure
+netplan's configuration files use the
+[YAML](http://yaml.org/spec/1.1/current.html) format. All
+`/{lib,etc,run}/netplan/*.yaml` are considered. Lexicographically later files
+(regardless of in which directory they are) amend (new mapping keys) or
+override (same mapping keys) previous ones. A file in `/run/netplan`
+completely shadows a file with same name in `/etc/netplan`, and a file in
+either of those directories shadows a file with the same name in `/lib/netplan`.
+
+The top-level node in a netplan configuration file is a `network:` mapping
+that contains `version: 2` (the YAML currently being used by curtin, MaaS,
+etc. is version 1), and then device definitions grouped by their type, such as
+`ethernets:`, `modems:`, `wifis:`, or `bridges:`. These are the types that our
+renderer can understand and are supported by our backends.
+
+Each type block contains device definitions as a map where the keys (called
+"configuration IDs") are defined as below.
+
+## Device configuration IDs
+The key names below the per-device-type definition maps (like `ethernets:`)
+are called "ID"s. They must be unique throughout the entire set of
+configuration files. Their primary purpose is to serve as anchor names for
+composite devices, for example to enumerate the members of a bridge that is
+currently being defined.
+
+(Since 0.97) If an interface is defined with an ID in a configuration file; it
+will be brought up by the applicable renderer. To not have netplan touch an
+interface at all, it should be completely omitted from the netplan configuration
+files.
+
+There are two physically/structurally different classes of device definitions,
+and the ID field has a different interpretation for each:
+
+Physical devices
+
+> (Examples: ethernet, modem, wifi) These can dynamically come and go between
+> reboots and even during runtime (hot plugging). In the generic case, they
+> can be selected by `match:` rules on desired properties, such as name/name
+> pattern, MAC address, driver, or device paths. In general these will match
+> any number of devices (unless they refer to properties which are unique
+> such as the full path or MAC address), so without further knowledge about
+> the hardware these will always be considered as a group.
+>
+> It is valid to specify no match rules at all, in which case the ID field is
+> simply the interface name to be matched. This is mostly useful if you want
+> to keep simple cases simple, and it's how network device configuration has
+> been done for a long time.
+>
+> If there are ``match``: rules, then the ID field is a purely opaque name
+> which is only being used for references from definitions of compound
+> devices in the config.
+
+Virtual devices
+
+> (Examples: veth, bridge, bond, vrf) These are fully under the control of the
+> config file(s) and the network stack. I. e. these devices are being created
+> instead of matched. Thus `match:` and `set-name:` are not applicable for
+> these, and the ID field is the name of the created virtual device.
+
+## Common properties for physical device types
+
+**Note:** Some options will not work reliably for devices matched by name only
+and rendered by networkd, due to interactions with device renaming in udev.
+Match devices by MAC when setting options like: `wakeonlan` or `*-offload`.
+
+- **match** (mapping)
+
+ > This selects a subset of available physical devices by various hardware
+ > properties. The following configuration will then apply to all matching
+ > devices, as soon as they appear. *All* specified properties must match.
+
+ - **name** (scalar)
+
+ > Current interface name. Globs are supported, and the primary use case for
+ > matching on names, as selecting one fixed name can be more easily achieved
+ > with having no `match:` at all and just using the ID (see above).
+ > (`NetworkManager`: as of v1.14.0)
+
+ - **macaddress** (scalar)
+
+ > Device's 6-byte MAC address in the form "XX:XX:XX:XX:XX:XX" or 20 bytes
+ > for InfiniBand devices (IPoIB). Globs are not allowed.
+
+ - **driver** (scalar or sequence of scalars) – sequence since **0.104**
+
+ > Kernel driver name, corresponding to the `DRIVER` udev property.
+ > A sequence of globs is supported, any of which must match.
+ > Matching on driver is *only* supported with networkd.
+
+ Examples:
+
+ - All cards on second PCI bus:
+ ```yaml
+ match:
+ name: enp2*
+ ```
+
+ - Fixed MAC address:
+ ```yaml
+ match:
+ macaddress: 11:22:33:AA:BB:FF
+ ```
+
+ - First card of driver ``ixgbe``:
+ ```yaml
+ match:
+ driver: ixgbe
+ name: en*s0
+ ```
+
+ - First card with a driver matching ``bcmgenet`` or ``smsc*``:
+ ```yaml
+ match:
+ driver: ["bcmgenet", "smsc*"]
+ name: en*
+ ```
+
+- **set-name** (scalar)
+
+ > When matching on unique properties such as path or MAC, or with additional
+ > assumptions such as "there will only ever be one wifi device", match rules
+ > can be written so that they only match one device. Then this property can be
+ > used to give that device a more specific/desirable/nicer name than the
+ > default from udev's ifnames. Any additional device that satisfies the match
+ > rules will then fail to get renamed and keep the original kernel name (and
+ > dmesg will show an error).
+
+- **wakeonlan** (bool)
+
+ > Enable wake on LAN. Off by default.
+
+- **emit-lldp** (bool) – since **0.99**
+
+ > (networkd backend only) Whether to emit LLDP packets. Off by default.
+
+- **receive-checksum-offload** (bool) – since **0.104**
+
+ > (networkd backend only) If set to true (false), the hardware offload for
+ > checksumming of ingress network packets is enabled (disabled). When unset,
+ > the kernel's default will be used.
+
+- **transmit-checksum-offload** (bool) – since **0.104**
+
+ > (networkd backend only) If set to true (false), the hardware offload for
+ > checksumming of egress network packets is enabled (disabled). When unset,
+ > the kernel's default will be used.
+
+- **tcp-segmentation-offload** (bool) – since **0.104**
+
+ > (networkd backend only) If set to true (false), the TCP Segmentation
+ > Offload (TSO) is enabled (disabled). When unset, the kernel's default will
+ > be used.
+
+- **tcp6-segmentation-offload** (bool) – since **0.104**
+
+ > (networkd backend only) If set to true (false), the TCP6 Segmentation
+ > Offload (tx-tcp6-segmentation) is enabled (disabled). When unset, the
+ > kernel's default will be used.
+
+- **generic-segmentation-offload** (bool) – since **0.104**
+
+ > (networkd backend only) If set to true (false), the Generic Segmentation
+ > Offload (GSO) is enabled (disabled). When unset, the kernel's default will
+ > be used.
+
+- **generic-receive-offload** (bool) – since **0.104**
+
+ > (networkd backend only) If set to true (false), the Generic Receive
+ > Offload (GRO) is enabled (disabled). When unset, the kernel's default will
+ > be used.
+
+- **large-receive-offload** (bool) – since **0.104**
+
+ > (networkd backend only) If set to true (false), the Large Receive Offload
+ > (LRO) is enabled (disabled). When unset, the kernel's default will
+ > be used.
+
+- **openvswitch** (mapping) – since **0.100**
+
+ > This provides additional configuration for the openvswitch network device.
+ > If Open vSwitch is not available on the system, netplan treats the presence
+ > of `openvswitch` configuration as an error.
+ >
+ > Any supported network device that is declared with the `openvswitch`
+ > mapping (or any bond/bridge that includes an interface with an openvswitch
+ > configuration) will be created in openvswitch instead of the defined
+ > renderer. In the case of a `vlan` definition declared the same way,
+ > netplan will create a fake VLAN bridge in openvswitch with the requested
+ > `vlan` properties.
+
+ - **external-ids** (mapping) – since **0.100**
+
+ > Passed-through directly to Open vSwitch
+
+ - **other-config** (mapping) – since **0.100**
+
+ > Passed-through directly to Open vSwitch
+
+ - **lacp** (scalar) – since **0.100**
+
+ > Valid for bond interfaces. Accepts `active`, `passive` or `off` (the
+ > default).
+
+ - **fail-mode** (scalar) – since **0.100**
+
+ > Valid for bridge interfaces. Accepts `secure` or `standalone` (the
+ > default).
+
+ - **mcast-snooping** (bool) – since **0.100**
+
+ > Valid for bridge interfaces. False by default.
+
+ - **protocols** (sequence of scalars) – since **0.100**
+
+ > Valid for bridge interfaces or the network section. List of protocols to
+ > be used when negotiating a connection with the controller. Accepts
+ > `OpenFlow10`, `OpenFlow11`, `OpenFlow12`, `OpenFlow13`, `OpenFlow14`,
+ > `OpenFlow15` and `OpenFlow16`.
+
+ - **rstp** (bool) – since **0.100**
+
+ > Valid for bridge interfaces. False by default.
+
+ - **controller** (mapping) – since **0.100**
+
+ > Valid for bridge interfaces. Specify an external OpenFlow controller.
+
+ - **addresses** (sequence of scalars)
+
+ > Set the list of addresses to use for the controller targets. The
+ > syntax of these addresses is as defined in ovs-vsctl(8). Example:
+ > addresses: `[tcp:127.0.0.1:6653, "ssl:[fe80::1234%eth0]:6653"]`
+
+ - **connection-mode** (scalar)
+
+ > Set the connection mode for the controller. Supported options are
+ > `in-band` and `out-of-band`. The default is `in-band`.
+
+ - **ports** (sequence of sequence of scalars) – since **0.100**
+
+ > Open vSwitch patch ports. Each port is declared as a pair of names
+ > which can be referenced as interfaces in dependent virtual devices
+ > (bonds, bridges).
+
+ Example:
+ ```yaml
+ openvswitch:
+ ports:
+ - [patch0-1, patch1-0]
+ ```
+
+ - **ssl** (mapping) – since **0.100**
+
+ > Valid for global `openvswitch` settings. Options for configuring SSL
+ > server endpoint for the switch.
+
+ - **ca-cert** (scalar)
+
+ > Path to a file containing the CA certificate to be used.
+
+ - **certificate** (scalar)
+
+ > Path to a file containing the server certificate.
+
+ - **private-key** (scalar)
+
+ > Path to a file containing the private key for the server.
+
+## Common properties for all device types
+
+- **renderer** (scalar)
+
+ > Use the given networking backend for this definition. Currently supported
+ > are `networkd` and `NetworkManager`. This property can be specified globally
+ > in `network:`, for a device type (in e. g. `ethernets:`) or
+ > for a particular device definition. Default is `networkd`.
+ >
+ > (Since 0.99) The `renderer` property has one additional acceptable value for
+ > vlan objects (i. e. defined in `vlans:`): `sriov`. If a vlan is defined with
+ > the `sriov` renderer for an SR-IOV Virtual Function interface, this causes
+ > netplan to set up a hardware VLAN filter for it. There can be only one
+ > defined per VF.
+
+- **dhcp4** (bool)
+
+ > Enable DHCP for IPv4. Off by default.
+
+- **dhcp6** (bool)
+
+ > Enable DHCP for IPv6. Off by default. This covers both stateless DHCP -
+ > where the DHCP server supplies information like DNS nameservers but not the
+ > IP address - and stateful DHCP, where the server provides both the address
+ > and the other information.
+ >
+ > If you are in an IPv6-only environment with completely stateless
+ > auto-configuration (SLAAC with RDNSS), this option can be set to cause the
+ > interface to be brought up. (Setting accept-ra alone is not sufficient.)
+ > Auto-configuration will still honor the contents of the router
+ > advertisement and only use DHCP if requested in the RA.
+ >
+ > Note that **`rdnssd`**(8) is required to use RDNSS with networkd. No extra
+ > software is required for NetworkManager.
+
+- **ipv6-mtu** (scalar) – since **0.98**
+ > Set the IPv6 MTU (only supported with `networkd` backend). Note
+ > that needing to set this is an unusual requirement.
+ >
+ > **Requires feature: ipv6-mtu**
+
+- **ipv6-privacy** (bool)
+
+ > Enable IPv6 Privacy Extensions (RFC 4941) for the specified interface, and
+ > prefer temporary addresses. Defaults to false - no privacy extensions. There
+ > is currently no way to have a private address but prefer the public address.
+
+- **link-local** (sequence of scalars)
+
+ > Configure the link-local addresses to bring up. Valid options are 'ipv4'
+ > and 'ipv6', which respectively allow enabling IPv4 and IPv6 link local
+ > addressing. If this field is not defined, the default is to enable only
+ > IPv6 link-local addresses. If the field is defined but configured as an
+ > empty set, IPv6 link-local addresses are disabled as well as IPv4 link-
+ > local addresses.
+ >
+ > This feature enables or disables link-local addresses for a protocol, but
+ > the actual implementation differs per backend. On networkd, this directly
+ > changes the behavior and may add an extra address on an interface. When
+ > using the NetworkManager backend, enabling link-local has no effect if the
+ > interface also has DHCP enabled.
+
+ Examples:
+
+ - Enable only IPv4 link-local: `link-local: [ ipv4 ]`
+ - Enable all link-local addresses: `link-local: [ ipv4, ipv6 ]`
+ - Disable all link-local addresses: `link-local: [ ]`
+
+- **ignore-carrier** (bool) – since **0.104**
+
+ > (networkd backend only) Allow the specified interface to be configured even
+ > if it has no carrier.
+
+- **critical** (bool)
+
+ > Designate the connection as "critical to the system", meaning that special
+ > care will be taken by to not release the assigned IP when the daemon is
+ > restarted. (not recognized by NetworkManager)
+
+- **dhcp-identifier** (scalar)
+
+ > (networkd backend only) Sets the source of DHCPv4 client identifier. If
+ > `mac` is specified, the MAC address of the link is used. If this option is
+ > omitted, or if `duid` is specified, networkd will generate an
+ > RFC4361-compliant client identifier for the interface by combining the
+ > link's IAID and DUID.
+
+- **dhcp4-overrides** (mapping)
+
+ > (networkd backend only) Overrides default DHCP behavior; see the
+ > `DHCP Overrides` section below.
+
+- **dhcp6-overrides** (mapping)
+
+ > (networkd backend only) Overrides default DHCP behavior; see the
+ > `DHCP Overrides` section below.
+
+- **accept-ra** (bool)
+
+ > Accept Router Advertisement that would have the kernel configure IPv6 by
+ > itself. When enabled, accept Router Advertisements. When disabled, do not
+ > respond to Router Advertisements. If unset use the host kernel default
+ > setting.
+
+- **addresses** (sequence of scalars and mappings)
+
+ > Add static addresses to the interface in addition to the ones received
+ > through DHCP or RA. Each sequence entry is in CIDR notation, i. e. of the
+ > form `addr/prefixlen`. `addr` is an IPv4 or IPv6 address as recognized
+ > by **`inet_pton`**(3) and `prefixlen` the number of bits of the subnet.
+ >
+ > For virtual devices (bridges, bonds, vlan) if there is no address
+ > configured and DHCP is disabled, the interface may still be brought online,
+ > but will not be addressable from the network.
+ >
+ > In addition to the addresses themselves one can specify configuration
+ > parameters as mappings. Current supported options are:
+
+ - **lifetime** (scalar) – since **0.100**
+
+ > Default: ``forever``. This can be ``forever`` or ``0`` and corresponds
+ > to the ``PreferredLifetime`` option in ``systemd-networkd``'s Address
+ > section. Currently supported on the ``networkd`` backend only.
+
+ - **label** (scalar) – since **0.100**
+
+ > An IP address label, equivalent to the ``ip address label``
+ > command. Currently supported on the ``networkd`` backend only.
+
+ Examples:
+
+ - Simple: ``addresses: [192.168.14.2/24, "2001:1::1/64"]``
+ - Advanced:
+ ```yaml
+ ethernets:
+ eth0:
+ addresses:
+ - "10.0.0.15/24":
+ lifetime: 0
+ label: "maas"
+ - "2001:1::1/64"
+ ```
+
+- **ipv6-address-generation** (scalar) – since **0.99**
+
+ > Configure method for creating the address for use with RFC4862 IPv6
+ > Stateless Address Auto-configuration (only supported with `NetworkManager`
+ > backend). Possible values are `eui64` or `stable-privacy`.
+
+- **ipv6-address-token** (scalar) – since **0.100**
+
+ > Define an IPv6 address token for creating a static interface identifier for
+ > IPv6 Stateless Address Auto-configuration. This is mutually exclusive with
+ > `ipv6-address-generation`.
+
+- **gateway4**, **gateway6** (scalar)
+
+ > Deprecated, see `Default routes`.
+ > Set default gateway for IPv4/6, for manual address configuration. This
+ > requires setting `addresses` too. Gateway IPs must be in a form
+ > recognized by **`inet_pton`**(3). There should only be a single gateway
+ > per IP address family set in your global config, to make it unambiguous.
+ > If you need multiple default routes, please define them via
+ > `routing-policy`.
+
+ Examples
+
+ - IPv4: `gateway4: 172.16.0.1`
+ - IPv6: `gateway6: "2001:4::1"`
+
+- **nameservers** (mapping)
+
+ > Set DNS servers and search domains, for manual address configuration. There
+ > are two supported fields: `addresses:` is a list of IPv4 or IPv6 addresses
+ > similar to `gateway*`, and `search:` is a list of search domains.
+
+ Example:
+
+ ```yaml
+ ethernets:
+ id0:
+ [...]
+ nameservers:
+ search: [lab, home]
+ addresses: [8.8.8.8, "FEDC::1"]
+ ```
+
+- **macaddress** (scalar)
+
+ > Set the device's MAC address. The MAC address must be in the form
+ > "XX:XX:XX:XX:XX:XX".
+ >
+ > **Note:** This will not work reliably for devices matched by name
+ > only and rendered by networkd, due to interactions with device
+ > renaming in udev. Match devices by MAC when setting MAC addresses.
+
+ Example:
+
+ ```yaml
+ ethernets:
+ id0:
+ match:
+ macaddress: 52:54:00:6b:3c:58
+ [...]
+ macaddress: 52:54:00:6b:3c:59
+ ```
+
+- **mtu** (scalar)
+
+ > Set the Maximum Transmission Unit for the interface. The default is 1500.
+ > Valid values depend on your network interface.
+ >
+ > **Note:** This will not work reliably for devices matched by name
+ > only and rendered by networkd, due to interactions with device
+ > renaming in udev. Match devices by MAC when setting MTU.
+
+- **optional** (bool)
+
+ > An optional device is not required for booting. Normally, networkd will
+ > wait some time for device to become configured before proceeding with
+ > booting. However, if a device is marked as optional, networkd will not wait
+ > for it. This is *only* supported by networkd, and the default is false.
+
+ Example:
+
+ ```yaml
+ ethernets:
+ eth7:
+ # this is plugged into a test network that is often
+ # down - don't wait for it to come up during boot.
+ dhcp4: true
+ optional: true
+ ```
+
+- **optional-addresses** (sequence of scalars)
+
+ > Specify types of addresses that are not required for a device to be
+ > considered online. This changes the behavior of backends at boot time to
+ > avoid waiting for addresses that are marked optional, and thus consider
+ > the interface as "usable" sooner. This does not disable these addresses,
+ > which will be brought up anyway.
+
+ Example:
+
+ ```yaml
+ ethernets:
+ eth7:
+ dhcp4: true
+ dhcp6: true
+ optional-addresses: [ ipv4-ll, dhcp6 ]
+ ```
+
+- **activation-mode** (scalar) – since **0.103**
+
+ > Allows specifying the management policy of the selected interface. By
+ > default, netplan brings up any configured interface if possible. Using the
+ > `activation-mode` setting users can override that behavior by either
+ > specifying `manual`, to hand over control over the interface state to the
+ > administrator or (for networkd backend *only*) `off` to force the link
+ > in a down state at all times. Any interface with `activation-mode`
+ > defined is implicitly considered `optional`.
+ > Supported officially as of `networkd` v248+.
+
+ Example:
+
+ ```yaml
+ ethernets:
+ eth1:
+ # this interface will not be put into an UP state automatically
+ dhcp4: true
+ activation-mode: manual
+ ```
+
+- **routes** (sequence of mappings)
+
+ > Configure static routing for the device; see the `Routing` section below.
+
+- **routing-policy** (sequence of mappings)
+
+ > Configure policy routing for the device; see the `Routing` section below.
+
+- **neigh-suppress** (scalar) – since **0.105**
+
+ > Takes a boolean. Configures whether ARP and ND neighbor suppression is
+ > enabled for this port. When unset, the kernel's default will be used.
+
+## DHCP Overrides
+Several DHCP behavior overrides are available. Most currently only have any
+effect when using the `networkd` backend, with the exception of `use-routes`
+and `route-metric`.
+
+Overrides only have an effect if the corresponding `dhcp4` or `dhcp6` is
+set to `true`.
+
+If both `dhcp4` and `dhcp6` are `true`, the `networkd` backend requires
+that `dhcp4-overrides` and `dhcp6-overrides` contain the same keys and
+values. If the values do not match, an error will be shown and the network
+configuration will not be applied.
+
+When using the NetworkManager backend, different values may be specified for
+`dhcp4-overrides` and `dhcp6-overrides`, and will be applied to the DHCP
+client processes as specified in the netplan YAML.
+
+- **dhcp4-overrides**, **dhcp6-overrides** (mapping)
+
+ > The `dhcp4-overrides` and `dhcp6-override`` mappings override the
+ > default DHCP behavior.
+
+ - **use-dns** (bool)
+
+ > Default: `true`. When `true`, the DNS servers received from the
+ > DHCP server will be used and take precedence over any statically
+ > configured ones. Currently only has an effect on the `networkd`
+ > backend.
+
+ - **use-ntp** (bool)
+
+ > Default: `true`. When `true`, the NTP servers received from the
+ > DHCP server will be used by systemd-timesyncd and take precedence
+ > over any statically configured ones. Currently only has an effect on
+ > the `networkd` backend.
+
+ - **send-hostname** (bool)
+
+ > Default: `true`. When `true`, the machine's hostname will be sent
+ > to the DHCP server. Currently only has an effect on the `networkd`
+ > backend.
+
+ - **use-hostname** (bool)
+
+ > Default: `true`. When `true`, the hostname received from the DHCP
+ > server will be set as the transient hostname of the system. Currently
+ > only has an effect on the `networkd` backend.
+
+ - **use-mtu** (bool)
+
+ > Default: `true`. When `true`, the MTU received from the DHCP
+ > server will be set as the MTU of the network interface. When `false`,
+ > the MTU advertised by the DHCP server will be ignored. Currently only
+ > has an effect on the `networkd` backend.
+
+ - **hostname** (scalar)
+
+ > Use this value for the hostname which is sent to the DHCP server,
+ > instead of machine's hostname. Currently only has an effect on the
+ > `networkd` backend.
+
+ - **use-routes** (bool)
+
+ > Default: `true`. When `true`, the routes received from the DHCP
+ > server will be installed in the routing table normally. When set to
+ > `false`, routes from the DHCP server will be ignored: in this case,
+ > the user is responsible for adding static routes if necessary for
+ > correct network operation. This allows users to avoid installing a
+ > default gateway for interfaces configured via DHCP. Available for
+ > both the `networkd` and `NetworkManager` backends.
+
+ - **route-metric** (scalar)
+
+ > Use this value for default metric for automatically-added routes.
+ > Use this to prioritize routes for devices by setting a lower metric
+ > on a preferred interface. Available for both the `networkd` and
+ > `NetworkManager` backends.
+
+ - **use-domains** (scalar) – since **0.98**
+
+ > Takes a boolean, or the special value "route". When true, the domain
+ > name received from the DHCP server will be used as DNS search domain
+ > over this link, similar to the effect of the Domains= setting. If set
+ > to "route", the domain name received from the DHCP server will be
+ > used for routing DNS queries only, but not for searching, similar to
+ > the effect of the Domains= setting when the argument is prefixed with
+ > "~".
+ >
+ > **Requires feature: dhcp-use-domains**
+
+
+## Routing
+Complex routing is possible with netplan. Standard static routes as well
+as policy routing using routing tables are supported via the `networkd`
+backend.
+
+These options are available for all types of interfaces.
+
+### Default routes
+
+The most common need for routing concerns the definition of default routes to
+reach the wider Internet. Those default routes can only defined once per IP
+family and routing table. A typical example would look like the following:
+
+```yaml
+eth0:
+ [...]
+ routes:
+ - to: default # could be 0/0 or 0.0.0.0/0 optionally
+ via: 10.0.0.1
+ metric: 100
+ on-link: true
+ - to: default # could be ::/0 optionally
+ via: cf02:de:ad:be:ef::2
+eth1:
+ [...]
+ routes:
+ - to: default
+ via: 172.134.67.1
+ metric: 100
+ on-link: true
+ # Not on the main routing table,
+ # does not conflict with the eth0 default route
+ table: 76
+```
+
+- **routes** (mapping)
+
+ > The `routes` block defines standard static routes for an interface.
+ > At least `to` must be specified. If type is `local` or `nat` a
+ > default scope of `host` is assumed.
+ > If type is `unicast` and no gateway (`via`) is given or type is
+ > `broadcast`, `multicast` or `anycast` a default scope of `link`
+ > is assumed. Otherwise, a ``global`` scope is the default setting.
+ >
+ > For `from`, `to`, and `via`, both IPv4 and IPv6 addresses are
+ > recognized, and must be in the form `addr/prefixlen` or `addr`.
+
+ - **from** (scalar)
+
+ > Set a source IP address for traffic going through the route.
+ > (`NetworkManager`: as of v1.8.0)
+
+ - **to** (scalar)
+
+ > Destination address for the route.
+
+ - **via** (scalar)
+
+ > Address to the gateway to use for this route.
+
+ - **on-link** (bool)
+
+ > When set to "true", specifies that the route is directly connected
+ > to the interface.
+ > (`NetworkManager`: as of v1.12.0 for IPv4 and v1.18.0 for IPv6)
+
+ - **metric** (scalar)
+
+ > The relative priority of the route. Must be a positive integer value.
+
+ - **type** (scalar)
+
+ > The type of route. Valid options are "unicast" (default), "anycast",
+ > "blackhole", "broadcast", "local", "multicast", "nat", "prohibit",
+ > "throw", "unreachable" or "xresolve".
+
+ - **scope** (scalar)
+
+ > The route scope, how wide-ranging it is to the network. Possible
+ > values are "global", "link", or "host".
+
+ - **table** (scalar)
+
+ > The table number to use for the route. In some scenarios, it may be
+ > useful to set routes in a separate routing table. It may also be used
+ > to refer to routing policy rules which also accept a `table`
+ > parameter. Allowed values are positive integers starting from 1.
+ > Some values are already in use to refer to specific routing tables:
+ > see `/etc/iproute2/rt_tables`.
+ > (`NetworkManager`: as of v1.10.0)
+
+ - **mtu** (scalar) – since **0.101**
+
+ > The MTU to be used for the route, in bytes. Must be a positive integer
+ > value.
+
+ - **congestion-window** (scalar) – since **0.102**
+
+ > The congestion window to be used for the route, represented by number
+ > of segments. Must be a positive integer value.
+
+ - **advertised-receive-window** (scalar) – since **0.102**
+
+ > The receive window to be advertised for the route, represented by
+ > number of segments. Must be a positive integer value.
+
+- **routing-policy** (mapping)
+
+ > The `routing-policy` block defines extra routing policy for a network,
+ > where traffic may be handled specially based on the source IP, firewall
+ > marking, etc.
+ >
+ > For `from`, `to`, both IPv4 and IPv6 addresses are recognized, and
+ > must be in the form `addr/prefixlen` or `addr`.
+
+ - **from** (scalar)
+
+ > Set a source IP address to match traffic for this policy rule.
+
+ - **to** (scalar)
+
+ > Match on traffic going to the specified destination.
+
+ - **table** (scalar)
+
+ > The table number to match for the route. In some scenarios, it may be
+ > useful to set routes in a separate routing table. It may also be used
+ > to refer to routes which also accept a `table` parameter.
+ > Allowed values are positive integers starting from 1.
+ > Some values are already in use to refer to specific routing tables:
+ > see `/etc/iproute2/rt_tables`.
+
+ - **priority** (scalar)
+
+ > Specify a priority for the routing policy rule, to influence the order
+ > in which routing rules are processed. A higher number means lower
+ > priority: rules are processed in order by increasing priority number.
+
+ - **mark** (scalar)
+
+ > Have this routing policy rule match on traffic that has been marked
+ > by the iptables firewall with this value. Allowed values are positive
+ > integers starting from 1.
+
+ - **type-of-service** (scalar)
+
+ > Match this policy rule based on the type of service number applied to
+ > the traffic.
+
+## Authentication
+Netplan supports advanced authentication settings for ethernet and wifi
+interfaces, as well as individual wifi networks, by means of the `auth` block.
+
+- **auth** (mapping)
+
+ > Specifies authentication settings for a device of type `ethernets:`, or
+ > an `access-points:` entry on a `wifis:` device.
+ >
+ > The `auth` block supports the following properties:
+
+ - **key-management** (scalar)
+
+ > The supported key management modes are `none` (no key management);
+ > `psk` (WPA with pre-shared key, common for home wifi); `eap` (WPA
+ > with EAP, common for enterprise wifi); and `802.1x` (used primarily
+ > for wired Ethernet connections).
+
+ - **password** (scalar)
+
+ > The password string for EAP, or the pre-shared key for WPA-PSK.
+
+ The following properties can be used if `key-management` is `eap`
+ or `802.1x`:
+
+ - **method** (scalar)
+
+ > The EAP method to use. The supported EAP methods are `tls` (TLS),
+ > `peap` (Protected EAP), and `ttls` (Tunneled TLS).
+
+ - **identity** (scalar)
+
+ > The identity to use for EAP.
+
+ - **anonymous-identity** (scalar)
+
+ > The identity to pass over the unencrypted channel if the chosen EAP
+ > method supports passing a different tunnelled identity.
+
+ - **ca-certificate** (scalar)
+
+ > Path to a file with one or more trusted certificate authority (CA)
+ > certificates.
+
+ - **client-certificate** (scalar)
+
+ > Path to a file containing the certificate to be used by the client
+ > during authentication.
+
+ - **client-key** (scalar)
+
+ > Path to a file containing the private key corresponding to
+ > `client-certificate`.
+
+ - **client-key-password** (scalar)
+
+ > Password to use to decrypt the private key specified in
+ > `client-key` if it is encrypted.
+
+ - **phase2-auth** (scalar) – since **0.99**
+
+ > Phase 2 authentication mechanism.
+
+
+## Properties for device type `ethernets:`
+Ethernet device definitions, beyond common ones described above, also support
+some additional properties that can be used for SR-IOV devices.
+
+- **link** (scalar) – since **0.99**
+
+ > (SR-IOV devices only) The `link` property declares the device as a
+ > Virtual Function of the selected Physical Function device, as identified
+ > by the given netplan id.
+
+ Example:
+
+ ```yaml
+ ethernets:
+ enp1: {...}
+ enp1s16f1:
+ link: enp1
+ ```
+
+- **virtual-function-count** (scalar) – since **0.99**
+
+ > (SR-IOV devices only) In certain special cases VFs might need to be
+ > configured outside of netplan. For such configurations
+ > `virtual-function-count` can be optionally used to set an explicit number of
+ > Virtual Functions for the given Physical Function. If unset, the default is
+ > to create only as many VFs as are defined in the netplan configuration. This
+ > should be used for special cases only.
+ >
+ > **Requires feature: sriov**
+
+- **embedded-switch-mode** (scalar) – since **0.104**
+
+ > (SR-IOV devices only) Change the operational mode of the embedded switch
+ > of a supported SmartNIC PCI device (e.g. Mellanox ConnectX-5). Possible
+ > values are `switchdev` or `legacy`, if unspecified the vendor's
+ > default configuration is used.
+ >
+ > **Requires feature: eswitch-mode**
+
+- **delay-virtual-functions-rebind** (bool) – since **0.104**
+
+ > (SR-IOV devices only) Delay rebinding of SR-IOV virtual functions to its
+ > driver after changing the embedded-switch-mode setting to a later stage.
+ > Can be enabled when bonding/VF LAG is in use. Defaults to `false`.
+ >
+ > **Requires feature: eswitch-mode**
+
+- **infiniband-mode** (scalar) – since **0.105**
+
+ > (InfiniBand devices only) Change the operational mode of a IPoIB device.
+ > Possible values are `datagram` or `connected`. If unspecified the
+ > kernel's default configuration is used.
+ >
+ > **Requires feature: infiniband**
+
+## Properties for device type `modems:`
+GSM/CDMA modem configuration is only supported for the `NetworkManager`
+backend. `systemd-networkd` does not support modems.
+
+**Requires feature: modems**
+
+- **apn** (scalar) – since **0.99**
+
+ > Set the carrier APN (Access Point Name). This can be omitted if
+ > `auto-config` is enabled.
+
+- **auto-config** (bool) – since **0.99**
+
+ > Specify whether to try and auto-configure the modem by doing a lookup of
+ > the carrier against the Mobile Broadband Provider database. This may not
+ > work for all carriers.
+
+- **device-id** (scalar) – since **0.99**
+
+ > Specify the device ID (as given by the WWAN management service) of the
+ > modem to match. This can be found using `mmcli`.
+
+- **network-id** (scalar) – since **0.99**
+
+ > Specify the Network ID (GSM LAI format). If this is specified, the device
+ > will not roam networks.
+
+- **number** (scalar) – since **0.99**
+
+ > The number to dial to establish the connection to the mobile broadband
+ > network. (Deprecated for GSM)
+
+- **password** (scalar) – since **0.99**
+
+ > Specify the password used to authenticate with the carrier network. This
+ > can be omitted if `auto-config` is enabled.
+
+- **pin** (scalar) – since **0.99**
+
+ > Specify the SIM PIN to allow it to operate if a PIN is set.
+
+- **sim-id** (scalar) – since **0.99**
+
+ > Specify the SIM unique identifier (as given by the WWAN management service)
+ > which this connection applies to. If given, the connection will apply to
+ > any device also allowed by `device-id` which contains a SIM card matching
+ > the given identifier.
+
+- **sim-operator-id** (scalar) – since **0.99**
+
+ > Specify the MCC/MNC string (such as "310260" or "21601") which identifies
+ > the carrier that this connection should apply to. If given, the connection
+ > will apply to any device also allowed by `device-id` and `sim-id`
+ > which contains a SIM card provisioned by the given operator.
+
+- **username** (scalar) – since **0.99**
+
+ > Specify the username used to authenticate with the carrier network. This
+ > can be omitted if `auto-config` is enabled.
+
+## Properties for device type `wifis:`
+Note that `systemd-networkd` does not natively support wifi, so you need
+wpasupplicant installed if you let the `networkd` renderer handle wifi.
+
+- **access-points** (mapping)
+
+ > This provides pre-configured connections to NetworkManager. Note that
+ > users can of course select other access points/SSIDs. The keys of the
+ > mapping are the SSIDs, and the values are mappings with the following
+ > supported properties:
+
+ - **password** (scalar)
+
+ > Enable WPA2 authentication and set the passphrase for it. If neither
+ > this nor an `auth` block are given, the network is assumed to be
+ > open. The setting
+ > ```yaml
+ > password: "S3kr1t"
+ > ```
+ > is equivalent to
+ > ```yaml
+ > auth:
+ > key-management: psk
+ > password: "S3kr1t"
+ > ```
+
+ - **mode** (scalar)
+
+ > Possible access point modes are `infrastructure` (the default),
+ > `ap` (create an access point to which other devices can connect),
+ > and `adhoc` (peer to peer networks without a central access point).
+ > `ap` is only supported with NetworkManager.
+
+ - **bssid** (scalar) – since **0.99**
+
+ > If specified, directs the device to only associate with the given
+ > access point.
+
+ - **band** (scalar) – since **0.99**
+
+ > Possible bands are `5GHz` (for 5GHz 802.11a) and `2.4GHz`
+ > (for 2.4GHz 802.11), do not restrict the 802.11 frequency band of the
+ > network if unset (the default).
+
+ - **channel** (scalar) – since **0.99**
+
+ > Wireless channel to use for the Wi-Fi connection. Because channel
+ > numbers overlap between bands, this property takes effect only if
+ > the `band` property is also set.
+
+ - **hidden** (bool) – since **0.100**
+
+ > Set to `true` to change the SSID scan technique for connecting to
+ > hidden WiFi networks. Note this may have slower performance compared
+ > to `false` (the default) when connecting to publicly broadcast
+ > SSIDs.
+
+- **wakeonwlan** (sequence of scalars) – since **0.99**
+
+ > This enables WakeOnWLan on supported devices. Not all drivers support all
+ > options. May be any combination of `any`, `disconnect`, `magic_pkt`,
+ > `gtk_rekey_failure`, `eap_identity_req`, `four_way_handshake`,
+ > `rfkill_release` or `tcp` (NetworkManager only). Or the exclusive
+ > `default` flag (the default).
+
+- **regulatory-domain** (scalar) – since **0.105**
+
+ > This can be used to define the radio's regulatory domain, to make use of
+ > additional WiFi channels outside the "world domain". Takes an ISO /
+ > IEC 3166 country code (like `GB`) or `00` to reset to the "world domain".
+ > See [wireless-regdb](https://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git/tree/db.txt)
+ > for available values.
+ >
+ > **Requires dependency: iw**, if it is to be used outside the `networkd`
+ > (wpa_supplicant) backend.
+
+## Properties for device type `bridges:`
+
+- **interfaces** (sequence of scalars)
+
+ > All devices matching this ID list will be added to the bridge. This may
+ > be an empty list, in which case the bridge will be brought online with
+ > no member interfaces.
+
+ Example:
+
+ ```yaml
+ ethernets:
+ switchports:
+ match: {name: "enp2*"}
+ [...]
+ bridges:
+ br0:
+ interfaces: [switchports]
+ ```
+
+- **parameters** (mapping)
+
+ > Customization parameters for special bridging options. Time intervals
+ > may need to be expressed as a number of seconds or milliseconds: the
+ > default value type is specified below. If necessary, time intervals can
+ > be qualified using a time suffix (such as "s" for seconds, "ms" for
+ > milliseconds) to allow for more control over its behavior.
+
+ - **ageing-time**, **aging-time** (scalar)
+
+ > Set the period of time to keep a MAC address in the forwarding
+ > database after a packet is received. This maps to the AgeingTimeSec=
+ > property when the networkd renderer is used. If no time suffix is
+ > specified, the value will be interpreted as seconds.
+
+ - **priority** (scalar)
+
+ > Set the priority value for the bridge. This value should be a
+ > number between `0` and `65535`. Lower values mean higher
+ > priority. The bridge with the higher priority will be elected as
+ > the root bridge.
+
+ - **port-priority** (scalar)
+
+ > Set the port priority to <priority>. The priority value is
+ > a number between `0` and `63`. This metric is used in the
+ > designated port and root port selection algorithms.
+
+ - **forward-delay** (scalar)
+
+ > Specify the period of time the bridge will remain in Listening and
+ > Learning states before getting to the Forwarding state. This field
+ > maps to the ForwardDelaySec= property for the networkd renderer.
+ > If no time suffix is specified, the value will be interpreted as
+ > seconds.
+
+ - **hello-time** (scalar)
+
+ > Specify the interval between two hello packets being sent out from
+ > the root and designated bridges. Hello packets communicate
+ > information about the network topology. When the networkd renderer
+ > is used, this maps to the HelloTimeSec= property. If no time suffix
+ > is specified, the value will be interpreted as seconds.
+
+ - **max-age** (scalar)
+
+ > Set the maximum age of a hello packet. If the last hello packet is
+ > older than that value, the bridge will attempt to become the root
+ > bridge. This maps to the MaxAgeSec= property when the networkd
+ > renderer is used. If no time suffix is specified, the value will be
+ > interpreted as seconds.
+
+ - **path-cost** (scalar)
+
+ > Set the cost of a path on the bridge. Faster interfaces should have
+ > a lower cost. This allows a finer control on the network topology
+ > so that the fastest paths are available whenever possible.
+
+ - **stp** (bool)
+
+ > Define whether the bridge should use Spanning Tree Protocol. The
+ > default value is "true", which means that Spanning Tree should be
+ > used.
+
+
+## Properties for device type `bonds:`
+
+- **interfaces** (sequence of scalars)
+
+ > All devices matching this ID list will be added to the bond.
+
+ Example:
+
+ ```yaml
+ ethernets:
+ switchports:
+ match: {name: "enp2*"}
+ [...]
+ bonds:
+ bond0:
+ interfaces: [switchports]
+ ```
+
+- **parameters** (mapping)
+
+ > Customization parameters for special bonding options. Time intervals
+ > may need to be expressed as a number of seconds or milliseconds: the
+ > default value type is specified below. If necessary, time intervals can
+ > be qualified using a time suffix (such as "s" for seconds, "ms" for
+ > milliseconds) to allow for more control over its behavior.
+
+ - **mode** (scalar)
+
+ > Set the bonding mode used for the interfaces. The default is
+ > `balance-rr` (round robin). Possible values are `balance-rr`,
+ > `active-backup`, `balance-xor`, `broadcast`, `802.3ad`,
+ > `balance-tlb`, and `balance-alb`.
+ > For Open vSwitch `active-backup` and the additional modes
+ > `balance-tcp` and `balance-slb` are supported.
+
+ - **lacp-rate** (scalar)
+
+ > Set the rate at which LACPDUs are transmitted. This is only useful
+ > in 802.3ad mode. Possible values are `slow` (30 seconds, default),
+ > and `fast` (every second).
+
+ - **mii-monitor-interval** (scalar)
+
+ > Specifies the interval for MII monitoring (verifying if an interface
+ > of the bond has carrier). The default is `0`; which disables MII
+ > monitoring. This is equivalent to the MIIMonitorSec= field for the
+ > networkd backend. If no time suffix is specified, the value will be
+ > interpreted as milliseconds.
+
+ - **min-links** (scalar)
+
+ > The minimum number of links up in a bond to consider the bond
+ > interface to be up.
+
+ - **transmit-hash-policy** (scalar)
+
+ > Specifies the transmit hash policy for the selection of slaves. This
+ > is only useful in balance-xor, 802.3ad and balance-tlb modes.
+ > Possible values are `layer2`, `layer3+4`, `layer2+3`,
+ > `encap2+3`, and `encap3+4`.
+
+ - **ad-select** (scalar)
+
+ > Set the aggregation selection mode. Possible values are `stable`,
+ > `bandwidth`, and `count`. This option is only used in 802.3ad
+ > mode.
+
+ - **all-slaves-active** (bool)
+
+ > If the bond should drop duplicate frames received on inactive ports,
+ > set this option to `false`. If they should be delivered, set this
+ > option to `true`. The default value is false, and is the desirable
+ > behavior in most situations.
+
+ - **arp-interval** (scalar)
+
+ > Set the interval value for how frequently ARP link monitoring should
+ > happen. The default value is `0`, which disables ARP monitoring.
+ > For the networkd backend, this maps to the ARPIntervalSec= property.
+ > If no time suffix is specified, the value will be interpreted as
+ > milliseconds.
+
+ - **arp-ip-targets** (sequence of scalars)
+
+ > IPs of other hosts on the link which should be sent ARP requests in
+ > order to validate that a slave is up. This option is only used when
+ > `arp-interval` is set to a value other than `0`. At least one IP
+ > address must be given for ARP link monitoring to function. Only IPv4
+ > addresses are supported. You can specify up to 16 IP addresses. The
+ > default value is an empty list.
+
+ - **arp-validate** (scalar)
+
+ > Configure how ARP replies are to be validated when using ARP link
+ > monitoring. Possible values are `none`, `active`, `backup`,
+ > and `all`.
+
+ - **arp-all-targets** (scalar)
+
+ > Specify whether to use any ARP IP target being up as sufficient for
+ > a slave to be considered up; or if all the targets must be up. This
+ > is only used for `active-backup` mode when `arp-validate` is
+ > enabled. Possible values are `any` and `all`.
+
+ - **up-delay** (scalar)
+
+ > Specify the delay before enabling a link once the link is physically
+ > up. The default value is `0`. This maps to the UpDelaySec= property
+ > for the networkd renderer. This option is only valid for the miimon
+ > link monitor. If no time suffix is specified, the value will be
+ > interpreted as milliseconds.
+
+ - **down-delay** (scalar)
+
+ > Specify the delay before disabling a link once the link has been
+ > lost. The default value is `0`. This maps to the DownDelaySec=
+ > property for the networkd renderer. This option is only valid for the
+ > miimon link monitor. If no time suffix is specified, the value will
+ > be interpreted as milliseconds.
+
+ - **fail-over-mac-policy** (scalar)
+
+ > Set whether to set all slaves to the same MAC address when adding
+ > them to the bond, or how else the system should handle MAC addresses.
+ > The possible values are `none`, `active`, and `follow`.
+
+ - **gratuitous-arp** (scalar)
+
+ > Specify how many ARP packets to send after failover. Once a link is
+ > up on a new slave, a notification is sent and possibly repeated if
+ > this value is set to a number greater than `1`. The default value
+ > is `1` and valid values are between `1` and `255`. This only
+ > affects `active-backup` mode.
+ >
+ > For historical reasons, the misspelling `gratuitious-arp` is also
+ > accepted and has the same function.
+
+ - **packets-per-slave** (scalar)
+
+ > In `balance-rr` mode, specifies the number of packets to transmit
+ > on a slave before switching to the next. When this value is set to
+ > `0`, slaves are chosen at random. Allowable values are between
+ > `0` and `65535`. The default value is `1`. This setting is
+ > only used in `balance-rr` mode.
+
+ - **primary-reselect-policy** (scalar)
+
+ > Set the reselection policy for the primary slave. On failure of the
+ > active slave, the system will use this policy to decide how the new
+ > active slave will be chosen and how recovery will be handled. The
+ > possible values are `always`, `better`, and `failure`.
+
+ - **resend-igmp** (scalar)
+
+ > In modes `balance-rr`, `active-backup`, `balance-tlb` and
+ > `balance-alb`, a failover can switch IGMP traffic from one
+ > slave to another.
+ >
+ > This parameter specifies how many IGMP membership reports
+ > are issued on a failover event. Values range from 0 to 255. 0
+ > disables sending membership reports. Otherwise, the first
+ > membership report is sent on failover and subsequent reports
+ > are sent at 200ms intervals.
+
+ - **learn-packet-interval** (scalar)
+
+ > Specify the interval between sending learning packets to
+ > each slave. The value range is between `1` and `0x7fffffff`.
+ > The default value is `1`. This option only affects `balance-tlb`
+ > and `balance-alb` modes. Using the networkd renderer, this field
+ > maps to the LearnPacketIntervalSec= property. If no time suffix is
+ > specified, the value will be interpreted as seconds.
+
+ - **primary** (scalar)
+
+ > Specify a device to be used as a primary slave, or preferred device
+ > to use as a slave for the bond (i.e. the preferred device to send
+ > data through), whenever it is available. This only affects
+ > `active-backup`, `balance-alb`, and `balance-tlb` modes.
+
+
+## Properties for device type `tunnels:`
+
+Tunnels allow traffic to pass as if it was between systems on the same local
+network, although systems may be far from each other but reachable via the
+Internet. They may be used to support IPv6 traffic on a network where the ISP
+does not provide the service, or to extend and "connect" separate local
+networks. Please see <https://en.wikipedia.org/wiki/Tunneling_protocol> for
+more general information about tunnels.
+
+- **mode** (scalar)
+
+ > Defines the tunnel mode. Valid options are `sit`, `gre`, `ip6gre`,
+ > `ipip`, `ipip6`, `ip6ip6`, `vti`, `vti6`, `wireguard` and `vxlan`.
+ > Additionally, the `networkd` backend also supports `gretap` and
+ > `ip6gretap` modes.
+ > In addition, the `NetworkManager` backend supports `isatap` tunnels.
+
+- **local** (scalar)
+
+ > Defines the address of the local endpoint of the tunnel. (For VXLAN) This
+ > should match one of the parent's IP addresses or make use of the networkd
+ > special values.
+
+
+- **remote** (scalar)
+
+ > Defines the address of the remote endpoint of the tunnel or multicast group
+ > IP address for VXLAN.
+
+- **ttl** (scalar) – since **0.103**
+
+ > Defines the Time To Live (TTL) of the tunnel.
+ > Takes a number in the range `1..255`.
+
+- **key** (scalar or mapping)
+
+ > Define keys to use for the tunnel. The key can be a number or a dotted
+ > quad (an IPv4 address). For `wireguard` it can be a base64-encoded
+ > private key or (as of `networkd` v242+) an absolute path to a file,
+ > containing the private key (since 0.100).
+ > It is used for identification of IP transforms. This is only required
+ > for `vti` and `vti6` when using the networkd backend.
+ >
+ > This field may be used as a scalar (meaning that a single key is
+ > specified and to be used for input, output and private key), or as a
+ > mapping, where you can further specify `input`/`output`/`private`.
+
+ - **input** (scalar)
+
+ > The input key for the tunnel
+
+ - **output** (scalar)
+
+ > The output key for the tunnel
+
+ - **private** (scalar) – since **0.100**
+
+ > A base64-encoded private key required for WireGuard tunnels. When the
+ > `systemd-networkd` backend (v242+) is used, this can also be an
+ > absolute path to a file containing the private key.
+
+- **keys** (scalar or mapping)
+
+ > Alternate name for the `key` field. See above.
+
+ Examples:
+
+ ```yaml
+ tunnels:
+ tun0:
+ mode: gre
+ local: ...
+ remote: ...
+ keys:
+ input: 1234
+ output: 5678
+ ```
+ ```yaml
+ tunnels:
+ tun0:
+ mode: vti6
+ local: ...
+ remote: ...
+ key: 59568549
+ ```
+ ```yaml
+ tunnels:
+ wg0:
+ mode: wireguard
+ addresses: [...]
+ peers:
+ - keys:
+ public: rlbInAj0qV69CysWPQY7KEBnKxpYCpaWqOs/dLevdWc=
+ shared: /path/to/shared.key
+ ...
+ key: mNb7OIIXTdgW4khM7OFlzJ+UPs7lmcWHV7xjPgakMkQ=
+ ```
+ ```yaml
+ tunnels:
+ wg0:
+ mode: wireguard
+ addresses: [...]
+ peers:
+ - keys:
+ public: rlbInAj0qV69CysWPQY7KEBnKxpYCpaWqOs/dLevdWc=
+ ...
+ keys:
+ private: /path/to/priv.key
+ ```
+
+
+WireGuard specific keys:
+
+- **mark** (scalar) – since **0.100**
+
+ > Firewall mark for outgoing WireGuard packets from this interface,
+ > optional.
+
+- **port** (scalar) – since **0.100**
+
+ > UDP port to listen at or `auto`. Optional, defaults to `auto`.
+
+- **peers** (sequence of mappings) – since **0.100**
+
+ > A list of peers, each having keys documented below.
+
+ Example:
+
+ ```yaml
+ tunnels:
+ wg0:
+ mode: wireguard
+ key: /path/to/private.key
+ mark: 42
+ port: 5182
+ peers:
+ - keys:
+ public: rlbInAj0qV69CysWPQY7KEBnKxpYCpaWqOs/dLevdWc=
+ allowed-ips: [0.0.0.0/0, "2001:fe:ad:de:ad:be:ef:1/24"]
+ keepalive: 23
+ endpoint: 1.2.3.4:5
+ - keys:
+ public: M9nt4YujIOmNrRmpIRTmYSfMdrpvE7u6WkG8FY8WjG4=
+ shared: /some/shared.key
+ allowed-ips: [10.10.10.20/24]
+ keepalive: 22
+ endpoint: 5.4.3.2:1
+ ```
+
+ - **endpoint** (scalar) – since **0.100**
+
+ > Remote endpoint IPv4/IPv6 address or a hostname, followed by a colon
+ > and a port number.
+
+ - **allowed-ips** (sequence of scalars) – since **0.100**
+
+ > A list of IP (v4 or v6) addresses with CIDR masks from which this peer
+ > is allowed to send incoming traffic and to which outgoing traffic for
+ > this peer is directed. The catch-all 0.0.0.0/0 may be specified for
+ > matching all IPv4 addresses, and ::/0 may be specified for matching
+ > all IPv6 addresses.
+
+ - **keepalive** (scalar) – since **0.100**
+
+ > An interval in seconds, between 1 and 65535 inclusive, of how often to
+ > send an authenticated empty packet to the peer for the purpose of
+ > keeping a stateful firewall or NAT mapping valid persistently. Optional.
+
+ - **keys** (mapping) – since **0.100**
+
+ > Define keys to use for the WireGuard peers.
+ >
+ > This field can be used as a mapping, where you can further specify the
+ > `public` and `shared` keys.
+
+ - **public** (scalar) – since **0.100**
+
+ > A base64-encoded public key, required for WireGuard peers.
+
+ - **shared** (scalar) – since **0.100**
+
+ > A base64-encoded preshared key. Optional for WireGuard peers.
+ > When the `systemd-networkd` backend (v242+) is used, this can
+ > also be an absolute path to a file containing the preshared key.
+
+VXLAN specific keys:
+
+- **id** (scalar) – since **0.105**
+
+ > The VXLAN Network Identifier (VNI or VXLAN Segment ID).
+ > Takes a number in the range `1..16777215`.
+
+- **link** (scalar) – since **0.105**
+
+ > netplan ID of the parent device definition to which this VXLAN gets
+ > connected.
+
+- **type-of-service** (scalar) – since **0.105**
+
+ > The Type Of Service byte value for a vxlan interface.
+
+- **mac-learning** (scalar) – since **0.105**
+
+ > Takes a boolean. When `true`, enables dynamic MAC learning to discover
+ > remote MAC addresses.
+
+- **ageing**, **aging** (scalar) – since **0.105**
+
+ > The lifetime of Forwarding Database entry learnt by the kernel, in
+ > seconds.
+
+- **limit** (scalar) – since **0.105**
+
+ > Configures maximum number of FDB entries.
+
+- **arp-proxy** (scalar) – since **0.105**
+
+ > Takes a boolean. When `true`, bridge-connected VXLAN tunnel endpoint
+ > answers ARP requests from the local bridge on behalf of remote Distributed
+ > Overlay Virtual Ethernet (DOVE) clients. Defaults to `false`.
+
+- **notifications** (sequence of scalars) – since **0.105**
+
+ > Takes the flags `l2-miss` and `l3-miss` to enable netlink LLADDR and/or
+ > netlink IP address miss notifications.
+
+- **short-circuit** (scalar) – since **0.105**
+
+ > Takes a boolean. When `true`, route short circuiting is turned on.
+
+- **checksums** (sequence of scalars) – since **0.105**
+
+ > Takes the flags `udp`, `zero-udp6-tx`, `zero-udp6-rx`, `remote-tx` and
+ > `remote-rx` to enable transmitting UDP checksums in VXLAN/IPv4,
+ > send/receive zero checksums in VXLAN/IPv6 and enable sending/receiving
+ > checksum offloading in VXLAN.
+
+- **extensions** (sequence of scalars) – since **0.105**
+
+ > Takes the flags `group-policy` and `generic-protocol` to enable the "Group
+ > Policy" and/or "Generic Protocol" VXLAN extensions.
+
+- **port** (scalar) – since **0.105**
+
+ > Configures the default destination UDP port. If the destination port is
+ > not specified then Linux kernel default will be used. Set to `4789` to get
+ > the IANA assigned value.
+
+- **port-range** (sequence of scalars) – since **0.105**
+
+ > Configures the source port range for the VXLAN. The kernel assigns the
+ > source UDP port based on the flow to help the receiver to do load
+ > balancing. When this option is not set, the normal range of local UDP
+ > ports is used. Uses the form `[LOWER, UPPER]`.
+
+- **flow-label** (scalar) – since **0.105**
+
+ > Specifies the flow label to use in outgoing packets. The valid range
+ > is `0-1048575`.
+
+- **do-not-fragment** (scalar) – since **0.105**
+
+ > Allows setting the IPv4 Do not Fragment (DF) bit in outgoing packets.
+ > Takes a boolean value. When unset, the kernel's default will be used.
+
+## Properties for device type `vlans:`
+
+- **id** (scalar)
+
+ > VLAN ID, a number between `0` and `4094`.
+
+- **link** (scalar)
+
+ > netplan ID of the underlying device definition on which this VLAN gets
+ > created.
+
+Example:
+
+```yaml
+ethernets:
+ eno1: {...}
+vlans:
+ en-intra:
+ id: 1
+ link: eno1
+ dhcp4: yes
+ en-vpn:
+ id: 2
+ link: eno1
+ addresses: [...]
+```
+
+## Properties for device type `vrfs:`
+
+- **table** (scalar) – since **0.105**
+
+ > The numeric routing table identifier. This setting is compulsory.
+
+- **interfaces** (sequence of scalars) – since **0.105**
+
+ > All devices matching this ID list will be added to the vrf. This may
+ > be an empty list, in which case the vrf will be brought online with
+ > no member interfaces.
+
+- **routes** (sequence of mappings) – since **0.105**
+
+ > Configure static routing for the device; see the `Routing` section.
+ > The `table` value is implicitly set to the VRF's `table`.
+
+- **routing-policy** (sequence of mappings) – since **0.105**
+
+ > Configure policy routing for the device; see the ``Routing`` section.
+ > The `table` value is implicitly set to the VRF's `table`.
+
+Example:
+
+```yaml
+vrfs:
+ vrf20:
+ table: 20
+ interfaces: [ br0 ]
+ routes:
+ - to: default
+ via: 10.10.10.3
+ routing-policy:
+ - from: 10.10.10.42
+ [...]
+ bridges:
+ br0:
+ interfaces: []
+```
+
+## Properties for device type `nm-devices:`
+
+The `nm-devices` device type is for internal use only and should not be used in
+normal configuration files. It enables a fallback mode for unsupported settings,
+using the `passthrough` mapping.
+
+
+## Backend-specific configuration parameters
+
+In addition to the other fields available to configure interfaces, some
+backends may require to record some of their own parameters in netplan,
+especially if the netplan definitions are generated automatically by the
+consumer of that backend. Currently, this is only used with `NetworkManager`.
+
+- **networkmanager** (mapping) – since **0.99**
+
+ > Keeps the NetworkManager-specific configuration parameters used by the
+ > daemon to recognize connections.
+
+ - **name** (scalar) – since **0.99**
+
+ > Set the display name for the connection.
+
+ - **uuid** (scalar) – since **0.99**
+
+ > Defines the UUID (unique identifier) for this connection, as
+ > generated by NetworkManager itself.
+
+ - **stable-id** (scalar) – since **0.99**
+
+ > Defines the stable ID (a different form of a connection name) used
+ > by NetworkManager in case the name of the connection might otherwise
+ > change, such as when sharing connections between users.
+
+ - **device** (scalar) – since **0.99**
+
+ > Defines the interface name for which this connection applies.
+
+ - **passthrough** (mapping) – since **0.102**
+
+ > Can be used as a fallback mechanism to missing keyfile settings.
+
+<!--- vim: ft=markdown -->
diff --git a/doc/netplan.md b/doc/netplan.md
index e9b4e92..6b0b685 100644..120000
--- a/doc/netplan.md
+++ b/doc/netplan.md
@@ -1,1529 +1 @@
-## Introduction
-Distribution installers, cloud instantiation, image builds for particular
-devices, or any other way to deploy an operating system put its desired
-network configuration into YAML configuration file(s). During
-early boot, the netplan "network renderer" runs which reads
-``/{lib,etc,run}/netplan/*.yaml`` and writes configuration to ``/run`` to hand
-off control of devices to the specified networking daemon.
-
- - Configured devices get handled by systemd-networkd by default,
- unless explicitly marked as managed by a specific renderer (NetworkManager)
- - Devices not covered by the network config do not get touched at all.
- - Usable in initramfs (few dependencies and fast)
- - No persistent generated config, only original YAML config
- - Parser supports multiple config files to allow applications like libvirt or lxd
- to package up expected network config (``virbr0``, ``lxdbr0``), or to change the
- global default policy to use NetworkManager for everything.
- - Retains the flexibility to change backends/policy later or adjust to
- removing NetworkManager, as generated configuration is ephemeral.
-
-## General structure
-netplan's configuration files use the
-[YAML](<http://yaml.org/spec/1.1/current.html>) format. All
-``/{lib,etc,run}/netplan/*.yaml`` are considered. Lexicographically later files
-(regardless of in which directory they are) amend (new mapping keys) or
-override (same mapping keys) previous ones. A file in ``/run/netplan``
-completely shadows a file with same name in ``/etc/netplan``, and a file in
-either of those directories shadows a file with the same name in
-``/lib/netplan``.
-
-The top-level node in a netplan configuration file is a ``network:`` mapping
-that contains ``version: 2`` (the YAML currently being used by curtin, MaaS,
-etc. is version 1), and then device definitions grouped by their type, such as
-``ethernets:``, ``modems:``, ``wifis:``, or ``bridges:``. These are the types that our
-renderer can understand and are supported by our backends.
-
-Each type block contains device definitions as a map where the keys (called
-"configuration IDs") are defined as below.
-
-## Device configuration IDs
-The key names below the per-device-type definition maps (like ``ethernets:``)
-are called "ID"s. They must be unique throughout the entire set of
-configuration files. Their primary purpose is to serve as anchor names for
-composite devices, for example to enumerate the members of a bridge that is
-currently being defined.
-
-(Since 0.97) If an interface is defined with an ID in a configuration file; it will
-be brought up by the applicable renderer. To not have netplan touch an interface
-at all, it should be completely omitted from the netplan configuration files.
-
-There are two physically/structurally different classes of device definitions,
-and the ID field has a different interpretation for each:
-
-Physical devices
-
-: (Examples: ethernet, modem, wifi) These can dynamically come and go between
- reboots and even during runtime (hotplugging). In the generic case, they
- can be selected by ``match:`` rules on desired properties, such as name/name
- pattern, MAC address, driver, or device paths. In general these will match
- any number of devices (unless they refer to properties which are unique
- such as the full path or MAC address), so without further knowledge about
- the hardware these will always be considered as a group.
-
- It is valid to specify no match rules at all, in which case the ID field is
- simply the interface name to be matched. This is mostly useful if you want
- to keep simple cases simple, and it's how network device configuration has
- been done for a long time.
-
- If there are ``match``: rules, then the ID field is a purely opaque name
- which is only being used for references from definitions of compound
- devices in the config.
-
-
-Virtual devices
-
-: (Examples: veth, bridge, bond) These are fully under the control of the
- config file(s) and the network stack. I. e. these devices are being created
- instead of matched. Thus ``match:`` and ``set-name:`` are not applicable for
- these, and the ID field is the name of the created virtual device.
-
-## Common properties for physical device types
-
-``match`` (mapping)
-
-: This selects a subset of available physical devices by various hardware
- properties. The following configuration will then apply to all matching
- devices, as soon as they appear. *All* specified properties must match.
-
- ``name`` (scalar)
- : Current interface name. Globs are supported, and the primary use case
- for matching on names, as selecting one fixed name can be more easily
- achieved with having no ``match:`` at all and just using the ID (see
- above).
- (``NetworkManager``: as of v1.14.0)
-
- ``macaddress`` (scalar)
- : Device's MAC address in the form "XX:XX:XX:XX:XX:XX". Globs are not
- allowed.
-
- ``driver`` (scalar or sequence of scalars) – sequence since **0.104**
- : Kernel driver name, corresponding to the ``DRIVER`` udev property.
- A sequence of globs is supported, any of which must match.
- Matching on driver is *only* supported with networkd.
-
- Examples:
-
- - all cards on second PCI bus:
-
- match:
- name: enp2*
-
- - fixed MAC address:
-
- match:
- macaddress: 11:22:33:AA:BB:FF
-
- - first card of driver ``ixgbe``:
-
- match:
- driver: ixgbe
- name: en*s0
-
- - first card with a driver matching ``bcmgenet`` or ``smsc*``:
-
- match:
- driver: ["bcmgenet", "smsc*"]
- name: en*
-
-``set-name`` (scalar)
-
-: When matching on unique properties such as path or MAC, or with additional
- assumptions such as "there will only ever be one wifi device",
- match rules can be written so that they only match one device. Then this
- property can be used to give that device a more specific/desirable/nicer
- name than the default from udev’s ifnames. Any additional device that
- satisfies the match rules will then fail to get renamed and keep the
- original kernel name (and dmesg will show an error).
-
-``wakeonlan`` (bool)
-
-: Enable wake on LAN. Off by default.
-
- **Note:** This will not work reliably for devices matched by name
- only and rendered by networkd, due to interactions with device
- renaming in udev. Match devices by MAC when setting wake on LAN.
-
-``emit-lldp`` (bool) – since **0.99**
-
-: (networkd backend only) Whether to emit LLDP packets. Off by default.
-
-``receive-checksum-offload`` (bool) – since **0.104**
-
-: (networkd backend only) If set to true, the hardware offload for
- checksumming of ingress network packets is enabled. When unset,
- the kernel's default will be used.
-
-``transmit-checksum-offload`` (bool) – since **0.104**
-
-: (networkd backend only) If set to true, the hardware offload for
- checksumming of egress network packets is enabled. When unset,
- the kernel's default will be used.
-
-``tcp-segmentation-offload`` (bool) – since **0.104**
-
-: (networkd backend only) If set to true, the TCP Segmentation
- Offload (TSO) is enabled. When unset, the kernel's default will
- be used.
-
-``tcp6-segmentation-offload`` (bool) – since **0.104**
-
-: (networkd backend only) If set to true, the TCP6 Segmentation
- Offload (tx-tcp6-segmentation) is enabled. When unset, the
- kernel's default will be used.
-
-``generic-segmentation-offload`` (bool) – since **0.104**
-
-: (networkd backend only) If set to true, the Generic Segmentation
- Offload (GSO) is enabled. When unset, the kernel's default will
- be used.
-
-``generic-receive-offload`` (bool) – since **0.104**
-
-: (networkd backend only) If set to true, the Generic Receive
- Offload (GRO) is enabled. When unset, the kernel's default will
- be used.
-
-``large-receive-offload`` (bool) – since **0.104**
-
-: (networkd backend only) If set to true, the Generic Receive
- Offload (GRO) is enabled. When unset, the kernel's default will
- be used.
-
-``openvswitch`` (mapping) – since **0.100**
-
-: This provides additional configuration for the network device for openvswitch.
- If openvswitch is not available on the system, netplan treats the presence of
- openvswitch configuration as an error.
-
- Any supported network device that is declared with the ``openvswitch`` mapping
- (or any bond/bridge that includes an interface with an openvswitch configuration)
- will be created in openvswitch instead of the defined renderer.
- In the case of a ``vlan`` definition declared the same way, netplan will create
- a fake VLAN bridge in openvswitch with the requested vlan properties.
-
- ``external-ids`` (mapping) – since **0.100**
- : Passed-through directly to OpenVSwitch
-
- ``other-config`` (mapping) – since **0.100**
- : Passed-through directly to OpenVSwitch
-
- ``lacp`` (scalar) – since **0.100**
- : Valid for bond interfaces. Accepts ``active``, ``passive`` or ``off`` (the default).
-
- ``fail-mode`` (scalar) – since **0.100**
- : Valid for bridge interfaces. Accepts ``secure`` or ``standalone`` (the default).
-
- ``mcast-snooping`` (bool) – since **0.100**
- : Valid for bridge interfaces. False by default.
-
- ``protocols`` (sequence of scalars) – since **0.100**
- : Valid for bridge interfaces or the network section. List of protocols to be used when
- negotiating a connection with the controller. Accepts ``OpenFlow10``, ``OpenFlow11``,
- ``OpenFlow12``, ``OpenFlow13``, ``OpenFlow14``, ``OpenFlow15`` and ``OpenFlow16``.
-
- ``rstp`` (bool) – since **0.100**
- : Valid for bridge interfaces. False by default.
-
- ``controller`` (mapping) – since **0.100**
- : Valid for bridge interfaces. Specify an external OpenFlow controller.
-
- ``addresses`` (sequence of scalars)
- : Set the list of addresses to use for the controller targets. The
- syntax of these addresses is as defined in ovs-vsctl(8). Example:
- addresses: ``[tcp:127.0.0.1:6653, "ssl:[fe80::1234%eth0]:6653"]``
-
- ``connection-mode`` (scalar)
- : Set the connection mode for the controller. Supported options are
- ``in-band`` and ``out-of-band``. The default is ``in-band``.
-
- ``ports`` (sequence of sequence of scalars) – since **0.100**
- : OpenvSwitch patch ports. Each port is declared as a pair of names
- which can be referenced as interfaces in dependent virtual devices
- (bonds, bridges).
-
- Example:
-
- openvswitch:
- ports:
- - [patch0-1, patch1-0]
-
- ``ssl`` (mapping) – since **0.100**
- : Valid for global ``openvswitch`` settings. Options for configuring SSL
- server endpoint for the switch.
-
- ``ca-cert`` (scalar)
- : Path to a file containing the CA certificate to be used.
-
- ``certificate`` (scalar)
- : Path to a file containing the server certificate.
-
- ``private-key`` (scalar)
- : Path to a file containing the private key for the server.
-
-## Common properties for all device types
-
-``renderer`` (scalar)
-
-: Use the given networking backend for this definition. Currently supported are
- ``networkd`` and ``NetworkManager``. This property can be specified globally
- in ``network:``, for a device type (in e. g. ``ethernets:``) or
- for a particular device definition. Default is ``networkd``.
-
- (Since 0.99) The ``renderer`` property has one additional acceptable value for vlan
- objects (i. e. defined in ``vlans:``): ``sriov``. If a vlan is defined with the
- ``sriov`` renderer for an SR-IOV Virtual Function interface, this causes netplan to
- set up a hardware VLAN filter for it. There can be only one defined per VF.
-
-``dhcp4`` (bool)
-
-: Enable DHCP for IPv4. Off by default.
-
-``dhcp6`` (bool)
-
-: Enable DHCP for IPv6. Off by default. This covers both stateless DHCP -
- where the DHCP server supplies information like DNS nameservers but not the
- IP address - and stateful DHCP, where the server provides both the address
- and the other information.
-
- If you are in an IPv6-only environment with completely stateless
- autoconfiguration (SLAAC with RDNSS), this option can be set to cause the
- interface to be brought up. (Setting accept-ra alone is not sufficient.)
- Autoconfiguration will still honour the contents of the router advertisement
- and only use DHCP if requested in the RA.
-
- Note that **``rdnssd``**(8) is required to use RDNSS with networkd. No extra
- software is required for NetworkManager.
-
-``ipv6-mtu`` (scalar) – since **0.98**
-: Set the IPv6 MTU (only supported with `networkd` backend). Note
- that needing to set this is an unusual requirement.
-
- **Requires feature: ipv6-mtu**
-
-``ipv6-privacy`` (bool)
-
-: Enable IPv6 Privacy Extensions (RFC 4941) for the specified interface, and
- prefer temporary addresses. Defaults to false - no privacy extensions. There
- is currently no way to have a private address but prefer the public address.
-
-``link-local`` (sequence of scalars)
-
-: Configure the link-local addresses to bring up. Valid options are 'ipv4'
- and 'ipv6', which respectively allow enabling IPv4 and IPv6 link local
- addressing. If this field is not defined, the default is to enable only
- IPv6 link-local addresses. If the field is defined but configured as an
- empty set, IPv6 link-local addresses are disabled as well as IPv4 link-
- local addresses.
-
- This feature enables or disables link-local addresses for a protocol, but
- the actual implementation differs per backend. On networkd, this directly
- changes the behavior and may add an extra address on an interface. When
- using the NetworkManager backend, enabling link-local has no effect if the
- interface also has DHCP enabled.
-
- Example to enable only IPv4 link-local: ``link-local: [ ipv4 ]``
- Example to enable all link-local addresses: ``link-local: [ ipv4, ipv6 ]``
- Example to disable all link-local addresses: ``link-local: [ ]``
-
-``ignore-carrier`` (bool) – since **0.104**
-
-: (networkd backend only) Allow the specified interface to be configured even
- if it has no carrier.
-
-``critical`` (bool)
-
-: Designate the connection as "critical to the system", meaning that special
- care will be taken by to not release the assigned IP when the daemon is
- restarted. (not recognized by NetworkManager)
-
-``dhcp-identifier`` (scalar)
-
-: (networkd backend only) Sets the source of DHCPv4 client identifier. If ``mac``
- is specified, the MAC address of the link is used. If this option is omitted,
- or if ``duid`` is specified, networkd will generate an RFC4361-compliant client
- identifier for the interface by combining the link's IAID and DUID.
-
- ``dhcp4-overrides`` (mapping)
-
- : (networkd backend only) Overrides default DHCP behavior; see the
- ``DHCP Overrides`` section below.
-
- ``dhcp6-overrides`` (mapping)
-
- : (networkd backend only) Overrides default DHCP behavior; see the
- ``DHCP Overrides`` section below.
-
-``accept-ra`` (bool)
-
-: Accept Router Advertisement that would have the kernel configure IPv6 by itself.
- When enabled, accept Router Advertisements. When disabled, do not respond to
- Router Advertisements. If unset use the host kernel default setting.
-
-``addresses`` (sequence of scalars and mappings)
-
-: Add static addresses to the interface in addition to the ones received
- through DHCP or RA. Each sequence entry is in CIDR notation, i. e. of the
- form ``addr/prefixlen``. ``addr`` is an IPv4 or IPv6 address as recognized
- by **``inet_pton``**(3) and ``prefixlen`` the number of bits of the subnet.
-
- For virtual devices (bridges, bonds, vlan) if there is no address
- configured and DHCP is disabled, the interface may still be brought online,
- but will not be addressable from the network.
-
- In addition to the addresses themselves one can specify configuration
- parameters as mappings. Current supported options are:
-
- ``lifetime`` (scalar) – since **0.100**
- : Default: ``forever``. This can be ``forever`` or ``0`` and corresponds
- to the ``PreferredLifetime`` option in ``systemd-networkd``'s Address
- section. Currently supported on the ``networkd`` backend only.
-
- ``label`` (scalar) – since **0.100**
- : An IP address label, equivalent to the ``ip address label``
- command. Currently supported on the ``networkd`` backend only.
-
- Example: ``addresses: [192.168.14.2/24, "2001:1::1/64"]``
-
- Example:
-
- ethernets:
- eth0:
- addresses:
- - 10.0.0.15/24:
- lifetime: 0
- label: "maas"
- - "2001:1::1/64"
-
-``ipv6-address-generation`` (scalar) – since **0.99**
-
-: Configure method for creating the address for use with RFC4862 IPv6
- Stateless Address Autoconfiguration (only supported with `NetworkManager`
- backend). Possible values are ``eui64`` or ``stable-privacy``.
-
-``ipv6-address-token`` (scalar) – since **0.100**
-
-: Define an IPv6 address token for creating a static interface identifier for
- IPv6 Stateless Address Autoconfiguration. This is mutually exclusive with
- ``ipv6-address-generation``.
-
-``gateway4``, ``gateway6`` (scalar)
-
-: Deprecated, see ``Default routes``.
- Set default gateway for IPv4/6, for manual address configuration. This
- requires setting ``addresses`` too. Gateway IPs must be in a form
- recognized by **``inet_pton``**(3). There should only be a single gateway
- per IP address family set in your global config, to make it unambiguous.
- If you need multiple default routes, please define them via
- ``routing-policy``.
-
- Example for IPv4: ``gateway4: 172.16.0.1``
- Example for IPv6: ``gateway6: "2001:4::1"``
-
-``nameservers`` (mapping)
-
-: Set DNS servers and search domains, for manual address configuration. There
-are two supported fields: ``addresses:`` is a list of IPv4 or IPv6 addresses
-similar to ``gateway*``, and ``search:`` is a list of search domains.
-
- Example:
-
- ethernets:
- id0:
- [...]
- nameservers:
- search: [lab, home]
- addresses: [8.8.8.8, "FEDC::1"]
-
-``macaddress`` (scalar)
-
-: Set the device's MAC address. The MAC address must be in the form
- "XX:XX:XX:XX:XX:XX".
-
- **Note:** This will not work reliably for devices matched by name
- only and rendered by networkd, due to interactions with device
- renaming in udev. Match devices by MAC when setting MAC addresses.
-
- Example:
-
- ethernets:
- id0:
- match:
- macaddress: 52:54:00:6b:3c:58
- [...]
- macaddress: 52:54:00:6b:3c:59
-
-``mtu`` (scalar)
-
-: Set the Maximum Transmission Unit for the interface. The default is 1500.
- Valid values depend on your network interface.
-
- **Note:** This will not work reliably for devices matched by name
- only and rendered by networkd, due to interactions with device
- renaming in udev. Match devices by MAC when setting MTU.
-
-``optional`` (bool)
-
-: An optional device is not required for booting. Normally, networkd will
- wait some time for device to become configured before proceeding with
- booting. However, if a device is marked as optional, networkd will not wait
- for it. This is *only* supported by networkd, and the default is false.
-
- Example:
-
- ethernets:
- eth7:
- # this is plugged into a test network that is often
- # down - don't wait for it to come up during boot.
- dhcp4: true
- optional: true
-
-``optional-addresses`` (sequence of scalars)
-
-: Specify types of addresses that are not required for a device to be
- considered online. This changes the behavior of backends at boot time to
- avoid waiting for addresses that are marked optional, and thus consider
- the interface as "usable" sooner. This does not disable these addresses,
- which will be brought up anyway.
-
- Example:
-
- ethernets:
- eth7:
- dhcp4: true
- dhcp6: true
- optional-addresses: [ ipv4-ll, dhcp6 ]
-
-``activation-mode`` (scalar) – since **0.103**
-
-: Allows specifying the management policy of the selected interface. By
- default, netplan brings up any configured interface if possible. Using the
- ``activation-mode`` setting users can override that behavior by either
- specifying ``manual``, to hand over control over the interface state to the
- administrator or (for networkd backend *only*) ``off`` to force the link
- in a down state at all times. Any interface with ``activation-mode``
- defined is implicitly considered ``optional``.
- Supported officially as of ``networkd`` v248+.
-
- Example:
-
- ethernets:
- eth1:
- # this interface will not be put into an UP state automatically
- dhcp4: true
- activation-mode: manual
-
-``routes`` (sequence of mappings)
-
-: Configure static routing for the device; see the ``Routing`` section below.
-
-``routing-policy`` (sequence of mappings)
-
-: Configure policy routing for the device; see the ``Routing`` section below.
-
-## DHCP Overrides
-Several DHCP behavior overrides are available. Most currently only have any
-effect when using the ``networkd`` backend, with the exception of ``use-routes``
-and ``route-metric``.
-
-Overrides only have an effect if the corresponding ``dhcp4`` or ``dhcp6`` is
-set to ``true``.
-
-If both ``dhcp4`` and ``dhcp6`` are ``true``, the ``networkd`` backend requires
-that ``dhcp4-overrides`` and ``dhcp6-overrides`` contain the same keys and
-values. If the values do not match, an error will be shown and the network
-configuration will not be applied.
-
-When using the NetworkManager backend, different values may be specified for
-``dhcp4-overrides`` and ``dhcp6-overrides``, and will be applied to the DHCP
-client processes as specified in the netplan YAML.
-
-``dhcp4-overrides``, ``dhcp6-overrides`` (mapping)
-
-: The ``dhcp4-overrides`` and ``dhcp6-overrides`` mappings override the
- default DHCP behavior.
-
- ``use-dns`` (bool)
- : Default: ``true``. When ``true``, the DNS servers received from the
- DHCP server will be used and take precedence over any statically
- configured ones. Currently only has an effect on the ``networkd``
- backend.
-
- ``use-ntp`` (bool)
- : Default: ``true``. When ``true``, the NTP servers received from the
- DHCP server will be used by systemd-timesyncd and take precedence
- over any statically configured ones. Currently only has an effect on
- the ``networkd`` backend.
-
- ``send-hostname`` (bool)
- : Default: ``true``. When ``true``, the machine's hostname will be sent
- to the DHCP server. Currently only has an effect on the ``networkd``
- backend.
-
- ``use-hostname`` (bool)
- : Default: ``true``. When ``true``, the hostname received from the DHCP
- server will be set as the transient hostname of the system. Currently
- only has an effect on the ``networkd`` backend.
-
- ``use-mtu`` (bool)
- : Default: ``true``. When ``true``, the MTU received from the DHCP
- server will be set as the MTU of the network interface. When ``false``,
- the MTU advertised by the DHCP server will be ignored. Currently only
- has an effect on the ``networkd`` backend.
-
- ``hostname`` (scalar)
- : Use this value for the hostname which is sent to the DHCP server,
- instead of machine's hostname. Currently only has an effect on the
- ``networkd`` backend.
-
- ``use-routes`` (bool)
- : Default: ``true``. When ``true``, the routes received from the DHCP
- server will be installed in the routing table normally. When set to
- ``false``, routes from the DHCP server will be ignored: in this case,
- the user is responsible for adding static routes if necessary for
- correct network operation. This allows users to avoid installing a
- default gateway for interfaces configured via DHCP. Available for
- both the ``networkd`` and ``NetworkManager`` backends.
-
- ``route-metric`` (scalar)
- : Use this value for default metric for automatically-added routes.
- Use this to prioritize routes for devices by setting a lower metric
- on a preferred interface. Available for both the ``networkd`` and
- ``NetworkManager`` backends.
-
- ``use-domains`` (scalar) – since **0.98**
- : Takes a boolean, or the special value "route". When true, the domain
- name received from the DHCP server will be used as DNS search domain
- over this link, similar to the effect of the Domains= setting. If set
- to "route", the domain name received from the DHCP server will be
- used for routing DNS queries only, but not for searching, similar to
- the effect of the Domains= setting when the argument is prefixed with
- "~".
-
- **Requires feature: dhcp-use-domains**
-
-
-## Routing
-Complex routing is possible with netplan. Standard static routes as well
-as policy routing using routing tables are supported via the ``networkd``
-backend.
-
-These options are available for all types of interfaces.
-
-### Default routes
-
-The most common need for routing concerns the definition of default routes to
-reach the wider Internet. Those default routes can only defined once per IP family
-and routing table. A typical example would look like the following:
-
-```yaml
-eth0:
- [...]
- routes:
- - to: default # could be 0/0 or 0.0.0.0/0 optionally
- via: 10.0.0.1
- metric: 100
- on-link: true
- - to: default # could be ::/0 optionally
- via: cf02:de:ad:be:ef::2
-eth1:
- [...]
- routes:
- - to: default
- via: 172.134.67.1
- metric: 100
- on-link: true
- table: 76 # Not on the main routing table, does not conflict with the eth0 default route
-```
-
-``routes`` (mapping)
-
-: The ``routes`` block defines standard static routes for an interface.
- At least ``to`` must be specified. If type is ``local`` or ``nat`` a
- default scope of ``host`` is assumed.
- If type is ``unicast`` and no gateway (``via``) is given or type is
- ``broadcast``, ``multicast`` or ``anycast`` a default scope of ``link``
- is assumend. Otherwise, a ``global`` scope is the default setting.
-
- For ``from``, ``to``, and ``via``, both IPv4 and IPv6 addresses are
- recognized, and must be in the form ``addr/prefixlen`` or ``addr``.
-
- ``from`` (scalar)
- : Set a source IP address for traffic going through the route.
- (``NetworkManager``: as of v1.8.0)
-
- ``to`` (scalar)
- : Destination address for the route.
-
- ``via`` (scalar)
- : Address to the gateway to use for this route.
-
- ``on-link`` (bool)
- : When set to "true", specifies that the route is directly connected
- to the interface.
- (``NetworkManager``: as of v1.12.0 for IPv4 and v1.18.0 for IPv6)
-
- ``metric`` (scalar)
- : The relative priority of the route. Must be a positive integer value.
-
- ``type`` (scalar)
- : The type of route. Valid options are "unicast" (default), "anycast",
- "blackhole", "broadcast", "local", "multicast", "nat", "prohibit",
- "throw", "unreachable" or "xresolve".
-
- ``scope`` (scalar)
- : The route scope, how wide-ranging it is to the network. Possible
- values are "global", "link", or "host".
-
- ``table`` (scalar)
- : The table number to use for the route. In some scenarios, it may be
- useful to set routes in a separate routing table. It may also be used
- to refer to routing policy rules which also accept a ``table``
- parameter. Allowed values are positive integers starting from 1.
- Some values are already in use to refer to specific routing tables:
- see ``/etc/iproute2/rt_tables``.
- (``NetworkManager``: as of v1.10.0)
-
- ``mtu`` (scalar) – since **0.101**
- : The MTU to be used for the route, in bytes. Must be a positive integer
- value.
-
- ``congestion-window`` (scalar) – since **0.102**
- : The congestion window to be used for the route, represented by number
- of segments. Must be a positive integer value.
-
- ``advertised-receive-window`` (scalar) – since **0.102**
- : The receive window to be advertised for the route, represented by
- number of segments. Must be a positive integer value.
-
-``routing-policy`` (mapping)
-
-: The ``routing-policy`` block defines extra routing policy for a network,
- where traffic may be handled specially based on the source IP, firewall
- marking, etc.
-
- For ``from``, ``to``, both IPv4 and IPv6 addresses are recognized, and
- must be in the form ``addr/prefixlen`` or ``addr``.
-
- ``from`` (scalar)
- : Set a source IP address to match traffic for this policy rule.
-
- ``to`` (scalar)
- : Match on traffic going to the specified destination.
-
- ``table`` (scalar)
- : The table number to match for the route. In some scenarios, it may be
- useful to set routes in a separate routing table. It may also be used
- to refer to routes which also accept a ``table`` parameter.
- Allowed values are positive integers starting from 1.
- Some values are already in use to refer to specific routing tables:
- see ``/etc/iproute2/rt_tables``.
-
- ``priority`` (scalar)
- : Specify a priority for the routing policy rule, to influence the order
- in which routing rules are processed. A higher number means lower
- priority: rules are processed in order by increasing priority number.
-
- ``mark`` (scalar)
- : Have this routing policy rule match on traffic that has been marked
- by the iptables firewall with this value. Allowed values are positive
- integers starting from 1.
-
- ``type-of-service`` (scalar)
- : Match this policy rule based on the type of service number applied to
- the traffic.
-
-## Authentication
-Netplan supports advanced authentication settings for ethernet and wifi
-interfaces, as well as individual wifi networks, by means of the ``auth`` block.
-
-``auth`` (mapping)
-
-: Specifies authentication settings for a device of type ``ethernets:``, or
- an ``access-points:`` entry on a ``wifis:`` device.
-
- The ``auth`` block supports the following properties:
-
- ``key-management`` (scalar)
- : The supported key management modes are ``none`` (no key management);
- ``psk`` (WPA with pre-shared key, common for home wifi); ``eap`` (WPA
- with EAP, common for enterprise wifi); and ``802.1x`` (used primarily
- for wired Ethernet connections).
-
- ``password`` (scalar)
- : The password string for EAP, or the pre-shared key for WPA-PSK.
-
- The following properties can be used if ``key-management`` is ``eap``
- or ``802.1x``:
-
- ``method`` (scalar)
- : The EAP method to use. The supported EAP methods are ``tls`` (TLS),
- ``peap`` (Protected EAP), and ``ttls`` (Tunneled TLS).
-
- ``identity`` (scalar)
- : The identity to use for EAP.
-
- ``anonymous-identity`` (scalar)
- : The identity to pass over the unencrypted channel if the chosen EAP
- method supports passing a different tunnelled identity.
-
- ``ca-certificate`` (scalar)
- : Path to a file with one or more trusted certificate authority (CA)
- certificates.
-
- ``client-certificate`` (scalar)
- : Path to a file containing the certificate to be used by the client
- during authentication.
-
- ``client-key`` (scalar)
- : Path to a file containing the private key corresponding to
- ``client-certificate``.
-
- ``client-key-password`` (scalar)
- : Password to use to decrypt the private key specified in
- ``client-key`` if it is encrypted.
-
- ``phase2-auth`` (scalar) – since **0.99**
- : Phase 2 authentication mechanism.
-
-
-## Properties for device type ``ethernets:``
-Ethernet device definitions, beyond common ones described above, also support
-some additional properties that can be used for SR-IOV devices.
-
-``link`` (scalar) – since **0.99**
-
-: (SR-IOV devices only) The ``link`` property declares the device as a
- Virtual Function of the selected Physical Function device, as identified
- by the given netplan id.
-
-Example:
-
- ethernets:
- enp1: {...}
- enp1s16f1:
- link: enp1
-
-``virtual-function-count`` (scalar) – since **0.99**
-
-: (SR-IOV devices only) In certain special cases VFs might need to be
- configured outside of netplan. For such configurations ``virtual-function-count``
- can be optionally used to set an explicit number of Virtual Functions for
- the given Physical Function. If unset, the default is to create only as many
- VFs as are defined in the netplan configuration. This should be used for special
- cases only.
-
- **Requires feature: sriov**
-
-``embedded-switch-mode`` (scalar) – since **0.104**
-
-: (SR-IOV devices only) Change the operational mode of the embedded switch
- of a supported SmartNIC PCI device (e.g. Mellanox ConnectX-5). Possible
- values are ``switchdev`` or ``legacy``, if unspecified the vendor's
- default configuration is used.
-
- **Requires feature: eswitch-mode**
-
-``delay-virtual-functions-rebind`` (bool) – since **0.104**
-
-: (SR-IOV devices only) Delay rebinding of SR-IOV virtual functions to its
- driver after changing the embedded-switch-mode setting to a later stage.
- Can be enabled when bonding/VF LAG is in use. Defaults to ``false``.
-
- **Requires feature: eswitch-mode**
-
-## Properties for device type ``modems:``
-GSM/CDMA modem configuration is only supported for the ``NetworkManager``
-backend. ``systemd-networkd`` does not support modems.
-
-**Requires feature: modems**
-
-``apn`` (scalar) – since **0.99**
-
-: Set the carrier APN (Access Point Name). This can be omitted if
- ``auto-config`` is enabled.
-
-``auto-config`` (bool) – since **0.99**
-
-: Specify whether to try and autoconfigure the modem by doing a lookup of
- the carrier against the Mobile Broadband Provider database. This may not
- work for all carriers.
-
-``device-id`` (scalar) – since **0.99**
-
-: Specify the device ID (as given by the WWAN management service) of the
- modem to match. This can be found using ``mmcli``.
-
-``network-id`` (scalar) – since **0.99**
-
-: Specify the Network ID (GSM LAI format). If this is specified, the device
- will not roam networks.
-
-``number`` (scalar) – since **0.99**
-
-: The number to dial to establish the connection to the mobile broadband
- network. (Deprecated for GSM)
-
-``password`` (scalar) – since **0.99**
-
-: Specify the password used to authenticate with the carrier network. This
- can be omitted if ``auto-config`` is enabled.
-
-``pin`` (scalar) – since **0.99**
-
-: Specify the SIM PIN to allow it to operate if a PIN is set.
-
-``sim-id`` (scalar) – since **0.99**
-
-: Specify the SIM unique identifier (as given by the WWAN management service)
- which this connection applies to. If given, the connection will apply to
- any device also allowed by ``device-id`` which contains a SIM card matching
- the given identifier.
-
-``sim-operator-id`` (scalar) – since **0.99**
-
-: Specify the MCC/MNC string (such as "310260" or "21601") which identifies
- the carrier that this connection should apply to. If given, the connection
- will apply to any device also allowed by ``device-id`` and ``sim-id``
- which contains a SIM card provisioned by the given operator.
-
-``username`` (scalar) – since **0.99**
-
-: Specify the username used to authentiate with the carrier network. This
- can be omitted if ``auto-config`` is enabled.
-
-## Properties for device type ``wifis:``
-Note that ``systemd-networkd`` does not natively support wifi, so you need
-wpasupplicant installed if you let the ``networkd`` renderer handle wifi.
-
-``access-points`` (mapping)
-
-: This provides pre-configured connections to NetworkManager. Note that
- users can of course select other access points/SSIDs. The keys of the
- mapping are the SSIDs, and the values are mappings with the following
- supported properties:
-
- ``password`` (scalar)
- : Enable WPA2 authentication and set the passphrase for it. If neither
- this nor an ``auth`` block are given, the network is assumed to be
- open. The setting
-
- password: "S3kr1t"
-
- is equivalent to
-
- auth:
- key-management: psk
- password: "S3kr1t"
-
- ``mode`` (scalar)
- : Possible access point modes are ``infrastructure`` (the default),
- ``ap`` (create an access point to which other devices can connect),
- and ``adhoc`` (peer to peer networks without a central access point).
- ``ap`` is only supported with NetworkManager.
-
- ``bssid`` (scalar) – since **0.99**
- : If specified, directs the device to only associate with the given
- access point.
-
- ``band`` (scalar) – since **0.99**
- : Possible bands are ``5GHz`` (for 5GHz 802.11a) and ``2.4GHz``
- (for 2.4GHz 802.11), do not restrict the 802.11 frequency band of the
- network if unset (the default).
-
- ``channel`` (scalar) – since **0.99**
- : Wireless channel to use for the Wi-Fi connection. Because channel
- numbers overlap between bands, this property takes effect only if
- the ``band`` property is also set.
-
- ``hidden`` (bool) – since **0.100**
- : Set to ``true`` to change the SSID scan technique for connecting to
- hidden WiFi networks. Note this may have slower performance compared
- to ``false`` (the default) when connecting to publicly broadcast
- SSIDs.
-
-``wakeonwlan`` (sequence of scalars) – since **0.99**
-
-: This enables WakeOnWLan on supported devices. Not all drivers support all
- options. May be any combination of ``any``, ``disconnect``, ``magic_pkt``,
- ``gtk_rekey_failure``, ``eap_identity_req``, ``four_way_handshake``,
- ``rfkill_release`` or ``tcp`` (NetworkManager only). Or the exclusive
- ``default`` flag (the default).
-
-## Properties for device type ``bridges:``
-
-``interfaces`` (sequence of scalars)
-
-: All devices matching this ID list will be added to the bridge. This may
- be an empty list, in which case the bridge will be brought online with
- no member interfaces.
-
- Example:
-
- ethernets:
- switchports:
- match: {name: "enp2*"}
- [...]
- bridges:
- br0:
- interfaces: [switchports]
-
-``parameters`` (mapping)
-
-: Customization parameters for special bridging options. Time intervals
- may need to be expressed as a number of seconds or milliseconds: the
- default value type is specified below. If necessary, time intervals can
- be qualified using a time suffix (such as "s" for seconds, "ms" for
- milliseconds) to allow for more control over its behavior.
-
- ``ageing-time`` (scalar)
- : Set the period of time to keep a MAC address in the forwarding
- database after a packet is received. This maps to the AgeingTimeSec=
- property when the networkd renderer is used. If no time suffix is
- specified, the value will be interpreted as seconds.
-
- ``priority`` (scalar)
- : Set the priority value for the bridge. This value should be a
- number between ``0`` and ``65535``. Lower values mean higher
- priority. The bridge with the higher priority will be elected as
- the root bridge.
-
- ``port-priority`` (scalar)
- : Set the port priority to <priority>. The priority value is
- a number between ``0`` and ``63``. This metric is used in the
- designated port and root port selection algorithms.
-
- ``forward-delay`` (scalar)
- : Specify the period of time the bridge will remain in Listening and
- Learning states before getting to the Forwarding state. This field
- maps to the ForwardDelaySec= property for the networkd renderer.
- If no time suffix is specified, the value will be interpreted as
- seconds.
-
- ``hello-time`` (scalar)
- : Specify the interval between two hello packets being sent out from
- the root and designated bridges. Hello packets communicate
- information about the network topology. When the networkd renderer
- is used, this maps to the HelloTimeSec= property. If no time suffix
- is specified, the value will be interpreted as seconds.
-
- ``max-age`` (scalar)
- : Set the maximum age of a hello packet. If the last hello packet is
- older than that value, the bridge will attempt to become the root
- bridge. This maps to the MaxAgeSec= property when the networkd
- renderer is used. If no time suffix is specified, the value will be
- interpreted as seconds.
-
- ``path-cost`` (scalar)
- : Set the cost of a path on the bridge. Faster interfaces should have
- a lower cost. This allows a finer control on the network topology
- so that the fastest paths are available whenever possible.
-
- ``stp`` (bool)
- : Define whether the bridge should use Spanning Tree Protocol. The
- default value is "true", which means that Spanning Tree should be
- used.
-
-
-## Properties for device type ``bonds:``
-
-``interfaces`` (sequence of scalars)
-
-: All devices matching this ID list will be added to the bond.
-
- Example:
-
- ethernets:
- switchports:
- match: {name: "enp2*"}
- [...]
- bonds:
- bond0:
- interfaces: [switchports]
-
-``parameters`` (mapping)
-
-: Customization parameters for special bonding options. Time intervals
- may need to be expressed as a number of seconds or milliseconds: the
- default value type is specified below. If necessary, time intervals can
- be qualified using a time suffix (such as "s" for seconds, "ms" for
- milliseconds) to allow for more control over its behavior.
-
- ``mode`` (scalar)
- : Set the bonding mode used for the interfaces. The default is
- ``balance-rr`` (round robin). Possible values are ``balance-rr``,
- ``active-backup``, ``balance-xor``, ``broadcast``, ``802.3ad``,
- ``balance-tlb``, and ``balance-alb``.
- For OpenVSwitch ``active-backup`` and the additional modes
- ``balance-tcp`` and ``balance-slb`` are supported.
-
- ``lacp-rate`` (scalar)
- : Set the rate at which LACPDUs are transmitted. This is only useful
- in 802.3ad mode. Possible values are ``slow`` (30 seconds, default),
- and ``fast`` (every second).
-
- ``mii-monitor-interval`` (scalar)
- : Specifies the interval for MII monitoring (verifying if an interface
- of the bond has carrier). The default is ``0``; which disables MII
- monitoring. This is equivalent to the MIIMonitorSec= field for the
- networkd backend. If no time suffix is specified, the value will be
- interpreted as milliseconds.
-
- ``min-links`` (scalar)
- : The minimum number of links up in a bond to consider the bond
- interface to be up.
-
- ``transmit-hash-policy`` (scalar)
- : Specifies the transmit hash policy for the selection of slaves. This
- is only useful in balance-xor, 802.3ad and balance-tlb modes.
- Possible values are ``layer2``, ``layer3+4``, ``layer2+3``,
- ``encap2+3``, and ``encap3+4``.
-
- ``ad-select`` (scalar)
- : Set the aggregation selection mode. Possible values are ``stable``,
- ``bandwidth``, and ``count``. This option is only used in 802.3ad
- mode.
-
- ``all-slaves-active`` (bool)
- : If the bond should drop duplicate frames received on inactive ports,
- set this option to ``false``. If they should be delivered, set this
- option to ``true``. The default value is false, and is the desirable
- behavior in most situations.
-
- ``arp-interval`` (scalar)
- : Set the interval value for how frequently ARP link monitoring should
- happen. The default value is ``0``, which disables ARP monitoring.
- For the networkd backend, this maps to the ARPIntervalSec= property.
- If no time suffix is specified, the value will be interpreted as
- milliseconds.
-
- ``arp-ip-targets`` (sequence of scalars)
- : IPs of other hosts on the link which should be sent ARP requests in
- order to validate that a slave is up. This option is only used when
- ``arp-interval`` is set to a value other than ``0``. At least one IP
- address must be given for ARP link monitoring to function. Only IPv4
- addresses are supported. You can specify up to 16 IP addresses. The
- default value is an empty list.
-
- ``arp-validate`` (scalar)
- : Configure how ARP replies are to be validated when using ARP link
- monitoring. Possible values are ``none``, ``active``, ``backup``,
- and ``all``.
-
- ``arp-all-targets`` (scalar)
- : Specify whether to use any ARP IP target being up as sufficient for
- a slave to be considered up; or if all the targets must be up. This
- is only used for ``active-backup`` mode when ``arp-validate`` is
- enabled. Possible values are ``any`` and ``all``.
-
- ``up-delay`` (scalar)
- : Specify the delay before enabling a link once the link is physically
- up. The default value is ``0``. This maps to the UpDelaySec= property
- for the networkd renderer. This option is only valid for the miimon
- link monitor. If no time suffix is specified, the value will be
- interpreted as milliseconds.
-
- ``down-delay`` (scalar)
- : Specify the delay before disabling a link once the link has been
- lost. The default value is ``0``. This maps to the DownDelaySec=
- property for the networkd renderer. This option is only valid for the
- miimon link monitor. If no time suffix is specified, the value will
- be interpreted as milliseconds.
-
- ``fail-over-mac-policy`` (scalar)
- : Set whether to set all slaves to the same MAC address when adding
- them to the bond, or how else the system should handle MAC addresses.
- The possible values are ``none``, ``active``, and ``follow``.
-
- ``gratuitous-arp`` (scalar)
- : Specify how many ARP packets to send after failover. Once a link is
- up on a new slave, a notification is sent and possibly repeated if
- this value is set to a number greater than ``1``. The default value
- is ``1`` and valid values are between ``1`` and ``255``. This only
- affects ``active-backup`` mode.
-
- For historical reasons, the misspelling ``gratuitious-arp`` is also
- accepted and has the same function.
-
- ``packets-per-slave`` (scalar)
- : In ``balance-rr`` mode, specifies the number of packets to transmit
- on a slave before switching to the next. When this value is set to
- ``0``, slaves are chosen at random. Allowable values are between
- ``0`` and ``65535``. The default value is ``1``. This setting is
- only used in ``balance-rr`` mode.
-
- ``primary-reselect-policy`` (scalar)
- : Set the reselection policy for the primary slave. On failure of the
- active slave, the system will use this policy to decide how the new
- active slave will be chosen and how recovery will be handled. The
- possible values are ``always``, ``better``, and ``failure``.
-
- ``resend-igmp`` (scalar)
- : In modes ``balance-rr``, ``active-backup``, ``balance-tlb`` and
- ``balance-alb``, a failover can switch IGMP traffic from one
- slave to another.
-
- This parameter specifies how many IGMP membership reports
- are issued on a failover event. Values range from 0 to 255. 0
- disables sending membership reports. Otherwise, the first
- membership report is sent on failover and subsequent reports
- are sent at 200ms intervals.
-
- ``learn-packet-interval`` (scalar)
- : Specify the interval between sending learning packets to
- each slave. The value range is between ``1`` and ``0x7fffffff``.
- The default value is ``1``. This option only affects ``balance-tlb``
- and ``balance-alb`` modes. Using the networkd renderer, this field
- maps to the LearnPacketIntervalSec= property. If no time suffix is
- specified, the value will be interpreted as seconds.
-
- ``primary`` (scalar)
- : Specify a device to be used as a primary slave, or preferred device
- to use as a slave for the bond (ie. the preferred device to send
- data through), whenever it is available. This only affects
- ``active-backup``, ``balance-alb``, and ``balance-tlb`` modes.
-
-
-## Properties for device type ``tunnels:``
-
-Tunnels allow traffic to pass as if it was between systems on the same local
-network, although systems may be far from each other but reachable via the
-Internet. They may be used to support IPv6 traffic on a network where the ISP
-does not provide the service, or to extend and "connect" separate local
-networks. Please see https://en.wikipedia.org/wiki/Tunneling_protocol for
-more general information about tunnels.
-
-``mode`` (scalar)
-
-: Defines the tunnel mode. Valid options are ``sit``, ``gre``, ``ip6gre``,
- ``ipip``, ``ipip6``, ``ip6ip6``, ``vti``, ``vti6`` and ``wireguard``.
- Additionally, the ``networkd`` backend also supports ``gretap`` and
- ``ip6gretap`` modes.
- In addition, the ``NetworkManager`` backend supports ``isatap`` tunnels.
-
-``local`` (scalar)
-
-: Defines the address of the local endpoint of the tunnel.
-
-``remote`` (scalar)
-
-: Defines the address of the remote endpoint of the tunnel.
-
-``ttl`` (scalar) – since **0.103**
-
-: Defines the TTL of the tunnel.
-
-``key`` (scalar or mapping)
-
-: Define keys to use for the tunnel. The key can be a number or a dotted
- quad (an IPv4 address). For ``wireguard`` it can be a base64-encoded
- private key or (as of ``networkd`` v242+) an absolute path to a file,
- containing the private key (since 0.100).
- It is used for identification of IP transforms. This is only required
- for ``vti`` and ``vti6`` when using the networkd backend, and for
- ``gre`` or ``ip6gre`` tunnels when using the NetworkManager backend.
-
- This field may be used as a scalar (meaning that a single key is
- specified and to be used for input, output and private key), or as a
- mapping, where you can further specify ``input``/``output``/``private``.
-
- ``input`` (scalar)
- : The input key for the tunnel
-
- ``output`` (scalar)
- : The output key for the tunnel
-
- ``private`` (scalar) – since **0.100**
- : A base64-encoded private key required for WireGuard tunnels. When the
- ``systemd-networkd`` backend (v242+) is used, this can also be an
- absolute path to a file containing the private key.
-
-``keys`` (scalar or mapping)
-
-: Alternate name for the ``key`` field. See above.
-
-Examples:
-
- tunnels:
- tun0:
- mode: gre
- local: ...
- remote: ...
- keys:
- input: 1234
- output: 5678
-
- tunnels:
- tun0:
- mode: vti6
- local: ...
- remote: ...
- key: 59568549
-
- tunnels:
- wg0:
- mode: wireguard
- addresses: [...]
- peers:
- - keys:
- public: rlbInAj0qV69CysWPQY7KEBnKxpYCpaWqOs/dLevdWc=
- shared: /path/to/shared.key
- ...
- key: mNb7OIIXTdgW4khM7OFlzJ+UPs7lmcWHV7xjPgakMkQ=
-
- tunnels:
- wg0:
- mode: wireguard
- addresses: [...]
- peers:
- - keys:
- public: rlbInAj0qV69CysWPQY7KEBnKxpYCpaWqOs/dLevdWc=
- ...
- keys:
- private: /path/to/priv.key
-
-
-WireGuard specific keys:
-
-``mark`` (scalar) – since **0.100**
-
-: Firewall mark for outgoing WireGuard packets from this interface,
- optional.
-
-``port`` (scalar) – since **0.100**
-
-: UDP port to listen at or ``auto``. Optional, defaults to ``auto``.
-
-``peers`` (sequence of mappings) – since **0.100**
-
-: A list of peers, each having keys documented below.
-
-Example:
-
- tunnels:
- wg0:
- mode: wireguard
- key: /path/to/private.key
- mark: 42
- port: 5182
- peers:
- - keys:
- public: rlbInAj0qV69CysWPQY7KEBnKxpYCpaWqOs/dLevdWc=
- allowed-ips: [0.0.0.0/0, "2001:fe:ad:de:ad:be:ef:1/24"]
- keepalive: 23
- endpoint: 1.2.3.4:5
- - keys:
- public: M9nt4YujIOmNrRmpIRTmYSfMdrpvE7u6WkG8FY8WjG4=
- shared: /some/shared.key
- allowed-ips: [10.10.10.20/24]
- keepalive: 22
- endpoint: 5.4.3.2:1
-
-``endpoint`` (scalar) – since **0.100**
-
-: Remote endpoint IPv4/IPv6 address or a hostname, followed by a colon
- and a port number.
-
-``allowed-ips`` (sequence of scalars) – since **0.100**
-
-: A list of IP (v4 or v6) addresses with CIDR masks from which this peer
- is allowed to send incoming traffic and to which outgoing traffic for
- this peer is directed. The catch-all 0.0.0.0/0 may be specified for
- matching all IPv4 addresses, and ::/0 may be specified for matching
- all IPv6 addresses.
-
-``keepalive`` (scalar) – since **0.100**
-
-: An interval in seconds, between 1 and 65535 inclusive, of how often to
- send an authenticated empty packet to the peer for the purpose of
- keeping a stateful firewall or NAT mapping valid persistently. Optional.
-
-``keys`` (mapping) – since **0.100**
-
-: Define keys to use for the WireGuard peers.
-
- This field can be used as a mapping, where you can further specify the
- ``public`` and ``shared`` keys.
-
- ``public`` (scalar) – since **0.100**
- : A base64-encoded public key, required for WireGuard peers.
-
- ``shared`` (scalar) – since **0.100**
- : A base64-encoded preshared key. Optional for WireGuard peers.
- When the ``systemd-networkd`` backend (v242+) is used, this can
- also be an absolute path to a file containing the preshared key.
-
-## Properties for device type ``vlans:``
-
-``id`` (scalar)
-
-: VLAN ID, a number between 0 and 4094.
-
-``link`` (scalar)
-
-: netplan ID of the underlying device definition on which this VLAN gets
- created.
-
-Example:
-
- ethernets:
- eno1: {...}
- vlans:
- en-intra:
- id: 1
- link: eno1
- dhcp4: yes
- en-vpn:
- id: 2
- link: eno1
- addresses: ...
-
-## Properties for device type ``nm-devices:``
-
-The ``nm-devices`` device type is for internal use only and should not be used in normal configuration files. It enables a fallback mode for unsupported settings, using the ``passthrough`` mapping.
-
-
-## Backend-specific configuration parameters
-
-In addition to the other fields available to configure interfaces, some
-backends may require to record some of their own parameters in netplan,
-especially if the netplan definitions are generated automatically by the
-consumer of that backend. Currently, this is only used with ``NetworkManager``.
-
-``networkmanager`` (mapping) – since **0.99**
-
-: Keeps the NetworkManager-specific configuration parameters used by the
- daemon to recognize connections.
-
- ``name`` (scalar) – since **0.99**
- : Set the display name for the connection.
-
- ``uuid`` (scalar) – since **0.99**
- : Defines the UUID (unique identifier) for this connection, as
- generated by NetworkManager itself.
-
- ``stable-id`` (scalar) – since **0.99**
- : Defines the stable ID (a different form of a connection name) used
- by NetworkManager in case the name of the connection might otherwise
- change, such as when sharing connections between users.
-
- ``device`` (scalar) – since **0.99**
- : Defines the interface name for which this connection applies.
-
- ``passthrough`` (mapping) – since **0.102**
- : Can be used as a fallback mechanism to missing keyfile settings.
-
-## Examples
-Configure an ethernet device with networkd, identified by its name, and enable
-DHCP:
-
- network:
- version: 2
- ethernets:
- eno1:
- dhcp4: true
-
-This is an example of a static-configured interface with multiple IPv4 addresses
-and multiple gateways with networkd, with equal route metric levels, and static
-DNS nameservers (Google DNS for this example):
-
- network:
- version: 2
- renderer: networkd
- ethernets:
- eno1:
- addresses:
- - 10.0.0.10/24
- - 11.0.0.11/24
- nameservers:
- addresses:
- - 8.8.8.8
- - 8.8.4.4
- routes:
- - to: 0.0.0.0/0
- via: 10.0.0.1
- metric: 100
- - to: 0.0.0.0/0
- via: 11.0.0.1
- metric: 100
-
-This is a complex example which shows most available features:
-
- network:
- version: 2
- # if specified, can only realistically have that value, as networkd cannot
- # render wifi/3G.
- renderer: NetworkManager
- ethernets:
- # opaque ID for physical interfaces, only referred to by other stanzas
- id0:
- match:
- macaddress: 00:11:22:33:44:55
- wakeonlan: true
- dhcp4: true
- addresses:
- - 192.168.14.2/24
- - 192.168.14.3/24
- - "2001:1::1/64"
- nameservers:
- search: [foo.local, bar.local]
- addresses: [8.8.8.8]
- routes:
- - to: default
- via: 192.168.14.1
- - to: default
- via: "2001:1::2"
- - to: 0.0.0.0/0
- via: 11.0.0.1
- table: 70
- on-link: true
- metric: 3
- routing-policy:
- - to: 10.0.0.0/8
- from: 192.168.14.2/24
- table: 70
- priority: 100
- - to: 20.0.0.0/8
- from: 192.168.14.3/24
- table: 70
- priority: 50
- # only networkd can render on-link routes and routing policies
- renderer: networkd
- lom:
- match:
- driver: ixgbe
- # you are responsible for setting tight enough match rules
- # that only match one device if you use set-name
- set-name: lom1
- dhcp6: true
- switchports:
- # all cards on second PCI bus unconfigured by
- # themselves, will be added to br0 below
- match:
- name: enp2*
- mtu: 1280
- wifis:
- all-wlans:
- # useful on a system where you know there is
- # only ever going to be one device
- match: {}
- access-points:
- "Joe's home":
- # mode defaults to "infrastructure" (client)
- password: "s3kr1t"
- # this creates an AP on wlp1s0 using hostapd
- # no match rules, thus the ID is the interface name
- wlp1s0:
- access-points:
- "guest":
- mode: ap
- # no WPA config implies default of open
- bridges:
- # the key name is the name for virtual (created) interfaces
- # no match: and set-name: allowed
- br0:
- # IDs of the components; switchports expands into multiple interfaces
- interfaces: [wlp1s0, switchports]
- dhcp4: true
-
-<!--- vim: ft=markdown
--->
+netplan-yaml.md \ No newline at end of file
diff --git a/doc/reference.md b/doc/reference.md
new file mode 100644
index 0000000..ea6ac85
--- /dev/null
+++ b/doc/reference.md
@@ -0,0 +1,29 @@
+# Reference
+
+Netplan's configuration files use the
+[YAML](<http://yaml.org/spec/1.1/current.html>) format. All
+`/{lib,etc,run}/netplan/*.yaml` are considered.
+
+The top-level node in a netplan configuration file is a ``network:`` mapping
+that contains ``version: 2`` (the YAML currently being used by curtin, MaaS,
+etc. is version 1), and then device definitions grouped by their type, such as
+``ethernets:``, ``modems:``, ``wifis:``, or ``bridges:``. These are the types
+that our renderer can understand and are supported by our backends.
+
+```{toctree}
+---
+maxdepth: 1
+---
+netplan-yaml
+```
+
+## API specification
+`libnetplan` is a component of the Netplan. project that contains the logic for
+data parsing, validation and generation. It is build as a dynamic `.so` library
+that can be used from different binaries (like Netplan’s `generate`,
+`netplan-dbus`, the `netplan apply/try/get/set/...` CLI or via the corresponding
+Python bindings or external applications like the NetworkManager, using the
+Netplan backend).
+
+* [API reference](https://discourse.ubuntu.com/t/29106)
+ – C API and Python bindings for libnetplan
diff --git a/doc/requirements.txt b/doc/requirements.txt
new file mode 100644
index 0000000..0e59553
--- /dev/null
+++ b/doc/requirements.txt
@@ -0,0 +1,7 @@
+sphinx
+furo
+sphinx-design
+sphinxcontrib-spelling
+sphinx-autobuild
+pyenchant
+myst-parser
diff --git a/doc/spelling_wordlist.txt b/doc/spelling_wordlist.txt
new file mode 100644
index 0000000..561c25b
--- /dev/null
+++ b/doc/spelling_wordlist.txt
@@ -0,0 +1,76 @@
+abc
+anycast
+backend
+Backend
+backends
+blackhole
+bootup
+cancelled
+checksumming
+config
+ConnectX
+curtin
+dbus
+DBus
+dhcp
+dmesg
+engreen
+enred
+ethernet
+failover
+Fi
+hostname
+ifnames
+initramfs
+ip
+ipip
+iptables
+ipv
+IPv
+keyfile
+libnetplan
+libvirt
+libvirtd
+loopback
+lxd
+MaaS
+Mellanox
+miimon
+multicast
+nameservers
+nat
+natively
+netplan
+networkd
+NetworkManager
+openvswitch
+ovs
+pre
+prefixtlb
+preshared
+ra
+SSIDs
+stateful
+statelessly
+subnet
+subtree
+systemd
+tcp
+timesyncd
+tlb
+tunnelled
+tx
+udev
+unencrypted
+unicast
+veth
+vlan
+vsctl
+vSwitch
+Wi
+wifi
+wifis
+wpa
+wpasupplicant
+xresolve
+yaml
diff --git a/doc/tutorials.md b/doc/tutorials.md
new file mode 100644
index 0000000..1a5123c
--- /dev/null
+++ b/doc/tutorials.md
@@ -0,0 +1,15 @@
+# Tutorials
+
+Here you can find some external blog posts, describing the usage of Netplan on different systems.
+
+## Ubuntu 22.04 LTS
+* <https://vitux.com/how-to-configure-networking-with-netplan-on-ubuntu/>
+
+## Ubuntu 20.04 LTS
+* <https://linux-on-z.blogspot.com/p/using-netplan-on-ibm-z.html>
+* <https://linuxconfig.org/netplan-network-configuration-tutorial-for-beginners>
+* <https://www.serverlab.ca/tutorials/linux/administration-linux/how-to-configure-networking-in-ubuntu-20-04-with-netplan/>
+
+## Ubuntu 18.04 LTS
+* <https://www.linux.com/topic/distributions/how-use-netplan-network-configuration-tool-linux/>
+* <https://linuxhint.com/install_netplan_ubuntu/> \ No newline at end of file
diff --git a/examples/infiniband.yaml b/examples/infiniband.yaml
new file mode 100644
index 0000000..e0cfed4
--- /dev/null
+++ b/examples/infiniband.yaml
@@ -0,0 +1,9 @@
+network:
+ version: 2
+ renderer: networkd
+ ethernets:
+ ib0:
+ match:
+ macaddress: "11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00"
+ dhcp4: true
+ infiniband-mode: "connected"
diff --git a/examples/meson.build b/examples/meson.build
new file mode 100644
index 0000000..40677e8
--- /dev/null
+++ b/examples/meson.build
@@ -0,0 +1,6 @@
+#https://mesonbuild.com/FAQ.html#but-i-really-want-to-use-wildcards
+c = run_command(find, '-name', '*.yaml', check: true)
+examples = c.stdout().strip().split('\n')
+install_data(
+ examples,
+ install_dir: join_paths(get_option('datadir'), 'doc', 'netplan', 'examples'))
diff --git a/examples/vrf.yaml b/examples/vrf.yaml
new file mode 100644
index 0000000..585f5f9
--- /dev/null
+++ b/examples/vrf.yaml
@@ -0,0 +1,18 @@
+network:
+ renderer: networkd
+ vrfs:
+ vrf1005:
+ table: 1005
+ interfaces:
+ - br1
+ - br1005
+ routes:
+ - to: default
+ via: 10.10.10.4
+ routing-policy:
+ - from: 10.10.10.42
+ bridges:
+ br1:
+ interfaces: []
+ br1005:
+ interfaces: []
diff --git a/examples/vxlan.yaml b/examples/vxlan.yaml
new file mode 100644
index 0000000..21951f0
--- /dev/null
+++ b/examples/vxlan.yaml
@@ -0,0 +1,40 @@
+network:
+ renderer: networkd
+ ethernets:
+ lo:
+ addresses:
+ - 192.168.10.10/32
+ vrfs:
+ vrf1005:
+ table: 1005
+ interfaces:
+ - br1
+ - br1005
+ bridges:
+ br1:
+ interfaces:
+ - vxlan1
+ br1005:
+ interfaces:
+ - vxlan1005
+ tunnels:
+ vxlan1005:
+ mode: vxlan
+ id: 1005
+ link: lo
+ mtu: 8950
+ accept-ra: no
+ neigh-suppress: true
+ mac-learning: false
+ port: 4789
+ local: 192.168.10.10
+ vxlan1:
+ mode: vxlan
+ id: 1
+ link: lo
+ mtu: 8950
+ accept-ra: no
+ neigh-suppress: true
+ mac-learning: false
+ port: 4789
+ local: 192.168.10.10
diff --git a/examples/wireless.yaml b/examples/wireless.yaml
index a7d82ad..4215931 100644
--- a/examples/wireless.yaml
+++ b/examples/wireless.yaml
@@ -3,6 +3,7 @@ network:
renderer: networkd
wifis:
wlp2s0b1:
+ regulatory-domain: "GB"
dhcp4: no
dhcp6: no
addresses: [192.168.0.21/24]
diff --git a/features_h_generator.sh b/features_h_generator.sh
new file mode 100755
index 0000000..ed9c3cc
--- /dev/null
+++ b/features_h_generator.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+OUTPUT=src/_features.h
+INPUT=src/[^_]*.[hc]
+printf "#include <stddef.h>\nstatic const char *feature_flags[] __attribute__((__unused__)) = {\n" > $OUTPUT
+awk 'match ($0, /netplan-feature:.*/ ) { $0=substr($0, RSTART, RLENGTH); print "\""$2"\"," }' $INPUT >> $OUTPUT
+echo "NULL, };" >> $OUTPUT
diff --git a/features_py_generator.sh b/features_py_generator.sh
new file mode 100755
index 0000000..ecb42af
--- /dev/null
+++ b/features_py_generator.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+OUTPUT=netplan/_features.py
+INPUT=src/[^_]*.[hc]
+echo "# Generated file" > $OUTPUT
+echo "NETPLAN_FEATURE_FLAGS = [" >> $OUTPUT
+awk 'match ($0, /netplan-feature:.*/ ) { $0=substr($0, RSTART, RLENGTH); print " \""$2"\"," }' $INPUT >> $OUTPUT
+echo "]" >> $OUTPUT
diff --git a/include/meson.build b/include/meson.build
new file mode 100644
index 0000000..dd73482
--- /dev/null
+++ b/include/meson.build
@@ -0,0 +1,2 @@
+install_headers('netplan.h', 'parse.h', 'parse-nm.h', 'util.h',
+ subdir: 'netplan')
diff --git a/include/netplan.h b/include/netplan.h
index ac444a9..d595927 100644
--- a/include/netplan.h
+++ b/include/netplan.h
@@ -17,11 +17,16 @@
#pragma once
#include <glib.h>
+#include <stdlib.h>
#define NETPLAN_PUBLIC __attribute__ ((visibility("default")))
#define NETPLAN_INTERNAL __attribute__ ((visibility("default")))
#define NETPLAN_ABI __attribute__ ((visibility("default")))
+#define NETPLAN_DEPRECATED __attribute__ ((deprecated))
+
+#define NETPLAN_BUFFER_TOO_SMALL -2
+
/**
* Represent a configuration stanza
*/
@@ -54,6 +59,26 @@ netplan_state_get_netdefs_size(const NetplanState* np_state);
NETPLAN_PUBLIC NetplanNetDefinition*
netplan_state_get_netdef(const NetplanState* np_state, const char* id);
+/* Write the selected yaml file. All definitions that originate from this file,
+ * as well as those without any given origin, are written to it.
+ */
+NETPLAN_PUBLIC gboolean
+netplan_state_write_yaml_file(
+ const NetplanState* np_state,
+ const char* filename,
+ const char* rootdir,
+ GError** error);
+
+/* Update all the YAML files that were used to create this state.
+ * The definitions without clear origin are written to @default_filename.
+ */
+NETPLAN_PUBLIC gboolean
+netplan_state_update_yaml_hierarchy(
+ const NetplanState* np_state,
+ const char* default_filename,
+ const char* rootdir,
+ GError** error);
+
/* Dump the whole yaml configuration into the given file, regardless of the origin
* of each definition.
*/
@@ -70,7 +95,12 @@ netplan_netdef_write_yaml(
const char* rootdir,
GError** error);
-NETPLAN_PUBLIC const char *
+NETPLAN_PUBLIC ssize_t
+netplan_netdef_get_filepath(const NetplanNetDefinition* netdef, char* out_buffer, size_t out_buffer_size);
+
+/********** Old API below this ***********/
+
+NETPLAN_DEPRECATED NETPLAN_PUBLIC const char *
netplan_netdef_get_filename(const NetplanNetDefinition* netdef);
NETPLAN_PUBLIC void
diff --git a/include/parse.h b/include/parse.h
index 342403d..f15dff1 100644
--- a/include/parse.h
+++ b/include/parse.h
@@ -36,6 +36,7 @@ typedef enum {
NETPLAN_DEF_TYPE_VLAN,
NETPLAN_DEF_TYPE_TUNNEL,
NETPLAN_DEF_TYPE_PORT,
+ NETPLAN_DEF_TYPE_VRF,
/* Type fallback/passthrough */
NETPLAN_DEF_TYPE_NM,
NETPLAN_DEF_TYPE_MAX_
@@ -62,6 +63,12 @@ netplan_parser_load_yaml(NetplanParser* npp, const char* filename, GError** erro
NETPLAN_PUBLIC gboolean
netplan_state_import_parser_results(NetplanState* np_state, NetplanParser* npp, GError** error);
+NETPLAN_PUBLIC gboolean
+netplan_parser_load_yaml_from_fd(NetplanParser* npp, int input_fd, GError** error);
+
+NETPLAN_PUBLIC gboolean
+netplan_parser_load_nullable_fields(NetplanParser* npp, int input_fd, GError** error);
+
/********** Old API below this ***********/
NETPLAN_PUBLIC gboolean
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..3ab0776
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,125 @@
+project('netplan', 'c',
+ version: '0.105',
+ license: 'GPL3',
+ default_options: [
+ 'c_std=c99',
+ 'warning_level=1',
+ 'werror=true',
+ ],
+ meson_version: '>= 0.61.0',
+)
+
+glib = dependency('glib-2.0')
+gio = dependency('gio-2.0')
+yaml = dependency('yaml-0.1')
+uuid = dependency('uuid')
+libsystemd = dependency('libsystemd')
+
+systemd = dependency('systemd')
+completions = dependency('bash-completion')
+systemd_generator_dir = systemd.get_variable(pkgconfig: 'systemdsystemgeneratordir')
+bash_completions_dir = completions.get_variable(pkgconfig: 'completionsdir', default_value: '/etc/bash_completion.d')
+
+# Order: Fedora/Mageia/openSUSE || Debian/Ubuntu
+pyflakes = find_program('pyflakes-3', 'pyflakes3', required: false)
+pycodestyle = find_program('pycodestyle-3', 'pycodestyle', 'pep8', required: false)
+nose = find_program('nosetests-3', 'nosetests3')
+pandoc = find_program('pandoc', required: false)
+find = find_program('find')
+
+add_project_arguments(
+ '-DSBINDIR="' + join_paths(get_option('prefix'), get_option('sbindir')) + '"',
+ '-D_XOPEN_SOURCE=700',
+ language: 'c')
+
+message('Generating the _features.[py|h] code')
+#XXX: this is ugly as it produces artifacts in the source directory
+run_command('features_h_generator.sh', check: true)
+run_command('features_py_generator.sh', check: true)
+
+inc = include_directories('include')
+subdir('include')
+subdir('src')
+subdir('dbus')
+subdir('netplan')
+subdir('examples')
+subdir('doc')
+
+pkg_mod = import('pkgconfig')
+pkg_mod.generate(
+ libraries: libnetplan,
+ subdirs: ['netplan'],
+ name: 'libnetplan',
+ filebase: 'netplan',
+ description: 'YAML network configuration abstraction runtime library')
+
+install_data(
+ 'netplan.completions',
+ rename: 'netplan',
+ install_dir: bash_completions_dir)
+
+###########
+# Testing #
+###########
+test_env = [
+ 'PYTHONPATH=' + meson.current_source_dir(),
+ 'LD_LIBRARY_PATH=' + join_paths(meson.current_build_dir(), 'src'),
+ 'NETPLAN_GENERATE_PATH=' + join_paths(meson.current_build_dir(), 'src', 'generate'),
+ 'NETPLAN_DBUS_CMD=' + join_paths(meson.current_build_dir(), 'dbus', 'netplan-dbus'),
+]
+#FIXME: exclude doc/env/
+test('linting',
+ pyflakes,
+ args: [meson.current_source_dir()])
+test('codestyle',
+ pycodestyle,
+ args: ['--max-line-length=130', '--exclude=doc/env', meson.current_source_dir()])
+test('documentation',
+ find_program('tests/validate_docs.sh'),
+ workdir: meson.current_source_dir())
+test('legacy-tests',
+ find_program('tests/cli.py'),
+ timeout: 120,
+ env: test_env)
+#TODO: split out dbus tests into own test() instance, to run in parallel
+test('unit-tests',
+ nose,
+ args: ['-v', '--with-coverage', meson.current_source_dir()],
+ timeout: 600,
+ env: test_env)
+
+#TODO: the coverage section should probably be cleaned up a bit
+if get_option('b_coverage')
+ message('Find coverage reports in <BUILDDIR>/meson-logs/coveragereport[-py]/')
+ # Using gcovr instead of lcov/gcov.
+ # The 'ninja coverage' command will produce the html/txt reports for C implicitly
+ #lcov = find_program('lcov')
+ #gcov = find_program('gcov')
+ #genhtml = find_program('genhtml')
+ gcovr = find_program('gcovr')
+ ninja = find_program('ninja')
+ grep = find_program('grep')
+ pycoverage = find_program('python3-coverage')
+ test('coverage-c-output',
+ find_program('ninja'),
+ args: ['-C', meson.current_build_dir(), 'coverage'],
+ priority: -90, # run before 'coverage-c'
+ is_parallel: false)
+ test('coverage-c',
+ grep,
+ args: ['^TOTAL.*100%$', join_paths(meson.current_build_dir(), 'meson-logs', 'coverage.txt')],
+ priority: -99, # run last
+ is_parallel: false)
+ test('coverage-py-output',
+ pycoverage,
+ args: ['html', '-d', join_paths(meson.current_build_dir(),
+ 'meson-logs', 'coveragereport-py'), '--omit=/usr*'],
+ priority: -99, # run last
+ is_parallel: false)
+ test('coverage-py',
+ pycoverage,
+ args: ['report', '--omit=/usr*', '--show-missing', '--fail-under=100'],
+ priority: -99, # run last
+ is_parallel: false)
+endif
+
diff --git a/netplan/cli/commands/apply.py b/netplan/cli/commands/apply.py
index d481184..ae28130 100644
--- a/netplan/cli/commands/apply.py
+++ b/netplan/cli/commands/apply.py
@@ -156,13 +156,13 @@ class NetplanApply(utils.NetplanCommand):
if not restart_nm and old_files_nm:
restart_nm = True
+ # Running 'systemctl daemon-reload' will re-run the netplan systemd generator,
+ # so let's make sure we only run it iff we're willing to run 'netplan generate'
+ if run_generate:
+ utils.systemctl_daemon_reload()
# stop backends
if restart_networkd:
logging.debug('netplan generated networkd configuration changed, reloading networkd')
- # Running 'systemctl daemon-reload' will re-run the netplan systemd generator,
- # so let's make sure we only run it iff we're willing to run 'netplan generate'
- if run_generate:
- utils.systemctl_daemon_reload()
# Clean up any old netplan related OVS ports/bonds/bridges, if applicable
NetplanApply.process_ovs_cleanup(config_manager, old_files_ovs, restart_ovs, exit_on_error)
wpa_services = ['netplan-wpa-*.service']
@@ -221,13 +221,22 @@ class NetplanApply(utils.NetplanCommand):
'/sys/class/net/' + device],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
+ subprocess.check_call(['udevadm', 'test',
+ '/sys/class/net/' + device],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
logging.debug('Ignoring device without syspath: %s', device)
+ devices_after_udev = netifaces.interfaces()
# apply some more changes manually
for iface, settings in changes.items():
# rename non-critical network interfaces
- if settings.get('name'):
+ new_name = settings.get('name')
+ if new_name:
+ if iface in devices and new_name in devices_after_udev:
+ logging.debug('Interface rename {} -> {} already happened.'.format(iface, new_name))
+ continue # re-name already happened via 'udevadm test'
# bring down the interface, using its current (matched) interface name
subprocess.check_call(['ip', 'link', 'set', 'dev', iface, 'down'],
stdout=subprocess.DEVNULL,
@@ -239,11 +248,17 @@ class NetplanApply(utils.NetplanCommand):
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
+ # Reloading of udev rules happens during 'netplan generate' already
+ # subprocess.check_call(['udevadm', 'control', '--reload-rules'])
+ subprocess.check_call(['udevadm', 'trigger', '--attr-match=subsystem=net'])
subprocess.check_call(['udevadm', 'settle'])
# apply any SR-IOV related changes, if applicable
NetplanApply.process_sriov_config(config_manager, exit_on_error)
+ # (re)set global regulatory domain
+ if os.path.exists('/run/systemd/system/netplan-regdom.service'):
+ utils.systemctl('start', ['netplan-regdom.service'])
# (re)start backends
if restart_networkd:
netplan_wpa = [os.path.basename(f) for f in glob.glob('/run/systemd/system/*.wants/netplan-wpa-*.service')]
@@ -252,7 +267,13 @@ class NetplanApply(utils.NetplanCommand):
if not f.endswith('/' + OVS_CLEANUP_SERVICE)]
# Run 'systemctl start' command synchronously, to avoid race conditions
# with 'oneshot' systemd service units, e.g. netplan-ovs-*.service.
- utils.networkctl_reconfigure(utils.networkd_interfaces())
+ try:
+ utils.networkctl_reload()
+ utils.networkctl_reconfigure(utils.networkd_interfaces())
+ except subprocess.CalledProcessError:
+ # (re-)start systemd-networkd if it is not running, yet
+ logging.warning('Falling back to a hard restart of systemd-networkd.service')
+ utils.systemctl('restart', ['systemd-networkd.service'], sync=True)
# 1st: execute OVS cleanup, to avoid races while applying OVS config
utils.systemctl('start', [OVS_CLEANUP_SERVICE], sync=True)
# 2nd: start all other services
@@ -262,6 +283,9 @@ class NetplanApply(utils.NetplanCommand):
# new, non netplan-* connection profiles, using the existing IPs.
for iface in utils.nm_interfaces(restart_nm_glob, devices):
utils.ip_addr_flush(iface)
+ # clear NM state, especially the [device].managed=true config, as that might have been
+ # re-set via an udev rule setting "NM_UNMANAGED=1"
+ subprocess.check_call(['rm', '-rf', '/run/NetworkManager/devices'])
utils.systemctl_network_manager('start', sync=sync)
if sync:
# wait up to 2 sec for 'STATE=connected (site/local-only)' or
@@ -317,45 +341,41 @@ class NetplanApply(utils.NetplanCommand):
return dropped_interfaces
@staticmethod
- def process_link_changes(interfaces, config_manager): # pragma: nocover (covered in autopkgtest)
+ def process_link_changes(interfaces, config_manager: ConfigManager): # pragma: nocover (covered in autopkgtest)
"""
Go through the pending changes and pick what needs special handling.
Only applies to non-critical interfaces which can be safely updated.
"""
changes = {}
- phys = dict(config_manager.physical_interfaces)
composite_interfaces = [config_manager.bridges, config_manager.bonds]
# Find physical interfaces which need a rename
# But do not rename virtual interfaces
- for phy, settings in phys.items():
- if not settings or not isinstance(settings, dict):
- continue # Skip special values, like "renderer: ..."
- newname = settings.get('set-name')
+ for netdef in config_manager.physical_interfaces.values():
+ newname = netdef.set_name
if not newname:
continue # Skip if no new name needs to be set
- match = settings.get('match')
- if not match:
+ if not netdef.has_match:
continue # Skip if no match for current name is given
- if NetplanApply.is_composite_member(composite_interfaces, phy):
- logging.debug('Skipping composite member {}'.format(phy))
+ if NetplanApply.is_composite_member(composite_interfaces, netdef.id):
+ logging.debug('Skipping composite member {}'.format(netdef.id))
# do not rename members of virtual devices. MAC addresses
# may be the same for all interface members.
continue
# Find current name of the interface, according to match conditions and globs (name, mac, driver)
- current_iface_name = utils.find_matching_iface(interfaces, match)
+ current_iface_name = utils.find_matching_iface(interfaces, netdef)
if not current_iface_name:
- logging.warning('Cannot find unique matching interface for {}: {}'.format(phy, match))
+ logging.warning('Cannot find unique matching interface for {}'.format(netdef.id))
continue
if current_iface_name == newname:
# Skip interface if it already has the correct name
logging.debug('Skipping correctly named interface: {}'.format(newname))
continue
- if settings.get('critical', False):
+ if netdef.critical:
# Skip interfaces defined as critical, as we should not take them down in order to rename
logging.warning('Cannot rename {} ({} -> {}) at runtime (needs reboot), due to being critical'
- .format(phy, current_iface_name, newname))
+ .format(netdef.id, current_iface_name, newname))
continue
# record the interface rename change
@@ -368,7 +388,7 @@ class NetplanApply(utils.NetplanCommand):
def process_sriov_config(config_manager, exit_on_error=True): # pragma: nocover (covered in autopkgtest)
try:
apply_sriov_config(config_manager)
- except (ConfigurationError, RuntimeError) as e:
+ except utils.config_errors as e:
logging.error(str(e))
if exit_on_error:
sys.exit(1)
diff --git a/netplan/cli/commands/ip.py b/netplan/cli/commands/ip.py
index b7a7f29..9a5d99f 100644
--- a/netplan/cli/commands/ip.py
+++ b/netplan/cli/commands/ip.py
@@ -31,7 +31,7 @@ lease_path = {
'method': 'ifindex',
},
'NetworkManager': {
- 'pattern': 'var/lib/NetworkManager/dhclient-{lease_id}-{interface}.lease',
+ 'pattern': 'var/lib/NetworkManager/internal-{lease_id}-{interface}.lease',
'method': 'nm_connection',
},
}
@@ -90,21 +90,19 @@ class NetplanIpLeases(utils.NetplanCommand):
logging.debug('Cannot read file %s: %s', ifindex_f, str(e))
raise
- def lease_method_nm_connection(): # pragma: nocover (covered in autopkgtest)
+ def lease_method_nm_connection():
# FIXME: handle older versions of NM where 'nmcli dev show' doesn't exist
try:
- nmcli_dev_out = subprocess.Popen(['nmcli', 'dev', 'show', self.interface],
- env={'LC_ALL': 'C'},
- stdout=subprocess.PIPE)
- for line in nmcli_dev_out.stdout:
- line = line.decode('utf-8')
+ nmcli_dev_out = subprocess.check_output(['nmcli', 'dev', 'show', self.interface],
+ env=dict(LC_ALL='C', PATH=os.environ.get('PATH', os.defpath)),
+ universal_newlines=True)
+ for line in nmcli_dev_out.splitlines():
if 'GENERAL.CONNECTION' in line:
conn_id = line.split(':')[1].rstrip().strip()
- nmcli_con_out = subprocess.Popen(['nmcli', 'con', 'show', 'id', conn_id],
- env={'LC_ALL': 'C'},
- stdout=subprocess.PIPE)
- for line in nmcli_con_out.stdout:
- line = line.decode('utf-8')
+ nmcli_con_out = subprocess.check_output(['nmcli', 'con', 'show', 'id', conn_id],
+ env=dict(LC_ALL='C', PATH=os.environ.get('PATH', os.defpath)),
+ universal_newlines=True)
+ for line in nmcli_con_out.splitlines():
if 'connection.uuid' in line:
return line.split(':')[1].rstrip().strip()
except Exception as e:
@@ -122,10 +120,15 @@ class NetplanIpLeases(utils.NetplanCommand):
# we'll rely on open() throwing an error.
# This might happen if networkd doesn't use DHCP for the interface,
# for instance.
- with open(os.path.join('/',
- os.path.abspath(self.root_dir) if self.root_dir else "",
- lease_pattern.format(interface=self.interface,
- lease_id=lease_id))) as f:
+ path = os.path.join('/',
+ os.path.abspath(self.root_dir) if self.root_dir else "",
+ lease_pattern.format(interface=self.interface,
+ lease_id=lease_id))
+ # Fallback to 'dhclient' if no lease of NetworkManager's
+ # internal DHCP client is found
+ if not os.path.isfile(path):
+ path = path.replace('NetworkManager/internal-', 'NetworkManager/dhclient-')
+ with open(path) as f:
for line in f.readlines():
print(line.rstrip())
except Exception as e:
diff --git a/netplan/cli/commands/set.py b/netplan/cli/commands/set.py
index f113f71..ee82fa0 100644
--- a/netplan/cli/commands/set.py
+++ b/netplan/cli/commands/set.py
@@ -17,19 +17,14 @@
'''netplan set command line'''
-import os
-import yaml
import tempfile
import re
-import logging
-import shutil
-import glob
+import io
from netplan.cli.utils import NetplanCommand
import netplan.libnetplan as libnetplan
-from netplan.configmanager import ConfigManager
-FALLBACK_HINT = '70-netplan-set'
+FALLBACK_FILENAME = '70-netplan-set.yaml'
GLOBAL_KEYS = ['renderer', 'version']
@@ -54,151 +49,37 @@ class NetplanSet(NetplanCommand):
self.parse_args()
self.run_command()
- def is_emtpy_yaml(self, tree):
- if isinstance(tree, dict) and list(tree.keys()) == ['network'] and tree['network'] is None:
- return True
- return False
-
- def split_tree_by_hint(self, set_tree) -> (str, dict):
- network = set_tree.get('network', {})
- # A mapping of 'origin-hint' -> YAML tree (one subtree per netdef)
- subtrees = dict()
- for devtype in network:
- if devtype in GLOBAL_KEYS:
- continue # special handling of global keys down below
- devtype_content = network.get(devtype, [])
- # Special case: removal of a whole devtype.
- # We replace the devtype null node with a dict of all defined netdefs
- # set to None.
- if devtype_content is None:
- devtype_content = {dev: None for dev in libnetplan.netplan_get_ids_for_devtype(devtype, self.root_dir)}
- network[devtype] = devtype_content
- for netdef in devtype_content:
- hint = FALLBACK_HINT
- filename = libnetplan.netplan_get_filename_by_id(netdef, self.root_dir)
- if filename:
- hint = os.path.basename(filename)[:-5] # strip prefix and .yaml
- netdef_tree = {'network': {devtype: {netdef: network.get(devtype).get(netdef)}}}
- # Merge all netdef trees which are going to be written to the same file/hint
- subtrees[hint] = self.merge(subtrees.get(hint, {}), netdef_tree)
-
- # Merge GLOBAL_KEYS into one of the available subtrees
- # Write to same file (if only one hint/subtree is available)
- # Write to FALLBACK_HINT if multiple hints/subtrees are available, as we do not know where it is supposed to go
- if any(network.get(key) for key in GLOBAL_KEYS):
- # Write to the same file, if we have only one file-hint or to FALLBACK_HINT otherwise
- hint = list(subtrees)[0] if len(subtrees) == 1 else FALLBACK_HINT
- for key in GLOBAL_KEYS:
- tree = {'network': {key: network.get(key)}}
- subtrees[hint] = self.merge(subtrees.get(hint, {}), tree)
-
- # return a list of (str:hint, dict:subtree) tuples
- return subtrees.items()
-
def command_set(self):
if self.origin_hint is not None and len(self.origin_hint) == 0:
raise Exception('Invalid/empty origin-hint')
+ if self.origin_hint:
+ filename = '.'.join((self.origin_hint, 'yaml'))
+ else:
+ filename = None
split = self.key_value.split('=', 1)
if len(split) != 2:
raise Exception('Invalid value specified')
- key, value = split
- set_tree = self.parse_key(key, yaml.safe_load(value))
-
- # special case: clear all YAML (or a specific hint file) if "network=null" is set
- if self.is_emtpy_yaml(set_tree):
- path = os.path.join('etc', 'netplan')
- if self.origin_hint: # clear specific hint file, it it does exist
- hint_path = os.path.join(self.root_dir, path, self.origin_hint + '.yaml')
- if os.path.isfile(hint_path):
- os.remove(hint_path)
- else: # clear all YAML files in <ROOT_DIR>/etc/netplan/*.yaml
- yaml_files = glob.glob(os.path.join(self.root_dir, path, '*.yaml'))
- for f in yaml_files:
- os.remove(f)
- return
-
- hints = [(self.origin_hint, set_tree)]
- # Override YAML config in each individual netdef file if origin-hint is not set
- if self.origin_hint is None:
- hints = self.split_tree_by_hint(set_tree)
- for hint, subtree in hints:
- self.write_file(subtree, hint + '.yaml', self.root_dir)
-
- def parse_key(self, key, value):
- # The 'network.' prefix is optional for netsted keys, its always assumed to be there
- if not key.startswith('network.') and not key == 'network':
- key = 'network.' + key
- # Split at '.' but not at '\.' via negative lookbehind expression
- split = re.split(r'(?<!\\)\.', key)
- tree = {}
- i = 1
- t = tree
- for part in split:
- part = part.replace('\\.', '.') # Unescape interface-ids, containing dots
- val = {}
- if i == len(split):
- val = value
- t = t.setdefault(part, val)
- i += 1
- return tree
-
- def merge(self, a, b, path=None):
- """
- Merges tree/dict 'b' into tree/dict 'a'
- """
- if path is None:
- path = []
- for key in b:
- if key in a:
- if isinstance(a[key], dict) and isinstance(b[key], dict):
- self.merge(a[key], b[key], path + [str(key)])
- elif b[key] is None:
- del a[key]
- else:
- # Overwrite existing key with new key/value from 'set' command
- a[key] = b[key]
- else:
- a[key] = b[key]
- return a
-
- def write_file(self, set_tree, name, rootdir='/'):
- tmproot = tempfile.TemporaryDirectory(prefix='netplan-set_')
- path = os.path.join('etc', 'netplan')
- os.makedirs(os.path.join(tmproot.name, path))
-
- config = {'network': {}}
- absp = os.path.join(rootdir, path, name)
- # check stat(absp), as we don't care about empty hint files
- if os.path.isfile(absp) and os.stat(absp).st_size > 0:
- with open(absp, 'r') as f:
- c = yaml.safe_load(f)
- if c is not None: # ignore empty file, even if it contains whitespace
- config = c
-
- new_tree = self.merge(config, set_tree)
- stripped = ConfigManager.strip_tree(new_tree)
- logging.debug('Writing file {}: {}'.format(name, stripped))
- if 'network' in stripped and list(stripped['network'].keys()) == ['version']:
- # Clear file if only 'network: {version: 2}' is left
- logging.debug('Empty YAML, deleting file {}'.format(absp))
- if os.path.isfile(absp):
- os.remove(absp)
- elif 'network' in stripped:
- tmpp = os.path.join(tmproot.name, path, name)
- with open(tmpp, 'w+') as f:
- new_yaml = yaml.dump(stripped, indent=2, default_flow_style=False)
- f.write(new_yaml)
- # Validate the newly created file, by parsing it via libnetplan
- libnetplan.netplan_parse(tmpp)
- # Valid, move it to final destination
- shutil.copy2(tmpp, absp)
- os.remove(tmpp)
- elif stripped == {}:
- # Clear file (if it exists) if the last/only key got removed
- # do nothing otherwise
- logging.debug('Removed last key from YAML, deleting file {}'.format(absp))
- if os.path.isfile(absp):
- os.remove(absp)
- else: # pragma nocover
- raise Exception('Invalid input: {}'.format(set_tree))
+ key, value = split
+ if not key.startswith('network'):
+ key = '.'.join(('network', key))
+
+ # Split the string into a list on the dot separators, and unescape the remaining dots
+ yaml_path = [s.replace(r'\.', '.') for s in re.split(r'(?<!\\)\.', key)]
+
+ parser = libnetplan.Parser()
+ with tempfile.TemporaryFile() as tmp:
+ libnetplan.create_yaml_patch(yaml_path, value, tmp)
+ tmp.flush()
+ tmp.seek(0, io.SEEK_SET)
+ parser.load_nullable_fields(tmp)
+ parser.load_yaml_hierarchy(self.root_dir)
+ tmp.seek(0, io.SEEK_SET)
+ parser.load_yaml(tmp)
+
+ state = libnetplan.State()
+ state.import_parser_results(parser)
+ if self.origin_hint:
+ state.write_yaml_file(filename, self.root_dir)
+ else:
+ state.update_yaml_hierarchy(FALLBACK_FILENAME, self.root_dir)
diff --git a/netplan/cli/commands/try_command.py b/netplan/cli/commands/try_command.py
index 9796bd8..1a433b7 100644
--- a/netplan/cli/commands/try_command.py
+++ b/netplan/cli/commands/try_command.py
@@ -17,18 +17,19 @@
'''netplan try command line'''
+import logging
import os
import time
import shutil
import signal
import sys
import tempfile
-import logging
from netplan.configmanager import ConfigManager
import netplan.cli.utils as utils
from netplan.cli.commands.apply import NetplanApply
import netplan.terminal
+import netplan.libnetplan as libnetplan
# Keep a timeout long enough to allow the network to converge, 60 seconds may
# be slightly short given some complex configs, i.e. if STP must reconverge.
@@ -44,6 +45,7 @@ class NetplanTry(utils.NetplanCommand):
leaf=True)
self.configuration_changed = False
self.new_interfaces = None
+ self.config_file = None
self._config_manager = None
self.t_settings = None
self.t = None
@@ -53,7 +55,7 @@ class NetplanTry(utils.NetplanCommand):
@property
def config_manager(self): # pragma: nocover (called by later commands)
if not self._config_manager:
- self._config_manager = ConfigManager()
+ self._config_manager = ConfigManager(prefix=self._rootdir)
return self._config_manager
def clear_ready_stamp(self):
@@ -147,7 +149,7 @@ class NetplanTry(utils.NetplanCommand):
def cleanup(self): # pragma: nocover (requires user input)
self.config_manager.cleanup()
- def is_revertable(self): # pragma: nocover (requires user input)
+ def is_revertable(self):
'''
Check if the configuration is revertable, if it doesn't contain bits
that we know are likely to render the system unstable if we apply it,
@@ -161,15 +163,15 @@ class NetplanTry(utils.NetplanCommand):
to not cleanly revert via the backend.
'''
- # Parse; including any new config file passed on the command-line:
- # new config might include things we can't revert.
extra_config = []
if self.config_file:
extra_config.append(self.config_file)
- self.config_manager.parse(extra_config=extra_config)
- self.new_interfaces = self.config_manager.new_interfaces
-
- logging.debug("New interfaces: {}".format(self.new_interfaces))
+ np_state = None
+ try:
+ np_state = self.config_manager.parse(extra_config=extra_config)
+ except utils.config_errors as e:
+ logging.error(e)
+ sys.exit(os.EX_CONFIG)
revert_unsupported = []
@@ -177,13 +179,13 @@ class NetplanTry(utils.NetplanCommand):
# more than one device in them, and they can be set with special parameters
# to tweak their behavior, which are really hard to "revert", especially
# as systemd-networkd doesn't necessarily touch them when config changes.
- multi_iface = {}
- multi_iface.update(self.config_manager.bridges)
- multi_iface.update(self.config_manager.bonds)
- for ifname, settings in multi_iface.items():
- if settings and 'parameters' in settings:
+ multi_iface = {} # type: dict[str, libnetplan.NetDefinition]
+ multi_iface.update(np_state.bridges)
+ multi_iface.update(np_state.bonds)
+ for itf in multi_iface.values():
+ if not itf.is_trivial_compound_itf:
reason = "reverting custom parameters for bridges and bonds is not supported"
- revert_unsupported.append((ifname, reason))
+ revert_unsupported.append((itf.id, reason))
if revert_unsupported:
for ifname, reason in revert_unsupported:
diff --git a/netplan/cli/ovs.py b/netplan/cli/ovs.py
index 6b7a035..0139e7c 100644
--- a/netplan/cli/ovs.py
+++ b/netplan/cli/ovs.py
@@ -104,15 +104,10 @@ def clear_setting(type, iface, setting, value):
subprocess.check_call([OPENVSWITCH_OVS_VSCTL, 'remove', type, iface, 'external-ids', setting])
-def is_ovs_interface(iface, interfaces):
- assert isinstance(interfaces, dict)
- if not isinstance(interfaces.get(iface), dict):
- logging.debug('Ignoring special key: {} ({})'.format(iface, interfaces.get(iface)))
- return False
- elif interfaces.get(iface, {}).get('openvswitch') is not None:
- return True
- else:
- return any(is_ovs_interface(i, interfaces) for i in interfaces.get(iface, {}).get('interfaces', []))
+def is_ovs_interface(iface, np_interface_dict):
+ assert isinstance(np_interface_dict, dict)
+ np_def = np_interface_dict.get(iface, None)
+ return np_def and np_def.backend == 'OpenVSwitch'
def apply_ovs_cleanup(config_manager, ovs_old, ovs_current): # pragma: nocover (covered in autopkgtest)
@@ -125,8 +120,8 @@ def apply_ovs_cleanup(config_manager, ovs_old, ovs_current): # pragma: nocover
"""
config_manager.parse()
ovs_ifaces = set()
- for i in config_manager.interfaces.keys():
- if (is_ovs_interface(i, config_manager.interfaces)):
+ for i in config_manager.all_defs.keys():
+ if (is_ovs_interface(i, config_manager.all_defs)):
ovs_ifaces.add(i)
# Tear down old OVS interfaces, not defined in the current config.
diff --git a/netplan/cli/sriov.py b/netplan/cli/sriov.py
index b83e37f..ed81209 100644
--- a/netplan/cli/sriov.py
+++ b/netplan/cli/sriov.py
@@ -182,30 +182,24 @@ def unbind_vfs(vfs: typing.Iterable[PCIDevice], driver) -> typing.Iterable[PCIDe
return unbound_vfs
-def _get_target_interface(interfaces, config_manager, pf_link, pfs):
+def _get_target_interface(interfaces, np_state, pf_link, pfs):
if pf_link not in pfs:
# handle the match: syntax, get the actual device name
- pf_dev = config_manager.ethernets[pf_link]
- pf_match = pf_dev.get('match')
- if pf_match:
+ pf_dev = np_state[pf_link]
+ if pf_dev.has_match:
# now here it's a bit tricky
- set_name = pf_dev.get('set-name')
+ set_name = pf_dev.set_name
if set_name and set_name in interfaces:
# if we had a match: stanza and set-name: this means we should
# assume that, if found, the interface has already been
# renamed - use the new name
pfs[pf_link] = set_name
else:
- # no set-name (or interfaces not yet renamed) so we need to do
- # the matching ourselves
- by_name = pf_match.get('name')
- by_mac = pf_match.get('macaddress')
- by_driver = pf_match.get('driver')
-
for interface in interfaces:
- if ((by_name and not utils.is_interface_matching_name(interface, by_name)) or
- (by_mac and not utils.is_interface_matching_macaddress(interface, by_mac)) or
- (by_driver and not utils.is_interface_matching_driver_name(interface, by_driver))):
+ if not pf_dev.match_interface(
+ itf_name=interface,
+ itf_driver=utils.get_interface_driver_name(interface),
+ itf_mac=utils.get_interface_macaddress(interface)):
continue
# we have a matching PF
# store the matching interface in the dictionary of
@@ -238,53 +232,27 @@ def _get_pci_slot_name(netdev):
raise RuntimeError('failed parsing PCI slot name for %s: %s' % (netdev, str(e)))
-def get_vf_count_and_functions(interfaces, config_manager,
+def get_vf_count_and_functions(interfaces, np_state,
vf_counts, vfs, pfs):
"""
Go through the list of netplan ethernet devices and identify which are
PFs and VFs, matching the former with actual networking interfaces.
Count how many VFs each PF will need.
"""
- explicit_counts = {}
- for ethernet, settings in config_manager.ethernets.items():
- if not settings:
- continue
- if ethernet == 'renderer':
+ for nid, netdef in np_state.ethernets.items():
+ if netdef.sriov_link and _get_target_interface(interfaces, np_state, netdef.sriov_link.id, pfs):
+ vfs[nid] = None
+
+ try:
+ count = netdef.vf_count
+ except libnetplan.LibNetplanException as e:
+ raise ConfigurationError(*e.args)
+ if count == 0:
continue
- # we now also support explicitly stating how many VFs should be
- # allocated for a PF
- explicit_num = settings.get('virtual-function-count')
- if explicit_num:
- pf = _get_target_interface(interfaces, config_manager, ethernet, pfs)
- if pf:
- explicit_counts[pf] = explicit_num
- continue
-
- pf_link = settings.get('link')
- if pf_link and pf_link in config_manager.ethernets:
- _get_target_interface(interfaces, config_manager, pf_link, pfs)
-
- if pf_link in pfs:
- vf_counts[pfs[pf_link]] += 1
- else:
- logging.warning('could not match physical interface for the defined PF: %s' % pf_link)
- # continue looking for other VFs
- continue
-
- # we can't yet perform matching on VFs as those are only
- # created later - but store, for convenience, all the valid
- # VFs that we encounter so far
- vfs[ethernet] = None
-
- # sanity check: since we can explicitly state the VF count, make sure
- # that this number isn't smaller than the actual number of VFs declared
- # the explicit number also overrides the number of actual VFs
- for pf, count in explicit_counts.items():
- if pf in vf_counts and vf_counts[pf] > count:
- raise ConfigurationError(
- 'more VFs allocated than the explicit size declared: %s > %s' % (vf_counts[pf], count))
- vf_counts[pf] = count
+ pf = _get_target_interface(interfaces, np_state, nid, pfs)
+ if pf:
+ vf_counts[pf] = count
def set_numvfs_for_pf(pf, vf_count):
@@ -350,7 +318,7 @@ def perform_hardware_specific_quirks(pf):
combined_id = ':'.join([vendor_id, device_id])
quirk_devices = () # TODO: add entries to the list
- if combined_id in quirk_devices:
+ if combined_id in quirk_devices: # pragma: nocover (empty quirk_devices)
# some devices need special handling, so this is the place
# Currently this part is empty, but has been added as a preemptive
@@ -415,6 +383,7 @@ def apply_sriov_config(config_manager, rootdir='/'):
config_manager.parse()
interfaces = netifaces.interfaces()
+ np_state = config_manager.np_state
# for sr-iov devices, we identify VFs by them having a link: field
# pointing to an PF. So let's browse through all ethernet devices,
@@ -427,7 +396,7 @@ def apply_sriov_config(config_manager, rootdir='/'):
pfs = {}
get_vf_count_and_functions(
- interfaces, config_manager, vf_counts, vfs, pfs)
+ interfaces, np_state, vf_counts, vfs, pfs)
# setup the required number of VFs per PF
# at the same time store which PFs got changed in case the NICs
@@ -456,22 +425,17 @@ def apply_sriov_config(config_manager, rootdir='/'):
# filtered VLANs for those.
# XXX: does matching those even make sense?
for vf in vfs:
- settings = config_manager.ethernets.get(vf)
- match = settings.get('match')
- if match:
+ netdef = np_state[vf]
+ if netdef.has_match:
# right now we only match by name, as I don't think matching per
# driver and/or macaddress makes sense
- by_name = match.get('name')
- # by_mac = match.get('macaddress')
- # by_driver = match.get('driver')
# TODO: print warning if other matches are provided
for interface in interfaces:
- if by_name and not utils.is_interface_matching_name(interface, by_name):
- continue
- if vf in vfs and vfs[vf]:
- raise ConfigurationError('matched more than one interface for a VF device: %s' % vf)
- vfs[vf] = interface
+ if netdef.match_interface(itf_name=interface):
+ if vf in vfs and vfs[vf]:
+ raise ConfigurationError('matched more than one interface for a VF device: %s' % vf)
+ vfs[vf] = interface
else:
if vf in interfaces:
vfs[vf] = vf
@@ -495,31 +459,31 @@ def apply_sriov_config(config_manager, rootdir='/'):
bind_vfs(pcidev.vfs, pcidev.driver)
filtered_vlans_set = set()
- for vlan, settings in config_manager.vlans.items():
+ for vlan, netdef in np_state.vlans.items():
# there is a special sriov vlan renderer that one can use to mark
# a selected vlan to be done in hardware (VLAN filtering)
- if settings.get('renderer') == 'sriov':
+ if netdef.has_sriov_vlan_filter:
# this only works for SR-IOV VF interfaces
- link = settings.get('link')
- vlan_id = settings.get('id')
- vf = vfs.get(link)
+ link = netdef.vlan_link
+ vlan_id = netdef.vlan_id
+
+ vf = vfs.get(link.id)
if not vf:
# it is possible this is not an error, for instance when
# the configuration has been defined 'for the future'
# XXX: but maybe we should error out here as well?
logging.warning(
- 'SR-IOV vlan defined for %s but link %s is either not a VF or has no matches' % (vlan, link))
+ 'SR-IOV vlan defined for %s but link %s is either not a VF or has no matches' % (vlan, link.id))
continue
# get the parent pf interface
# first we fetch the related vf netplan entry
- vf_parent_entry = config_manager.ethernets.get(link).get('link')
# and finally, get the matched pf interface
- pf = pfs.get(vf_parent_entry)
+ pf = pfs.get(link.sriov_link.id)
if vf in filtered_vlans_set:
raise ConfigurationError(
- 'interface %s for netplan device %s (%s) already has an SR-IOV vlan defined' % (vf, link, vlan))
+ 'interface %s for netplan device %s (%s) already has an SR-IOV vlan defined' % (vf, link.id, vlan))
# TODO: make sure that we don't apply the filter twice
apply_vlan_filter_for_vf(pf, vf, vlan, vlan_id)
diff --git a/netplan/cli/utils.py b/netplan/cli/utils.py
index a05b42b..450e300 100644
--- a/netplan/cli/utils.py
+++ b/netplan/cli/utils.py
@@ -24,11 +24,19 @@ import netifaces
import fnmatch
import re
+import netplan.libnetplan as np
+from netplan.configmanager import ConfigurationError
+from netplan.libnetplan import LibNetplanException
+
+
NM_SERVICE_NAME = 'NetworkManager.service'
NM_SNAP_SERVICE_NAME = 'snap.network-manager.networkmanager.service'
+config_errors = (ConfigurationError, LibNetplanException, RuntimeError)
+
def get_generator_path():
+ # FIXME: meson build uses proper libexecdir (+symlink)
return os.environ.get('NETPLAN_GENERATE_PATH', '/lib/netplan/generate')
@@ -95,12 +103,15 @@ def networkd_interfaces():
for line in out.splitlines():
s = line.strip().split(' ')
if s[0].isnumeric() and s[-1] not in ['unmanaged', 'linger']:
- interfaces.add(s[1])
+ interfaces.add(s[0])
return interfaces
-def networkctl_reconfigure(interfaces):
+def networkctl_reload():
subprocess.check_call(['networkctl', 'reload'])
+
+
+def networkctl_reconfigure(interfaces):
if len(interfaces) >= 1:
subprocess.check_call(['networkctl', 'reconfigure'] + list(interfaces))
@@ -151,43 +162,14 @@ def get_interface_macaddress(interface):
return link.get('addr', '')
-def is_interface_matching_name(interface, match_name):
- # globs are supported
- return fnmatch.fnmatchcase(interface, match_name)
-
-
-def is_interface_matching_driver_name(interface, match_driver):
- driver_globs = match_driver
- if isinstance(driver_globs, str):
- driver_globs = [match_driver]
- driver_name = get_interface_driver_name(interface)
- # globs are supported
- return any(
- fnmatch.fnmatchcase(driver_name, pattern)
- for pattern in driver_globs
- )
-
-
-def is_interface_matching_macaddress(interface, match_mac):
- macaddress = get_interface_macaddress(interface)
- # exact, case insensitive match. globs are not supported
- return match_mac.lower() == macaddress.lower()
-
-
-def find_matching_iface(interfaces, match):
- assert isinstance(match, dict)
-
- # Filter for match.name glob, fallback to '*'
- name_glob = match.get('name') if match.get('name', False) else '*'
- matches = fnmatch.filter(interfaces, name_glob)
-
- # Filter for match.macaddress (exact match)
- if len(matches) > 1 and match.get('macaddress'):
- matches = list(filter(lambda iface: is_interface_matching_macaddress(iface, match.get('macaddress')), matches))
+def find_matching_iface(interfaces: list, netdef):
+ assert isinstance(netdef, np.NetDefinition)
+ assert netdef.has_match
- # Filter for match.driver glob
- if len(matches) > 1 and match.get('driver'):
- matches = list(filter(lambda iface: is_interface_matching_driver_name(iface, match.get('driver')), matches))
+ matches = list(filter(lambda itf: netdef.match_interface(
+ itf_name=itf,
+ itf_driver=get_interface_driver_name(itf),
+ itf_mac=get_interface_macaddress(itf)), interfaces))
# Return current name of unique matched interface, if available
if len(matches) != 1:
@@ -205,6 +187,7 @@ class NetplanCommand(argparse.Namespace):
self.testing = testing
self._args = None
self.debug = False
+ self.breakpoint = False
self.commandclass = None
self.subcommands = {}
self.subcommand = None
@@ -215,6 +198,8 @@ class NetplanCommand(argparse.Namespace):
add_help=True)
self.parser.add_argument('--debug', action='store_true',
help='Enable debug messages')
+ self.parser.add_argument('--breakpoint', action='store_true',
+ help=argparse.SUPPRESS)
if not leaf:
self.subparsers = self.parser.add_subparsers(title='Available commands',
metavar='', dest='subcommand')
@@ -241,6 +226,8 @@ class NetplanCommand(argparse.Namespace):
if self.leaf_command and 'help' in self._args: # pragma: nocover (covered in autopkgtest)
self.print_usage()
+ if self.breakpoint: # pragma: nocover (cannot be automatically tested)
+ breakpoint()
self.func()
def print_usage(self):
diff --git a/netplan/configmanager.py b/netplan/configmanager.py
index 186d561..7905f76 100644
--- a/netplan/configmanager.py
+++ b/netplan/configmanager.py
@@ -17,173 +17,85 @@
'''netplan configuration manager'''
-import glob
import logging
import os
import shutil
import sys
import tempfile
-import yaml
+from typing import Optional
+
+from netplan import libnetplan
-class ConfigManager(object):
+class ConfigManager(object):
def __init__(self, prefix="/", extra_files={}):
self.prefix = prefix
self.tempdir = tempfile.mkdtemp(prefix='netplan_')
self.temp_etc = os.path.join(self.tempdir, "etc")
self.temp_run = os.path.join(self.tempdir, "run")
self.extra_files = extra_files
- self.config = {}
self.new_interfaces = set()
+ self.np_state: Optional[libnetplan.State] = None
- @property
- def network(self):
- return self.config['network']
-
- @property
- def interfaces(self):
- interfaces = {}
- interfaces.update(self.ovs_ports)
- interfaces.update(self.ethernets)
- interfaces.update(self.modems)
- interfaces.update(self.wifis)
- interfaces.update(self.bridges)
- interfaces.update(self.bonds)
- interfaces.update(self.tunnels)
- interfaces.update(self.vlans)
- return interfaces
+ def __getattr__(self, attr):
+ assert self.np_state is not None, "Must call parse() before accessing the config."
+ return getattr(self.np_state, attr)
@property
def physical_interfaces(self):
+ assert self.np_state is not None, "Must call parse() before accessing the config."
interfaces = {}
- interfaces.update(self.ethernets)
- interfaces.update(self.modems)
- interfaces.update(self.wifis)
+ interfaces.update(self.np_state.ethernets)
+ interfaces.update(self.np_state.modems)
+ interfaces.update(self.np_state.wifis)
return interfaces
@property
def virtual_interfaces(self):
+ assert self.np_state is not None, "Must call parse() before accessing the config."
interfaces = {}
# what about ovs_ports?
- interfaces.update(self.bridges)
- interfaces.update(self.bonds)
- interfaces.update(self.tunnels)
- interfaces.update(self.vlans)
+ interfaces.update(self.np_state.bridges)
+ interfaces.update(self.np_state.bonds)
+ interfaces.update(self.np_state.tunnels)
+ interfaces.update(self.np_state.vlans)
+ interfaces.update(self.np_state.vrfs)
return interfaces
- @property
- def ovs_ports(self):
- return self.network['ovs_ports']
-
- @property
- def openvswitch(self):
- return self.network['openvswitch']
-
- @property
- def ethernets(self):
- return self.network['ethernets']
-
- @property
- def modems(self):
- return self.network['modems']
-
- @property
- def wifis(self):
- return self.network['wifis']
-
- @property
- def bridges(self):
- return self.network['bridges']
-
- @property
- def bonds(self):
- return self.network['bonds']
-
- @property
- def tunnels(self):
- return self.network['tunnels']
-
- @property
- def vlans(self):
- return self.network['vlans']
-
- @property
- def nm_devices(self):
- return self.network['nm-devices']
-
- @property
- def version(self):
- return self.network['version']
-
- @property
- def renderer(self):
- return self.network['renderer']
-
- @property
- def tree(self):
- return self.strip_tree(self.config)
-
- @staticmethod
- def strip_tree(data):
- '''clear empty branches'''
- new_data = {}
- for k, v in data.items():
- if isinstance(v, dict):
- v = ConfigManager.strip_tree(v)
- if v not in (u'', None, {}):
- new_data[k] = v
- return new_data
-
- def parse(self, extra_config=[]):
+ def parse(self, extra_config=None):
"""
Parse all our config files to return an object that describes the system's
entire configuration, so that it can later be interrogated.
- Returns a dict that contains the entire, collated and merged YAML.
+ Returns a libnetplan State wrapper
"""
- # TODO: Clean this up, there's no solid reason why we should parse YAML
- # in two different spots; here and in parse.c. We'd do better by
- # parsing things once, in C form, and having some small glue
- # Cpython code to call on the right methods and return an object
- # that is meaningful for the Python code; but minimal parsing in
- # pure Python will do for now. ~cyphermox
# /run/netplan shadows /etc/netplan/, which shadows /lib/netplan
- names_to_paths = {}
- for yaml_dir in ['lib', 'etc', 'run']:
- for yaml_file in glob.glob(os.path.join(self.prefix, yaml_dir, 'netplan', '*.yaml')):
- names_to_paths[os.path.basename(yaml_file)] = yaml_file
-
- files = [names_to_paths[name] for name in sorted(names_to_paths.keys())]
+ parser = libnetplan.Parser()
+ try:
+ parser.load_yaml_hierarchy(rootdir=self.prefix)
- self.config['network'] = {
- 'ovs_ports': {},
- 'openvswitch': {},
- 'ethernets': {},
- 'modems': {},
- 'wifis': {},
- 'bridges': {},
- 'bonds': {},
- 'tunnels': {},
- 'vlans': {},
- 'nm-devices': {},
- 'version': None,
- 'renderer': None
- }
- for yaml_file in files:
- self._merge_yaml_config(yaml_file)
+ if extra_config:
+ for f in extra_config:
+ parser.load_yaml(f)
- for yaml_file in extra_config:
- self.new_interfaces |= self._merge_yaml_config(yaml_file)
+ self.np_state = libnetplan.State()
+ self.np_state.import_parser_results(parser)
+ except libnetplan.LibNetplanException as e:
+ raise ConfigurationError(*e.args)
- logging.debug("Merged config:\n{}".format(yaml.dump(self.tree, default_flow_style=False)))
+ self.np_state.dump_to_logs()
+ return self.np_state
def add(self, config_dict):
for config_file in config_dict:
self._copy_file(config_file, config_dict[config_file])
self.extra_files.update(config_dict)
+ # Invalidate the current parsed state
+ self.np_state = None
+
def backup(self, backup_config_dir=True):
if backup_config_dir:
self._copy_tree(os.path.join(self.prefix, "etc/netplan"),
@@ -243,92 +155,6 @@ class ConfigManager(object):
else:
raise
- def _merge_ovs_ports_config(self, orig, new):
- new_interfaces = set()
- ports = dict()
- if 'ports' in new:
- for p1, p2 in new.get('ports'):
- # Spoof an interface config for patch ports, which are usually
- # just strings. Add 'peer' and mark it via 'openvswitch' key.
- ports[p1] = {'peer': p2, 'openvswitch': {}}
- ports[p2] = {'peer': p1, 'openvswitch': {}}
- changed_ifaces = list(ports.keys())
-
- for ifname in changed_ifaces:
- iface = ports.pop(ifname)
- if ifname in orig:
- logging.debug("{} exists in {}".format(ifname, orig))
- orig[ifname].update(iface)
- else:
- logging.debug("{} not found in {}".format(ifname, orig))
- orig[ifname] = iface
- new_interfaces.add(ifname)
-
- return new_interfaces
-
- def _merge_interface_config(self, orig, new):
- new_interfaces = set()
- changed_ifaces = list(new.keys())
-
- for ifname in changed_ifaces:
- iface = new.pop(ifname)
- if ifname in orig:
- logging.debug("{} exists in {}".format(ifname, orig))
- orig[ifname].update(iface)
- else:
- logging.debug("{} not found in {}".format(ifname, orig))
- orig[ifname] = iface
- new_interfaces.add(ifname)
-
- return new_interfaces
-
- def _merge_yaml_config(self, yaml_file):
- new_interfaces = set()
-
- try:
- with open(yaml_file) as f:
- yaml_data = yaml.load(f, Loader=yaml.CSafeLoader)
- network = None
- if yaml_data is not None:
- network = yaml_data.get('network')
- if network:
- if 'openvswitch' in network:
- new = self._merge_ovs_ports_config(self.ovs_ports, network.get('openvswitch'))
- new_interfaces |= new
- self.network['openvswitch'] = network.get('openvswitch')
- if 'ethernets' in network:
- new = self._merge_interface_config(self.ethernets, network.get('ethernets'))
- new_interfaces |= new
- if 'modems' in network:
- new = self._merge_interface_config(self.modems, network.get('modems'))
- new_interfaces |= new
- if 'wifis' in network:
- new = self._merge_interface_config(self.wifis, network.get('wifis'))
- new_interfaces |= new
- if 'bridges' in network:
- new = self._merge_interface_config(self.bridges, network.get('bridges'))
- new_interfaces |= new
- if 'bonds' in network:
- new = self._merge_interface_config(self.bonds, network.get('bonds'))
- new_interfaces |= new
- if 'tunnels' in network:
- new = self._merge_interface_config(self.tunnels, network.get('tunnels'))
- new_interfaces |= new
- if 'vlans' in network:
- new = self._merge_interface_config(self.vlans, network.get('vlans'))
- new_interfaces |= new
- if 'nm-devices' in network:
- new = self._merge_interface_config(self.nm_devices, network.get('nm-devices'))
- new_interfaces |= new
- if 'version' in network:
- self.network['version'] = network.get('version')
- if 'renderer' in network:
- self.network['renderer'] = network.get('renderer')
- return new_interfaces
- except (IOError, yaml.YAMLError): # pragma: nocover (filesystem failures/invalid YAML)
- logging.error('Error while loading {}, aborting.'.format(yaml_file))
- sys.exit(1)
-
class ConfigurationError(Exception):
"""
diff --git a/netplan/libnetplan.py b/netplan/libnetplan.py
index aac41b3..797ac86 100644
--- a/netplan/libnetplan.py
+++ b/netplan/libnetplan.py
@@ -16,9 +16,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import tempfile
+import logging
import ctypes
import ctypes.util
-from ctypes import c_char_p, c_void_p, c_int
+from ctypes import c_char_p, c_void_p, c_int, c_uint, c_size_t, c_ssize_t
+from typing import List, Union, IO
class LibNetplanException(Exception):
@@ -42,10 +45,6 @@ class _netplan_net_definition(ctypes.Structure):
lib = ctypes.CDLL(ctypes.util.find_library('netplan'))
-lib.netplan_parse_yaml.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.POINTER(_GError))]
-lib.netplan_get_filename_by_id.restype = ctypes.c_char_p
-lib.process_yaml_hierarchy.argtypes = [ctypes.c_char_p]
-lib.process_yaml_hierarchy.restype = ctypes.c_int
_GErrorPP = ctypes.POINTER(ctypes.POINTER(_GError))
_NetplanParserP = ctypes.POINTER(_netplan_parser)
@@ -55,17 +54,22 @@ _NetplanNetDefinitionP = ctypes.POINTER(_netplan_net_definition)
lib.netplan_get_id_from_nm_filename.restype = ctypes.c_char_p
-def netplan_parse(path):
- # Clear old NetplanNetDefinitions from libnetplan memory
- lib.netplan_clear_netdefs()
- err = ctypes.POINTER(_GError)()
- ret = bool(lib.netplan_parse_yaml(path.encode(), ctypes.byref(err)))
- if not ret:
- raise Exception(err.contents.message.decode('utf-8'))
- lib.netplan_finish_parse(ctypes.byref(err))
- if err:
- raise Exception(err.contents.message.decode('utf-8'))
- return True
+def _string_realloc_call_no_error(function):
+ size = 16
+ while size < 1073741824: # 1MB
+ buffer = ctypes.create_string_buffer(size)
+ code = function(buffer)
+ if code == -2:
+ size = size * 2
+ continue
+
+ if code < 0: # pragma: nocover
+ raise LibNetplanException("Unknown error: %d" % code)
+ elif code == 0:
+ return None # pragma: nocover as it's hard to trigger for now
+ else:
+ return buffer.value.decode('utf-8')
+ raise LibNetplanException('Aborting due to string buffer size > 1M') # pragma: nocover
def _checked_lib_call(fn, *args):
@@ -75,11 +79,6 @@ def _checked_lib_call(fn, *args):
raise LibNetplanException(err.contents.message.decode('utf-8'))
-def netplan_get_filename_by_id(netdef_id, rootdir):
- res = lib.netplan_get_filename_by_id(netdef_id.encode(), rootdir.encode())
- return res.decode('utf-8') if res else None
-
-
class Parser:
_abi_loaded = False
@@ -94,6 +93,12 @@ class Parser:
lib.netplan_parser_load_yaml.argtypes = [_NetplanParserP, c_char_p, _GErrorPP]
lib.netplan_parser_load_yaml.restype = c_int
+ lib.netplan_parser_load_yaml_from_fd.argtypes = [_NetplanParserP, c_int, _GErrorPP]
+ lib.netplan_parser_load_yaml_from_fd.restype = c_int
+
+ lib.netplan_parser_load_nullable_fields.argtypes = [_NetplanParserP, c_int, _GErrorPP]
+ lib.netplan_parser_load_nullable_fields.restype = c_int
+
cls._abi_loaded = True
def __init__(self):
@@ -103,12 +108,18 @@ class Parser:
def __del__(self):
lib.netplan_parser_clear(ctypes.byref(self._ptr))
- def load_yaml(self, filename):
- _checked_lib_call(lib.netplan_parser_load_yaml, self._ptr, filename.encode('utf-8'))
+ def load_yaml(self, input_file: Union[str, IO]):
+ if isinstance(input_file, str):
+ _checked_lib_call(lib.netplan_parser_load_yaml, self._ptr, input_file.encode('utf-8'))
+ else:
+ _checked_lib_call(lib.netplan_parser_load_yaml_from_fd, self._ptr, input_file.fileno())
def load_yaml_hierarchy(self, rootdir):
_checked_lib_call(lib.netplan_parser_load_yaml_hierarchy, self._ptr, rootdir.encode('utf-8'))
+ def load_nullable_fields(self, input_file: IO):
+ _checked_lib_call(lib.netplan_parser_load_nullable_fields, self._ptr, input_file.fileno())
+
class State:
_abi_loaded = False
@@ -130,6 +141,12 @@ class State:
lib.netplan_state_get_netdef.argtypes = [_NetplanStateP, c_char_p]
lib.netplan_state_get_netdef.restype = _NetplanNetDefinitionP
+ lib.netplan_state_write_yaml_file.argtypes = [_NetplanStateP, c_char_p, c_char_p, _GErrorPP]
+ lib.netplan_state_write_yaml_file.restype = c_int
+
+ lib.netplan_state_update_yaml_hierarchy.argtypes = [_NetplanStateP, c_char_p, c_char_p, _GErrorPP]
+ lib.netplan_state_update_yaml_hierarchy.restype = c_int
+
lib.netplan_state_dump_yaml.argtypes = [_NetplanStateP, c_int, _GErrorPP]
lib.netplan_state_dump_yaml.restype = c_int
@@ -139,6 +156,9 @@ class State:
lib.netplan_netdef_get_delay_virtual_functions_rebind.argtypes = [_NetplanNetDefinitionP]
lib.netplan_netdef_get_delay_virtual_functions_rebind.restype = c_int
+ lib.netplan_state_get_backend.argtypes = [_NetplanStateP]
+ lib.netplan_state_get_backend.restype = c_int
+
cls._abi_loaded = True
def __init__(self):
@@ -151,6 +171,16 @@ class State:
def import_parser_results(self, parser):
_checked_lib_call(lib.netplan_state_import_parser_results, self._ptr, parser._ptr)
+ def write_yaml_file(self, filename, rootdir):
+ name = filename.encode('utf-8') if filename else None
+ root = rootdir.encode('utf-8') if rootdir else None
+ _checked_lib_call(lib.netplan_state_write_yaml_file, self._ptr, name, root)
+
+ def update_yaml_hierarchy(self, default_filename, rootdir):
+ name = default_filename.encode('utf-8')
+ root = rootdir.encode('utf-8') if rootdir else None
+ _checked_lib_call(lib.netplan_state_update_yaml_hierarchy, self._ptr, name, root)
+
def dump_yaml(self, output_file):
fd = output_file.fileno()
_checked_lib_call(lib.netplan_state_dump_yaml, self._ptr, fd)
@@ -164,6 +194,60 @@ class State:
raise IndexError()
return NetDefinition(self, ptr)
+ @property
+ def all_defs(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, None))
+
+ @property
+ def ethernets(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "ethernets"))
+
+ @property
+ def modems(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "modems"))
+
+ @property
+ def wifis(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "wifis"))
+
+ @property
+ def vlans(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "vlans"))
+
+ @property
+ def bridges(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "bridges"))
+
+ @property
+ def bonds(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "bonds"))
+
+ @property
+ def tunnels(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "tunnels"))
+
+ @property
+ def vrfs(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "vrfs"))
+
+ @property
+ def ovs_ports(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "_ovs-ports"))
+
+ @property
+ def nm_devices(self):
+ return dict((nd.id, nd) for nd in _NetdefIterator(self, "nm-devices"))
+
+ @property
+ def backend(self):
+ return lib.netplan_backend_name(lib.netplan_state_get_backend(self._ptr)).decode('utf-8')
+
+ def dump_to_logs(self):
+ # Convoluted way to dump the parsed config to the logs...
+ with tempfile.TemporaryFile() as tmp:
+ self.dump_yaml(output_file=tmp)
+ logging.debug("Merged config:\n{}".format(tmp.read()))
+
class NetDefinition:
_abi_loaded = False
@@ -173,8 +257,53 @@ class NetDefinition:
if cls._abi_loaded:
return
- lib.netplan_netdef_get_id.argtypes = [_NetplanNetDefinitionP]
- lib.netplan_netdef_get_id.restype = c_char_p
+ lib.netplan_netdef_has_match.argtypes = [_NetplanNetDefinitionP]
+ lib.netplan_netdef_has_match.restype = c_int
+
+ lib.netplan_netdef_get_id.argtypes = [_NetplanNetDefinitionP, c_char_p, c_size_t]
+ lib.netplan_netdef_get_id.restype = c_ssize_t
+
+ lib.netplan_netdef_get_filepath.argtypes = [_NetplanNetDefinitionP, c_char_p, c_size_t]
+ lib.netplan_netdef_get_filepath.restype = c_ssize_t
+
+ lib.netplan_netdef_get_backend.argtypes = [_NetplanNetDefinitionP]
+ lib.netplan_netdef_get_backend.restype = c_int
+
+ lib.netplan_netdef_get_type.argtypes = [_NetplanNetDefinitionP]
+ lib.netplan_netdef_get_type.restype = c_int
+
+ lib.netplan_netdef_get_set_name.argtypes = [_NetplanNetDefinitionP, c_char_p, c_size_t]
+ lib.netplan_netdef_get_set_name.restype = c_ssize_t
+
+ lib._netplan_netdef_get_critical.argtypes = [_NetplanNetDefinitionP]
+ lib._netplan_netdef_get_critical.restype = c_int
+
+ lib._netplan_netdef_get_sriov_link.argtypes = [_NetplanNetDefinitionP]
+ lib._netplan_netdef_get_sriov_link.restype = _NetplanNetDefinitionP
+
+ lib._netplan_netdef_get_vlan_link.argtypes = [_NetplanNetDefinitionP]
+ lib._netplan_netdef_get_vlan_link.restype = _NetplanNetDefinitionP
+
+ lib._netplan_netdef_get_vlan_id.argtypes = [_NetplanNetDefinitionP]
+ lib._netplan_netdef_get_vlan_id.restype = c_uint
+
+ lib._netplan_netdef_get_sriov_vlan_filter.argtypes = [_NetplanNetDefinitionP]
+ lib._netplan_netdef_get_sriov_vlan_filter.restype = c_int
+
+ lib.netplan_netdef_match_interface.argtypes = [_NetplanNetDefinitionP]
+ lib.netplan_netdef_match_interface.restype = c_int
+
+ lib.netplan_backend_name.argtypes = [c_int]
+ lib.netplan_backend_name.restype = c_char_p
+
+ lib.netplan_def_type_name.argtypes = [c_int]
+ lib.netplan_def_type_name.restype = c_char_p
+
+ lib._netplan_state_get_vf_count_for_def.argtypes = [_NetplanStateP, _NetplanNetDefinitionP, _GErrorPP]
+ lib._netplan_state_get_vf_count_for_def.restype = c_int
+
+ lib._netplan_netdef_is_trivial_compound_itf.argtypes = [_NetplanNetDefinitionP]
+ lib._netplan_netdef_is_trivial_compound_itf.restype = c_int
cls._abi_loaded = True
@@ -191,8 +320,58 @@ class NetDefinition:
self._parent = np_state
@property
+ def has_match(self):
+ return bool(lib.netplan_netdef_has_match(self._ptr))
+
+ @property
+ def set_name(self):
+ return _string_realloc_call_no_error(lambda b: lib.netplan_netdef_get_set_name(self._ptr, b, len(b)))
+
+ @property
+ def critical(self):
+ return bool(lib._netplan_netdef_get_critical(self._ptr))
+
+ @property
+ def sriov_link(self):
+ link_ptr = lib._netplan_netdef_get_sriov_link(self._ptr)
+ if link_ptr:
+ return NetDefinition(self._parent, link_ptr)
+ return None
+
+ @property
+ def vlan_link(self):
+ link_ptr = lib._netplan_netdef_get_vlan_link(self._ptr)
+ if link_ptr:
+ return NetDefinition(self._parent, link_ptr)
+ return None
+
+ @property
+ def vlan_id(self):
+ vlan_id = lib._netplan_netdef_get_vlan_id(self._ptr)
+ # No easy way to get UINT_MAX besides this...
+ if vlan_id == c_uint(-1).value:
+ return None
+ return vlan_id
+
+ @property
+ def has_sriov_vlan_filter(self):
+ return bool(lib._netplan_netdef_get_sriov_vlan_filter(self._ptr))
+
+ @property
+ def backend(self):
+ return lib.netplan_backend_name(lib.netplan_netdef_get_backend(self._ptr)).decode('utf-8')
+
+ @property
+ def type(self):
+ return lib.netplan_def_type_name(lib.netplan_netdef_get_type(self._ptr)).decode('utf-8')
+
+ @property
def id(self):
- return lib.netplan_netdef_get_id(self._ptr).decode('utf-8')
+ return _string_realloc_call_no_error(lambda b: lib.netplan_netdef_get_id(self._ptr, b, len(b)))
+
+ @property
+ def filepath(self):
+ return _string_realloc_call_no_error(lambda b: lib.netplan_netdef_get_filepath(self._ptr, b, len(b)))
@property
def embedded_switch_mode(self):
@@ -203,6 +382,29 @@ class NetDefinition:
def delay_virtual_functions_rebind(self):
return bool(lib.netplan_netdef_get_delay_virtual_functions_rebind(self._ptr))
+ def match_interface(self, itf_name=None, itf_driver=None, itf_mac=None):
+ return bool(lib.netplan_netdef_match_interface(
+ self._ptr,
+ itf_name and itf_name.encode('utf-8'),
+ itf_mac and itf_mac.encode('utf-8'),
+ itf_driver and itf_driver.encode('utf-8')))
+
+ @property
+ def vf_count(self):
+ err = ctypes.POINTER(_GError)()
+ count = lib._netplan_state_get_vf_count_for_def(self._parent._ptr, self._ptr, ctypes.byref(err))
+ if count < 0:
+ raise LibNetplanException(err.contents.message.decode('utf-8'))
+ return count
+
+ @property
+ def is_trivial_compound_itf(self):
+ '''
+ Returns True if the interface is a compound interface (bond or bridge),
+ and its configuration is trivial, without any variation from the defaults.
+ '''
+ return bool(lib._netplan_netdef_is_trivial_compound_itf(self._ptr))
+
class _NetdefIterator:
_abi_loaded = False
@@ -250,29 +452,20 @@ class _NetdefIterator:
return NetDefinition(self.np_state, next_value)
-class __GlobalState(State):
- def __init__(self):
- self._ptr = ctypes.cast(lib.global_state, _NetplanStateP)
-
- def __del__(self):
- pass
-
-
-def netplan_get_ids_for_devtype(devtype, rootdir):
- err = ctypes.POINTER(_GError)()
- lib.netplan_clear_netdefs()
- lib.process_yaml_hierarchy(rootdir.encode('utf-8'))
- lib.netplan_finish_parse(ctypes.byref(err))
- if err: # pragma: nocover (this is a "break in case of emergency" thing)
- raise Exception(err.contents.message.decode('utf-8'))
- nds = list(_NetdefIterator(__GlobalState(), devtype))
- return [nd.id for nd in nds]
-
+lib.netplan_util_create_yaml_patch.argtypes = [c_char_p, c_char_p, c_int, _GErrorPP]
+lib.netplan_util_create_yaml_patch.restype = c_int
lib.netplan_util_dump_yaml_subtree.argtypes = [c_char_p, c_int, c_int, _GErrorPP]
lib.netplan_util_dump_yaml_subtree.restype = c_int
+def create_yaml_patch(patch_object_path: List[str], patch_payload: str, patch_output):
+ _checked_lib_call(lib.netplan_util_create_yaml_patch,
+ '\t'.join(patch_object_path).encode('utf-8'),
+ patch_payload.encode('utf-8'),
+ patch_output.fileno())
+
+
def dump_yaml_subtree(prefix, input_file, output_file):
_checked_lib_call(lib.netplan_util_dump_yaml_subtree,
prefix.encode('utf-8'),
diff --git a/netplan/meson.build b/netplan/meson.build
new file mode 100644
index 0000000..1313cb4
--- /dev/null
+++ b/netplan/meson.build
@@ -0,0 +1,35 @@
+install_data('../src/netplan.script')
+install_symlink(
+ 'netplan',
+ pointing_to: '../share/netplan/netplan.script',
+ install_dir: get_option('sbindir'))
+
+netplan_sources = files(
+ '__init__.py',
+ '_features.py',
+ 'configmanager.py',
+ 'terminal.py')
+
+cli_sources = files(
+ 'cli/__init__.py',
+ 'cli/core.py',
+ 'cli/ovs.py',
+ 'cli/sriov.py',
+ 'cli/utils.py')
+
+commands_sources = files(
+ 'cli/commands/__init__.py',
+ 'cli/commands/apply.py',
+ 'cli/commands/generate.py',
+ 'cli/commands/get.py',
+ 'cli/commands/info.py',
+ 'cli/commands/ip.py',
+ 'cli/commands/migrate.py',
+ 'cli/commands/set.py',
+ 'cli/commands/try_command.py')
+
+netplan_module = join_paths(get_option('datadir'), meson.project_name(), 'netplan')
+install_data(netplan_sources, install_dir: netplan_module)
+install_data(cli_sources, install_dir: join_paths(netplan_module, 'cli'))
+install_data(commands_sources, install_dir: join_paths(netplan_module, 'cli', 'commands'))
+
diff --git a/rpm/netplan.spec b/rpm/netplan.spec
index c3edab1..285481e 100644
--- a/rpm/netplan.spec
+++ b/rpm/netplan.spec
@@ -13,25 +13,34 @@
# Force auto-byte-compilation to Python 3
%global __python %{__python3}
+# networkd is not available everywhere
+%if 0%{?rhel}
+%bcond_with networkd_support
+%else
+%bcond_without networkd_support
+%endif
Name: netplan
-Version: 0.95
+Version: 0.105
Release: 0%{?dist}
Summary: Network configuration tool using YAML
Group: System Environment/Base
License: GPLv3
URL: http://netplan.io/
-Source0: https://github.com/canonical/%{name}/archive/%{version}/%{version}.tar.gz
+Source0: https://github.com/canonical/%{name}/archive/%{version}/%{name}-%{version}.tar.gz
BuildRequires: gcc
BuildRequires: make
BuildRequires: pkgconfig(bash-completion)
-BuildRequires: pkgconfig(systemd)
BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(gio-2.0)
+BuildRequires: pkgconfig(libsystemd)
+BuildRequires: pkgconfig(systemd)
BuildRequires: pkgconfig(yaml-0.1)
BuildRequires: pkgconfig(uuid)
-BuildRequires: %{_bindir}/pandoc
BuildRequires: python%{python3_pkgversion}-devel
+BuildRequires: systemd-rpm-macros
+BuildRequires: %{_bindir}/pandoc
# For tests
BuildRequires: %{_sbindir}/ip
BuildRequires: python%{python3_pkgversion}-coverage
@@ -47,18 +56,13 @@ Requires: python%{python3_pkgversion}-PyYAML
# 'ip' command is used in netplan apply subcommand
Requires: %{_sbindir}/ip
-# netplan supports either systemd or NetworkManager as backends to configure the network
-Requires: systemd
+# Netplan requires a backend for configuration
+Requires: %{name}-default-backend
+# Prefer NetworkManager
+Suggests: %{name}-default-backend-NetworkManager
-%if 0%{?el7}
-# systemd-networkd is a separate subpackage in EL7
-Requires: systemd-networkd
-%endif
-
-%if 0%{?fedora} || 0%{?rhel} >= 8
-# NetworkManager is preferred, but wpa_supplicant can be used directly for Wi-Fi networks
-Suggests: (NetworkManager or wpa_supplicant)
-%endif
+# Netplan requires its core libraries
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
# Provide the package name that Ubuntu uses for it too...
Provides: %{ubuntu_name} = %{version}-%{release}
@@ -70,8 +74,136 @@ installers, cloud image instantiations, or other OS deployments. During early bo
backend specific configuration files in /run to hand off control of devices to a particular
networking daemon.
-Currently supported backends are systemd-networkd and NetworkManager.
+Currently supported backends are NetworkManager and systemd-networkd.
+
+%files
+%license COPYING
+%doc %{_docdir}/%{name}/
+%{_sbindir}/%{name}
+%{_datadir}/%{name}/
+%{_datadir}/dbus-1/system-services/io.netplan.Netplan.service
+%{_datadir}/dbus-1/system.d/io.netplan.Netplan.conf
+%{_systemdgeneratordir}/%{name}
+%{_mandir}/man5/%{name}.5*
+%{_mandir}/man8/%{name}*.8*
+%dir %{_sysconfdir}/%{name}
+%{_prefix}/lib/%{name}/
+%{_datadir}/bash-completion/completions/%{name}
+
+# ------------------------------------------------------------------------------------------------
+%package libs
+Summary: Network configuration tool using YAML (core library)
+Group: System Environment/Libraries
+
+%description libs
+netplan reads network configuration from /etc/netplan/*.yaml which are written by administrators,
+installers, cloud image instantiations, or other OS deployments. During early boot, it generates
+backend specific configuration files in /run to hand off control of devices to a particular
+networking daemon.
+
+This package provides Netplan's core libraries.
+
+%if 0%{?rhel} && 0%{?rhel} < 8
+%post libs -p /sbin/ldconfig
+%postun libs -p /sbin/ldconfig
+%endif
+
+%files libs
+%license COPYING
+%{_libdir}/libnetplan.so.*
+
+# ------------------------------------------------------------------------------------------------
+
+%package devel
+Summary: Network configuration tool using YAML (development files)
+Group: Development/Libraries
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description devel
+netplan reads network configuration from /etc/netplan/*.yaml which are written by administrators,
+installers, cloud image instantiations, or other OS deployments. During early boot, it generates
+backend specific configuration files in /run to hand off control of devices to a particular
+networking daemon.
+
+This package provides development headers and libraries for building applications using Netplan.
+
+%files devel
+%{_includedir}/%{name}/
+%{_libdir}/libnetplan.so
+
+# ------------------------------------------------------------------------------------------------
+
+%package default-backend-NetworkManager
+Summary: Network configuration tool using YAML (NetworkManager backend)
+Group: System Environment/Base
+Requires: %{name} = %{version}-%{release}
+# Netplan requires NetworkManager for configuration
+Requires: NetworkManager
+# Disable NetworkManager's autoconfiguration
+Requires: NetworkManager-config-server
+
+%if 0%{?rhel} && 0%{?rhel} < 8
+# This is needed for Wi-Fi
+Requires: NetworkManager-wifi
+%else
+# Generally, if linux-firmware-whence is installed, we want Wi-Fi capabilities
+Recommends: (NetworkManager-wifi if linux-firmware-whence)
+# This is preferred for Wi-Fi
+Suggests: NetworkManager-wifi
+%endif
+
+# One and only one default backend permitted
+Conflicts: %{name}-default-backend
+Provides: %{name}-default-backend
+
+BuildArch: noarch
+
+%description default-backend-NetworkManager
+netplan reads network configuration from /etc/netplan/*.yaml which are written by administrators,
+installers, cloud image instantiations, or other OS deployments. During early boot, it generates
+backend specific configuration files in /run to hand off control of devices to a particular
+networking daemon.
+
+This package configures Netplan to use NetworkManager as its backend.
+
+%files default-backend-NetworkManager
+%{_prefix}/lib/%{name}/00-netplan-default-renderer-nm.yaml
+
+# ------------------------------------------------------------------------------------------------
+
+%if %{with networkd_support}
+%package default-backend-networkd
+Summary: Network configuration tool using YAML (systemd-networkd backend)
+Group: System Environment/Base
+Requires: %{name} = %{version}-%{release}
+# Netplan requires systemd-networkd for configuration
+Requires: systemd-networkd
+
+# Generally, if linux-firmware-whence is installed, we want Wi-Fi capabilities
+Recommends: (wpa_supplicant if linux-firmware-whence)
+# This is preferred for Wi-Fi
+Suggests: wpa_supplicant
+
+# One and only one default backend permitted
+Conflicts: %{name}-default-backend
+Provides: %{name}-default-backend
+
+BuildArch: noarch
+
+%description default-backend-networkd
+netplan reads network configuration from /etc/netplan/*.yaml which are written by administrators,
+installers, cloud image instantiations, or other OS deployments. During early boot, it generates
+backend specific configuration files in /run to hand off control of devices to a particular
+networking daemon.
+
+This package configures Netplan to use systemd-networkd as its backend.
+
+%files default-backend-networkd
+%{_prefix}/lib/%{name}/00-netplan-default-renderer-networkd.yaml
+%endif
+
+# ------------------------------------------------------------------------------------------------
%prep
%autosetup -p1
@@ -86,32 +218,36 @@ sed -e "s/-Werror//g" -i Makefile
%install
-%make_install ROOTPREFIX=%{_prefix}
+%make_install ROOTPREFIX=%{_prefix} LIBDIR=%{_libdir} LIBEXECDIR=%{_libexecdir}
# Pre-create the config directory
mkdir -p %{buildroot}%{_sysconfdir}/%{name}
+# Generate Netplan default renderer configuration
+cat > %{buildroot}%{_prefix}/lib/%{name}/00-netplan-default-renderer-nm.yaml <<EOF
+network:
+ renderer: NetworkManager
+EOF
+%if %{with networkd_support}
+cat > %{buildroot}%{_prefix}/lib/%{name}/00-netplan-default-renderer-networkd.yaml <<EOF
+network:
+ renderer: networkd
+EOF
+%endif
+
%check
make check
-%files
-%license COPYING
-%doc debian/changelog
-%doc %{_docdir}/%{name}/
-%{_sbindir}/%{name}
-%{_datadir}/%{name}/
-%{_unitdir}/%{name}*.service
-%{_systemdgeneratordir}/%{name}
-%{_mandir}/man5/%{name}.5*
-%{_mandir}/man8/%{name}*.8*
-%dir %{_sysconfdir}/%{name}
-%{_prefix}/lib/%{name}/
-%{_datadir}/bash-completion/completions/%{name}
+%changelog
+* Thu Aug 18 2022 Lukas Märdian <slyon@ubuntu.com> - 0.105-0
+- Update to 0.105
+* Sun Feb 20 2022 Neal Gompa <ngompa13@gmail.com> - 0.104-0
+- Update to 0.104
+- Resync with Fedora spec
-%changelog
* Fri Dec 14 2018 Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com> - 0.95
- Update to 0.95
diff --git a/src/abi.h b/src/abi.h
new file mode 100644
index 0000000..a292a59
--- /dev/null
+++ b/src/abi.h
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2022 Canonical, Ltd.
+ * Author: Lukas Märdian <slyon@ubuntu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <glib.h>
+#include <parse.h>
+#include <uuid.h>
+
+typedef int NetplanFlags;
+
+/* Those types are part of our ABI as they have been exposed in older versions */
+
+typedef enum {
+ NETPLAN_OPTIONAL_IPV4_LL = 1<<0,
+ NETPLAN_OPTIONAL_IPV6_RA = 1<<1,
+ NETPLAN_OPTIONAL_DHCP4 = 1<<2,
+ NETPLAN_OPTIONAL_DHCP6 = 1<<3,
+ NETPLAN_OPTIONAL_STATIC = 1<<4,
+} NetplanOptionalAddressFlag;
+
+/* Fields below are valid for dhcp4 and dhcp6 unless otherwise noted. */
+typedef struct dhcp_overrides {
+ gboolean use_dns;
+ gboolean use_ntp;
+ gboolean send_hostname;
+ gboolean use_hostname;
+ gboolean use_mtu;
+ gboolean use_routes;
+ char* use_domains; /* netplan-feature: dhcp-use-domains */
+ char* hostname;
+ guint metric;
+} NetplanDHCPOverrides;
+
+typedef enum {
+ NETPLAN_RA_MODE_KERNEL,
+ NETPLAN_RA_MODE_ENABLED,
+ NETPLAN_RA_MODE_DISABLED,
+} NetplanRAMode;
+
+typedef enum {
+ NETPLAN_IB_MODE_KERNEL,
+ NETPLAN_IB_MODE_DATAGRAM,
+ NETPLAN_IB_MODE_CONNECTED,
+
+ NETPLAN_IB_MODE_MAX_,
+} NetplanInfinibandMode;
+
+typedef enum {
+ NETPLAN_WIFI_WOWLAN_DEFAULT = 1<<0,
+ NETPLAN_WIFI_WOWLAN_ANY = 1<<1,
+ NETPLAN_WIFI_WOWLAN_DISCONNECT = 1<<2,
+ NETPLAN_WIFI_WOWLAN_MAGIC = 1<<3,
+ NETPLAN_WIFI_WOWLAN_GTK_REKEY_FAILURE = 1<<4,
+ NETPLAN_WIFI_WOWLAN_EAP_IDENTITY_REQ = 1<<5,
+ NETPLAN_WIFI_WOWLAN_4WAY_HANDSHAKE = 1<<6,
+ NETPLAN_WIFI_WOWLAN_RFKILL_RELEASE = 1<<7,
+ NETPLAN_WIFI_WOWLAN_TCP = 1<<8,
+} NetplanWifiWowlanFlag;
+
+struct NetplanWifiWowlanType {
+ char* name;
+ NetplanWifiWowlanFlag flag;
+};
+
+/* Tunnel mode enum; sync with NetworkManager's DBUS API */
+/* TODO: figure out whether networkd's GRETAP and NM's ISATAP
+ * are the same thing.
+ */
+typedef enum {
+ NETPLAN_TUNNEL_MODE_UNKNOWN = 0,
+ NETPLAN_TUNNEL_MODE_IPIP = 1,
+ NETPLAN_TUNNEL_MODE_GRE = 2,
+ NETPLAN_TUNNEL_MODE_SIT = 3,
+ NETPLAN_TUNNEL_MODE_ISATAP = 4, // NM only.
+ NETPLAN_TUNNEL_MODE_VTI = 5,
+ NETPLAN_TUNNEL_MODE_IP6IP6 = 6,
+ NETPLAN_TUNNEL_MODE_IPIP6 = 7,
+ NETPLAN_TUNNEL_MODE_IP6GRE = 8,
+ NETPLAN_TUNNEL_MODE_VTI6 = 9,
+ NETPLAN_TUNNEL_MODE_VXLAN = 10,
+
+ /* systemd-only, apparently? */
+ NETPLAN_TUNNEL_MODE_GRETAP = 101,
+ NETPLAN_TUNNEL_MODE_IP6GRETAP = 102,
+ NETPLAN_TUNNEL_MODE_WIREGUARD = 103,
+
+ NETPLAN_TUNNEL_MODE_MAX_,
+} NetplanTunnelMode;
+
+typedef enum {
+ NETPLAN_AUTH_KEY_MANAGEMENT_NONE,
+ NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK,
+ NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP,
+ NETPLAN_AUTH_KEY_MANAGEMENT_8021X,
+ NETPLAN_AUTH_KEY_MANAGEMENT_MAX,
+} NetplanAuthKeyManagementType;
+
+typedef enum {
+ NETPLAN_AUTH_EAP_NONE,
+ NETPLAN_AUTH_EAP_TLS,
+ NETPLAN_AUTH_EAP_PEAP,
+ NETPLAN_AUTH_EAP_TTLS,
+ NETPLAN_AUTH_EAP_METHOD_MAX,
+} NetplanAuthEAPMethod;
+
+typedef struct authentication_settings {
+ NetplanAuthKeyManagementType key_management;
+ NetplanAuthEAPMethod eap_method;
+ char* identity;
+ char* anonymous_identity;
+ char* password;
+ char* ca_certificate;
+ char* client_certificate;
+ char* client_key;
+ char* client_key_password;
+ char* phase2_auth; /* netplan-feature: auth-phase2 */
+} NetplanAuthenticationSettings;
+
+typedef struct ovs_controller {
+ char* connection_mode;
+ GArray* addresses;
+} NetplanOVSController;
+
+typedef struct ovs_settings {
+ GHashTable* external_ids;
+ GHashTable* other_config;
+ char* lacp;
+ char* fail_mode;
+ gboolean mcast_snooping;
+ GArray* protocols;
+ gboolean rstp;
+ NetplanOVSController controller;
+ NetplanAuthenticationSettings ssl;
+} NetplanOVSSettings;
+
+typedef union {
+ struct NetplanNMSettings {
+ char *name;
+ char *uuid;
+ char *stable_id;
+ char *device;
+ GData* passthrough; /* See g_datalist* functions */
+ } nm;
+ struct NetplanNetworkdSettings {
+ char *unit;
+ } networkd;
+} NetplanBackendSettings;
+
+typedef enum
+{
+ /**
+ * @brief Tristate enum type
+ *
+ * This type defines a boolean which can be unset, i.e.
+ * this type has three states. The enum is ordered so
+ * that
+ *
+ * UNSET -> -1
+ * FALSE -> 0
+ * TRUE -> 1
+ *
+ * And the integer values can be used directly when
+ * converting to string.
+ */
+ NETPLAN_TRISTATE_UNSET = -1, /* -1 */
+ NETPLAN_TRISTATE_FALSE, /* 0 */
+ NETPLAN_TRISTATE_TRUE, /* 1 */
+} NetplanTristate;
+
+typedef struct netplan_vxlan NetplanVxlan;
+
+/* Keep 'struct netplan_net_definition' in a separate header file, to allow for
+ * abidiff to consider it "public API" (although it isn't) and notify us about
+ * ABI compatibility issues. */
+struct netplan_net_definition {
+ NetplanDefType type;
+ NetplanBackend backend;
+ char* id;
+ /* only necessary for NetworkManager connection UUIDs in some cases */
+ uuid_t uuid;
+
+ /* status options */
+ gboolean optional;
+ NetplanOptionalAddressFlag optional_addresses;
+ gboolean critical;
+
+ /* addresses */
+ gboolean dhcp4;
+ gboolean dhcp6;
+ char* dhcp_identifier;
+ NetplanDHCPOverrides dhcp4_overrides;
+ NetplanDHCPOverrides dhcp6_overrides;
+ NetplanRAMode accept_ra;
+ GArray* ip4_addresses;
+ GArray* ip6_addresses;
+ GArray* address_options;
+ gboolean ip6_privacy;
+ guint ip6_addr_gen_mode;
+ char* ip6_addr_gen_token;
+ char* gateway4;
+ char* gateway6;
+ GArray* ip4_nameservers;
+ GArray* ip6_nameservers;
+ GArray* search_domains;
+ GArray* routes;
+ GArray* ip_rules;
+ GArray* wireguard_peers;
+ struct {
+ gboolean ipv4;
+ gboolean ipv6;
+ } linklocal;
+
+ /* master ID for slave devices */
+ char* bridge;
+ char* bond;
+
+ /* peer ID for OVS patch ports */
+ char* peer;
+
+ /* vlan */
+ guint vlan_id;
+ NetplanNetDefinition* vlan_link;
+ gboolean has_vlans;
+
+ /* Configured custom MAC address */
+ char* set_mac;
+
+ /* interface mtu */
+ guint mtubytes;
+ /* ipv6 mtu */
+ /* netplan-feature: ipv6-mtu */
+ guint ipv6_mtubytes;
+
+ /* these properties are only valid for physical interfaces (type < ND_VIRTUAL) */
+ char* set_name;
+ struct {
+ /* A glob (or tab-separated list of globs) to match a specific driver */
+ char* driver;
+ char* mac;
+ char* original_name;
+ } match;
+ gboolean has_match;
+ gboolean wake_on_lan;
+ NetplanWifiWowlanFlag wowlan;
+ gboolean emit_lldp;
+
+ /* these properties are only valid for NETPLAN_DEF_TYPE_WIFI */
+ GHashTable* access_points; /* SSID → NetplanWifiAccessPoint* */
+
+ struct {
+ char* mode;
+ char* lacp_rate;
+ char* monitor_interval;
+ guint min_links;
+ char* transmit_hash_policy;
+ char* selection_logic;
+ gboolean all_slaves_active;
+ char* arp_interval;
+ GArray* arp_ip_targets;
+ char* arp_validate;
+ char* arp_all_targets;
+ char* up_delay;
+ char* down_delay;
+ char* fail_over_mac_policy;
+ guint gratuitous_arp;
+ /* TODO: unsolicited_na */
+ guint packets_per_slave;
+ char* primary_reselect_policy;
+ guint resend_igmp;
+ char* learn_interval;
+ char* primary_slave;
+ } bond_params;
+
+ /* netplan-feature: modems */
+ struct {
+ char* apn;
+ gboolean auto_config;
+ char* device_id;
+ char* network_id;
+ char* number;
+ char* password;
+ char* pin;
+ char* sim_id;
+ char* sim_operator_id;
+ char* username;
+ } modem_params;
+
+ struct {
+ char* ageing_time;
+ guint priority;
+ guint port_priority;
+ char* forward_delay;
+ char* hello_time;
+ char* max_age;
+ guint path_cost;
+ gboolean stp;
+ } bridge_params;
+ gboolean custom_bridging;
+
+ struct {
+ NetplanTunnelMode mode;
+ char *local_ip;
+ char *remote_ip;
+ char *input_key;
+ char *output_key;
+ char *private_key; /* used for wireguard */
+ guint fwmark;
+ guint port;
+ } tunnel;
+
+ NetplanAuthenticationSettings auth;
+ gboolean has_auth;
+
+ /* these properties are only valid for SR-IOV NICs */
+ /* netplan-feature: sriov */
+ struct netplan_net_definition* sriov_link;
+ gboolean sriov_vlan_filter;
+ guint sriov_explicit_vf_count;
+
+ /* these properties are only valid for OpenVSwitch */
+ /* netplan-feature: openvswitch */
+ NetplanOVSSettings ovs_settings;
+
+ NetplanBackendSettings backend_settings;
+
+ char* filepath;
+ /* it cannot be in the tunnel struct: https://github.com/canonical/netplan/pull/206 */
+ guint tunnel_ttl;
+
+ /* netplan-feature: activation-mode */
+ char* activation_mode;
+
+ /* configure without carrier */
+ gboolean ignore_carrier;
+
+ /* offload options */
+ NetplanTristate receive_checksum_offload;
+ NetplanTristate transmit_checksum_offload;
+ NetplanTristate tcp_segmentation_offload;
+ NetplanTristate tcp6_segmentation_offload;
+ NetplanTristate generic_segmentation_offload;
+ NetplanTristate generic_receive_offload;
+ NetplanTristate large_receive_offload;
+
+ struct private_netdef_data* _private;
+
+ /* netplan-feature: eswitch-mode */
+ char* embedded_switch_mode;
+ gboolean sriov_delay_virtual_functions_rebind;
+
+ /* netplan-feature: infiniband */
+ NetplanInfinibandMode ib_mode; /* IPoIB */
+
+ /* netplan-feature: regdom */
+ char* regulatory_domain;
+
+ /* vrf */
+ /* netplan-feature: vrf */
+ NetplanNetDefinition* vrf_link;
+ guint vrf_table;
+
+ NetplanTristate bridge_neigh_suppress;
+
+ /* vxlan */
+ /* netplan-feature: vxlan */
+ gboolean has_vxlans;
+ NetplanVxlan* vxlan;
+};
diff --git a/src/abi_compat.c b/src/abi_compat.c
index 191ccee..73e8f4b 100644
--- a/src/abi_compat.c
+++ b/src/abi_compat.c
@@ -74,6 +74,7 @@ netplan_get_global_backend()
/**
* Clear NetplanNetDefinition hashtable
*/
+// LCOV_EXCL_START
guint
netplan_clear_netdefs()
{
@@ -98,7 +99,7 @@ netplan_finish_parse(GError** error)
{
if (netplan_state_import_parser_results(&global_state, &global_parser, error))
return global_state.netdefs;
- return NULL;
+ return NULL; // LCOV_EXCL_LINE
}
/**
@@ -113,9 +114,6 @@ write_netplan_conf(const NetplanNetDefinition* def, const char* rootdir)
netplan_netdef_write_yaml(&global_state, def, rootdir, NULL);
}
-gboolean
-netplan_state_write_yaml(const NetplanState* np_state, const char* file_hint, const char* rootdir, GError** error);
-
/**
* Generate the Netplan YAML configuration for all currently parsed netdefs
* @file_hint: Name hint for the generated output YAML file
@@ -182,6 +180,7 @@ _write_netplan_conf(const char* netdef_id, const char* rootdir)
g_warning("_write_netplan_conf: netdef_id (%s) not found.", netdef_id); // LCOV_EXCL_LINE
}
+// LCOV_EXCL_START
/**
* Get the filename from which the given netdef has been parsed.
* @rootdir: ID of the netdef to be looked up
@@ -192,7 +191,7 @@ netplan_get_filename_by_id(const char* netdef_id, const char* rootdir)
{
NetplanParser* npp = netplan_parser_new();
NetplanState* np_state = netplan_state_new();
- char *filename = NULL;
+ char *filepath = NULL;
GError* error = NULL;
if (!netplan_parser_load_yaml_hierarchy(npp, rootdir, &error) ||
@@ -204,12 +203,11 @@ netplan_get_filename_by_id(const char* netdef_id, const char* rootdir)
NetplanNetDefinition* netdef = netplan_state_get_netdef(np_state, netdef_id);
if (netdef)
- filename = g_strdup(netplan_netdef_get_filename(netdef));
+ filepath = g_strdup(netdef->filepath);
netplan_state_clear(&np_state);
- return filename;
+ return filepath;
}
-// LCOV_EXCL_START
NETPLAN_INTERNAL struct netdef_pertype_iter*
_netplan_state_new_netdef_pertype_iter(NetplanState* np_state, const char* devtype);
@@ -218,4 +216,17 @@ _netplan_iter_defs_per_devtype_init(const char *devtype)
{
return _netplan_state_new_netdef_pertype_iter(&global_state, devtype);
}
+
+NETPLAN_INTERNAL const char*
+_netplan_netdef_id(NetplanNetDefinition* netdef)
+{
+ return netdef->id;
+}
+
+NETPLAN_ABI const char *
+netplan_netdef_get_filename(const NetplanNetDefinition* netdef)
+{
+ g_assert(netdef);
+ return netdef->filepath;
+}
// LCOV_EXCL_STOP
diff --git a/src/dbus.c b/src/dbus.c
index 4d9b17a..7f633e6 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -512,14 +512,19 @@ method_try(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
// LCOV_EXCL_STOP
/* wait for the /run/netplan/netplan-try.ready stamp file to appear */
- guint poll_timeout = 500;
- if (seconds > 0 && seconds < 5)
+ guint poll_timeout = 1000;
+ /* Replace the default timeout with the one specified by the caller */
+ if (seconds > 0)
poll_timeout = seconds * 100;
- /* Timeout after up to 5 sec of waiting for the stamp file */
+ /* Timeout after up to 10 sec of waiting for the stamp file */
for (int i = 0; i < poll_timeout; i++) {
+ struct timespec timeout = {
+ .tv_sec = 0,
+ .tv_nsec = 1000 * 1000 * 10, // 10 ms
+ };
if (stat(netplan_try_stamp, &buf) == 0)
break;
- usleep(1000 * 10);
+ nanosleep(&timeout, NULL);
}
if (stat(netplan_try_stamp, &buf) != 0) {
g_debug("cannot find %s stamp file", netplan_try_stamp);
diff --git a/src/error.c b/src/error.c
index 85f29ba..024e678 100644
--- a/src/error.c
+++ b/src/error.c
@@ -43,7 +43,7 @@ static char *
get_syntax_error_context(const NetplanParser* npp, const int line_num, const int column, GError **error)
{
GString *message = NULL;
- GFile *cur_file = g_file_new_for_path(npp->current.filename);
+ GFile *cur_file = g_file_new_for_path(npp->current.filepath);
GFileInputStream *file_stream;
GDataInputStream *stream;
gsize len;
@@ -106,6 +106,7 @@ gboolean
parser_error(const yaml_parser_t* parser, const char* yaml, GError** error)
{
char *error_context = get_parser_error_context(parser, error);
+ yaml = yaml ? yaml : "(unnamed file)";
if ((char)*parser->buffer.pointer == '\t')
g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
"%s:%zu:%zu: Invalid YAML: tabs are not allowed for indent:\n%s",
@@ -158,7 +159,7 @@ yaml_error(const NetplanParser *npp, const yaml_node_t* node, GError** error, co
error_context = get_syntax_error_context(npp, node->start_mark.line, node->start_mark.column, error);
g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
"%s:%zu:%zu: Error in network definition: %s\n%s",
- npp->current.filename,
+ npp->current.filepath,
node->start_mark.line + 1,
node->start_mark.column + 1,
s,
diff --git a/src/generate.c b/src/generate.c
index 6f2e3a0..906799d 100644
--- a/src/generate.c
+++ b/src/generate.c
@@ -38,6 +38,7 @@
static gchar* rootdir;
static gchar** files;
static gboolean any_networkd = FALSE;
+static gboolean any_nm = FALSE;
static gchar* mapping_iface;
static GOptionEntry options[] = {
@@ -272,6 +273,7 @@ int main(int argc, char** argv)
CHECK_CALL(netplan_netdef_write_ovs(np_state, def, rootdir, &has_been_written, &error));
CHECK_CALL(netplan_netdef_write_nm(np_state, def, rootdir, &has_been_written, &error));
+ any_nm = any_nm || has_been_written;
}
CHECK_CALL(netplan_state_finish_nm_write(np_state, rootdir, &error));
@@ -286,8 +288,8 @@ int main(int argc, char** argv)
}
/* Disable /usr/lib/NetworkManager/conf.d/10-globally-managed-devices.conf
- * (which restricts NM to wifi and wwan) if global renderer is NM */
- if (netplan_state_get_backend(np_state) == NETPLAN_BACKEND_NM)
+ * (which restricts NM to wifi and wwan) if "renderer: NetworkManager" is used anywhere */
+ if (netplan_state_get_backend(np_state) == NETPLAN_BACKEND_NM || any_nm)
g_string_free_to_file(g_string_new(NULL), rootdir, "/run/NetworkManager/conf.d/10-globally-managed-devices.conf", NULL);
if (called_as_generator) {
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..b44649c
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,46 @@
+sources = files(
+ 'abi_compat.c',
+ 'error.c',
+ 'names.c',
+ 'netplan.c',
+ 'networkd.c',
+ 'nm.c',
+ 'openvswitch.c',
+ 'parse.c',
+ 'parse-nm.c',
+ 'sriov.c',
+ 'types.c',
+ 'util.c',
+ 'validation.c')
+
+linker_script = meson.project_source_root() / 'abicompat.lds'
+libnetplan = library(
+ 'netplan',
+ sources,
+ gnu_symbol_visibility: 'hidden',
+ link_args: ['-T', linker_script],
+ link_depends: linker_script,
+ dependencies: [glib, gio, yaml, uuid],
+ include_directories: inc,
+ soversion: '0.0',
+ install: true)
+
+libexec_netplan = join_paths(get_option('libexecdir'), 'netplan')
+executable(
+ 'generate',
+ 'generate.c',
+ include_directories: inc,
+ link_with: libnetplan,
+ dependencies: [glib, gio, yaml, uuid],
+ install_dir: libexec_netplan,
+ install: true)
+install_symlink(
+ 'netplan',
+ pointing_to: join_paths('..', '..', '..') + join_paths(get_option('prefix'), libexec_netplan, 'generate'),
+ install_dir: systemd_generator_dir)
+# Install this symlink for legacy reasons, see netplan/cli/utils.py: get_generator_path()
+install_symlink(
+ 'generate',
+ pointing_to: join_paths('..', '..') + join_paths(get_option('prefix'), libexec_netplan, 'generate'),
+ install_dir: join_paths('/', 'lib', 'netplan'))
+
diff --git a/src/names.c b/src/names.c
index 7364378..44b0a57 100644
--- a/src/names.c
+++ b/src/names.c
@@ -47,6 +47,7 @@ netplan_def_type_to_str[NETPLAN_DEF_TYPE_MAX_] = {
[NETPLAN_DEF_TYPE_BRIDGE] = "bridges",
[NETPLAN_DEF_TYPE_BOND] = "bonds",
[NETPLAN_DEF_TYPE_VLAN] = "vlans",
+ [NETPLAN_DEF_TYPE_VRF] = "vrfs",
[NETPLAN_DEF_TYPE_TUNNEL] = "tunnels",
[NETPLAN_DEF_TYPE_PORT] = "_ovs-ports",
[NETPLAN_DEF_TYPE_NM] = "nm-devices",
@@ -80,6 +81,7 @@ netplan_tunnel_mode_to_str[NETPLAN_TUNNEL_MODE_MAX_] = {
[NETPLAN_TUNNEL_MODE_IPIP6] = "ipip6",
[NETPLAN_TUNNEL_MODE_IP6GRE] = "ip6gre",
[NETPLAN_TUNNEL_MODE_VTI6] = "vti6",
+ [NETPLAN_TUNNEL_MODE_VXLAN] = "vxlan",
[NETPLAN_TUNNEL_MODE_GRETAP] = "gretap",
[NETPLAN_TUNNEL_MODE_IP6GRETAP] = "ip6gretap",
[NETPLAN_TUNNEL_MODE_WIREGUARD] = "wireguard",
@@ -92,9 +94,24 @@ netplan_addr_gen_mode_to_str[NETPLAN_ADDRGEN_MAX] = {
[NETPLAN_ADDRGEN_STABLEPRIVACY] = "stable-privacy"
};
+static const char* const
+netplan_infiniband_mode_to_str[NETPLAN_IB_MODE_MAX_] = {
+ [NETPLAN_IB_MODE_KERNEL] = NULL,
+ [NETPLAN_IB_MODE_DATAGRAM] = "datagram",
+ [NETPLAN_IB_MODE_CONNECTED] = "connected"
+};
+
#define NAME_FUNCTION(_radical, _type) const char *netplan_ ## _radical ## _name( _type val) \
{ \
- return (val < sizeof( netplan_ ## _radical ## _to_str )) ? netplan_ ## _radical ## _to_str [val] : NULL; \
+ return (val < sizeof(netplan_ ## _radical ## _to_str) / sizeof(char *)) ? netplan_ ## _radical ## _to_str [val] : NULL; \
+}
+
+/* @num_flags needs to account for the 0x0 value (index 0), which doesn't
+ * represent any flag, but still exists. So subtract 1 from the array length. */
+#define NAME_FUNCTION_FLAGS(_radical) const char *netplan_ ## _radical ## _name( NetplanFlags val) \
+{ \
+ size_t num_flags = sizeof(netplan_ ## _radical ## _to_str) / sizeof(char *) - 1; \
+ return (val <= 1 << (num_flags - 1)) ? netplan_ ## _radical ## _to_str [__builtin_ffs(val)] : NULL; \
}
NAME_FUNCTION(backend, NetplanBackend);
@@ -104,6 +121,10 @@ NAME_FUNCTION(auth_eap_method, NetplanAuthEAPMethod);
NAME_FUNCTION(tunnel_mode, NetplanTunnelMode);
NAME_FUNCTION(addr_gen_mode, NetplanAddrGenMode);
NAME_FUNCTION(wifi_mode, NetplanWifiMode);
+NAME_FUNCTION(infiniband_mode, NetplanInfinibandMode);
+NAME_FUNCTION_FLAGS(vxlan_notification);
+NAME_FUNCTION_FLAGS(vxlan_checksum);
+NAME_FUNCTION_FLAGS(vxlan_extension);
#define ENUM_FUNCTION(_radical, _type) _type netplan_ ## _radical ## _from_name(const char* val) \
{ \
diff --git a/src/names.h b/src/names.h
index 6cc2641..ed759ed 100644
--- a/src/names.h
+++ b/src/names.h
@@ -23,7 +23,7 @@
NETPLAN_INTERNAL const char*
netplan_backend_name(NetplanBackend val);
-const char*
+NETPLAN_INTERNAL const char*
netplan_def_type_name(NetplanDefType val);
const char*
@@ -41,5 +41,39 @@ netplan_addr_gen_mode_name(NetplanAddrGenMode val);
const char*
netplan_wifi_mode_name(NetplanWifiMode val);
+const char*
+netplan_infiniband_mode_name(NetplanInfinibandMode val);
+
+const char*
+netplan_vxlan_notification_name(int val);
+
+const char*
+netplan_vxlan_checksum_name(int val);
+
+const char*
+netplan_vxlan_extension_name(int val);
+
NetplanDefType
netplan_def_type_from_name(const char* val);
+
+/* Netplan flag names */
+static const char* const
+netplan_vxlan_notification_to_str[] = {
+ [__builtin_ffs(NETPLAN_VXLAN_NOTIFICATION_L2_MISS)] = "l2-miss",
+ [__builtin_ffs(NETPLAN_VXLAN_NOTIFICATION_L3_MISS)] = "l3-miss",
+};
+
+static const char* const
+netplan_vxlan_checksum_to_str[] = {
+ [__builtin_ffs(NETPLAN_VXLAN_CHECKSUM_UDP)] = "udp",
+ [__builtin_ffs(NETPLAN_VXLAN_CHECKSUM_ZERO_UDP6_TX)] = "zero-udp6-tx",
+ [__builtin_ffs(NETPLAN_VXLAN_CHECKSUM_ZERO_UDP6_RX)] = "zero-udp6-rx",
+ [__builtin_ffs(NETPLAN_VXLAN_CHECKSUM_REMOTE_TX)] = "remote-tx",
+ [__builtin_ffs(NETPLAN_VXLAN_CHECKSUM_REMOTE_RX)] = "remote-rx",
+};
+
+static const char* const
+netplan_vxlan_extension_to_str[] = {
+ [__builtin_ffs(NETPLAN_VXLAN_EXTENSION_GROUP_POLICY)] = "group-policy",
+ [__builtin_ffs(NETPLAN_VXLAN_EXTENSION_GENERIC_PROTOCOL)] = "generic-protocol",
+};
diff --git a/src/netplan.c b/src/netplan.c
index 7b387b4..2fe56d6 100644
--- a/src/netplan.c
+++ b/src/netplan.c
@@ -89,14 +89,12 @@ gchar *tmp = NULL;
} \
}\
-static gboolean
-complex_object_is_dirty(const NetplanNetDefinition* def, char* obj, size_t obj_size) {
- /* We literally check every address in the object! Ugly, but it works. */
- for (size_t i = 0; i < obj_size; ++i) {
- if (DIRTY_REF(def, obj+i))
- return TRUE;
- }
- return FALSE;
+#define YAML_BOOL_TRISTATE(_def, event_ptr, emitter_ptr, key, value) {\
+ if (value == NETPLAN_TRISTATE_TRUE) { \
+ YAML_NONNULL_STRING_PLAIN(event_ptr, emitter_ptr, key, "true"); \
+ } else if (value == NETPLAN_TRISTATE_FALSE) { \
+ YAML_NONNULL_STRING_PLAIN(event_ptr, emitter_ptr, key, "false"); \
+ } \
}
#define DIRTY_COMPLEX(_def, _data) complex_object_is_dirty(_def, (char*)(&_data), sizeof(_data))
@@ -204,6 +202,68 @@ err_path: return FALSE; // LCOV_EXCL_LINE
}
static gboolean
+write_vxlan(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDefinition* def)
+{
+ if (def->type == NETPLAN_DEF_TYPE_TUNNEL && def->tunnel.mode == NETPLAN_TUNNEL_MODE_VXLAN) {
+ g_assert(def->vxlan);
+ YAML_UINT_0(def, event, emitter, "id", def->vxlan->vni);
+ if (def->vxlan->link)
+ YAML_STRING(def, event, emitter, "link", def->vxlan->link->id);
+ if (def->vxlan->source_port_min && def->vxlan->source_port_max) {
+ YAML_SCALAR_PLAIN(event, emitter, "port-range");
+ YAML_SEQUENCE_OPEN(event, emitter);
+ tmp = g_strdup_printf("%u", def->vxlan->source_port_min);
+ YAML_SCALAR_PLAIN(event, emitter, tmp);
+ g_free(tmp);
+ tmp = g_strdup_printf("%u", def->vxlan->source_port_max);
+ YAML_SCALAR_PLAIN(event, emitter, tmp);
+ g_free(tmp);
+ YAML_SEQUENCE_CLOSE(event, emitter);
+ }
+ YAML_UINT_DEFAULT(def, event, emitter, "flow-label", def->vxlan->flow_label, G_MAXUINT);
+ YAML_UINT_0(def, event, emitter, "limit", def->vxlan->limit);
+ YAML_UINT_0(def, event, emitter, "type-of-service", def->vxlan->tos);
+ YAML_UINT_0(def, event, emitter, "ageing", def->vxlan->ageing);
+ YAML_BOOL_TRUE(def, event, emitter, "mac-learning", def->vxlan->mac_learning);
+ YAML_BOOL_TRUE(def, event, emitter, "arp-proxy", def->vxlan->arp_proxy);
+ YAML_BOOL_TRUE(def, event, emitter, "short-circuit", def->vxlan->short_circuit);
+ YAML_BOOL_TRISTATE(def, event, emitter, "do-not-fragment", def->vxlan->do_not_fragment);
+ if (def->vxlan->notifications) {
+ YAML_SCALAR_PLAIN(event, emitter, "notifications");
+ YAML_SEQUENCE_OPEN(event, emitter);
+ int f = def->vxlan->notifications;
+ const char* (*fn)(int) = &netplan_vxlan_notification_name;
+ YAML_FLAG(event, emitter, NETPLAN_VXLAN_NOTIFICATION_L2_MISS, f, (*fn));
+ YAML_FLAG(event, emitter, NETPLAN_VXLAN_NOTIFICATION_L3_MISS, f, (*fn));
+ YAML_SEQUENCE_CLOSE(event, emitter);
+ }
+ if (def->vxlan->checksums) {
+ YAML_SCALAR_PLAIN(event, emitter, "checksums");
+ YAML_SEQUENCE_OPEN(event, emitter);
+ int f = def->vxlan->checksums;
+ const char* (*fn)(int) = &netplan_vxlan_checksum_name;
+ YAML_FLAG(event, emitter, NETPLAN_VXLAN_CHECKSUM_UDP, f, (*fn));
+ YAML_FLAG(event, emitter, NETPLAN_VXLAN_CHECKSUM_ZERO_UDP6_TX, f, (*fn));
+ YAML_FLAG(event, emitter, NETPLAN_VXLAN_CHECKSUM_ZERO_UDP6_RX, f, (*fn));
+ YAML_FLAG(event, emitter, NETPLAN_VXLAN_CHECKSUM_REMOTE_TX, f, (*fn));
+ YAML_FLAG(event, emitter, NETPLAN_VXLAN_CHECKSUM_REMOTE_RX, f, (*fn));
+ YAML_SEQUENCE_CLOSE(event, emitter);
+ }
+ if (def->vxlan->extensions) {
+ YAML_SCALAR_PLAIN(event, emitter, "extensions");
+ YAML_SEQUENCE_OPEN(event, emitter);
+ int f = def->vxlan->extensions;
+ const char* (*fn)(int) = &netplan_vxlan_extension_name;
+ YAML_FLAG(event, emitter, NETPLAN_VXLAN_EXTENSION_GROUP_POLICY, f, (*fn));
+ YAML_FLAG(event, emitter, NETPLAN_VXLAN_EXTENSION_GENERIC_PROTOCOL, f, (*fn));
+ YAML_SEQUENCE_CLOSE(event, emitter);
+ }
+ }
+ return TRUE;
+err_path: return FALSE; // LCOV_EXCL_LINE
+}
+
+static gboolean
write_bridge_params(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDefinition* def, const GArray *interfaces)
{
if (def->custom_bridging || DIRTY_COMPLEX(def, def->bridge_params)) {
@@ -443,6 +503,9 @@ write_tunnel_settings(yaml_event_t* event, yaml_emitter_t* emitter, const Netpla
YAML_UINT_0(def, event, emitter, "port", def->tunnel.port);
YAML_UINT_0(def, event, emitter, "ttl", def->tunnel_ttl);
+ /* VXLAN settings */
+ write_vxlan(event, emitter, def);
+
if (def->tunnel.input_key || def->tunnel.output_key || def->tunnel.private_key) {
if ( g_strcmp0(def->tunnel.input_key, def->tunnel.output_key) == 0
&& g_strcmp0(def->tunnel.input_key, def->tunnel.private_key) == 0) {
@@ -505,7 +568,9 @@ write_routes(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDefin
if (r->scope && g_strcmp0(r->scope, "global") != 0)
YAML_NONNULL_STRING(event, emitter, "scope", r->scope);
YAML_UINT_DEFAULT(def, event, emitter, "metric", r->metric, NETPLAN_METRIC_UNSPEC);
- YAML_UINT_DEFAULT(def, event, emitter, "table", r->table, NETPLAN_ROUTE_TABLE_UNSPEC);
+ /* VRF devices use the VRF routing table implicitly */
+ if (def->type != NETPLAN_DEF_TYPE_VRF)
+ YAML_UINT_DEFAULT(def, event, emitter, "table", r->table, NETPLAN_ROUTE_TABLE_UNSPEC);
YAML_UINT_0(def, event, emitter, "mtu", r->mtubytes);
YAML_UINT_0(def, event, emitter, "congestion-window", r->congestion_window);
YAML_UINT_0(def, event, emitter, "advertised-receive-window", r->advertised_receive_window);
@@ -524,7 +589,9 @@ write_routes(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDefin
for (unsigned i = 0; i < def->ip_rules->len; ++i) {
NetplanIPRule *r = g_array_index(def->ip_rules, NetplanIPRule*, i);
YAML_MAPPING_OPEN(event, emitter);
- YAML_UINT_DEFAULT(def, event, emitter, "table", r->table, NETPLAN_ROUTE_TABLE_UNSPEC);
+ /* VRF devices use the VRF routing table implicitly */
+ if (def->type != NETPLAN_DEF_TYPE_VRF)
+ YAML_UINT_DEFAULT(def, event, emitter, "table", r->table, NETPLAN_ROUTE_TABLE_UNSPEC);
YAML_UINT_DEFAULT(def, event, emitter, "priority", r->priority, NETPLAN_IP_RULE_PRIO_UNSPEC);
YAML_UINT_DEFAULT(def, event, emitter, "type-of-service", r->tos, NETPLAN_IP_RULE_TOS_UNSPEC);
YAML_UINT_DEFAULT(def, event, emitter, "mark", r->fwmark, NETPLAN_IP_RULE_FW_MARK_UNSPEC);
@@ -712,12 +779,12 @@ _serialize_yaml(
def->sriov_delay_virtual_functions_rebind);
/* Search interfaces */
- if (def->type == NETPLAN_DEF_TYPE_BRIDGE || def->type == NETPLAN_DEF_TYPE_BOND) {
+ if (def->type == NETPLAN_DEF_TYPE_BRIDGE || def->type == NETPLAN_DEF_TYPE_BOND || def->type == NETPLAN_DEF_TYPE_VRF) {
tmp_arr = g_array_new(FALSE, FALSE, sizeof(NetplanNetDefinition*));
g_hash_table_iter_init(&iter, np_state->netdefs);
while (g_hash_table_iter_next (&iter, &key, &value)) {
NetplanNetDefinition *nd = (NetplanNetDefinition *) value;
- if (g_strcmp0(nd->bond, def->id) == 0 || g_strcmp0(nd->bridge, def->id) == 0)
+ if (g_strcmp0(nd->bond, def->id) == 0 || g_strcmp0(nd->bridge, def->id) == 0 || nd->vrf_link == def)
g_array_append_val(tmp_arr, nd);
}
if (tmp_arr->len > 0) {
@@ -735,6 +802,7 @@ _serialize_yaml(
}
write_routes(event, emitter, def);
+ YAML_BOOL_TRISTATE(def, event, emitter, "neigh-suppress", def->bridge_neigh_suppress);
/* VLAN settings */
if (def->type == NETPLAN_DEF_TYPE_VLAN) {
@@ -743,6 +811,10 @@ _serialize_yaml(
YAML_STRING(def, event, emitter, "link", def->vlan_link->id);
}
+ /* VRF settings */
+ if (def->type == NETPLAN_DEF_TYPE_VRF)
+ YAML_UINT_DEFAULT(def, event, emitter, "table", def->vrf_table, G_MAXUINT);
+
/* Tunnel settings */
if (def->type == NETPLAN_DEF_TYPE_TUNNEL) {
write_tunnel_settings(event, emitter, def);
@@ -752,13 +824,20 @@ _serialize_yaml(
YAML_BOOL_TRUE(def, event, emitter, "wakeonlan", def->wake_on_lan);
/* Offload options */
- YAML_BOOL_TRUE(def, event, emitter, "receive-checksum-offload", def->receive_checksum_offload);
- YAML_BOOL_TRUE(def, event, emitter, "transmit-checksum-offload", def->transmit_checksum_offload);
- YAML_BOOL_TRUE(def, event, emitter, "tcp-segmentation-offload", def->tcp_segmentation_offload);
- YAML_BOOL_TRUE(def, event, emitter, "tcp6-segmentation-offload", def->tcp6_segmentation_offload);
- YAML_BOOL_TRUE(def, event, emitter, "generic-segmentation-offload", def->generic_segmentation_offload);
- YAML_BOOL_TRUE(def, event, emitter, "generic-receive-offload", def->generic_receive_offload);
- YAML_BOOL_TRUE(def, event, emitter, "large-receive-offload", def->large_receive_offload);
+ if (def->receive_checksum_offload != NETPLAN_TRISTATE_UNSET)
+ YAML_BOOL_TRUE(def, event, emitter, "receive-checksum-offload", def->receive_checksum_offload);
+ if (def->transmit_checksum_offload != NETPLAN_TRISTATE_UNSET)
+ YAML_BOOL_TRUE(def, event, emitter, "transmit-checksum-offload", def->transmit_checksum_offload);
+ if (def->tcp_segmentation_offload != NETPLAN_TRISTATE_UNSET)
+ YAML_BOOL_TRUE(def, event, emitter, "tcp-segmentation-offload", def->tcp_segmentation_offload);
+ if (def->tcp6_segmentation_offload != NETPLAN_TRISTATE_UNSET)
+ YAML_BOOL_TRUE(def, event, emitter, "tcp6-segmentation-offload", def->tcp6_segmentation_offload);
+ if (def->generic_segmentation_offload != NETPLAN_TRISTATE_UNSET)
+ YAML_BOOL_TRUE(def, event, emitter, "generic-segmentation-offload", def->generic_segmentation_offload);
+ if (def->generic_receive_offload != NETPLAN_TRISTATE_UNSET)
+ YAML_BOOL_TRUE(def, event, emitter, "generic-receive-offload", def->generic_receive_offload);
+ if (def->large_receive_offload != NETPLAN_TRISTATE_UNSET)
+ YAML_BOOL_TRUE(def, event, emitter, "large-receive-offload", def->large_receive_offload);
if (def->wowlan && def->wowlan != NETPLAN_WIFI_WOWLAN_DEFAULT) {
YAML_SCALAR_PLAIN(event, emitter, "wakeonwlan");
@@ -783,6 +862,8 @@ _serialize_yaml(
YAML_SEQUENCE_CLOSE(event, emitter);
}
+ YAML_STRING(def, event, emitter, "regulatory-domain", def->regulatory_domain);
+
if (def->optional_addresses) {
YAML_SCALAR_PLAIN(event, emitter, "optional-addresses");
YAML_SEQUENCE_OPEN(event, emitter);
@@ -812,6 +893,12 @@ _serialize_yaml(
write_openvswitch(event, emitter, &def->ovs_settings, def->backend, NULL);
+ /* InfiniBand */
+ if (def->ib_mode != NETPLAN_IB_MODE_KERNEL) {
+ const char* ib_mode_str = netplan_infiniband_mode_name(def->ib_mode);
+ YAML_STRING(def, event, emitter, "infiniband-mode", ib_mode_str);
+ }
+
if (def->type == NETPLAN_DEF_TYPE_MODEM)
write_modem_params(event, emitter, def);
@@ -990,6 +1077,64 @@ file_error:
}
/**
+ * Generate the YAML configuration, filtered to the data relevant to a particular file.
+ * Any data that's assigned to another file is ignored. Data that is not assigned is considered
+ * relevant.
+ *
+ * @np_state: the state for which to generate the config
+ * @filename: Relevant file basename
+ * @rootdir: If not %NULL, generate configuration in this root directory
+ * (useful for testing).
+ */
+gboolean
+netplan_state_write_yaml_file(const NetplanState* np_state, const char* filename, const char* rootdir, GError** error)
+{
+ GList* iter = np_state->netdefs_ordered;
+ g_autofree gchar* path = NULL;
+ g_autofree gchar* tmp_path = NULL;
+ GList* to_write = NULL;
+ int out_fd;
+
+ path = g_build_path(G_DIR_SEPARATOR_S, rootdir ?: G_DIR_SEPARATOR_S, "etc", "netplan", filename, NULL);
+
+ while (iter) {
+ NetplanNetDefinition* netdef = iter->data;
+ const char* fname = netdef->filepath ? netdef->filepath : path;
+ if (g_strcmp0(fname, path) == 0)
+ to_write = g_list_append(to_write, netdef);
+ iter = iter->next;
+ }
+
+ /* Remove any existing file if there is no data to write */
+ if (to_write == NULL) {
+ if (unlink(path) && errno != ENOENT) {
+ g_set_error(error, G_FILE_ERROR, errno, "%s", strerror(errno));
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ tmp_path = g_strdup_printf("%s.XXXXXX", path);
+ out_fd = mkstemp(tmp_path);
+ if (out_fd < 0) {
+ g_set_error(error, G_FILE_ERROR, errno, "%s", strerror(errno));
+ return FALSE;
+ }
+
+ gboolean ret = netplan_netdef_list_write_yaml(np_state, to_write, out_fd, error);
+ g_list_free(to_write);
+ close(out_fd);
+ if (ret) {
+ if (rename(tmp_path, path) == 0)
+ return TRUE;
+ g_set_error(error, G_FILE_ERROR, errno, "%s", strerror(errno));
+ }
+ /* Something went wrong, clean up the tempfile! */
+ unlink(tmp_path);
+ return FALSE;
+}
+
+/**
* Dump the whole state into a single YAML file.
*
* @np_state: the state for which to generate the config
@@ -1003,3 +1148,83 @@ netplan_state_dump_yaml(const NetplanState* np_state, int out_fd, GError** error
return netplan_netdef_list_write_yaml(np_state, np_state->netdefs_ordered, out_fd, error);
}
+
+/**
+ * Regenerate the YAML configuration files from a given state. Any state that
+ * hasn't an associated filepath will use the default_filename output in the
+ * standard config directory.
+ *
+ * @np_state: the state for which to generate the config
+ * @default_filename: Default config file, cannot be NULL or empty
+ * @rootdir: If not %NULL, generate configuration in this root directory
+ * (useful for testing).
+ */
+gboolean
+netplan_state_update_yaml_hierarchy(const NetplanState* np_state, const char* default_filename, const char* rootdir, GError** error)
+{
+ g_autofree gchar *default_path = NULL;
+ gboolean ret = FALSE;
+ GHashTableIter hash_iter;
+ gpointer key, value;
+ GHashTable *perfile_netdefs;
+
+ g_assert(default_filename != NULL && *default_filename != '\0');
+
+ perfile_netdefs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_list_free);
+ default_path = g_build_path(G_DIR_SEPARATOR_S, rootdir ?: G_DIR_SEPARATOR_S, "etc", "netplan", default_filename, NULL);
+ int out_fd = -1;
+
+ /* Dump global conf to the default path */
+ if (!np_state->netdefs || g_hash_table_size(np_state->netdefs) == 0) {
+ if ((np_state->backend != NETPLAN_BACKEND_NONE)
+ || has_openvswitch(&np_state->ovs_settings, NETPLAN_BACKEND_NONE, NULL)) {
+ g_hash_table_insert(perfile_netdefs, default_path, NULL);
+ }
+ } else {
+ GList* iter = np_state->netdefs_ordered;
+ while (iter) {
+ NetplanNetDefinition* netdef = iter->data;
+ const char* filename = netdef->filepath ? netdef->filepath : default_path;
+ GList* list = NULL;
+ g_hash_table_steal_extended(perfile_netdefs, filename, NULL, (gpointer*)&list);
+ g_hash_table_insert(perfile_netdefs, (gpointer)filename, g_list_append(list, netdef));
+ iter = iter->next;
+ }
+ }
+
+ g_hash_table_iter_init(&hash_iter, perfile_netdefs);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value)) {
+ const char *filename = key;
+ GList* netdefs = value;
+ out_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+ if (out_fd < 0)
+ goto file_error;
+ if (!netplan_netdef_list_write_yaml(np_state, netdefs, out_fd, error))
+ goto cleanup; // LCOV_EXCL_LINE
+ close(out_fd);
+ }
+
+ /* Remove any referenced source file that doesn't have any associated data.
+ Presumably, it is data that has been obsoleted by files loaded
+ afterwards, typically via `netplan set`. */
+ if (np_state->sources) {
+ g_hash_table_iter_init(&hash_iter, np_state->sources);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value)) {
+ if (!g_hash_table_contains(perfile_netdefs, key)) {
+ if (unlink(key) && errno != ENOENT)
+ goto file_error; // LCOV_EXCL_LINE
+ }
+ }
+ }
+ ret = TRUE;
+ goto cleanup;
+
+file_error:
+ g_set_error(error, G_FILE_ERROR, errno, "%s", strerror(errno));
+ ret = FALSE;
+cleanup:
+ if (out_fd >= 0)
+ close(out_fd);
+ g_hash_table_destroy(perfile_netdefs);
+ return ret;
+}
diff --git a/src/networkd.c b/src/networkd.c
index 6d26047..74cfd8f 100644
--- a/src/networkd.c
+++ b/src/networkd.c
@@ -243,13 +243,13 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
if (!def->set_name &&
!def->wake_on_lan &&
!def->mtubytes &&
- !def->receive_checksum_offload &&
- !def->transmit_checksum_offload &&
- !def->tcp_segmentation_offload &&
- !def->tcp6_segmentation_offload &&
- !def->generic_segmentation_offload &&
- !def->generic_receive_offload &&
- !def->large_receive_offload)
+ (def->receive_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
+ (def->transmit_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
+ (def->tcp_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
+ (def->tcp6_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
+ (def->generic_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
+ (def->generic_receive_offload == NETPLAN_TRISTATE_UNSET) &&
+ (def->large_receive_offload == NETPLAN_TRISTATE_UNSET))
return;
/* build file contents */
@@ -265,32 +265,65 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
g_string_append_printf(s, "MTUBytes=%u\n", def->mtubytes);
/* Offload options */
- if (def->receive_checksum_offload)
- g_string_append_printf(s, "ReceiveChecksumOffload=%u\n", def->receive_checksum_offload);
+ if (def->receive_checksum_offload != NETPLAN_TRISTATE_UNSET)
+ g_string_append_printf(s, "ReceiveChecksumOffload=%s\n",
+ (def->receive_checksum_offload ? "true" : "false"));
- if (def->transmit_checksum_offload)
- g_string_append_printf(s, "TransmitChecksumOffload=%u\n", def->transmit_checksum_offload);
+ if (def->transmit_checksum_offload != NETPLAN_TRISTATE_UNSET)
+ g_string_append_printf(s, "TransmitChecksumOffload=%s\n",
+ (def->transmit_checksum_offload ? "true" : "false"));
- if (def->tcp_segmentation_offload)
- g_string_append_printf(s, "TCPSegmentationOffload=%u\n", def->tcp_segmentation_offload);
+ if (def->tcp_segmentation_offload != NETPLAN_TRISTATE_UNSET)
+ g_string_append_printf(s, "TCPSegmentationOffload=%s\n",
+ (def->tcp_segmentation_offload ? "true" : "false"));
- if (def->tcp6_segmentation_offload)
- g_string_append_printf(s, "TCP6SegmentationOffload=%u\n", def->tcp6_segmentation_offload);
+ if (def->tcp6_segmentation_offload != NETPLAN_TRISTATE_UNSET)
+ g_string_append_printf(s, "TCP6SegmentationOffload=%s\n",
+ (def->tcp6_segmentation_offload ? "true" : "false"));
- if (def->generic_segmentation_offload)
- g_string_append_printf(s, "GenericSegmentationOffload=%u\n", def->generic_segmentation_offload);
+ if (def->generic_segmentation_offload != NETPLAN_TRISTATE_UNSET)
+ g_string_append_printf(s, "GenericSegmentationOffload=%s\n",
+ (def->generic_segmentation_offload ? "true" : "false"));
- if (def->generic_receive_offload)
- g_string_append_printf(s, "GenericReceiveOffload=%u\n", def->generic_receive_offload);
+ if (def->generic_receive_offload != NETPLAN_TRISTATE_UNSET)
+ g_string_append_printf(s, "GenericReceiveOffload=%s\n",
+ (def->generic_receive_offload ? "true" : "false"));
- if (def->large_receive_offload)
- g_string_append_printf(s, "LargeReceiveOffload=%u\n", def->large_receive_offload);
+ if (def->large_receive_offload != NETPLAN_TRISTATE_UNSET)
+ g_string_append_printf(s, "LargeReceiveOffload=%s\n",
+ (def->large_receive_offload ? "true" : "false"));
orig_umask = umask(022);
g_string_free_to_file(s, rootdir, path, ".link");
umask(orig_umask);
}
+static gboolean
+write_regdom(const NetplanNetDefinition* def, const char* rootdir, GError** error)
+{
+ g_assert(def->regulatory_domain);
+ g_autofree gchar* id_escaped = NULL;
+ g_autofree char* link = g_strjoin(NULL, rootdir ?: "", "/run/systemd/system/network.target.wants/netplan-regdom.service", NULL);
+ g_autofree char* path = g_strjoin(NULL, "/run/systemd/system/netplan-regdom.service", NULL);
+
+ GString* s = g_string_new("[Unit]\n");
+ g_string_append(s, "Description=Netplan regulatory-domain configuration\n");
+ g_string_append(s, "After=network.target\n");
+ g_string_append(s, "ConditionFileIsExecutable="SBINDIR"/iw\n");
+ g_string_append(s, "\n[Service]\nType=oneshot\n");
+ g_string_append_printf(s, "ExecStart="SBINDIR"/iw reg set %s\n", def->regulatory_domain);
+
+ g_string_free_to_file(s, rootdir, path, NULL);
+ safe_mkdir_p_dir(link);
+ if (symlink(path, link) < 0 && errno != EEXIST) {
+ // LCOV_EXCL_START
+ g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "failed to create enablement symlink: %m\n");
+ return FALSE;
+ // LCOV_EXCL_STOP
+ }
+ return TRUE;
+}
+
static gboolean
interval_has_suffix(const char* param) {
@@ -384,6 +417,79 @@ write_bond_parameters(const NetplanNetDefinition* def, GString* s)
}
static void
+write_vxlan_parameters(const NetplanNetDefinition* def, GString* s)
+{
+ g_assert(def->vxlan);
+ GString* params = NULL;
+
+ params = g_string_sized_new(200);
+
+ if (def->tunnel.remote_ip) {
+ if (is_multicast_address(def->tunnel.remote_ip))
+ g_string_append_printf(params, "\nGroup=%s", def->tunnel.remote_ip);
+ else
+ g_string_append_printf(params, "\nRemote=%s", def->tunnel.remote_ip);
+ }
+ if (def->tunnel.local_ip)
+ g_string_append_printf(params, "\nLocal=%s", def->tunnel.local_ip);
+ if (def->vxlan->tos)
+ g_string_append_printf(params, "\nTOS=%d", def->vxlan->tos);
+ if (def->tunnel_ttl)
+ g_string_append_printf(params, "\nTTL=%d", def->tunnel_ttl);
+ if (def->vxlan->mac_learning)
+ g_string_append_printf(params, "\nMacLearning=%s", def->vxlan->mac_learning ? "true" : "false");
+ if (def->vxlan->ageing)
+ g_string_append_printf(params, "\nFDBAgeingSec=%d", def->vxlan->ageing);
+ if (def->vxlan->limit)
+ g_string_append_printf(params, "\nMaximumFDBEntries=%d", def->vxlan->limit);
+ if (def->vxlan->arp_proxy)
+ g_string_append_printf(params, "\nReduceARPProxy=%s", def->vxlan->arp_proxy ? "true" : "false");
+ if (def->vxlan->notifications) {
+ if (def->vxlan->notifications & NETPLAN_VXLAN_NOTIFICATION_L2_MISS)
+ g_string_append(params, "\nL2MissNotification=true");
+ if (def->vxlan->notifications & NETPLAN_VXLAN_NOTIFICATION_L3_MISS)
+ g_string_append(params, "\nL3MissNotification=true");
+ }
+ if (def->vxlan->short_circuit)
+ g_string_append_printf(params, "\nRouteShortCircuit=%s", def->vxlan->short_circuit ? "true" : "false");
+ if (def->vxlan->checksums) {
+ if (def->vxlan->checksums & NETPLAN_VXLAN_CHECKSUM_UDP)
+ g_string_append(params, "\nUDPChecksum=true");
+ if (def->vxlan->checksums & NETPLAN_VXLAN_CHECKSUM_ZERO_UDP6_TX)
+ g_string_append(params, "\nUDP6ZeroChecksumTx=true");
+ if (def->vxlan->checksums & NETPLAN_VXLAN_CHECKSUM_ZERO_UDP6_RX)
+ g_string_append(params, "\nUDP6ZeroChecksumRx=true");
+ if (def->vxlan->checksums & NETPLAN_VXLAN_CHECKSUM_REMOTE_TX)
+ g_string_append(params, "\nRemoteChecksumTx=true");
+ if (def->vxlan->checksums & NETPLAN_VXLAN_CHECKSUM_REMOTE_RX)
+ g_string_append(params, "\nRemoteChecksumRx=true");
+ }
+ if (def->vxlan->extensions) {
+ if (def->vxlan->extensions & NETPLAN_VXLAN_EXTENSION_GROUP_POLICY)
+ g_string_append(params, "\nGroupPolicyExtension=true");
+ if (def->vxlan->extensions & NETPLAN_VXLAN_EXTENSION_GENERIC_PROTOCOL)
+ g_string_append(params, "\nGenericProtocolExtension=true");
+ }
+ if (def->tunnel.port)
+ g_string_append_printf(params, "\nDestinationPort=%d", def->tunnel.port);
+ if (def->vxlan->source_port_min && def->vxlan->source_port_max)
+ g_string_append_printf(params, "\nPortRange=%u-%u",
+ def->vxlan->source_port_min,
+ def->vxlan->source_port_max);
+ if (def->vxlan->flow_label != G_MAXUINT)
+ g_string_append_printf(params, "\nFlowLabel=%d", def->vxlan->flow_label);
+ if (def->vxlan->do_not_fragment != NETPLAN_TRISTATE_UNSET)
+ g_string_append_printf(params, "\nIPDoNotFragment=%s", def->vxlan->do_not_fragment ? "true" : "false");
+ if (!def->vxlan->link)
+ g_string_append(params, "\nIndependent=true");
+
+ if (params->len)
+ g_string_append_printf(s, "%s\n", params->str);
+
+ g_string_free(params, TRUE);
+}
+
+static void
write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const char* path)
{
GString* s = NULL;
@@ -420,6 +526,10 @@ write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const ch
g_string_append_printf(s, "Kind=vlan\n\n[VLAN]\nId=%u\n", def->vlan_id);
break;
+ case NETPLAN_DEF_TYPE_VRF:
+ g_string_append_printf(s, "Kind=vrf\n\n[VRF]\nTable=%u\n", def->vrf_table);
+ break;
+
case NETPLAN_DEF_TYPE_TUNNEL:
switch(def->tunnel.mode) {
case NETPLAN_TUNNEL_MODE_GRE:
@@ -435,6 +545,10 @@ write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const ch
netplan_tunnel_mode_name(def->tunnel.mode));
break;
+ case NETPLAN_TUNNEL_MODE_VXLAN:
+ g_string_append_printf(s, "Kind=vxlan\n\n[VXLAN]\nVNI=%u", def->vxlan->vni);
+ break;
+
case NETPLAN_TUNNEL_MODE_IP6IP6:
case NETPLAN_TUNNEL_MODE_IPIP6:
g_string_append(s, "Kind=ip6tnl\n");
@@ -447,6 +561,8 @@ write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const ch
}
if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_WIREGUARD)
write_wireguard_params(s, def);
+ else if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_VXLAN)
+ write_vxlan_parameters(def, s);
else
write_tunnel_params(s, def);
break;
@@ -483,7 +599,7 @@ write_route(NetplanIPRoute* r, GString* s)
if (g_strcmp0(r->type, "unicast") != 0)
g_string_append_printf(s, "Type=%s\n", r->type);
if (r->onlink)
- g_string_append_printf(s, "GatewayOnlink=true\n");
+ g_string_append_printf(s, "GatewayOnLink=true\n");
if (r->metric != NETPLAN_METRIC_UNSPEC)
g_string_append_printf(s, "Metric=%d\n", r->metric);
if (r->table != NETPLAN_ROUTE_TABLE_UNSPEC)
@@ -721,12 +837,18 @@ netplan_netdef_write_network_file(
if (def->bridge && def->backend != NETPLAN_BACKEND_OVS) {
g_string_append_printf(network, "Bridge=%s\n", def->bridge);
- if (def->bridge_params.path_cost || def->bridge_params.port_priority)
+ if ( def->bridge_params.path_cost
+ || def->bridge_params.port_priority
+ || def->bridge_neigh_suppress != NETPLAN_TRISTATE_UNSET)
g_string_append_printf(network, "\n[Bridge]\n");
if (def->bridge_params.path_cost)
g_string_append_printf(network, "Cost=%u\n", def->bridge_params.path_cost);
if (def->bridge_params.port_priority)
g_string_append_printf(network, "Priority=%u\n", def->bridge_params.port_priority);
+ if (def->bridge_neigh_suppress != NETPLAN_TRISTATE_UNSET) {
+ g_string_append_printf(network, "NeighborSuppression=%s\n", def->bridge_neigh_suppress ? "true" : "false");
+ }
+
}
if (def->bond && def->backend != NETPLAN_BACKEND_OVS) {
g_string_append_printf(network, "Bond=%s\n", def->bond);
@@ -746,6 +868,10 @@ netplan_netdef_write_network_file(
}
}
+ /* VRF linkage */
+ if (def->vrf_link)
+ g_string_append_printf(network, "VRF=%s\n", def->vrf_link->id);
+
if (def->routes != NULL) {
for (unsigned i = 0; i < def->routes->len; ++i) {
NetplanIPRoute* cur_route = g_array_index (def->routes, NetplanIPRoute*, i);
@@ -815,6 +941,25 @@ netplan_netdef_write_network_file(
g_string_append_printf(network, "Hostname=%s\n", combined_dhcp_overrides.hostname);
}
+ /* IP-over-InfiniBand, IPoIB */
+ if (def->ib_mode != NETPLAN_IB_MODE_KERNEL) {
+ g_string_append_printf(network, "\n[IPoIB]\nMode=%s\n", netplan_infiniband_mode_name(def->ib_mode));
+ }
+
+ /* VXLAN options */
+ if (def->has_vxlans) {
+ /* iterate over all netdefs to find VXLANs attached to us */
+ GList *l = np_state->netdefs_ordered;
+ const NetplanNetDefinition* nd;
+ for (; l != NULL; l = l->next) {
+ nd = l->data;
+ if (nd->vxlan && nd->vxlan->link == def &&
+ nd->type == NETPLAN_DEF_TYPE_TUNNEL &&
+ nd->tunnel.mode == NETPLAN_TUNNEL_MODE_VXLAN)
+ g_string_append_printf(network, "VXLAN=%s\n", nd->id);
+ }
+ }
+
if (network->len > 0 || link->len > 0) {
s = g_string_sized_new(200);
append_match_section(def, s, TRUE);
@@ -1020,6 +1165,10 @@ write_wpa_conf(const NetplanNetDefinition* def, const char* rootdir, GError** er
if (!append_wifi_wowlan_flags(def->wowlan, s, error))
return FALSE;
}
+ /* available as of wpa_supplicant version 0.6.7 */
+ if (def->regulatory_domain) {
+ g_string_append_printf(s, "country=%s\n", def->regulatory_domain);
+ }
NetplanWifiAccessPoint* ap;
g_hash_table_iter_init(&iter, def->access_points);
while (g_hash_table_iter_next(&iter, NULL, (gpointer) &ap)) {
@@ -1113,9 +1262,12 @@ netplan_netdef_write_networkd(
SET_OPT_OUT_PTR(has_been_written, FALSE);
/* We want this for all backends when renaming, as *.link and *.rules files are
- * evaluated by udev, not networkd itself or NetworkManager. */
+ * evaluated by udev, not networkd itself or NetworkManager. The regulatory
+ * domain applies to all backends, too. */
write_link_file(def, rootdir, path_base);
write_rules_file(def, rootdir);
+ if (def->regulatory_domain)
+ write_regdom(def, rootdir, NULL); /* overwrites global regdom */
if (def->backend != NETPLAN_BACKEND_NETWORKD) {
g_debug("networkd: definition %s is not for us (backend %i)", def->id, def->backend);
@@ -1173,6 +1325,8 @@ netplan_networkd_cleanup(const char* rootdir)
unlink_glob(rootdir, "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa-*.service");
unlink_glob(rootdir, "/run/systemd/system/netplan-wpa-*.service");
unlink_glob(rootdir, "/run/udev/rules.d/99-netplan-*");
+ unlink_glob(rootdir, "/run/systemd/system/network.target.wants/netplan-regdom.service");
+ unlink_glob(rootdir, "/run/systemd/system/netplan-regdom.service");
/* Historically (up to v0.98) we had netplan-wpa@*.service files, in case of an
* upgraded system, we need to make sure to clean those up. */
unlink_glob(rootdir, "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa@*.service");
diff --git a/src/nm.c b/src/nm.c
index 319a80b..5a2e62b 100644
--- a/src/nm.c
+++ b/src/nm.c
@@ -26,58 +26,15 @@
#include <glib/gprintf.h>
#include <uuid.h>
+#include "names.h"
#include "netplan.h"
#include "nm.h"
#include "parse.h"
#include "parse-globals.h"
+#include "parse-nm.h"
#include "util.h"
#include "util-internal.h"
#include "validation.h"
-#include "parse-nm.h"
-
-GString* udev_rules;
-
-/**
- * Append NM device specifier of @def to @s.
- */
-static void
-g_string_append_netdef_match(GString* s, const NetplanNetDefinition* def)
-{
- g_assert(!def->match.driver || def->set_name);
- if (def->match.mac || def->match.original_name || def->set_name || def->type >= NETPLAN_DEF_TYPE_VIRTUAL) {
- if (def->match.mac) {
- g_string_append_printf(s, "mac:%s,", def->match.mac);
- }
- /* MAC could change, e.g. for bond slaves. Ignore by interface-name as well */
- if (def->match.original_name || def->set_name || def->type >= NETPLAN_DEF_TYPE_VIRTUAL) {
- /* we always have the renamed name here */
- g_string_append_printf(s, "interface-name:%s,",
- (def->type >= NETPLAN_DEF_TYPE_VIRTUAL) ? def->id
- : (def->set_name ?: def->match.original_name));
- }
- } else {
- /* no matches → match all devices of that type */
- switch (def->type) {
- case NETPLAN_DEF_TYPE_ETHERNET:
- g_string_append(s, "type:ethernet,");
- break;
- /* This cannot be reached with just NM and networkd backends, as
- * networkd does not support wifi and thus we'll never blacklist a
- * wifi device from NM. This would become relevant with another
- * wifi-supporting backend, but until then this just spoils 100%
- * code coverage.
- case NETPLAN_DEF_TYPE_WIFI:
- g_string_append(s, "type:wifi");
- break;
- */
-
- // LCOV_EXCL_START
- default:
- g_assert_not_reached();
- // LCOV_EXCL_STOP
- }
- }
-}
/**
* Infer if this is a modem netdef of type GSM.
@@ -108,7 +65,11 @@ type_str(const NetplanNetDefinition* def)
const NetplanDefType type = def->type;
switch (type) {
case NETPLAN_DEF_TYPE_ETHERNET:
- return "ethernet";
+ /* 20-byte IPoIB MAC + colons */
+ if (def->ib_mode || (def->match.mac && strlen(def->match.mac) == 59))
+ return "infiniband";
+ else
+ return "ethernet";
case NETPLAN_DEF_TYPE_MODEM:
if (modem_is_gsm(def))
return "gsm";
@@ -122,13 +83,19 @@ type_str(const NetplanNetDefinition* def)
return "bond";
case NETPLAN_DEF_TYPE_VLAN:
return "vlan";
+ case NETPLAN_DEF_TYPE_VRF:
+ return "vrf";
case NETPLAN_DEF_TYPE_TUNNEL:
if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_WIREGUARD)
return "wireguard";
+ else if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_VXLAN)
+ return "vxlan";
return "ip-tunnel";
case NETPLAN_DEF_TYPE_NM:
/* needs to be overriden by passthrough "connection.type" setting */
- return NULL;
+ g_assert(def->backend_settings.nm.passthrough);
+ GData *passthrough = def->backend_settings.nm.passthrough;
+ return g_datalist_get_data(&passthrough, "connection.type");
// LCOV_EXCL_START
default:
g_assert_not_reached();
@@ -431,9 +398,6 @@ write_tunnel_params(const NetplanNetDefinition* def, GKeyFile *kf)
static void
write_dot1x_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *kf)
{
- if (auth->eap_method == NETPLAN_AUTH_EAP_NONE)
- return;
-
switch (auth->eap_method) {
case NETPLAN_AUTH_EAP_TLS:
g_key_file_set_string(kf, "802-1x", "eap", "tls");
@@ -468,14 +432,11 @@ write_dot1x_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile
static void
write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *kf)
{
- if (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_NONE)
- return;
-
switch (auth->key_management) {
+ case NETPLAN_AUTH_KEY_MANAGEMENT_NONE:
+ break;
case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK:
g_key_file_set_string(kf, "wifi-security", "key-mgmt", "wpa-psk");
- if (auth->password)
- g_key_file_set_string(kf, "wifi-security", "psk", auth->password);
break;
case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP:
g_key_file_set_string(kf, "wifi-security", "key-mgmt", "wpa-eap");
@@ -486,7 +447,10 @@ write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *
default: break; // LCOV_EXCL_LINE
}
- write_dot1x_auth_parameters(auth, kf);
+ if (auth->eap_method != NETPLAN_AUTH_EAP_NONE)
+ write_dot1x_auth_parameters(auth, kf);
+ else if (auth->password)
+ g_key_file_set_string(kf, "wifi-security", "psk", auth->password);
}
static void
@@ -496,6 +460,60 @@ maybe_generate_uuid(const NetplanNetDefinition* def)
uuid_generate((unsigned char*)def->uuid);
}
+static void
+write_vxlan_parameters(const NetplanNetDefinition* def, GKeyFile* kf)
+{
+ g_assert(def->vxlan);
+ char uuidstr[37];
+ if (def->vxlan->ageing)
+ g_key_file_set_uint64(kf, "vxlan", "ageing", def->vxlan->ageing);
+ if (def->tunnel.port)
+ g_key_file_set_uint64(kf, "vxlan", "destination-port", def->tunnel.port);
+ if (def->vxlan->vni)
+ g_key_file_set_uint64(kf, "vxlan", "id", def->vxlan->vni);
+ if (def->vxlan->mac_learning)
+ g_key_file_set_boolean(kf, "vxlan", "learning", def->vxlan->mac_learning);
+ if (def->vxlan->limit)
+ g_key_file_set_uint64(kf, "vxlan", "limit", def->vxlan->limit);
+ if (def->tunnel.local_ip)
+ g_key_file_set_string(kf, "vxlan", "local", def->tunnel.local_ip);
+ if (def->tunnel.remote_ip)
+ g_key_file_set_string(kf, "vxlan", "remote", def->tunnel.remote_ip);
+ if (def->vxlan->arp_proxy)
+ g_key_file_set_boolean(kf, "vxlan", "proxy", def->vxlan->arp_proxy);
+ if (def->vxlan->notifications) {
+ if (def->vxlan->notifications & NETPLAN_VXLAN_NOTIFICATION_L2_MISS)
+ g_key_file_set_boolean(kf, "vxlan", "l2-miss", TRUE);
+ if (def->vxlan->notifications & NETPLAN_VXLAN_NOTIFICATION_L3_MISS)
+ g_key_file_set_boolean(kf, "vxlan", "l3-miss", TRUE);
+ }
+ if (def->vxlan->source_port_min && def->vxlan->source_port_max) {
+ g_key_file_set_uint64(kf, "vxlan", "source-port-min", def->vxlan->source_port_min);
+ g_key_file_set_uint64(kf, "vxlan", "source-port-max", def->vxlan->source_port_max);
+ }
+ if (def->vxlan->tos)
+ g_key_file_set_uint64(kf, "vxlan", "tos", def->vxlan->tos);
+ if (def->tunnel_ttl)
+ g_key_file_set_uint64(kf, "vxlan", "ttl", def->tunnel_ttl);
+ if (def->vxlan->short_circuit)
+ g_key_file_set_boolean(kf, "vxlan", "rsc", def->vxlan->short_circuit);
+ if (def->vxlan->link) {
+ if (def->vxlan->link->has_match) {
+ /* we need to refer to the parent's UUID as we don't have an
+ * interface name with match: */
+ maybe_generate_uuid(def->vxlan->link);
+ uuid_unparse(def->vxlan->link->uuid, uuidstr);
+ g_key_file_set_string(kf, "vxlan", "parent", uuidstr);
+ } else {
+ /* if we have an interface name, use that as parent */
+ g_key_file_set_string(kf, "vxlan", "parent", def->vxlan->link->id);
+ }
+ }
+
+ if (def->vxlan->checksums || def->vxlan->extensions || def->vxlan->flow_label != G_MAXUINT || def->vxlan->do_not_fragment)
+ g_warning("%s: checksums/extensions/flow-lable/do-not-fragment are not supported by NetworkManager\n", def->id);
+}
+
/**
* Special handling for passthrough mode: read key-value pairs from
* "backend_settings.nm.passthrough" and inject them into the keyfile as-is.
@@ -595,17 +613,17 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir,
}
nm_type = type_str(def);
- if (nm_type)
+ if (nm_type && def->type != NETPLAN_DEF_TYPE_NM)
g_key_file_set_string(kf, "connection", "type", nm_type);
if (ap && ap->backend_settings.nm.uuid)
g_key_file_set_string(kf, "connection", "uuid", ap->backend_settings.nm.uuid);
else if (def->backend_settings.nm.uuid)
g_key_file_set_string(kf, "connection", "uuid", def->backend_settings.nm.uuid);
- /* VLAN devices refer to us as their parent; if our ID is not a name but we
- * have matches, parent= must be the connection UUID, so put it into the
- * connection */
- if (def->has_vlans && def->has_match) {
+ /* VLAN/VXLAN devices refer to us as their parent; if our ID is not a name
+ * but we have matches, parent= must be the connection UUID, so put it into
+ * the connection */
+ if ((def->has_vlans || def->has_vxlans) && def->has_match) {
maybe_generate_uuid(def);
uuid_unparse(def->uuid, uuidstr);
g_key_file_set_string(kf, "connection", "uuid", uuidstr);
@@ -646,6 +664,11 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir,
if (def->type == NETPLAN_DEF_TYPE_BRIDGE)
write_bridge_params(def, kf);
+ if (def->type == NETPLAN_DEF_TYPE_VRF) {
+ g_key_file_set_uint64(kf, "vrf", "table", def->vrf_table);
+ write_routes(def, kf, AF_INET, error);
+ write_routes(def, kf, AF_INET6, error);
+ }
}
if (def->type == NETPLAN_DEF_TYPE_MODEM) {
const char* modem_type = modem_is_gsm(def) ? "gsm" : "cdma";
@@ -692,35 +715,31 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir,
g_key_file_set_string(kf, "connection", "master", def->bond);
}
+ if (def->vrf_link) {
+ g_key_file_set_string(kf, "connection", "slave-type", "vrf");
+ g_key_file_set_string(kf, "connection", "master", def->vrf_link->id);
+ }
+
if (def->ipv6_mtubytes) {
g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "ERROR: %s: NetworkManager definitions do not support ipv6-mtu\n", def->id);
return FALSE;
}
if (def->type < NETPLAN_DEF_TYPE_VIRTUAL) {
- if (def->type == NETPLAN_DEF_TYPE_ETHERNET)
- g_key_file_set_integer(kf, "ethernet", "wake-on-lan", def->wake_on_lan ? 1 : 0);
-
- const char* con_type = NULL;
- switch (def->type) {
- case NETPLAN_DEF_TYPE_WIFI:
- con_type = "wifi";
- case NETPLAN_DEF_TYPE_MODEM:
- /* Avoid adding an [ethernet] section into the [gsm/cdma] description. */
- break;
- default:
- con_type = "ethernet";
- }
-
- if (con_type) {
+ /* Avoid adding an [ethernet] section into the [gsm/cdma] description. */
+ if (g_strcmp0(nm_type, "gsm") != 0 || g_strcmp0(nm_type, "cdma") != 0) {
+ if (g_strcmp0(nm_type, "ethernet") == 0)
+ g_key_file_set_integer(kf, nm_type, "wake-on-lan", def->wake_on_lan ? 1 : 0);
if (!def->set_name && def->match.mac)
- g_key_file_set_string(kf, con_type, "mac-address", def->match.mac);
+ g_key_file_set_string(kf, nm_type, "mac-address", def->match.mac);
if (def->set_mac)
- g_key_file_set_string(kf, con_type, "cloned-mac-address", def->set_mac);
+ g_key_file_set_string(kf, nm_type, "cloned-mac-address", def->set_mac);
if (def->mtubytes)
- g_key_file_set_uint64(kf, con_type, "mtu", def->mtubytes);
+ g_key_file_set_uint64(kf, nm_type, "mtu", def->mtubytes);
if (def->wowlan && def->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT)
- g_key_file_set_uint64(kf, con_type, "wake-on-wlan", def->wowlan);
+ g_key_file_set_uint64(kf, nm_type, "wake-on-wlan", def->wowlan);
+ if (def->ib_mode != NETPLAN_IB_MODE_KERNEL)
+ g_key_file_set_string(kf, nm_type, "transport-mode", netplan_infiniband_mode_name(def->ib_mode));
}
} else {
if (def->set_mac)
@@ -752,6 +771,8 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir,
if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_WIREGUARD) {
if (!write_wireguard_params(def, kf, error))
return FALSE;
+ } else if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_VXLAN) {
+ write_vxlan_parameters(def, kf);
} else
write_tunnel_params(def, kf);
}
@@ -960,49 +981,108 @@ netplan_netdef_write_nm(
return no_error;
}
-static void
-nd_append_non_nm_ids(gpointer data, gpointer str)
-{
- const NetplanNetDefinition* nd = data;
-
- if (nd->backend != NETPLAN_BACKEND_NM) {
- if (nd->match.driver) {
- /* TODO: NetworkManager supports (non-globbing) "driver:..." matching nowadays */
- /* NM cannot match on drivers, so ignore these via udev rules */
- if (!udev_rules)
- udev_rules = g_string_new(NULL);
- g_string_append_printf(udev_rules, "ACTION==\"add|change\", SUBSYSTEM==\"net\", ENV{ID_NET_DRIVER}==\"%s\", ENV{NM_UNMANAGED}=\"1\"\n", nd->match.driver);
- } else {
- g_string_append_netdef_match((GString*) str, nd);
- }
- }
-}
-
gboolean
netplan_state_finish_nm_write(
const NetplanState* np_state,
const char* rootdir,
GError** error)
{
- GString *s = NULL;
- gsize len;
+ GString* udev_rules = g_string_new(NULL);
+ GString *nm_conf = g_string_new(NULL);
if (netplan_state_get_netdefs_size(np_state) == 0)
return TRUE; // LCOV_EXCL_LINE as generate.c already deals with it.
/* Set all devices not managed by us to unmanaged, so that NM does not
- * auto-connect and interferes */
- s = g_string_new("[keyfile]\n# devices managed by networkd\nunmanaged-devices+=");
- len = s->len;
- g_list_foreach(np_state->netdefs_ordered, nd_append_non_nm_ids, s);
- if (s->len > len)
- g_string_free_to_file(s, rootdir, "run/NetworkManager/conf.d/netplan.conf", NULL);
+ * auto-connect and interferes.
+ * Also, mark all devices managed by us explicitly, so it won't get in
+ * conflict with the system's udev rules that might ignore some devices
+ * in containers via usr/lib/udev/rules.d/85-nm-unmanaged-devices.rules */
+ GList* iter = np_state->netdefs_ordered;
+ while (iter) {
+ const NetplanNetDefinition* nd = iter->data;
+ const gchar* nm_type;
+ GString *tmp = NULL;
+ guint unmanaged = nd->backend == NETPLAN_BACKEND_NM ? 0 : 1;
+
+ /* Special case: manage or ignore any device of given type on empty "match: {}" stanza */
+ if (nd->has_match && !nd->match.driver && !nd->match.mac && !nd->match.original_name) {
+ nm_type = type_str(nd);
+ g_assert(nm_type);
+ g_string_append_printf(nm_conf, "[device-netplan.%s.%s]\nmatch-device=type:%s\n"
+ "managed=%d\n\n", netplan_def_type_name(nd->type),
+ nd->id, nm_type, !unmanaged);
+ }
+ /* Normal case: manage or ignore devices by specific udev rules */
+ else {
+ const gchar *prefix = "SUBSYSTEM==\"net\", ACTION==\"add|change|move\",";
+ const gchar *suffix = nd->backend == NETPLAN_BACKEND_NM ? " ENV{NM_UNMANAGED}=\"0\"\n" : " ENV{NM_UNMANAGED}=\"1\"\n";
+ g_string_append_printf(udev_rules, "# netplan: network.%s.%s (on NetworkManager %s)\n",
+ netplan_def_type_name(nd->type), nd->id,
+ unmanaged ? "deny-list" : "allow-list");
+ /* Match by explicit interface name, if possible */
+ if (nd->set_name) {
+ // simple case: explicit new interface name
+ g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, nd->set_name, suffix);
+ } else if (!nd->has_match) {
+ // simple case: explicit netplan ID is interface name
+ g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, nd->id, suffix);
+ }
+ /* Also, match by explicit (new) MAC, if available */
+ if (nd->set_mac) {
+ tmp = g_string_new(nd->set_mac);
+ g_string_append_printf(udev_rules, "%s ATTR{address}==\"%s\",%s", prefix, g_string_ascii_down(tmp)->str, suffix);
+ g_string_free(tmp, TRUE);
+ }
+ /* Finally, add a full match, using all rules & globs available
+ * from the "match" stanza (e.g. original_name/mac/drivers)
+ * This will match the "old" interface (i.e. original MAC and/or
+ * interface name) if it got changed */
+ if (nd->has_match && (nd->match.original_name || nd->match.mac || nd->match.driver)) {
+ // match on original name glob
+ // TODO: maybe support matching on multiple name globs in the future (like drivers)
+ g_string_append(udev_rules, prefix);
+ if (nd->match.original_name)
+ g_string_append_printf(udev_rules, " ENV{ID_NET_NAME}==\"%s\",", nd->match.original_name);
+
+ // match on (explicit) MAC address. Yes this would be unique on its own, but we
+ // keep it within the "full match" to make the logic more comprehensible.
+ if (nd->match.mac) {
+ tmp = g_string_new(nd->match.mac);
+ g_string_append_printf(udev_rules, " ATTR{address}==\"%s\",", g_string_ascii_down(tmp)->str);
+ g_string_free(tmp, TRUE);
+ }
+
+ // match on (multiple) driver globs
+ if (nd->match.driver) {
+ gchar *drivers = NULL;
+ if (strchr(nd->match.driver, '\t')) {
+ gchar **split = g_strsplit(nd->match.driver, "\t", -1);
+ drivers = g_strjoinv("|", split);
+ g_strfreev(split);
+ } else
+ drivers = g_strdup(nd->match.driver);
+ g_string_append_printf(udev_rules, " ENV{ID_NET_DRIVER}==\"%s\",", drivers);
+ g_free(drivers);
+ }
+ g_string_append(udev_rules, suffix);
+ }
+ }
+ iter = iter->next;
+ }
+
+ /* write generated NetworkManager drop-in config */
+ if (nm_conf->len > 0)
+ g_string_free_to_file(nm_conf, rootdir, "run/NetworkManager/conf.d/netplan.conf", NULL);
else
- g_string_free(s, TRUE);
+ g_string_free(nm_conf, TRUE);
/* write generated udev rules */
- if (udev_rules)
+ if (udev_rules->len > 0)
g_string_free_to_file(udev_rules, rootdir, "run/udev/rules.d/90-netplan.rules", NULL);
+ else
+ g_string_free(udev_rules, TRUE);
+
return TRUE;
}
diff --git a/src/openvswitch.c b/src/openvswitch.c
index 7479267..b625588 100644
--- a/src/openvswitch.c
+++ b/src/openvswitch.c
@@ -59,7 +59,7 @@ write_ovs_systemd_unit(const char* id, const GString* cmds, const char* rootdir,
g_string_append_printf(s, "After=netplan-ovs-%s.service\n", dependency);
}
- g_string_append(s, "\n[Service]\nType=oneshot\n");
+ g_string_append(s, "\n[Service]\nType=oneshot\nTimeoutStartSec=10s\n");
g_string_append(s, cmds->str);
g_string_free_to_file(s, rootdir, path, NULL);
diff --git a/src/parse-nm.c b/src/parse-nm.c
index 38e25dc..05d63ad 100644
--- a/src/parse-nm.c
+++ b/src/parse-nm.c
@@ -229,7 +229,8 @@ parse_routes(GKeyFile* kf, const gchar* group, GArray** routes_arr)
route->to = g_strdup(split[0]); //no need to free, will stay in netdef
/* Append gateway/via IP */
if (split[0] && split[1] &&
- g_strcmp0(split[1], get_unspecified_address(route->family)) != 0) {
+ g_strcmp0(split[1], get_unspecified_address(route->family)) != 0 &&
+ g_strcmp0(split[1], "") != 0) {
route->scope = g_strdup("global");
route->via = g_strdup(split[1]); //no need to free, will stay in netdef
} else {
@@ -298,11 +299,12 @@ parse_dhcp_overrides(GKeyFile* kf, const gchar* group, NetplanDHCPOverrides* dat
handle_generic_uint(kf, group, "route-metric", &(*dataptr).metric, NETPLAN_METRIC_UNSPEC);
}
+/*
static void
parse_search_domains(GKeyFile* kf, const gchar* group, GArray** domains_arr)
{
- /* Keep "dns-search" as fallback/passthrough, as netplan cannot
- * differentiate between ipv4.dns-search and ipv6.dns-search */
+ // Keep "dns-search" as fallback/passthrough, as netplan cannot
+ // differentiate between ipv4.dns-search and ipv6.dns-search
g_assert(domains_arr);
gsize len = 0;
gchar **split = g_key_file_get_string_list(kf, group, "dns-search", &len, NULL);
@@ -323,6 +325,7 @@ parse_search_domains(GKeyFile* kf, const gchar* group, GArray** domains_arr)
g_strfreev(split);
}
}
+*/
static void
parse_nameservers(GKeyFile* kf, const gchar* group, GArray** nameserver_arr)
@@ -544,9 +547,10 @@ netplan_parser_load_keyfile(NetplanParser* npp, const char* filename, GError** e
parse_routes(kf, "ipv4", &nd->routes);
parse_routes(kf, "ipv6", &nd->routes);
- /* DNS: XXX: How to differentiate ip4/ip6 search_domains? */
+ /* DNS: XXX: How to differentiate ip4/ip6 search_domains?
parse_search_domains(kf, "ipv4", &nd->search_domains);
parse_search_domains(kf, "ipv6", &nd->search_domains);
+ */
parse_nameservers(kf, "ipv4", &nd->ip4_nameservers);
parse_nameservers(kf, "ipv6", &nd->ip6_nameservers);
diff --git a/src/parse.c b/src/parse.c
index 350c508..8150f8b 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -45,6 +45,7 @@
#define ovs_settings_offset(field) GUINT_TO_POINTER(offsetof(NetplanOVSSettings, field))
#define route_offset(field) GUINT_TO_POINTER(offsetof(NetplanIPRoute, field))
#define wireguard_peer_offset(field) GUINT_TO_POINTER(offsetof(NetplanWireguardPeer, field))
+#define vxlan_offset(field) GUINT_TO_POINTER(offsetof(NetplanVxlan, field))
/* convenience macro to avoid strdup'ing a string into a field if it's already set. */
#define set_str_if_null(dst, src) { if (dst) {\
@@ -58,8 +59,54 @@ extern NetplanState global_state;
NetplanParser global_parser = {0};
/**
+ * Load YAML file into a yaml_document_t.
+ *
+ * @input_fd: the file descriptor pointing to the YAML source file
+ * @doc: the output document structure
+ *
+ * Returns: TRUE on success, FALSE if the document is malformed; @error gets set then.
+ */
+static gboolean
+load_yaml_from_fd(int input_fd, yaml_document_t* doc, GError** error)
+{
+ int in_dup = -1;
+ FILE* fyaml = NULL;
+ yaml_parser_t parser;
+ gboolean ret = TRUE;
+
+ in_dup = dup(input_fd);
+ if (in_dup < 0)
+ goto file_error; // LCOV_EXCL_LINE
+
+ fyaml = fdopen(in_dup, "r");
+ if (!fyaml)
+ goto file_error; // LCOV_EXCL_LINE
+
+ yaml_parser_initialize(&parser);
+ yaml_parser_set_input_file(&parser, fyaml);
+ if (!yaml_parser_load(&parser, doc)) {
+ ret = parser_error(&parser, NULL, error);
+ }
+
+ yaml_parser_delete(&parser);
+ fclose(fyaml);
+ return ret;
+
+ // LCOV_EXCL_START
+file_error:
+ g_set_error(error, G_FILE_ERROR, errno, "Error when opening FD %d: %s", input_fd, g_strerror(errno));
+ if (in_dup >= 0)
+ close(in_dup);
+ return FALSE;
+ // LCOV_EXCL_STOP
+}
+
+/**
* Load YAML file name into a yaml_document_t.
*
+ * @yaml: file path to the YAML source file
+ * @doc: the output document structure
+ *
* Returns: TRUE on success, FALSE if the document is malformed; @error gets set then.
*/
static gboolean
@@ -187,7 +234,7 @@ netplan_netdef_new(NetplanParser *npp, const char* id, NetplanDefType type, Netp
typedef gboolean (*node_handler) (NetplanParser* npp, yaml_node_t* node, const void* data, GError** error);
-typedef gboolean (*custom_map_handler) (NetplanParser* npp, yaml_node_t* node, const void* data, GError** error);
+typedef gboolean (*custom_map_handler) (NetplanParser* npp, yaml_node_t* node, const char *prefix, const void* data, GError** error);
typedef struct mapping_entry_handler_s {
/* mapping key (must be scalar) */
@@ -230,7 +277,7 @@ get_handler(const mapping_entry_handler* handlers, const char* key)
* Returns: TRUE on success, FALSE on error (@error gets set then).
*/
static gboolean
-process_mapping(NetplanParser* npp, yaml_node_t* node, const mapping_entry_handler* handlers, GList** out_values, GError** error)
+process_mapping(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const mapping_entry_handler* handlers, GList** out_values, GError** error)
{
yaml_node_pair_t* entry;
@@ -240,12 +287,18 @@ process_mapping(NetplanParser* npp, yaml_node_t* node, const mapping_entry_handl
yaml_node_t* key, *value;
const mapping_entry_handler* h;
gboolean res = TRUE;
+ g_autofree char* full_key = NULL;
g_assert(error == NULL || *error == NULL);
key = yaml_document_get_node(&npp->doc, entry->key);
value = yaml_document_get_node(&npp->doc, entry->value);
assert_type(npp, key, YAML_SCALAR_NODE);
+ if (npp->null_fields && key_prefix) {
+ full_key = g_strdup_printf("%s\t%s", key_prefix, scalar(key));
+ if (g_hash_table_contains(npp->null_fields, full_key))
+ continue;
+ }
h = get_handler(handlers, scalar(key));
if (!h)
return yaml_error(npp, key, error, "unknown key '%s'", scalar(key));
@@ -254,11 +307,11 @@ process_mapping(NetplanParser* npp, yaml_node_t* node, const mapping_entry_handl
*out_values = g_list_prepend(*out_values, g_strdup(scalar(key)));
if (h->type == YAML_MAPPING_NODE) {
if (h->map.custom)
- res = h->map.custom(npp, value, h->data, error);
+ res = h->map.custom(npp, value, full_key, h->data, error);
else
- res = process_mapping(npp, value, h->map.handlers, NULL, error);
+ res = process_mapping(npp, value, full_key, h->map.handlers, NULL, error);
} else if (h->type == YAML_NO_NODE) {
- res = h->variable(npp, value, h->data, error);
+ res = h->variable(npp, value, full_key, h->data, error);
} else {
res = h->generic(npp, value, h->data, error);
}
@@ -290,7 +343,7 @@ handle_generic_guint(NetplanParser* npp, yaml_node_t* node, const void* entryptr
if (*endptr != '\0' || v > G_MAXUINT)
return yaml_error(npp, node, error, "invalid unsigned int value '%s'", scalar(node));
- mark_data_as_dirty(npp, endptr);
+ mark_data_as_dirty(npp, entryptr + offset);
*((guint*) ((void*) entryptr + offset)) = (guint) v;
return TRUE;
}
@@ -329,12 +382,12 @@ handle_generic_mac(NetplanParser* npp, yaml_node_t* node, void* entryptr, const
g_assert(node->type == YAML_SCALAR_NODE);
if (!re_inited) {
- g_assert(regcomp(&re, "^[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]$", REG_EXTENDED|REG_NOSUB) == 0);
+ g_assert(regcomp(&re, "^[[:xdigit:]][[:xdigit:]](:[[:xdigit:]][[:xdigit:]]){5}((:[[:xdigit:]][[:xdigit:]]){14})?$", REG_EXTENDED|REG_NOSUB) == 0);
re_inited = TRUE;
}
if (regexec(&re, scalar(node), 0, NULL, 0) != 0)
- return yaml_error(npp, node, error, "Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX", scalar(node));
+ return yaml_error(npp, node, error, "Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX or XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX", scalar(node));
return handle_generic_str(npp, node, entryptr, data, error);
}
@@ -374,9 +427,40 @@ handle_generic_bool(NetplanParser* npp, yaml_node_t* node, void* entryptr, const
* Handler for setting a HashTable field from a mapping node, inside a given struct
* @entryptr: pointer to the beginning of the to-be-modified data structure
* @data: offset into entryptr struct where the boolean field to write is located
+ */
+static gboolean
+handle_generic_tristate(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
+{
+ g_assert(entryptr);
+ NetplanTristate v;
+ guint offset = GPOINTER_TO_UINT(data);
+ NetplanTristate* dest = ((void*) entryptr + offset);
+
+ if (g_ascii_strcasecmp(scalar(node), "true") == 0 ||
+ g_ascii_strcasecmp(scalar(node), "on") == 0 ||
+ g_ascii_strcasecmp(scalar(node), "yes") == 0 ||
+ g_ascii_strcasecmp(scalar(node), "y") == 0)
+ v = NETPLAN_TRISTATE_TRUE;
+ else if (g_ascii_strcasecmp(scalar(node), "false") == 0 ||
+ g_ascii_strcasecmp(scalar(node), "off") == 0 ||
+ g_ascii_strcasecmp(scalar(node), "no") == 0 ||
+ g_ascii_strcasecmp(scalar(node), "n") == 0)
+ v = NETPLAN_TRISTATE_FALSE;
+ else
+ return yaml_error(npp, node, error, "invalid boolean value '%s'", scalar(node));
+
+ *dest = v;
+ mark_data_as_dirty(npp, dest);
+ return TRUE;
+}
+
+/*
+ * Handler for setting a HashTable field from a mapping node, inside a given struct
+ * @entryptr: pointer to the beginning of the to-be-modified data structure
+ * @data: offset into entryptr struct where the boolean field to write is located
*/
static gboolean
-handle_generic_map(NetplanParser *npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
+handle_generic_map(NetplanParser *npp, yaml_node_t* node, const char* key_prefix, void* entryptr, const void* data, GError** error)
{
guint offset = GPOINTER_TO_UINT(data);
GHashTable** map = (GHashTable**) ((void*) entryptr + offset);
@@ -392,9 +476,22 @@ handle_generic_map(NetplanParser *npp, yaml_node_t* node, void* entryptr, const
assert_type(npp, key, YAML_SCALAR_NODE);
assert_type(npp, value, YAML_SCALAR_NODE);
- /* TODO: make sure we free all the memory here */
- if (!g_hash_table_insert(*map, g_strdup(scalar(key)), g_strdup(scalar(value))))
+ if (key_prefix && npp->null_fields) {
+ g_autofree char* full_key = NULL;
+ full_key = g_strdup_printf("%s\t%s", key_prefix, key->data.scalar.value);
+ if (g_hash_table_contains(npp->null_fields, full_key))
+ continue;
+ }
+
+ char* stored_value = NULL;
+ if (g_hash_table_lookup_extended(*map, scalar(key), NULL, (void**)&stored_value)) {
+ /* We can safely skip this if it is the exact key/value match
+ * (probably caused by multi-pass processing) */
+ if (g_strcmp0(stored_value, scalar(value)) == 0)
+ continue;
return yaml_error(npp, node, error, "duplicate map entry '%s'", scalar(key));
+ } else
+ g_hash_table_insert(*map, g_strdup(scalar(key)), g_strdup(scalar(value)));
}
mark_data_as_dirty(npp, map);
@@ -407,7 +504,7 @@ handle_generic_map(NetplanParser *npp, yaml_node_t* node, void* entryptr, const
* @data: offset into entryptr struct where the boolean field to write is located
*/
static gboolean
-handle_generic_datalist(NetplanParser *npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
+handle_generic_datalist(NetplanParser *npp, yaml_node_t* node, const char* key_prefix, void* entryptr, const void* data, GError** error)
{
guint offset = GPOINTER_TO_UINT(data);
GData** list = (GData**) ((void*) entryptr + offset);
@@ -416,12 +513,18 @@ handle_generic_datalist(NetplanParser *npp, yaml_node_t* node, void* entryptr, c
for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) {
yaml_node_t* key, *value;
+ g_autofree char* full_key = NULL;
key = yaml_document_get_node(&npp->doc, entry->key);
value = yaml_document_get_node(&npp->doc, entry->value);
assert_type(npp, key, YAML_SCALAR_NODE);
assert_type(npp, value, YAML_SCALAR_NODE);
+ if (npp->null_fields && key_prefix) {
+ full_key = g_strdup_printf("%s\t%s", key_prefix, scalar(key));
+ if (g_hash_table_contains(npp->null_fields, full_key))
+ continue;
+ }
g_datalist_set_data_full(list, g_strdup(scalar(key)), g_strdup(scalar(value)), g_free);
}
@@ -463,6 +566,18 @@ handle_embedded_switch_mode(NetplanParser* npp, yaml_node_t* node, const void* d
return handle_netdef_str(npp, node, data, error);
}
+static gboolean
+handle_ib_mode(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+{
+ if (g_strcmp0(scalar(node), "datagram") == 0)
+ npp->current.netdef->ib_mode = NETPLAN_IB_MODE_DATAGRAM;
+ else if (g_strcmp0(scalar(node), "connected") == 0)
+ npp->current.netdef->ib_mode = NETPLAN_IB_MODE_CONNECTED;
+ else
+ return yaml_error(npp, node, error, "Value of 'infiniband-mode' needs to be 'datagram' or 'connected'");
+ return TRUE;
+}
+
/**
* Generic handler for setting a npp->current.netdef ID/iface name field referring to an
* existing ID from a scalar node. This handler also includes a special case
@@ -494,6 +609,29 @@ handle_netdef_id_ref(NetplanParser* npp, yaml_node_t* node, const void* data, GE
return TRUE;
}
+/**
+ * Handler for setting a npp->current.netdef ID/iface name field referring to an
+ * existing ID from a scalar node.
+ * @data: offset into NetplanVxlan where the NetplanNetDefinition* field to
+ * write is located
+ */
+static gboolean
+handle_vxlan_id_ref(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+{
+ guint offset = GPOINTER_TO_UINT(data);
+ NetplanNetDefinition* ref = NULL;
+ NetplanNetDefinition** dest = (void*) npp->current.vxlan + offset;
+
+ ref = g_hash_table_lookup(npp->parsed_defs, scalar(node));
+ if (!ref)
+ add_missing_node(npp, node);
+ else
+ *dest = ref;
+ mark_data_as_dirty(npp, dest);
+ return TRUE;
+}
+
+
/**
* Generic handler for setting a npp->current.netdef MAC address field from a scalar node
@@ -517,6 +655,16 @@ handle_netdef_bool(NetplanParser* npp, yaml_node_t* node, const void* data, GErr
}
/**
+ * Generic handler for tri-state settings that can bei "UNSET", "TRUE", or "FALSE".
+ * @data: offset into NetplanNetDefinition where the guint field to write is located
+ */
+static gboolean
+handle_netdef_tristate(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+{
+ return handle_generic_tristate(npp, node, npp->current.netdef, data, error);
+}
+
+/**
* Generic handler for setting a npp->current.netdef guint field from a scalar node
* @data: offset into NetplanNetDefinition where the guint field to write is located
*/
@@ -612,17 +760,17 @@ handle_netdef_addrtok(NetplanParser* npp, yaml_node_t* node, const void* data, G
}
static gboolean
-handle_netdef_map(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+handle_netdef_map(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error)
{
g_assert(npp->current.netdef);
- return handle_generic_map(npp, node, npp->current.netdef, data, error);
+ return handle_generic_map(npp, node, key_prefix, npp->current.netdef, data, error);
}
static gboolean
-handle_netdef_datalist(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+handle_netdef_datalist(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error)
{
g_assert(npp->current.netdef);
- return handle_generic_datalist(npp, node, npp->current.netdef, data, error);
+ return handle_generic_datalist(npp, node, key_prefix, npp->current.netdef, data, error);
}
/****************************************************
@@ -630,7 +778,7 @@ handle_netdef_datalist(NetplanParser* npp, yaml_node_t* node, const void* data,
****************************************************/
static gboolean
-handle_match_driver(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_match_driver(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
{
gboolean ret = FALSE;
yaml_node_t *elem = NULL;
@@ -761,10 +909,10 @@ handle_access_point_str(NetplanParser* npp, yaml_node_t* node, const void* data,
}
static gboolean
-handle_access_point_datalist(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+handle_access_point_datalist(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error)
{
g_assert(npp->current.access_point);
- return handle_generic_datalist(npp, node, npp->current.access_point, data, error);
+ return handle_generic_datalist(npp, node, key_prefix, npp->current.access_point, data, error);
}
static gboolean
@@ -799,7 +947,7 @@ handle_access_point_password(NetplanParser* npp, yaml_node_t* node, const void*
}
static gboolean
-handle_access_point_auth(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_access_point_auth(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
{
NetplanWifiAccessPoint *access_point = npp->current.access_point;
gboolean ret;
@@ -808,7 +956,7 @@ handle_access_point_auth(NetplanParser* npp, yaml_node_t* node, const void* _, G
access_point->has_auth = TRUE;
npp->current.auth = &access_point->auth;
- ret = process_mapping(npp, node, auth_handlers, NULL, error);
+ ret = process_mapping(npp, node, NULL, auth_handlers, NULL, error);
npp->current.auth = NULL;
return ret;
@@ -929,10 +1077,10 @@ handle_activation_mode(NetplanParser* npp, yaml_node_t* node, const void* data,
}
static gboolean
-handle_match(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_match(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
{
npp->current.netdef->has_match = TRUE;
- return process_mapping(npp, node, match_handlers, NULL, error);
+ return process_mapping(npp, node, key_prefix, match_handlers, NULL, error);
}
NETPLAN_ABI struct NetplanWifiWowlanType
@@ -973,14 +1121,14 @@ handle_wowlan(NetplanParser* npp, yaml_node_t* node, const void* _, GError** err
}
static gboolean
-handle_auth(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_auth(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
{
gboolean ret;
npp->current.netdef->has_auth = TRUE;
npp->current.auth = &npp->current.netdef->auth;
- ret = process_mapping(npp, node, auth_handlers, NULL, error);
+ ret = process_mapping(npp, node, key_prefix, auth_handlers, NULL, error);
mark_data_as_dirty(npp, &npp->current.netdef->auth);
npp->current.auth = NULL;
@@ -1064,7 +1212,7 @@ handle_generic_addresses(NetplanParser* npp, yaml_node_t* node, gboolean check_z
npp->current.addr_options = g_new0(NetplanAddressOptions, 1);
npp->current.addr_options->address = g_strdup(scalar(key));
- if (!process_mapping(npp, value, address_option_handlers, NULL, error))
+ if (!process_mapping(npp, value, NULL, address_option_handlers, NULL, error))
return FALSE;
g_array_append_val(npp->current.netdef->address_options, npp->current.addr_options);
@@ -1148,10 +1296,11 @@ handle_gateway6(NetplanParser* npp, yaml_node_t* node, const void* _, GError** e
}
static gboolean
-handle_wifi_access_points(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+handle_wifi_access_points(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error)
{
for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) {
NetplanWifiAccessPoint *access_point = NULL;
+ g_autofree char* full_key = NULL;
yaml_node_t* key, *value;
gboolean ret = TRUE;
@@ -1160,6 +1309,12 @@ handle_wifi_access_points(NetplanParser* npp, yaml_node_t* node, const void* dat
value = yaml_document_get_node(&npp->doc, entry->value);
assert_type(npp, value, YAML_MAPPING_NODE);
+ if (key_prefix && npp->null_fields) {
+ full_key = g_strdup_printf("%s\t%s", key_prefix, key->data.scalar.value);
+ if (g_hash_table_contains(npp->null_fields, full_key))
+ continue;
+ }
+
g_assert(access_point == NULL);
access_point = g_new0(NetplanWifiAccessPoint, 1);
access_point->ssid = g_strdup(scalar(key));
@@ -1172,7 +1327,7 @@ handle_wifi_access_points(NetplanParser* npp, yaml_node_t* node, const void* dat
}
npp->current.access_point = access_point;
- if (!ret || !process_mapping(npp, value, wifi_access_point_handlers, NULL, error)) {
+ if (!ret || !process_mapping(npp, value, full_key, wifi_access_point_handlers, NULL, error)) {
access_point_clear(&npp->current.access_point, npp->current.backend);
return FALSE;
}
@@ -1393,6 +1548,84 @@ handle_optional_addresses(NetplanParser* npp, yaml_node_t* node, const void* _,
return TRUE;
}
+/* TODO: unify optional_addresses/wowlan_types, using flags */
+static gboolean
+handle_vxlan_flags(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+{
+ g_assert(npp->current.vxlan);
+ assert_type(npp, node, YAML_SEQUENCE_NODE);
+ yaml_node_t* key_node = node-1; // The YAML key of given sequence `node`
+
+ guint offset = GPOINTER_TO_UINT(data);
+ const char* const* flags = NULL;
+ guint flags_size = 0;
+ NetplanFlags* out_ptr = NULL;
+ switch (offset) {
+ case offsetof(NetplanVxlan, notifications):
+ out_ptr = &npp->current.vxlan->notifications;
+ flags = netplan_vxlan_notification_to_str;
+ flags_size = sizeof(netplan_vxlan_notification_to_str);
+ break;
+ case offsetof(NetplanVxlan, checksums):
+ out_ptr = &npp->current.vxlan->checksums;
+ flags = netplan_vxlan_checksum_to_str;
+ flags_size = sizeof(netplan_vxlan_checksum_to_str);
+ break;
+ case offsetof(NetplanVxlan, extensions):
+ out_ptr = &npp->current.vxlan->extensions;
+ flags = netplan_vxlan_extension_to_str;
+ flags_size = sizeof(netplan_vxlan_extension_to_str);
+ break;
+ default: g_assert_not_reached(); // LCOV_EXCL_LINE
+ }
+ g_assert(flags);
+ g_assert(out_ptr);
+
+ for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
+ yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i);
+ assert_type(npp, entry, YAML_SCALAR_NODE);
+ int found = FALSE;
+ /* Loop through the flags to find a matching string.
+ * Once found, shift a bit to position INDEX-1 and use bitwise OR to
+ * apply it to the corresponding flags field (i.e. *out_ptr) */
+ // The minimum flag is always 0x1 (i.e. 0b0001), so start the loop at 1.
+ for (unsigned j = 1; j < flags_size/sizeof(char*); ++j) {
+ if (g_ascii_strcasecmp(scalar(entry), flags[j]) == 0) {
+ *out_ptr |= 1<<(j-1);
+ mark_data_as_dirty(npp, out_ptr);
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ return yaml_error(npp, node, error, "invalid value for %s: '%s'",
+ scalar(key_node), scalar(entry));
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+handle_vxlan_guint(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+{
+ g_assert(npp->current.vxlan);
+ return handle_generic_guint(npp, node, npp->current.vxlan, data, error);
+}
+
+static gboolean
+handle_vxlan_bool(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+{
+ g_assert(npp->current.vxlan);
+ return handle_generic_bool(npp, node, npp->current.vxlan, data, error);
+}
+
+static gboolean
+handle_vxlan_tristate(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+{
+ g_assert(npp->current.vxlan);
+ return handle_generic_tristate(npp, node, npp->current.vxlan, data, error);
+}
+
static int
get_ip_family(const char* address)
{
@@ -1557,7 +1790,7 @@ handle_ip_rule_tos(NetplanParser* npp, yaml_node_t* node, const void* data, GErr
****************************************************/
static gboolean
-handle_bridge_path_cost(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+handle_bridge_path_cost(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error)
{
for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) {
yaml_node_t* key, *value;
@@ -1571,6 +1804,13 @@ handle_bridge_path_cost(NetplanParser* npp, yaml_node_t* node, const void* data,
value = yaml_document_get_node(&npp->doc, entry->value);
assert_type(npp, value, YAML_SCALAR_NODE);
+ if (key_prefix && npp->null_fields) {
+ g_autofree char* full_key = NULL;
+ full_key = g_strdup_printf("%s\t%s", key_prefix, key->data.scalar.value);
+ if (g_hash_table_contains(npp->null_fields, full_key))
+ continue;
+ }
+
component = g_hash_table_lookup(npp->parsed_defs, scalar(key));
if (!component) {
add_missing_node(npp, key);
@@ -1594,7 +1834,7 @@ handle_bridge_path_cost(NetplanParser* npp, yaml_node_t* node, const void* data,
}
static gboolean
-handle_bridge_port_priority(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+handle_bridge_port_priority(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error)
{
for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) {
yaml_node_t* key, *value;
@@ -1608,6 +1848,13 @@ handle_bridge_port_priority(NetplanParser* npp, yaml_node_t* node, const void* d
value = yaml_document_get_node(&npp->doc, entry->value);
assert_type(npp, value, YAML_SCALAR_NODE);
+ if (key_prefix && npp->null_fields) {
+ g_autofree char* full_key = NULL;
+ full_key = g_strdup_printf("%s\t%s", key_prefix, key->data.scalar.value);
+ if (g_hash_table_contains(npp->null_fields, full_key))
+ continue;
+ }
+
component = g_hash_table_lookup(npp->parsed_defs, scalar(key));
if (!component) {
add_missing_node(npp, key);
@@ -1633,6 +1880,7 @@ handle_bridge_port_priority(NetplanParser* npp, yaml_node_t* node, const void* d
static const mapping_entry_handler bridge_params_handlers[] = {
{"ageing-time", YAML_SCALAR_NODE, {.generic=handle_netdef_str}, netdef_offset(bridge_params.ageing_time)},
+ {"aging-time", YAML_SCALAR_NODE, {.generic=handle_netdef_str}, netdef_offset(bridge_params.ageing_time)},
{"forward-delay", YAML_SCALAR_NODE, {.generic=handle_netdef_str}, netdef_offset(bridge_params.forward_delay)},
{"hello-time", YAML_SCALAR_NODE, {.generic=handle_netdef_str}, netdef_offset(bridge_params.hello_time)},
{"max-age", YAML_SCALAR_NODE, {.generic=handle_netdef_str}, netdef_offset(bridge_params.max_age)},
@@ -1644,11 +1892,11 @@ static const mapping_entry_handler bridge_params_handlers[] = {
};
static gboolean
-handle_bridge(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_bridge(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
{
npp->current.netdef->custom_bridging = TRUE;
npp->current.netdef->bridge_params.stp = TRUE;
- return process_mapping(npp, node, bridge_params_handlers, NULL, error);
+ return process_mapping(npp, node, key_prefix, bridge_params_handlers, NULL, error);
}
/****************************************************
@@ -1701,7 +1949,7 @@ handle_routes(NetplanParser* npp, yaml_node_t* node, const void* _, GError** err
npp->current.route = route;
- if (!process_mapping(npp, entry, routes_handlers, NULL, error))
+ if (!process_mapping(npp, entry, NULL, routes_handlers, NULL, error))
goto err;
/* Set the default scope, according to type */
@@ -1766,14 +2014,10 @@ handle_ip_rules(NetplanParser* npp, yaml_node_t* node, const void* _, GError** e
gboolean ret;
NetplanIPRule* ip_rule = g_new0(NetplanIPRule, 1);
- ip_rule->family = G_MAXUINT; /* 0 is a valid family ID */
- ip_rule->priority = NETPLAN_IP_RULE_PRIO_UNSPEC;
- ip_rule->table = NETPLAN_ROUTE_TABLE_UNSPEC;
- ip_rule->tos = NETPLAN_IP_RULE_TOS_UNSPEC;
- ip_rule->fwmark = NETPLAN_IP_RULE_FW_MARK_UNSPEC;
+ reset_ip_rule(ip_rule);
npp->current.ip_rule = ip_rule;
- ret = process_mapping(npp, entry, ip_rules_handlers, NULL, error);
+ ret = process_mapping(npp, entry, NULL, ip_rules_handlers, NULL, error);
npp->current.ip_rule = NULL;
if (ret && !ip_rule->from && !ip_rule->to)
@@ -1888,9 +2132,59 @@ static const mapping_entry_handler bond_params_handlers[] = {
};
static gboolean
-handle_bonding(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_vrf_interfaces(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
{
- return process_mapping(npp, node, bond_params_handlers, NULL, error);
+ /* all entries must refer to already defined IDs */
+ for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
+ yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i);
+ NetplanNetDefinition *component;
+
+ assert_type(npp, entry, YAML_SCALAR_NODE);
+ component = g_hash_table_lookup(npp->parsed_defs, scalar(entry));
+ if (!component) {
+ add_missing_node(npp, entry);
+ } else {
+ if (component->vrf_link && component->vrf_link != npp->current.netdef)
+ return yaml_error(npp, node, error, "%s: interface '%s' is already assigned to vrf %s",
+ npp->current.netdef->id, scalar(entry), component->vrf_link->id);
+ component->vrf_link = npp->current.netdef;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+handle_vxlan_source_port(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+{
+ assert_type(npp, node, YAML_SEQUENCE_NODE);
+ if (node->data.sequence.items.top - node->data.sequence.items.start != 2)
+ return yaml_error(npp, node, error, "%s: Expected exactly two values for port-range",
+ npp->current.netdef->id);
+
+ yaml_node_t* itm1 = yaml_document_get_node(&npp->doc, *node->data.sequence.items.start);
+ yaml_node_t* itm2 = yaml_document_get_node(&npp->doc, *node->data.sequence.items.start+1);
+
+ if (!handle_generic_guint(npp, itm1, npp->current.vxlan, vxlan_offset(source_port_min), error))
+ return FALSE;
+ if (!handle_generic_guint(npp, itm2, npp->current.vxlan, vxlan_offset(source_port_max), error))
+ return FALSE;
+
+ guint tmp = 0;
+ if (npp->current.netdef->vxlan->source_port_min > npp->current.netdef->vxlan->source_port_max) {
+ tmp = npp->current.netdef->vxlan->source_port_min;
+ npp->current.netdef->vxlan->source_port_min = npp->current.netdef->vxlan->source_port_max;
+ npp->current.netdef->vxlan->source_port_max = tmp;
+ g_warning("%s: swapped invalid port-range order [MIN, MAX]", npp->current.netdef->id);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+handle_bonding(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
+{
+ return process_mapping(npp, node, key_prefix, bond_params_handlers, NULL, error);
}
static gboolean
@@ -1962,7 +2256,7 @@ static const mapping_entry_handler tunnel_keys_handlers[] = {
};
static gboolean
-handle_tunnel_key_mapping(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_tunnel_key_mapping(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
{
gboolean ret = FALSE;
@@ -1976,7 +2270,7 @@ handle_tunnel_key_mapping(NetplanParser* npp, yaml_node_t* node, const void* _,
if (ret)
ret = handle_netdef_str(npp, node, netdef_offset(tunnel.private_key), error);
} else if (node->type == YAML_MAPPING_NODE)
- ret = process_mapping(npp, node, tunnel_keys_handlers, NULL, error);
+ ret = process_mapping(npp, node, key_prefix, tunnel_keys_handlers, NULL, error);
else
return yaml_error(npp, node, error, "invalid type for 'key[s]': must be a scalar or mapping");
@@ -2058,9 +2352,9 @@ static const mapping_entry_handler wireguard_peer_keys_handlers[] = {
};
static gboolean
-handle_wireguard_peer_key_mapping(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_wireguard_peer_key_mapping(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
{
- return process_mapping(npp, node, wireguard_peer_keys_handlers, NULL, error);
+ return process_mapping(npp, node, key_prefix, wireguard_peer_keys_handlers, NULL, error);
}
const mapping_entry_handler wireguard_peer_handlers[] = {
@@ -2094,7 +2388,7 @@ handle_wireguard_peers(NetplanParser* npp, yaml_node_t* node, const void* _, GEr
npp->current.wireguard_peer->allowed_ips = g_array_new(FALSE, FALSE, sizeof(char*));
g_debug("%s: adding new wireguard peer", npp->current.netdef->id);
- if (!process_mapping(npp, entry, wireguard_peer_handlers, NULL, error)) {
+ if (!process_mapping(npp, entry, NULL, wireguard_peer_handlers, NULL, error)) {
wireguard_peer_clear(&npp->current.wireguard_peer);
return FALSE;
}
@@ -2265,10 +2559,10 @@ static const mapping_entry_handler ovs_backend_settings_handlers[] = {
};
static gboolean
-handle_ovs_backend(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_ovs_backend(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
{
GList* values = NULL;
- gboolean ret = process_mapping(npp, node, ovs_backend_settings_handlers, &values, error);
+ gboolean ret = process_mapping(npp, node, key_prefix, ovs_backend_settings_handlers, &values, error);
guint len = g_list_length(values);
if (npp->current.netdef->type != NETPLAN_DEF_TYPE_BOND && npp->current.netdef->type != NETPLAN_DEF_TYPE_BRIDGE) {
@@ -2343,7 +2637,8 @@ static const mapping_entry_handler dhcp6_overrides_handlers[] = {
{"optional-addresses", YAML_SEQUENCE_NODE, {.generic=handle_optional_addresses}}, \
{"renderer", YAML_SCALAR_NODE, {.generic=handle_netdef_renderer}}, \
{"routes", YAML_SEQUENCE_NODE, {.generic=handle_routes}}, \
- {"routing-policy", YAML_SEQUENCE_NODE, {.generic=handle_ip_rules}}
+ {"routing-policy", YAML_SEQUENCE_NODE, {.generic=handle_ip_rules}}, \
+ {"neigh-suppress", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(bridge_neigh_suppress)}
#define COMMON_BACKEND_HANDLERS \
{"networkmanager", YAML_MAPPING_NODE, {.map={.handlers=nm_backend_settings_handlers}}}, \
@@ -2356,13 +2651,13 @@ static const mapping_entry_handler dhcp6_overrides_handlers[] = {
{"wakeonlan", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(wake_on_lan)}, \
{"wakeonwlan", YAML_SEQUENCE_NODE, {.generic=handle_wowlan}, netdef_offset(wowlan)}, \
{"emit-lldp", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(emit_lldp)}, \
- {"receive-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(receive_checksum_offload)}, \
- {"transmit-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(transmit_checksum_offload)}, \
- {"tcp-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(tcp_segmentation_offload)}, \
- {"tcp6-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(tcp6_segmentation_offload)}, \
- {"generic-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(generic_segmentation_offload)}, \
- {"generic-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(generic_receive_offload)}, \
- {"large-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(large_receive_offload)}
+ {"receive-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(receive_checksum_offload)}, \
+ {"transmit-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(transmit_checksum_offload)}, \
+ {"tcp-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(tcp_segmentation_offload)}, \
+ {"tcp6-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(tcp6_segmentation_offload)}, \
+ {"generic-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(generic_segmentation_offload)}, \
+ {"generic-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(generic_receive_offload)}, \
+ {"large-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(large_receive_offload)}
static const mapping_entry_handler ethernet_def_handlers[] = {
COMMON_LINK_HANDLERS,
@@ -2373,6 +2668,7 @@ static const mapping_entry_handler ethernet_def_handlers[] = {
{"virtual-function-count", YAML_SCALAR_NODE, {.generic=handle_netdef_guint}, netdef_offset(sriov_explicit_vf_count)},
{"embedded-switch-mode", YAML_SCALAR_NODE, {.generic=handle_embedded_switch_mode}, netdef_offset(embedded_switch_mode)},
{"delay-virtual-functions-rebind", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(sriov_delay_virtual_functions_rebind)},
+ {"infiniband-mode", YAML_SCALAR_NODE, {.generic=handle_ib_mode}, netdef_offset(ib_mode)},
{NULL}
};
@@ -2382,6 +2678,7 @@ static const mapping_entry_handler wifi_def_handlers[] = {
PHYSICAL_LINK_HANDLERS,
{"access-points", YAML_MAPPING_NODE, {.map={.custom=handle_wifi_access_points}}},
{"auth", YAML_MAPPING_NODE, {.map={.custom=handle_auth}}},
+ {"regulatory-domain", YAML_SCALAR_NODE, {.generic=handle_netdef_str}, netdef_offset(regulatory_domain)},
{NULL}
};
@@ -2409,6 +2706,16 @@ static const mapping_entry_handler vlan_def_handlers[] = {
{NULL}
};
+static const mapping_entry_handler vrf_def_handlers[] = {
+ COMMON_BACKEND_HANDLERS,
+ {"renderer", YAML_SCALAR_NODE, {.generic=handle_netdef_renderer}},
+ {"interfaces", YAML_SEQUENCE_NODE, {.generic=handle_vrf_interfaces}, NULL},
+ {"table", YAML_SCALAR_NODE, {.generic=handle_netdef_guint}, netdef_offset(vrf_table)},
+ {"routes", YAML_SEQUENCE_NODE, {.generic=handle_routes}},
+ {"routing-policy", YAML_SEQUENCE_NODE, {.generic=handle_ip_rules}},
+ {NULL}
+};
+
static const mapping_entry_handler modem_def_handlers[] = {
COMMON_LINK_HANDLERS,
COMMON_BACKEND_HANDLERS,
@@ -2443,6 +2750,23 @@ static const mapping_entry_handler tunnel_def_handlers[] = {
{"mark", YAML_SCALAR_NODE, {.generic=handle_netdef_guint}, netdef_offset(tunnel.fwmark)},
{"port", YAML_SCALAR_NODE, {.generic=handle_netdef_guint}, netdef_offset(tunnel.port)},
{"peers", YAML_SEQUENCE_NODE, {.generic=handle_wireguard_peers}},
+
+ /* vxlan */
+ {"link", YAML_SCALAR_NODE, {.generic=handle_vxlan_id_ref}, vxlan_offset(link)},
+ {"ageing", YAML_SCALAR_NODE, {.generic=handle_vxlan_guint}, vxlan_offset(ageing)},
+ {"aging", YAML_SCALAR_NODE, {.generic=handle_vxlan_guint}, vxlan_offset(ageing)},
+ {"id", YAML_SCALAR_NODE, {.generic=handle_vxlan_guint}, vxlan_offset(vni)},
+ {"limit", YAML_SCALAR_NODE, {.generic=handle_vxlan_guint}, vxlan_offset(limit)},
+ {"type-of-service", YAML_SCALAR_NODE, {.generic=handle_vxlan_guint}, vxlan_offset(tos)},
+ {"flow-label", YAML_SCALAR_NODE, {.generic=handle_vxlan_guint}, vxlan_offset(flow_label)},
+ {"do-not-fragment", YAML_SCALAR_NODE, {.generic=handle_vxlan_tristate}, vxlan_offset(do_not_fragment)},
+ {"short-circuit", YAML_SCALAR_NODE, {.generic=handle_vxlan_bool}, vxlan_offset(short_circuit)},
+ {"arp-proxy", YAML_SCALAR_NODE, {.generic=handle_vxlan_bool}, vxlan_offset(arp_proxy)},
+ {"mac-learning", YAML_SCALAR_NODE, {.generic=handle_vxlan_bool}, vxlan_offset(mac_learning)},
+ {"notifications", YAML_SEQUENCE_NODE, {.generic=handle_vxlan_flags}, vxlan_offset(notifications)},
+ {"checksums", YAML_SEQUENCE_NODE, {.generic=handle_vxlan_flags}, vxlan_offset(checksums)},
+ {"extensions", YAML_SEQUENCE_NODE, {.generic=handle_vxlan_flags}, vxlan_offset(extensions)},
+ {"port-range", YAML_SEQUENCE_NODE, {.generic=handle_vxlan_source_port}},
{NULL}
};
@@ -2469,9 +2793,9 @@ handle_network_renderer(NetplanParser* npp, yaml_node_t* node, const void* _, GE
}
static gboolean
-handle_network_ovs_settings_global(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+handle_network_ovs_settings_global(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error)
{
- return handle_generic_map(npp, node, &npp->global_ovs_settings, data, error);
+ return handle_generic_map(npp, node, key_prefix, &npp->global_ovs_settings, data, error);
}
static gboolean
@@ -2534,16 +2858,44 @@ handle_network_ovs_settings_global_ports(NetplanParser* npp, yaml_node_t* node,
return TRUE;
}
+static gboolean
+node_is_nulled_out(yaml_document_t* doc, yaml_node_t* node, const char* key_prefix, GHashTable* null_fields)
+{
+ if (node->type != YAML_MAPPING_NODE)
+ return FALSE;
+
+ // Empty nodes are not nulled-out, they're just empty!
+ if (node->data.mapping.pairs.start == node->data.mapping.pairs.top)
+ return FALSE;
+
+ for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) {
+ yaml_node_t* key, *value;
+ g_autofree char* full_key = NULL;
+
+ key = yaml_document_get_node(doc, entry->key);
+ value = yaml_document_get_node(doc, entry->value);
+
+ full_key = g_strdup_printf("%s\t%s", key_prefix, key->data.scalar.value);
+ // null detected, so we now flip the default return.
+ if (g_hash_table_contains(null_fields, full_key))
+ continue;
+ if (!node_is_nulled_out(doc, value, full_key, null_fields))
+ return FALSE;
+ }
+ return TRUE;
+}
+
/**
* Callback for a net device type entry like "ethernets:" in "network:"
* @data: netdef_type (as pointer)
*/
static gboolean
-handle_network_type(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
+handle_network_type(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error)
{
for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) {
yaml_node_t* key, *value;
const mapping_entry_handler* handlers;
+ g_autofree char* full_key = NULL;
key = yaml_document_get_node(&npp->doc, entry->key);
if (!assert_valid_id(npp, key, error))
@@ -2554,6 +2906,12 @@ handle_network_type(NetplanParser* npp, yaml_node_t* node, const void* data, GEr
value = yaml_document_get_node(&npp->doc, entry->value);
+ if (key_prefix && npp->null_fields) {
+ full_key = g_strdup_printf("%s\t%s", key_prefix, key->data.scalar.value);
+ if (g_hash_table_contains(npp->null_fields, full_key) || node_is_nulled_out(&npp->doc, value, full_key, npp->null_fields))
+ continue;
+ }
+
/* special-case "renderer:" key to set the per-type backend */
if (strcmp(scalar(key), "renderer") == 0) {
if (!parse_renderer(npp, value, &npp->current.backend, error))
@@ -2577,8 +2935,8 @@ handle_network_type(NetplanParser* npp, yaml_node_t* node, const void* data, GEr
} else {
npp->current.netdef = netplan_netdef_new(npp, scalar(key), GPOINTER_TO_UINT(data), npp->current.backend);
}
- g_assert(npp->current.filename);
- npp->current.netdef->filename = g_strdup(npp->current.filename);
+ if (npp->current.filepath)
+ npp->current.netdef->filepath = g_strdup(npp->current.filepath);
// XXX: breaks multi-pass parsing.
//if (!g_hash_table_add(ids_in_file, npp->current.netdef->id))
@@ -2592,6 +2950,7 @@ handle_network_type(NetplanParser* npp, yaml_node_t* node, const void* data, GEr
case NETPLAN_DEF_TYPE_MODEM: handlers = modem_def_handlers; break;
case NETPLAN_DEF_TYPE_TUNNEL: handlers = tunnel_def_handlers; break;
case NETPLAN_DEF_TYPE_VLAN: handlers = vlan_def_handlers; break;
+ case NETPLAN_DEF_TYPE_VRF: handlers = vrf_def_handlers; break;
case NETPLAN_DEF_TYPE_WIFI: handlers = wifi_def_handlers; break;
case NETPLAN_DEF_TYPE_NM:
g_warning("netplan: %s: handling NetworkManager passthrough device, settings are not fully supported.", npp->current.netdef->id);
@@ -2599,9 +2958,30 @@ handle_network_type(NetplanParser* npp, yaml_node_t* node, const void* data, GEr
break;
default: g_assert_not_reached(); // LCOV_EXCL_LINE
}
- if (!process_mapping(npp, value, handlers, NULL, error))
+
+ /* Preprocessing */
+ /* Any tunnel netdef needs to carry the 'vxlan' struct, as it might
+ * potentially be a VXLAN tunnel. */
+ if (npp->current.netdef->type == NETPLAN_DEF_TYPE_TUNNEL) {
+ NetplanVxlan* vxlan = g_new0(NetplanVxlan, 1);
+ reset_vxlan(vxlan);
+ npp->current.vxlan = vxlan;
+ npp->current.netdef->vxlan = vxlan;
+ }
+
+ if (!process_mapping(npp, value, full_key, handlers, NULL, error))
return FALSE;
+ /* Postprocessing */
+ /* Implicit VXLAN settings, which can be deduced from parsed data. */
+ if (npp->current.netdef->type == NETPLAN_DEF_TYPE_TUNNEL &&
+ npp->current.netdef->tunnel.mode == NETPLAN_TUNNEL_MODE_VXLAN) {
+ if (npp->current.netdef->vxlan->link)
+ npp->current.netdef->vxlan->link->has_vxlans = TRUE;
+ else
+ npp->current.netdef->vxlan->independent = TRUE;
+ }
+
/* validate definition-level conditions */
if (!validate_netdef_grammar(npp, npp->current.netdef, value, error))
return FALSE;
@@ -2623,12 +3003,12 @@ static const mapping_entry_handler ovs_global_ssl_handlers[] = {
};
static gboolean
-handle_ovs_global_ssl(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error)
+handle_ovs_global_ssl(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* _, GError** error)
{
gboolean ret;
npp->current.auth = &(npp->global_ovs_settings.ssl);
- ret = process_mapping(npp, node, ovs_global_ssl_handlers, NULL, error);
+ ret = process_mapping(npp, node, key_prefix, ovs_global_ssl_handlers, NULL, error);
npp->current.auth = NULL;
return ret;
@@ -2651,6 +3031,7 @@ static const mapping_entry_handler network_handlers[] = {
{"tunnels", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_TUNNEL)},
{"version", YAML_SCALAR_NODE, {.generic=handle_network_version}},
{"vlans", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VLAN)},
+ {"vrfs", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VRF)},
{"wifis", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_WIFI)},
{"modems", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_MODEM)},
{"nm-devices", YAML_MAPPING_NODE, {.map={.custom=handle_network_type}}, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_NM)},
@@ -2688,7 +3069,7 @@ process_document(NetplanParser* npp, GError** error)
g_clear_error(error);
- ret = process_mapping(npp, yaml_document_get_root_node(&npp->doc), root_handlers, NULL, error);
+ ret = process_mapping(npp, yaml_document_get_root_node(&npp->doc), "", root_handlers, NULL, error);
still_missing = g_hash_table_size(npp->missing_id);
@@ -2719,17 +3100,17 @@ process_document(NetplanParser* npp, GError** error)
return ret;
}
-/**
- * Parse given YAML file and create/update the parser's "netdefs" list.
- */
-gboolean
-netplan_parser_load_yaml(NetplanParser* npp, const char* filename, GError** error)
+static gboolean
+_netplan_parser_load_single_file(NetplanParser* npp, const char *opt_filepath, yaml_document_t *doc, GError** error)
{
- yaml_document_t *doc = &npp->doc;
- gboolean ret;
+ int ret = FALSE;
- if (!load_yaml(filename, doc, error))
- return FALSE;
+ if (opt_filepath) {
+ char* source = g_strdup(opt_filepath);
+ if (!npp->sources)
+ npp->sources = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ g_hash_table_add(npp->sources, source);
+ }
/* empty file? */
if (yaml_document_get_root_node(doc) == NULL)
@@ -2738,10 +3119,10 @@ netplan_parser_load_yaml(NetplanParser* npp, const char* filename, GError** erro
g_assert(npp->ids_in_file == NULL);
npp->ids_in_file = g_hash_table_new(g_str_hash, NULL);
- npp->current.filename = g_strdup(filename);
+ npp->current.filepath = opt_filepath? g_strdup(opt_filepath) : NULL;
ret = process_document(npp, error);
- g_free((void *)npp->current.filename);
- npp->current.filename = NULL;
+ g_free((void *)npp->current.filepath);
+ npp->current.filepath = NULL;
yaml_document_delete(doc);
g_hash_table_destroy(npp->ids_in_file);
@@ -2749,6 +3130,32 @@ netplan_parser_load_yaml(NetplanParser* npp, const char* filename, GError** erro
return ret;
}
+/**
+ * Parse given YAML file from FD and create/update the parser's "netdefs" list.
+ */
+gboolean
+netplan_parser_load_yaml_from_fd(NetplanParser* npp, int fd, GError** error)
+{
+ yaml_document_t *doc = &npp->doc;
+
+ if (!load_yaml_from_fd(fd, doc, error))
+ return FALSE;
+ return _netplan_parser_load_single_file(npp, NULL, doc, error);
+
+}
+/**
+ * Parse given YAML file and create/update the parser's "netdefs" list.
+ */
+gboolean
+netplan_parser_load_yaml(NetplanParser* npp, const char* filename, GError** error)
+{
+ yaml_document_t *doc = &npp->doc;
+
+ if (!load_yaml(filename, doc, error))
+ return FALSE;
+ return _netplan_parser_load_single_file(npp, filename, doc, error);
+}
+
static gboolean
finish_iterator(const NetplanParser* npp, NetplanNetDefinition* nd, GError **error)
{
@@ -2776,7 +3183,13 @@ netplan_state_import_parser_results(NetplanState* np_state, NetplanParser* npp,
GError *recoverable = NULL;
GHashTableIter iter;
gpointer key, value;
+ char *regdom = NULL;
g_debug("We have some netdefs, pass them through a final round of validation");
+
+ /* Check/adopt VRF routes before route consistency and validation */
+ if (!adopt_and_validate_vrf_routes(npp, npp->parsed_defs, error))
+ return FALSE;
+
if (!validate_default_route_consistency(npp, npp->parsed_defs, &recoverable)) {
g_warning("Problem encountered while validating default route consistency."
"Please set up multiple routing tables and use `routing-policy` instead.\n"
@@ -2789,7 +3202,15 @@ netplan_state_import_parser_results(NetplanState* np_state, NetplanParser* npp,
while (g_hash_table_iter_next (&iter, &key, &value)) {
g_assert(np_state->netdefs == NULL ||
g_hash_table_lookup(np_state->netdefs, key) == NULL);
- if (!finish_iterator(npp, (NetplanNetDefinition *) value, error))
+ NetplanNetDefinition *nd = value;
+ if (nd->regulatory_domain) {
+ if (!regdom)
+ regdom = nd->regulatory_domain;
+ else if (g_strcmp0(regdom, nd->regulatory_domain) != 0)
+ g_warning("%s: Conflicting regulatory-domain (%s vs %s)",
+ nd->id, regdom, nd->regulatory_domain);
+ }
+ if (!finish_iterator(npp, nd, error))
return FALSE;
g_debug("Configuration is valid");
}
@@ -2804,6 +3225,12 @@ netplan_state_import_parser_results(NetplanState* np_state, NetplanParser* npp,
np_state->ovs_settings = npp->global_ovs_settings;
np_state->backend = npp->global_backend;
+ if (npp->sources) {
+ if (!np_state->sources)
+ np_state->sources = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ g_hash_table_foreach_steal(npp->sources, insert_kv_into_hash, np_state->sources);
+ }
+
/* We need to reset those fields manually as we transfered ownership of the underlying
data to out. If we don't do this, netplan_clear_parser will deallocate data
that we don't own anymore. */
@@ -2851,21 +3278,22 @@ netplan_parser_reset(NetplanParser* npp)
/* These pointers are non-owning, it's not our place to free their resources*/
npp->current.netdef = NULL;
npp->current.auth = NULL;
+ npp->current.vxlan = NULL;
access_point_clear(&npp->current.access_point, npp->current.backend);
wireguard_peer_clear(&npp->current.wireguard_peer);
address_options_clear(&npp->current.addr_options);
route_clear(&npp->current.route);
ip_rule_clear(&npp->current.ip_rule);
- g_free((void *)npp->current.filename);
- npp->current.filename = NULL;
+ g_free((void *)npp->current.filepath);
+ npp->current.filepath = NULL;
- //LCOV_EXCL_START
+ // LCOV_EXCL_START
if (npp->ids_in_file) {
g_hash_table_destroy(npp->ids_in_file);
npp->ids_in_file = NULL;
}
- //LCOV_EXCL_STOP
+ // LCOV_EXCL_STOP
if (npp->missing_id) {
g_hash_table_destroy(npp->missing_id);
@@ -2873,6 +3301,17 @@ netplan_parser_reset(NetplanParser* npp)
}
npp->missing_ids_found = 0;
+
+ if (npp->null_fields) {
+ g_hash_table_destroy(npp->null_fields);
+ npp->null_fields = NULL;
+ }
+
+ if (npp->sources) {
+ /* Properly configured at creation not to leak */
+ g_hash_table_destroy(npp->sources);
+ npp->sources = NULL;
+ }
}
void
@@ -2883,3 +3322,61 @@ netplan_parser_clear(NetplanParser** npp_p)
netplan_parser_reset(npp);
g_free(npp);
}
+
+static void
+extract_null_fields(yaml_document_t* doc, yaml_node_t* node, GHashTable* null_fields, char* key_prefix)
+{
+ yaml_node_pair_t* entry;
+ switch (node->type) {
+ // LCOV_EXCL_START
+ case YAML_NO_NODE:
+ g_hash_table_add(null_fields, key_prefix);
+ key_prefix = NULL;
+ break;
+ // LCOV_EXCL_STOP
+ case YAML_SCALAR_NODE:
+ if ( g_ascii_strcasecmp("null", scalar(node)) == 0
+ || g_strcmp0((char*)node->tag, YAML_NULL_TAG) == 0
+ || g_strcmp0(scalar(node), "~") == 0) {
+ g_hash_table_add(null_fields, key_prefix);
+ key_prefix = NULL;
+ }
+ break;
+ case YAML_SEQUENCE_NODE:
+ /* Do nothing, we don't support nullifying *inside* sequences */
+ break;
+ case YAML_MAPPING_NODE:
+ for (entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) {
+ yaml_node_t* key, *value;
+ char* full_key;
+ key = yaml_document_get_node(doc, entry->key);
+ value = yaml_document_get_node(doc, entry->value);
+ full_key = g_strdup_printf("%s\t%s", key_prefix, key->data.scalar.value);
+ extract_null_fields(doc, value, null_fields, full_key);
+ }
+ break;
+ // LCOV_EXCL_START
+ default:
+ g_assert(FALSE); // supposedly unreachable!
+ // LCOV_EXCL_STOP
+ }
+ g_free(key_prefix);
+}
+
+gboolean
+netplan_parser_load_nullable_fields(NetplanParser* npp, int input_fd, GError** error)
+{
+ yaml_document_t doc;
+ if (!load_yaml_from_fd(input_fd, &doc, error))
+ return FALSE; // LCOV_EXCL_LINE
+
+ /* empty file? */
+ if (yaml_document_get_root_node(&doc) == NULL)
+ return TRUE; // LCOV_EXCL_LINE
+
+ if (!npp->null_fields)
+ npp->null_fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ extract_null_fields(&doc, yaml_document_get_root_node(&doc), npp->null_fields, g_strdup(""));
+ return TRUE;
+}
diff --git a/src/sriov.c b/src/sriov.c
index 966718f..5ab8449 100644
--- a/src/sriov.c
+++ b/src/sriov.c
@@ -122,3 +122,24 @@ netplan_sriov_cleanup(const char* rootdir)
return TRUE;
}
+
+NETPLAN_INTERNAL int
+_netplan_state_get_vf_count_for_def(const NetplanState* np_state, const NetplanNetDefinition* netdef, GError** error)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ int count = 0;
+
+ g_hash_table_iter_init(&iter, np_state->netdefs);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const NetplanNetDefinition* def = value;
+ if (def->sriov_link == netdef)
+ count++;
+ }
+
+ if (netdef->sriov_explicit_vf_count != G_MAXUINT && count > netdef->sriov_explicit_vf_count) {
+ g_set_error(error, 0, 0, "more VFs allocated than the explicit size declared: %d > %d", count, netdef->sriov_explicit_vf_count);
+ return -1;
+ }
+ return netdef->sriov_explicit_vf_count != G_MAXUINT ? netdef->sriov_explicit_vf_count : count;
+}
diff --git a/src/types.c b/src/types.c
index eb9f780..5982a39 100644
--- a/src/types.c
+++ b/src/types.c
@@ -147,6 +147,16 @@ reset_dhcp_overrides(NetplanDHCPOverrides* overrides)
overrides->metric = NETPLAN_METRIC_UNSPEC;
}
+void
+reset_ip_rule(NetplanIPRule* ip_rule)
+{
+ ip_rule->family = G_MAXUINT; /* 0 is a valid family ID */
+ ip_rule->priority = NETPLAN_IP_RULE_PRIO_UNSPEC;
+ ip_rule->table = NETPLAN_ROUTE_TABLE_UNSPEC;
+ ip_rule->tos = NETPLAN_IP_RULE_TOS_UNSPEC;
+ ip_rule->fwmark = NETPLAN_IP_RULE_FW_MARK_UNSPEC;
+}
+
/* Reset a backend settings object. The caller needs to specify the actual backend as it is not
* contained within the object itself! */
static void
@@ -177,6 +187,17 @@ reset_private_netdef_data(struct private_netdef_data* data) {
data->dirty_fields = NULL;
}
+void
+reset_vxlan(NetplanVxlan* vxlan)
+{
+ if (!vxlan)
+ return;
+ memset(vxlan, 0, sizeof(NetplanVxlan));
+ vxlan->link = NULL;
+ vxlan->flow_label = G_MAXUINT;
+ vxlan->do_not_fragment = NETPLAN_TRISTATE_UNSET;
+}
+
/* Free a heap-allocated NetplanWifiAccessPoint object.
* Signature made to match the g_hash_table_foreach function.
* @key: ignored
@@ -229,6 +250,7 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
FREE_AND_NULLIFY(netdef->gateway4);
FREE_AND_NULLIFY(netdef->gateway6);
+ FREE_AND_NULLIFY(netdef->regulatory_domain);
free_garray_with_destructor(&netdef->ip4_nameservers, g_free);
free_garray_with_destructor(&netdef->ip6_nameservers, g_free);
free_garray_with_destructor(&netdef->search_domains, g_free);
@@ -248,6 +270,9 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
netdef->vlan_link = NULL;
netdef->has_vlans = FALSE;
+ netdef->vrf_link = NULL;
+ netdef->vrf_table = G_MAXUINT;
+
FREE_AND_NULLIFY(netdef->set_mac);
netdef->mtubytes = 0;
netdef->ipv6_mtubytes = 0;
@@ -284,6 +309,10 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
FREE_AND_NULLIFY(netdef->bond_params.primary_slave);
memset(&netdef->bond_params, 0, sizeof(netdef->bond_params));
+ netdef->has_vxlans = FALSE;
+ reset_vxlan(netdef->vxlan);
+ FREE_AND_NULLIFY(netdef->vxlan);
+
FREE_AND_NULLIFY(netdef->modem_params.apn);
FREE_AND_NULLIFY(netdef->modem_params.device_id);
FREE_AND_NULLIFY(netdef->modem_params.network_id);
@@ -295,6 +324,7 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
FREE_AND_NULLIFY(netdef->modem_params.username);
memset(&netdef->modem_params, 0, sizeof(netdef->modem_params));
+ netdef->bridge_neigh_suppress = NETPLAN_TRISTATE_UNSET;
FREE_AND_NULLIFY(netdef->bridge_params.ageing_time);
FREE_AND_NULLIFY(netdef->bridge_params.forward_delay);
FREE_AND_NULLIFY(netdef->bridge_params.hello_time);
@@ -320,7 +350,7 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
reset_ovs_settings(&netdef->ovs_settings);
reset_backend_settings(&netdef->backend_settings, backend);
- FREE_AND_NULLIFY(netdef->filename);
+ FREE_AND_NULLIFY(netdef->filepath);
netdef->tunnel_ttl = 0;
FREE_AND_NULLIFY(netdef->activation_mode);
netdef->ignore_carrier = FALSE;
@@ -335,6 +365,16 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
reset_private_netdef_data(netdef->_private);
FREE_AND_NULLIFY(netdef->_private);
+
+ netdef->receive_checksum_offload = NETPLAN_TRISTATE_UNSET;
+ netdef->transmit_checksum_offload = NETPLAN_TRISTATE_UNSET;
+ netdef->tcp_segmentation_offload = NETPLAN_TRISTATE_UNSET;
+ netdef->tcp6_segmentation_offload = NETPLAN_TRISTATE_UNSET;
+ netdef->generic_segmentation_offload = NETPLAN_TRISTATE_UNSET;
+ netdef->generic_receive_offload = NETPLAN_TRISTATE_UNSET;
+ netdef->large_receive_offload = NETPLAN_TRISTATE_UNSET;
+
+ netdef->ib_mode = NETPLAN_IB_MODE_KERNEL;
}
static void
@@ -384,6 +424,12 @@ netplan_state_reset(NetplanState* np_state)
np_state->backend = NETPLAN_BACKEND_NONE;
reset_ovs_settings(&np_state->ovs_settings);
+
+ if (np_state->sources) {
+ /* Properly configured at creation to clean up after itself. */
+ g_hash_table_destroy(np_state->sources);
+ np_state->sources = NULL;
+ }
}
NetplanBackend
@@ -433,22 +479,31 @@ netplan_state_get_netdef(const NetplanState* np_state, const char* id)
return g_hash_table_lookup(np_state->netdefs, id);
}
-NETPLAN_PUBLIC const char *
-netplan_netdef_get_filename(const NetplanNetDefinition* netdef)
+NETPLAN_PUBLIC ssize_t
+netplan_netdef_get_filepath(const NetplanNetDefinition* netdef, char* out_buffer, size_t out_buf_size)
{
g_assert(netdef);
- return netdef->filename;
+ return netplan_copy_string(netdef->filepath, out_buffer, out_buf_size);
}
-NETPLAN_INTERNAL const char*
-netplan_netdef_get_id(const NetplanNetDefinition* netdef)
+NETPLAN_INTERNAL NetplanBackend
+netplan_netdef_get_backend(const NetplanNetDefinition* netdef)
{
- g_assert(netdef);
- return netdef->id;
+ return netdef->backend;
}
-NETPLAN_INTERNAL const char*
-_netplan_netdef_id(NetplanNetDefinition* netdef) __attribute__((alias("netplan_netdef_get_id")));
+NETPLAN_INTERNAL NetplanDefType
+netplan_netdef_get_type(const NetplanNetDefinition* netdef)
+{
+ return netdef->type;
+}
+
+NETPLAN_INTERNAL ssize_t
+netplan_netdef_get_id(const NetplanNetDefinition* netdef, char* out_buffer, size_t out_buf_size)
+{
+ g_assert(netdef);
+ return netplan_copy_string(netdef->id, out_buffer, out_buf_size);
+}
gboolean
netplan_state_has_nondefault_globals(const NetplanState* np_state)
@@ -470,3 +525,49 @@ netplan_netdef_get_delay_virtual_functions_rebind(const NetplanNetDefinition* ne
g_assert(netdef);
return netdef->sriov_delay_virtual_functions_rebind;
}
+
+NETPLAN_INTERNAL gboolean
+netplan_netdef_has_match(const NetplanNetDefinition* netdef)
+{
+ return netdef->has_match;
+}
+
+NETPLAN_INTERNAL NetplanNetDefinition*
+_netplan_netdef_get_sriov_link(const NetplanNetDefinition* netdef)
+{
+ return netdef->sriov_link;
+}
+
+NETPLAN_INTERNAL gboolean
+_netplan_netdef_get_sriov_vlan_filter(const NetplanNetDefinition* netdef) {
+ return netdef->sriov_vlan_filter;
+}
+
+NETPLAN_INTERNAL NetplanNetDefinition*
+_netplan_netdef_get_vlan_link(const NetplanNetDefinition* netdef)
+{
+ return netdef->vlan_link;
+}
+
+NETPLAN_INTERNAL guint
+_netplan_netdef_get_vlan_id(const NetplanNetDefinition* netdef)
+{
+ return netdef->vlan_id;
+}
+
+NETPLAN_INTERNAL gboolean
+_netplan_netdef_get_critical(const NetplanNetDefinition* netdef)
+{
+ return netdef->critical;
+}
+
+NETPLAN_INTERNAL gboolean
+_netplan_netdef_is_trivial_compound_itf(const NetplanNetDefinition* netdef)
+{
+ g_assert(netdef);
+ if (netdef->type == NETPLAN_DEF_TYPE_BOND)
+ return !complex_object_is_dirty(netdef, &netdef->bond_params, sizeof(netdef->bond_params));
+ else if (netdef->type == NETPLAN_DEF_TYPE_BRIDGE)
+ return !complex_object_is_dirty(netdef, &netdef->bridge_params, sizeof(netdef->bridge_params));
+ return FALSE;
+}
diff --git a/src/types.h b/src/types.h
index 27a23fc..e77a7f5 100644
--- a/src/types.h
+++ b/src/types.h
@@ -22,19 +22,10 @@
#include <yaml.h>
#include <uuid.h>
-typedef enum {
- NETPLAN_RA_MODE_KERNEL,
- NETPLAN_RA_MODE_ENABLED,
- NETPLAN_RA_MODE_DISABLED,
-} NetplanRAMode;
-
-typedef enum {
- NETPLAN_OPTIONAL_IPV4_LL = 1<<0,
- NETPLAN_OPTIONAL_IPV6_RA = 1<<1,
- NETPLAN_OPTIONAL_DHCP4 = 1<<2,
- NETPLAN_OPTIONAL_DHCP6 = 1<<3,
- NETPLAN_OPTIONAL_STATIC = 1<<4,
-} NetplanOptionalAddressFlag;
+/* Quite a few types are part of our current ABI, and so were isolated
+ * in order to make it easier to tell what's fair game and allow for ABI
+ * compatibility checks using 'abidiff' (abigail-tools). */
+#include "abi.h"
typedef enum {
NETPLAN_ADDRGEN_DEFAULT,
@@ -48,306 +39,34 @@ struct NetplanOptionalAddressType {
NetplanOptionalAddressFlag flag;
};
-// Not strictly speaking a type, but seems fair to keep it around.
-extern struct NetplanOptionalAddressType NETPLAN_OPTIONAL_ADDRESS_TYPES[];
-
-/* Tunnel mode enum; sync with NetworkManager's DBUS API */
-/* TODO: figure out whether networkd's GRETAP and NM's ISATAP
- * are the same thing.
- */
typedef enum {
- NETPLAN_TUNNEL_MODE_UNKNOWN = 0,
- NETPLAN_TUNNEL_MODE_IPIP = 1,
- NETPLAN_TUNNEL_MODE_GRE = 2,
- NETPLAN_TUNNEL_MODE_SIT = 3,
- NETPLAN_TUNNEL_MODE_ISATAP = 4, // NM only.
- NETPLAN_TUNNEL_MODE_VTI = 5,
- NETPLAN_TUNNEL_MODE_IP6IP6 = 6,
- NETPLAN_TUNNEL_MODE_IPIP6 = 7,
- NETPLAN_TUNNEL_MODE_IP6GRE = 8,
- NETPLAN_TUNNEL_MODE_VTI6 = 9,
-
- /* systemd-only, apparently? */
- NETPLAN_TUNNEL_MODE_GRETAP = 101,
- NETPLAN_TUNNEL_MODE_IP6GRETAP = 102,
- NETPLAN_TUNNEL_MODE_WIREGUARD = 103,
-
- NETPLAN_TUNNEL_MODE_MAX_,
-} NetplanTunnelMode;
+ NETPLAN_VXLAN_NOTIFICATION_L2_MISS = 1<<0,
+ NETPLAN_VXLAN_NOTIFICATION_L3_MISS = 1<<1,
+} NetplanVxlanNotificationFlags;
typedef enum {
- NETPLAN_WIFI_WOWLAN_DEFAULT = 1<<0,
- NETPLAN_WIFI_WOWLAN_ANY = 1<<1,
- NETPLAN_WIFI_WOWLAN_DISCONNECT = 1<<2,
- NETPLAN_WIFI_WOWLAN_MAGIC = 1<<3,
- NETPLAN_WIFI_WOWLAN_GTK_REKEY_FAILURE = 1<<4,
- NETPLAN_WIFI_WOWLAN_EAP_IDENTITY_REQ = 1<<5,
- NETPLAN_WIFI_WOWLAN_4WAY_HANDSHAKE = 1<<6,
- NETPLAN_WIFI_WOWLAN_RFKILL_RELEASE = 1<<7,
- NETPLAN_WIFI_WOWLAN_TCP = 1<<8,
-} NetplanWifiWowlanFlag;
-
-struct NetplanWifiWowlanType {
- char* name;
- NetplanWifiWowlanFlag flag;
-};
-
-extern struct NetplanWifiWowlanType NETPLAN_WIFI_WOWLAN_TYPES[];
+ NETPLAN_VXLAN_CHECKSUM_UDP = 1<<0,
+ NETPLAN_VXLAN_CHECKSUM_ZERO_UDP6_TX = 1<<1,
+ NETPLAN_VXLAN_CHECKSUM_ZERO_UDP6_RX = 1<<2,
+ NETPLAN_VXLAN_CHECKSUM_REMOTE_TX = 1<<3,
+ NETPLAN_VXLAN_CHECKSUM_REMOTE_RX = 1<<4,
+} NetplanVxlanChecksumFlags;
typedef enum {
- NETPLAN_AUTH_KEY_MANAGEMENT_NONE,
- NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK,
- NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP,
- NETPLAN_AUTH_KEY_MANAGEMENT_8021X,
- NETPLAN_AUTH_KEY_MANAGEMENT_MAX,
-} NetplanAuthKeyManagementType;
+ NETPLAN_VXLAN_EXTENSION_GROUP_POLICY = 1<<0,
+ NETPLAN_VXLAN_EXTENSION_GENERIC_PROTOCOL = 1<<1,
+} NetplanVxlanExtensionFlags;
-typedef enum {
- NETPLAN_AUTH_EAP_NONE,
- NETPLAN_AUTH_EAP_TLS,
- NETPLAN_AUTH_EAP_PEAP,
- NETPLAN_AUTH_EAP_TTLS,
- NETPLAN_AUTH_EAP_METHOD_MAX,
-} NetplanAuthEAPMethod;
+// Not strictly speaking a type, but seems fair to keep it around.
+extern struct NetplanOptionalAddressType NETPLAN_OPTIONAL_ADDRESS_TYPES[];
+
+extern struct NetplanWifiWowlanType NETPLAN_WIFI_WOWLAN_TYPES[];
typedef struct missing_node {
char* netdef_id;
const yaml_node_t* node;
} NetplanMissingNode;
-typedef struct authentication_settings {
- NetplanAuthKeyManagementType key_management;
- NetplanAuthEAPMethod eap_method;
- char* identity;
- char* anonymous_identity;
- char* password;
- char* ca_certificate;
- char* client_certificate;
- char* client_key;
- char* client_key_password;
- char* phase2_auth; /* netplan-feature: auth-phase2 */
-} NetplanAuthenticationSettings;
-
-/* Fields below are valid for dhcp4 and dhcp6 unless otherwise noted. */
-typedef struct dhcp_overrides {
- gboolean use_dns;
- gboolean use_ntp;
- gboolean send_hostname;
- gboolean use_hostname;
- gboolean use_mtu;
- gboolean use_routes;
- char* use_domains; /* netplan-feature: dhcp-use-domains */
- char* hostname;
- guint metric;
-} NetplanDHCPOverrides;
-
-typedef struct ovs_controller {
- char* connection_mode;
- GArray* addresses;
-} NetplanOVSController;
-
-typedef struct ovs_settings {
- GHashTable* external_ids;
- GHashTable* other_config;
- char* lacp;
- char* fail_mode;
- gboolean mcast_snooping;
- GArray* protocols;
- gboolean rstp;
- NetplanOVSController controller;
- NetplanAuthenticationSettings ssl;
-} NetplanOVSSettings;
-
-typedef union {
- struct NetplanNMSettings {
- char *name;
- char *uuid;
- char *stable_id;
- char *device;
- GData* passthrough; /* See g_datalist* functions */
- } nm;
- struct NetplanNetworkdSettings {
- char *unit;
- } networkd;
-} NetplanBackendSettings;
-
-struct netplan_net_definition {
- NetplanDefType type;
- NetplanBackend backend;
- char* id;
- /* only necessary for NetworkManager connection UUIDs in some cases */
- uuid_t uuid;
-
- /* status options */
- gboolean optional;
- NetplanOptionalAddressFlag optional_addresses;
- gboolean critical;
-
- /* addresses */
- gboolean dhcp4;
- gboolean dhcp6;
- char* dhcp_identifier;
- NetplanDHCPOverrides dhcp4_overrides;
- NetplanDHCPOverrides dhcp6_overrides;
- NetplanRAMode accept_ra;
- GArray* ip4_addresses;
- GArray* ip6_addresses;
- GArray* address_options;
- gboolean ip6_privacy;
- guint ip6_addr_gen_mode;
- char* ip6_addr_gen_token;
- char* gateway4;
- char* gateway6;
- GArray* ip4_nameservers;
- GArray* ip6_nameservers;
- GArray* search_domains;
- GArray* routes;
- GArray* ip_rules;
- GArray* wireguard_peers;
- struct {
- gboolean ipv4;
- gboolean ipv6;
- } linklocal;
-
- /* master ID for slave devices */
- char* bridge;
- char* bond;
-
- /* peer ID for OVS patch ports */
- char* peer;
-
- /* vlan */
- guint vlan_id;
- NetplanNetDefinition* vlan_link;
- gboolean has_vlans;
-
- /* Configured custom MAC address */
- char* set_mac;
-
- /* interface mtu */
- guint mtubytes;
- /* ipv6 mtu */
- /* netplan-feature: ipv6-mtu */
- guint ipv6_mtubytes;
-
- /* these properties are only valid for physical interfaces (type < ND_VIRTUAL) */
- char* set_name;
- struct {
- /* A glob (or tab-separated list of globs) to match a specific driver */
- char* driver;
- char* mac;
- char* original_name;
- } match;
- gboolean has_match;
- gboolean wake_on_lan;
- NetplanWifiWowlanFlag wowlan;
- gboolean emit_lldp;
-
- /* these properties are only valid for NETPLAN_DEF_TYPE_WIFI */
- GHashTable* access_points; /* SSID → NetplanWifiAccessPoint* */
-
- struct {
- char* mode;
- char* lacp_rate;
- char* monitor_interval;
- guint min_links;
- char* transmit_hash_policy;
- char* selection_logic;
- gboolean all_slaves_active;
- char* arp_interval;
- GArray* arp_ip_targets;
- char* arp_validate;
- char* arp_all_targets;
- char* up_delay;
- char* down_delay;
- char* fail_over_mac_policy;
- guint gratuitous_arp;
- /* TODO: unsolicited_na */
- guint packets_per_slave;
- char* primary_reselect_policy;
- guint resend_igmp;
- char* learn_interval;
- char* primary_slave;
- } bond_params;
-
- /* netplan-feature: modems */
- struct {
- char* apn;
- gboolean auto_config;
- char* device_id;
- char* network_id;
- char* number;
- char* password;
- char* pin;
- char* sim_id;
- char* sim_operator_id;
- char* username;
- } modem_params;
-
- struct {
- char* ageing_time;
- guint priority;
- guint port_priority;
- char* forward_delay;
- char* hello_time;
- char* max_age;
- guint path_cost;
- gboolean stp;
- } bridge_params;
- gboolean custom_bridging;
-
- struct {
- NetplanTunnelMode mode;
- char *local_ip;
- char *remote_ip;
- char *input_key;
- char *output_key;
- char *private_key; /* used for wireguard */
- guint fwmark;
- guint port;
- } tunnel;
-
- NetplanAuthenticationSettings auth;
- gboolean has_auth;
-
- /* these properties are only valid for SR-IOV NICs */
- /* netplan-feature: sriov */
- struct netplan_net_definition* sriov_link;
- gboolean sriov_vlan_filter;
- guint sriov_explicit_vf_count;
-
- /* these properties are only valid for OpenVSwitch */
- /* netplan-feature: openvswitch */
- NetplanOVSSettings ovs_settings;
-
- NetplanBackendSettings backend_settings;
-
- char* filename;
- /* it cannot be in the tunnel struct: https://github.com/canonical/netplan/pull/206 */
- guint tunnel_ttl;
-
- /* netplan-feature: activation-mode */
- char* activation_mode;
-
- /* configure without carrier */
- gboolean ignore_carrier;
-
- /* offload options */
- gboolean receive_checksum_offload;
- gboolean transmit_checksum_offload;
- gboolean tcp_segmentation_offload;
- gboolean tcp6_segmentation_offload;
- gboolean generic_segmentation_offload;
- gboolean generic_receive_offload;
- gboolean large_receive_offload;
-
- struct private_netdef_data* _private;
-
- /* netplan-feature: eswitch-mode */
- char* embedded_switch_mode;
- gboolean sriov_delay_virtual_functions_rebind;
-};
-
struct private_netdef_data {
GHashTable* dirty_fields;
};
@@ -430,6 +149,25 @@ typedef struct {
guint tos;
} NetplanIPRule;
+struct netplan_vxlan {
+ NetplanNetDefinition* link;
+ guint vni;
+ guint ageing;
+ guint limit;
+ guint tos;
+ guint flow_label;
+ guint source_port_min;
+ guint source_port_max;
+ gboolean mac_learning;
+ gboolean arp_proxy;
+ gboolean short_circuit;
+ gboolean independent;
+ NetplanFlags notifications;
+ NetplanFlags checksums;
+ NetplanFlags extensions;
+ NetplanTristate do_not_fragment;
+};
+
struct netplan_state {
/* Since both netdefs and netdefs_ordered store pointers to the same elements,
* we consider that only netdefs_ordered is owner of this data. One should not
@@ -440,6 +178,10 @@ struct netplan_state {
GList *netdefs_ordered;
NetplanBackend backend;
NetplanOVSSettings ovs_settings;
+
+ /* Hashset of the source files used to create this state. Owns its data (glib-allocated
+ * char*) and is initialized with g_hash_table_new_full to avoid leaks. */
+ GHashTable* sources;
};
struct netplan_parser {
@@ -453,6 +195,9 @@ struct netplan_parser {
NetplanBackend global_backend;
NetplanOVSSettings global_ovs_settings;
+ /* Keep track of the files used as data source */
+ GHashTable* sources;
+
/* Data currently being processed */
struct {
/* Refs to objects allocated elsewhere */
@@ -465,7 +210,8 @@ struct netplan_parser {
NetplanAddressOptions* addr_options;
NetplanIPRoute* route;
NetplanIPRule* ip_rule;
- const char *filename;
+ NetplanVxlan* vxlan;
+ const char *filepath;
/* Plain old data representing the backend for which we are
* currently parsing. Not necessarily the same as the global
@@ -491,6 +237,9 @@ struct netplan_parser {
* */
GHashTable* ids_in_file;
int missing_ids_found;
+
+ /* Which fields have been nullified by a subsequent patch? */
+ GHashTable* null_fields;
};
#define NETPLAN_ADVERTISED_RECEIVE_WINDOW_UNSPEC 0
@@ -506,9 +255,15 @@ void
reset_netdef(NetplanNetDefinition* netdef, NetplanDefType type, NetplanBackend renderer);
void
+reset_ip_rule(NetplanIPRule* ip_rule);
+
+void
reset_ovs_settings(NetplanOVSSettings *settings);
void
+reset_vxlan(NetplanVxlan* vxlan);
+
+void
access_point_clear(NetplanWifiAccessPoint** ap, NetplanBackend backend);
void
diff --git a/src/util-internal.h b/src/util-internal.h
index d787553..c0084ba 100644
--- a/src/util-internal.h
+++ b/src/util-internal.h
@@ -60,10 +60,13 @@ wifi_get_freq5(int channel);
NETPLAN_ABI gchar*
systemd_escape(char* string);
+NETPLAN_INTERNAL gboolean
+netplan_util_create_yaml_patch(const char* conf_obj_path, const char* obj_payload, int out_fd, GError** error);
+
#define OPENVSWITCH_OVS_VSCTL "/usr/bin/ovs-vsctl"
void
-mark_data_as_dirty(NetplanParser* npp, void* data_ptr);
+mark_data_as_dirty(NetplanParser* npp, const void* data_ptr);
const char*
tunnel_mode_to_string(NetplanTunnelMode mode);
@@ -88,3 +91,12 @@ process_yaml_hierarchy(const char* rootdir);
gboolean
has_openvswitch(const NetplanOVSSettings* ovs, NetplanBackend backend, GHashTable *ovs_ports);
+
+ssize_t
+netplan_copy_string(const char* input, char* out_buffer, size_t out_size);
+
+gboolean
+complex_object_is_dirty(const NetplanNetDefinition* def, const void* obj, size_t obj_size);
+
+gboolean
+is_multicast_address(const char*);
diff --git a/src/util.c b/src/util.c
index 841ec12..6e75cf5 100644
--- a/src/util.c
+++ b/src/util.c
@@ -18,7 +18,9 @@
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
+#include <fnmatch.h>
#include <errno.h>
+#include <string.h>
#include <glib.h>
#include <glib/gprintf.h>
@@ -122,6 +124,114 @@ int find_yaml_glob(const char* rootdir, glob_t* out_glob)
return 0;
}
+
+/**
+ * create a yaml patch from a "set expression"
+ *
+ * A "set expression" here consists of a path formed of TAB-separated
+ * keys, indicating where in the YAML doc we want to make our changes, and
+ * a valid YAML expression that will be the payload to insert at that
+ * place. The result is a well-formed YAML document.
+ *
+ * @conf_obj_path: TAB-separated YAML path
+ * @obj_payload: YAML expression
+ * @output_file: file path to write out the result document
+ */
+
+gboolean
+netplan_util_create_yaml_patch(const char* conf_obj_path, const char* obj_payload, int output_fd, GError** error)
+{
+ yaml_emitter_t emitter;
+ yaml_parser_t parser;
+ yaml_event_t event;
+ int token_depth = 0;
+ int out_dup = -1;
+ FILE* out_stream = NULL;
+ int ret = FALSE;
+
+ out_dup = dup(output_fd);
+ if (out_dup < 0)
+ goto file_error; // LCOV_EXCL_LINE
+ out_stream = fdopen(out_dup, "w");
+ if (!out_stream)
+ goto file_error; // LCOV_EXCL_LINE
+
+ yaml_emitter_initialize(&emitter);
+ yaml_emitter_set_output_file(&emitter, out_stream);
+ yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING);
+ if (!yaml_emitter_emit(&emitter, &event))
+ goto err_path; // LCOV_EXCL_LINE
+ yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 1);
+ if (!yaml_emitter_emit(&emitter, &event))
+ goto err_path; // LCOV_EXCL_LINE
+
+ char **tokens = g_strsplit_set(conf_obj_path, "\t", -1);
+ for (; tokens[token_depth] != NULL; token_depth++) {
+ YAML_MAPPING_OPEN(&event, &emitter);
+ YAML_SCALAR_PLAIN(&event, &emitter, tokens[token_depth]);
+ }
+ g_strfreev(tokens);
+
+ yaml_parser_initialize(&parser);
+ yaml_parser_set_input_string(&parser, (const unsigned char *)obj_payload, strlen(obj_payload));
+ while (TRUE) {
+ if (!yaml_parser_parse(&parser, &event)) {
+ g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Error parsing YAML: %s", parser.problem);
+ goto cleanup;
+ }
+ if (event.type == YAML_STREAM_END_EVENT || event.type == YAML_DOCUMENT_END_EVENT)
+ break;
+ switch (event.type) {
+ case YAML_STREAM_START_EVENT:
+ case YAML_DOCUMENT_START_EVENT:
+ break;
+ case YAML_MAPPING_START_EVENT:
+ YAML_MAPPING_OPEN(&event, &emitter);
+ break;
+ case YAML_SEQUENCE_START_EVENT:
+ YAML_SEQUENCE_OPEN(&event, &emitter);
+ break;
+ default:
+ if (!yaml_emitter_emit(&emitter, &event))
+ goto err_path; // LCOV_EXCL_LINE
+ }
+ }
+
+ for (; token_depth > 0; token_depth--)
+ YAML_MAPPING_CLOSE(&event, &emitter);
+
+ yaml_document_end_event_initialize(&event, 1);
+ if (!yaml_emitter_emit(&emitter, &event))
+ goto err_path; // LCOV_EXCL_LINE
+ yaml_stream_end_event_initialize(&event);
+ if (!yaml_emitter_emit(&emitter, &event))
+ goto err_path; // LCOV_EXCL_LINE
+ yaml_emitter_flush(&emitter);
+ fflush(out_stream);
+ ret = TRUE;
+ goto cleanup;
+
+// LCOV_EXCL_START
+err_path:
+ g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Error generating YAML: %s", emitter.problem);
+ ret = FALSE;
+// LCOV_EXCL_STOP
+cleanup:
+ /* also closes the dup FD */
+ fclose(out_stream);
+ yaml_emitter_delete(&emitter);
+ yaml_parser_delete(&parser);
+ return ret;
+
+// LCOV_EXCL_START
+file_error:
+ g_set_error(error, G_FILE_ERROR, errno, "Error when opening FD %d: %s", output_fd, g_strerror(errno));
+ if (out_dup >= 0)
+ close(out_dup);
+ return FALSE;
+// LCOV_EXCL_STOP
+}
+
static gboolean
copy_yaml_subtree(yaml_parser_t *parser, yaml_emitter_t *emitter, GError** error) {
yaml_event_t event;
@@ -374,7 +484,6 @@ systemd_escape(char* string)
gboolean
netplan_delete_connection(const char* id, const char* rootdir)
{
- g_autofree gchar* filename = NULL;
g_autofree gchar* del = NULL;
g_autoptr(GError) error = NULL;
NetplanNetDefinition* nd = NULL;
@@ -409,15 +518,13 @@ netplan_delete_connection(const char* id, const char* rootdir)
goto cleanup;
}
- filename = g_path_get_basename(nd->filename);
- filename[strlen(filename) - 5] = '\0'; //stip ".yaml" suffix
del = g_strdup_printf("network.%s.%s=NULL", netplan_def_type_name(nd->type), id);
/* TODO: refactor logic to actually be inside the library instead of spawning another process */
- const gchar *argv[] = { SBINDIR "/" "netplan", "set", del, "--origin-hint" , filename, NULL, NULL, NULL };
+ const gchar *argv[] = { SBINDIR "/" "netplan", "set", del, NULL, NULL, NULL };
if (rootdir) {
- argv[5] = "--root-dir";
- argv[6] = rootdir;
+ argv[3] = "--root-dir";
+ argv[4] = rootdir;
}
if (getenv("TEST_NETPLAN_CMD") != 0)
argv[0] = getenv("TEST_NETPLAN_CMD");
@@ -589,7 +696,7 @@ has_openvswitch(const NetplanOVSSettings* ovs, NetplanBackend backend, GHashTabl
}
void
-mark_data_as_dirty(NetplanParser* npp, void* data_ptr)
+mark_data_as_dirty(NetplanParser* npp, const void* data_ptr)
{
// We don't support dirty tracking for globals yet.
if (!npp->current.netdef)
@@ -598,5 +705,101 @@ mark_data_as_dirty(NetplanParser* npp, void* data_ptr)
npp->current.netdef->_private = g_new0(struct private_netdef_data, 1);
if (!npp->current.netdef->_private->dirty_fields)
npp->current.netdef->_private->dirty_fields = g_hash_table_new(g_direct_hash, g_direct_equal);
- g_hash_table_insert(npp->current.netdef->_private->dirty_fields, data_ptr, data_ptr);
+ g_hash_table_insert(npp->current.netdef->_private->dirty_fields, (void*)data_ptr, (void*)data_ptr);
+}
+
+gboolean
+complex_object_is_dirty(const NetplanNetDefinition* def, const void* obj, size_t obj_size) {
+ const char* ptr = obj;
+ if (def->_private == NULL || def->_private->dirty_fields == NULL)
+ return FALSE;
+ for (size_t i = 0; i < obj_size; ++i) {
+ if (g_hash_table_contains(def->_private->dirty_fields, ptr+i))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Copy a NUL-terminated string into a sized buffer, and returns the size of
+ * the copied string, including the final NUL character. If the buffer is too
+ * small, returns NETPLAN_BUFFER_TOO_SMALL instead.
+ *
+ * In all cases the contents of the output buffer will be entirely overwritten,
+ * except if the input string is NULL. Notably, if the buffer is too small its
+ * content will NOT be NUL-terminated.
+ *
+ * @input: the input string
+ * @out_buffer: a pointer to a buffer into which we want to copy the string
+ * @out_size: the size of the output buffer
+ */
+ssize_t
+netplan_copy_string(const char* input, char* out_buffer, size_t out_size)
+{
+ if (input == NULL)
+ return 0; // LCOV_EXCL_LINE
+ char* end = stpncpy(out_buffer, input, out_size);
+ // If it point to the first byte past the buffer, we don't have enough
+ // space in the buffer.
+ if (end - out_buffer == out_size)
+ return NETPLAN_BUFFER_TOO_SMALL;
+ return end - out_buffer + 1;
+}
+
+NETPLAN_INTERNAL gboolean
+netplan_netdef_match_interface(const NetplanNetDefinition* netdef, const char* name, const char* mac, const char* driver_name)
+{
+ if (!netdef->has_match)
+ return !g_strcmp0(name, netdef->id);
+
+ if (netdef->match.mac) {
+ if (g_ascii_strcasecmp(netdef->match.mac, mac))
+ return FALSE;
+ }
+
+ if (netdef->match.original_name) {
+ if (!name || fnmatch(netdef->match.original_name, name, 0))
+ return FALSE;
+ }
+
+ if (netdef->match.driver) {
+ gboolean matches_driver = FALSE;
+ char** tokens;
+ if (!driver_name)
+ return FALSE;
+ tokens = g_strsplit(netdef->match.driver, "\t", -1);
+ for (char** it = tokens; *it; it++) {
+ if (fnmatch(*it, driver_name, 0) == 0) {
+ matches_driver = TRUE;
+ break;
+ }
+ }
+ g_strfreev(tokens);
+ return matches_driver;
+ }
+
+ return TRUE;
+}
+
+NETPLAN_INTERNAL ssize_t
+netplan_netdef_get_set_name(const NetplanNetDefinition* netdef, char* out_buf, size_t out_size)
+{
+ return netplan_copy_string(netdef->set_name, out_buf, out_size);
+}
+
+gboolean
+is_multicast_address(const char* address)
+{
+ struct in_addr a4;
+ struct in6_addr a6;
+
+ if (inet_pton(AF_INET, address, &a4) > 0) {
+ if (ntohl(a4.s_addr) >> 28 == 0b1110) /* 224.0.0.0/4 */
+ return TRUE;
+ } else if (inet_pton(AF_INET6, address, &a6) > 0) {
+ if (a6.s6_addr[0] == 0xff) /* FF00::/8 */
+ return TRUE;
+ }
+
+ return FALSE;
}
diff --git a/src/validation.c b/src/validation.c
index a0b275c..24b80fb 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -204,10 +204,12 @@ validate_tunnel_grammar(const NetplanParser* npp, NetplanNetDefinition* nd, yaml
}
/* Validate local/remote IPs */
- if (!nd->tunnel.local_ip)
- return yaml_error(npp, node, error, "%s: missing 'local' property for tunnel", nd->id);
- if (!nd->tunnel.remote_ip)
- return yaml_error(npp, node, error, "%s: missing 'remote' property for tunnel", nd->id);
+ if (nd->tunnel.mode != NETPLAN_TUNNEL_MODE_VXLAN) {
+ if (!nd->tunnel.local_ip)
+ return yaml_error(npp, node, error, "%s: missing 'local' property for tunnel", nd->id);
+ if (!nd->tunnel.remote_ip)
+ return yaml_error(npp, node, error, "%s: missing 'remote' property for tunnel", nd->id);
+ }
if (nd->tunnel_ttl && nd->tunnel_ttl > 255)
return yaml_error(npp, node, error, "%s: 'ttl' property for tunnel must be in range [1...255]", nd->id);
@@ -223,6 +225,12 @@ validate_tunnel_grammar(const NetplanParser* npp, NetplanNetDefinition* nd, yaml
return yaml_error(npp, node, error, "%s: 'remote' must be a valid IPv6 address for this tunnel type", nd->id);
break;
+ case NETPLAN_TUNNEL_MODE_VXLAN:
+ if ((nd->tunnel.local_ip && nd->tunnel.remote_ip) &&
+ (is_ip6_address(nd->tunnel.local_ip) != is_ip6_address(nd->tunnel.remote_ip)))
+ return yaml_error(npp, node, error, "%s: 'local' and 'remote' must be of same IP family type", nd->id);
+ break;
+
default:
if (!is_ip4_address(nd->tunnel.local_ip))
return yaml_error(npp, node, error, "%s: 'local' must be a valid IPv4 address for this tunnel type", nd->id);
@@ -244,6 +252,10 @@ validate_tunnel_backend_rules(const NetplanParser* npp, NetplanNetDefinition* nd
case NETPLAN_TUNNEL_MODE_VTI:
case NETPLAN_TUNNEL_MODE_VTI6:
case NETPLAN_TUNNEL_MODE_WIREGUARD:
+ case NETPLAN_TUNNEL_MODE_GRE:
+ case NETPLAN_TUNNEL_MODE_IP6GRE:
+ case NETPLAN_TUNNEL_MODE_GRETAP:
+ case NETPLAN_TUNNEL_MODE_IP6GRETAP:
break;
/* TODO: Remove this exception and fix ISATAP handling with the
@@ -327,6 +339,24 @@ validate_netdef_grammar(const NetplanParser* npp, NetplanNetDefinition* nd, yaml
return yaml_error(npp, node, error, "%s: invalid id '%u' (allowed values are 0 to 4094)", nd->id, nd->vlan_id);
}
+ if (nd->type == NETPLAN_DEF_TYPE_TUNNEL &&
+ nd->tunnel.mode == NETPLAN_TUNNEL_MODE_VXLAN) {
+ if (nd->vxlan->vni == 0)
+ return yaml_error(npp, node, error,
+ "%s: missing 'id' property (VXLAN VNI)", nd->id);
+ if (nd->vxlan->vni < 1 || nd->vxlan->vni > 16777215)
+ return yaml_error(npp, node, error, "%s: VXLAN 'id' (VNI) "
+ "must be in range [1..16777215]", nd->id);
+ if (nd->vxlan->flow_label != G_MAXUINT && nd->vxlan->flow_label > 1048575)
+ return yaml_error(npp, node, error, "%s: VXLAN 'flow-label' "
+ "must be in range [0..1048575]", nd->id);
+ }
+
+ if (nd->type == NETPLAN_DEF_TYPE_VRF) {
+ if (nd->vrf_table == G_MAXUINT)
+ return yaml_error(npp, node, error, "%s: missing 'table' property", nd->id);
+ }
+
if (nd->type == NETPLAN_DEF_TYPE_TUNNEL) {
valid = validate_tunnel_grammar(npp, nd, node, error);
if (!valid)
@@ -415,6 +445,53 @@ sriov_rules_error:
return valid;
}
+gboolean
+adopt_and_validate_vrf_routes(const NetplanParser *npp, GHashTable *netdefs, GError **error)
+{
+ gpointer key, value;
+ GHashTableIter iter;
+
+ g_hash_table_iter_init (&iter, netdefs);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ NetplanNetDefinition *nd = value;
+ if (nd->type != NETPLAN_DEF_TYPE_VRF || !nd->routes)
+ continue;
+
+ /* Routes */
+ for (size_t i = 0; i < nd->routes->len; i++) {
+ NetplanIPRoute* r = g_array_index(nd->routes, NetplanIPRoute*, i);
+ if (r->table == nd->vrf_table) {
+ g_debug("%s: Ignoring redundant routes table %d (matches VRF table)", nd->id, r->table);
+ continue;
+ } else if (r->table != NETPLAN_ROUTE_TABLE_UNSPEC) {
+ g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "%s: VRF routes table mismatch (%d != %d)", nd->id, nd->vrf_table, r->table);
+ return FALSE;
+ } else {
+ r->table = nd->vrf_table;
+ g_debug("%s: Adopted VRF routes table to %d", nd->id, nd->vrf_table);
+ }
+ }
+ /* IP Rules */
+ for (size_t i = 0; i < nd->ip_rules->len; i++) {
+ NetplanIPRule* r = g_array_index(nd->ip_rules, NetplanIPRule*, i);
+ if (r->table == nd->vrf_table) {
+ g_debug("%s: Ignoring redundant routing-policy table %d (matches VRF table)", nd->id, r->table);
+ continue;
+ } else if (r->table != NETPLAN_ROUTE_TABLE_UNSPEC && r->table != nd->vrf_table) {
+ g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "%s: VRF routing-policy table mismatch (%d != %d)", nd->id, nd->vrf_table, r->table);
+ return FALSE;
+ } else {
+ r->table = nd->vrf_table;
+ g_debug("%s: Adopted VRF routing-policy table to %d", nd->id, nd->vrf_table);
+ }
+ }
+ }
+ return TRUE;
+}
+
struct _defroute_entry {
int family;
int table;
diff --git a/src/validation.h b/src/validation.h
index 70d3228..88e4762 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -41,3 +41,6 @@ validate_sriov_rules(const NetplanParser* npp, NetplanNetDefinition* nd, GError*
gboolean
validate_default_route_consistency(const NetplanParser* npp, GHashTable* netdefs, GError** error);
+
+gboolean
+adopt_and_validate_vrf_routes(const NetplanParser* npp, GHashTable* netdefs, GError** error);
diff --git a/src/yaml-helpers.h b/src/yaml-helpers.h
index 0f8e667..f69a8c7 100644
--- a/src/yaml-helpers.h
+++ b/src/yaml-helpers.h
@@ -45,6 +45,10 @@
if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \
}
+#define YAML_FLAG(event_ptr, emitter_ptr, flag, flags_ptr, flags_func) \
+ if (flags_ptr & flag) \
+ YAML_SCALAR_PLAIN(event_ptr, emitter_ptr, flags_func(flag));
+
#define YAML_NULL_PLAIN(event_ptr, emitter_ptr) \
yaml_scalar_event_initialize(event_ptr, NULL, (yaml_char_t*)YAML_NULL_TAG, (yaml_char_t*)"null", strlen("null"), 1, 0, YAML_PLAIN_SCALAR_STYLE); \
if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \
diff --git a/tests/cli.py b/tests/cli.py
index ee00ecc..e4c6f50 100755
--- a/tests/cli.py
+++ b/tests/cli.py
@@ -23,18 +23,29 @@ import subprocess
import unittest
import tempfile
import shutil
-
import yaml
+from tests.test_utils import MockCmd
+
rootdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
exe_cli = [os.path.join(rootdir, 'src', 'netplan.script')]
if shutil.which('python3-coverage'):
exe_cli = ['python3-coverage', 'run', '--append', '--'] + exe_cli
-# Make sure we can import our development netplan.
-os.environ.update({'PYTHONPATH': '.'})
os.environ.update({'LD_LIBRARY_PATH': '.:{}'.format(os.environ.get('LD_LIBRARY_PATH'))})
+# combined the output of 'nmcli dev' and 'nmcli con' into a single mock output,
+# connected via FAKE_NM_ID. Both are parsed line by line, checking for
+# 'GENERAL.CONNECTION' or 'connection.id/uuid' respectively
+nmcli_mock_output = '''
+# nmcli dev ... MOCK OUTPUT:
+GENERAL.CONNECTION: FAKE_NM_ID
+
+# nmcli con ... MOCK OUTPUT:
+connection.id: FAKE_NM_ID
+connection.uuid: 00000000-0000-0000-0000-000000000000
+'''
+
def _load_yaml(text):
return yaml.load(text, Loader=yaml.SafeLoader)
@@ -54,7 +65,7 @@ class TestArgs(unittest.TestCase):
self.assertIn(b'--root-dir', out)
def test_no_command(self):
- os.environ['NETPLAN_GENERATE_PATH'] = os.path.join(rootdir, 'generate')
+ os.environ.setdefault('NETPLAN_GENERATE_PATH', os.path.join(rootdir, 'generate'))
p = subprocess.Popen(exe_cli, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(out, err) = p.communicate()
@@ -102,7 +113,7 @@ class TestGenerate(unittest.TestCase):
['10-netplan-enlol.network'])
def test_mapping_for_unknown_iface(self):
- os.environ['NETPLAN_GENERATE_PATH'] = os.path.join(rootdir, 'generate')
+ os.environ.setdefault('NETPLAN_GENERATE_PATH', os.path.join(rootdir, 'generate'))
c = os.path.join(self.workdir.name, 'etc', 'netplan')
os.makedirs(c)
with open(os.path.join(c, 'a.yaml'), 'w') as f:
@@ -118,7 +129,7 @@ class TestGenerate(unittest.TestCase):
self.assertNotIn(b'nonexistent', out)
def test_mapping_for_interface(self):
- os.environ['NETPLAN_GENERATE_PATH'] = os.path.join(rootdir, 'generate')
+ os.environ.setdefault('NETPLAN_GENERATE_PATH', os.path.join(rootdir, 'generate'))
c = os.path.join(self.workdir.name, 'etc', 'netplan')
os.makedirs(c)
with open(os.path.join(c, 'a.yaml'), 'w') as f:
@@ -132,7 +143,7 @@ class TestGenerate(unittest.TestCase):
self.assertIn('enlol', out.decode('utf-8'))
def test_mapping_for_renamed_iface(self):
- os.environ['NETPLAN_GENERATE_PATH'] = os.path.join(rootdir, 'generate')
+ os.environ.setdefault('NETPLAN_GENERATE_PATH', os.path.join(rootdir, 'generate'))
c = os.path.join(self.workdir.name, 'etc', 'netplan')
os.makedirs(c)
with open(os.path.join(c, 'a.yaml'), 'w') as f:
@@ -609,7 +620,7 @@ class TestIp(unittest.TestCase):
self.assertNotEqual(p.returncode, 0)
def test_ip_leases_networkd(self):
- os.environ['NETPLAN_GENERATE_PATH'] = os.path.join(rootdir, 'generate')
+ os.environ.setdefault('NETPLAN_GENERATE_PATH', os.path.join(rootdir, 'generate'))
c = os.path.join(self.workdir.name, 'etc', 'netplan')
os.makedirs(c)
with open(os.path.join(c, 'a.yaml'), 'w') as f:
@@ -636,11 +647,92 @@ class TestIp(unittest.TestCase):
self.assertIn('FAKE NETIF', out.decode('utf-8'))
def test_ip_leases_nm(self):
- unittest.skip("Cannot be tested offline due to calls required to nmcli."
- "This is tested in integration tests.")
+ os.environ.setdefault('NETPLAN_GENERATE_PATH',
+ os.path.join(rootdir, 'generate'))
+ mock = MockCmd('nmcli')
+ mock.set_output(nmcli_mock_output)
+ new_path = os.pathsep.join([os.path.dirname(mock.path),
+ os.environ.get('PATH', os.defpath)])
+ mock_env = dict(os.environ, PATH=new_path)
+
+ c = os.path.join(self.workdir.name, 'etc', 'netplan')
+ os.makedirs(c)
+ with open(os.path.join(c, 'a.yaml'), 'w') as f:
+ f.write('''network:
+ version: 2
+ renderer: NetworkManager
+ ethernets: {lo: {dhcp4: yes}}
+''')
+ fake_lease_dir = os.path.join(self.workdir.name,
+ 'var', 'lib', 'NetworkManager')
+ os.makedirs(fake_lease_dir)
+ # using fake '0000..' UUID from nmcli_mock_output
+ lease_file = 'internal-00000000-0000-0000-0000-000000000000-lo.lease'
+ with open(os.path.join(fake_lease_dir, lease_file), 'w') as f:
+ f.write('''THIS IS A FAKE INTERNAL_NM LEASE FOR LO''')
+ out = subprocess.check_output(exe_cli +
+ ['ip', 'leases',
+ '--root-dir', self.workdir.name, 'lo'],
+ env=mock_env)
+ self.assertIn('FAKE INTERNAL_NM LEASE', out.decode('utf-8'))
+
+ def test_ip_leases_nm_dhclient_fallback(self):
+ os.environ.setdefault('NETPLAN_GENERATE_PATH',
+ os.path.join(rootdir, 'generate'))
+ mock = MockCmd('nmcli')
+ mock.set_output(nmcli_mock_output)
+ new_path = os.pathsep.join([os.path.dirname(mock.path),
+ os.environ.get('PATH', os.defpath)])
+ mock_env = dict(os.environ, PATH=new_path)
+
+ c = os.path.join(self.workdir.name, 'etc', 'netplan')
+ os.makedirs(c)
+ with open(os.path.join(c, 'a.yaml'), 'w') as f:
+ f.write('''network:
+ version: 2
+ renderer: NetworkManager
+ ethernets: {lo: {dhcp4: yes}}
+''')
+ fake_lease_dir = os.path.join(self.workdir.name,
+ 'var', 'lib', 'NetworkManager')
+ os.makedirs(fake_lease_dir)
+ # using fake '0000..' UUID from nmcli_mock_output
+ lease_file = 'dhclient-00000000-0000-0000-0000-000000000000-lo.lease'
+ with open(os.path.join(fake_lease_dir, lease_file), 'w') as f:
+ f.write('''THIS IS A FAKE DHCLIENT_NM LEASE FOR LO''')
+ out = subprocess.check_output(exe_cli +
+ ['ip', 'leases',
+ '--root-dir', self.workdir.name, 'lo'],
+ env=mock_env)
+ self.assertIn('FAKE DHCLIENT_NM LEASE', out.decode('utf-8'))
+
+ def test_ip_leases_nm_fail(self):
+ os.environ.setdefault('NETPLAN_GENERATE_PATH',
+ os.path.join(rootdir, 'generate'))
+ mock = MockCmd('nmcli')
+ mock.set_output(nmcli_mock_output)
+ mock.set_returncode(10)
+ new_path = os.pathsep.join([os.path.dirname(mock.path),
+ os.environ.get('PATH', os.defpath)])
+ mock_env = dict(os.environ, PATH=new_path)
+
+ c = os.path.join(self.workdir.name, 'etc', 'netplan')
+ os.makedirs(c)
+ with open(os.path.join(c, 'a.yaml'), 'w') as f:
+ f.write('''network:
+ version: 2
+ renderer: NetworkManager
+ ethernets: {lo: {dhcp4: yes}}
+''')
+ # the nmcli Mock's return value is 10, indicating an error
+ with self.assertRaises(Exception):
+ subprocess.check_output(exe_cli +
+ ['ip', 'leases',
+ '--root-dir', self.workdir.name, 'lo'],
+ env=mock_env)
def test_ip_leases_no_networkd_lease(self):
- os.environ['NETPLAN_GENERATE_PATH'] = os.path.join(rootdir, 'generate')
+ os.environ.setdefault('NETPLAN_GENERATE_PATH', os.path.join(rootdir, 'generate'))
c = os.path.join(self.workdir.name, 'etc', 'netplan')
os.makedirs(c)
with open(os.path.join(c, 'a.yaml'), 'w') as f:
@@ -664,12 +756,16 @@ class TestIp(unittest.TestCase):
self.assertNotEqual(p.returncode, 0)
def test_ip_leases_no_nm_lease(self):
- os.environ['NETPLAN_GENERATE_PATH'] = os.path.join(rootdir, 'generate')
+ os.environ.setdefault('NETPLAN_GENERATE_PATH', os.path.join(rootdir, 'generate'))
+ mock = MockCmd('nmcli')
+ mock.set_output('\n')
+ new_path = os.pathsep.join([os.path.dirname(mock.path),
+ os.environ.get('PATH', os.defpath)])
+ mock_env = dict(os.environ, PATH=new_path)
+
c = os.path.join(self.workdir.name, 'etc', 'netplan')
os.makedirs(c)
with open(os.path.join(c, 'a.yaml'), 'w') as f:
- # match against loopback so as to successfully get a predictable
- # ifindex
f.write('''network:
version: 2
renderer: NetworkManager
@@ -679,10 +775,11 @@ class TestIp(unittest.TestCase):
name: lo
dhcp4: yes
''')
+ # we didn't create a (mock) lease file, therefore expect stderr output
p = subprocess.Popen(exe_cli +
['ip', 'leases', '--root-dir', self.workdir.name, 'enlol'],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env=mock_env)
(out, err) = p.communicate()
self.assertEqual(out, b'')
self.assertIn(b'No lease found', err)
diff --git a/tests/dbus/test_dbus.py b/tests/dbus/test_dbus.py
index f3003a5..6704626 100644
--- a/tests/dbus/test_dbus.py
+++ b/tests/dbus/test_dbus.py
@@ -28,9 +28,8 @@ exe_cli = [os.path.join(rootdir, 'src', 'netplan.script')]
if shutil.which('python3-coverage'):
exe_cli = ['python3-coverage', 'run', '--append', '--'] + exe_cli
-# Make sure we can import our development netplan.
-os.environ.update({'PYTHONPATH': '.'})
-NETPLAN_DBUS_CMD = os.path.join(os.path.dirname(__file__), "..", "..", "netplan-dbus")
+NETPLAN_DBUS_CMD = os.environ.get('NETPLAN_DBUS_CMD',
+ os.path.join(os.path.dirname(__file__), "..", "..", "netplan-dbus"))
class TestNetplanDBus(unittest.TestCase):
diff --git a/tests/generator/base.py b/tests/generator/base.py
index d9396dd..0c61bcb 100644
--- a/tests/generator/base.py
+++ b/tests/generator/base.py
@@ -32,8 +32,9 @@ import ctypes.util
import yaml
import difflib
-exe_generate = os.path.join(os.path.dirname(os.path.dirname(
- os.path.dirname(os.path.abspath(__file__)))), 'generate')
+exe_generate = os.environ.get('NETPLAN_GENERATE_PATH',
+ os.path.join(os.path.dirname(os.path.dirname(
+ os.path.dirname(os.path.abspath(__file__)))), 'generate'))
# make sure we point to libnetplan properly.
os.environ.update({'LD_LIBRARY_PATH': '.:{}'.format(os.environ.get('LD_LIBRARY_PATH'))})
@@ -66,9 +67,9 @@ standalone\nExecStart=/usr/bin/ovs-vsctl set Bridge %(iface)s mcast_snooping_ena
Bridge %(iface)s external-ids:netplan/mcast_snooping_enable=false\nExecStart=/usr/bin/ovs-vsctl set Bridge %(iface)s \
rstp_enable=false\nExecStart=/usr/bin/ovs-vsctl set Bridge %(iface)s external-ids:netplan/rstp_enable=false\n'
OVS_BR_EMPTY = _OVS_BASE + 'After=netplan-ovs-cleanup.service\nBefore=network.target\nWants=network.target\n\n[Service]\n\
-Type=oneshot\nExecStart=/usr/bin/ovs-vsctl --may-exist add-br %(iface)s\n' + OVS_BR_DEFAULT
+Type=oneshot\nTimeoutStartSec=10s\nExecStart=/usr/bin/ovs-vsctl --may-exist add-br %(iface)s\n' + OVS_BR_DEFAULT
OVS_CLEANUP = _OVS_BASE + 'ConditionFileIsExecutable=/usr/bin/ovs-vsctl\nBefore=network.target\nWants=network.target\n\n\
-[Service]\nType=oneshot\nExecStart=/usr/sbin/netplan apply --only-ovs-cleanup\n'
+[Service]\nType=oneshot\nTimeoutStartSec=10s\nExecStart=/usr/sbin/netplan apply --only-ovs-cleanup\n'
UDEV_MAC_RULE = 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="%s", ATTR{address}=="%s", NAME="%s"\n'
UDEV_NO_MAC_RULE = 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="%s", NAME="%s"\n'
UDEV_SRIOV_RULE = 'ACTION=="add", SUBSYSTEM=="net", ATTRS{sriov_totalvfs}=="?*", RUN+="/usr/sbin/netplan apply --sriov-only"\n'
@@ -79,6 +80,8 @@ NM_WG = '[connection]\nid=netplan-wg0\ntype=wireguard\ninterface-name=wg0\n\n[wi
2001:de:ad:be:ef:ca:fe:1/128\nip6-privacy=0\n'
ND_WG = '[NetDev]\nName=wg0\nKind=wireguard\n\n[WireGuard]\nPrivateKey%s\nListenPort=%s\n%s\n'
ND_VLAN = '[NetDev]\nName=%s\nKind=vlan\n\n[VLAN]\nId=%d\n'
+ND_VXLAN = '[NetDev]\nName=%s\nKind=vxlan\n\n[VXLAN]\nVNI=%d\n'
+ND_VRF = '[NetDev]\nName=%s\nKind=vrf\n\n[VRF]\nTable=%d\n'
SD_WPA = '''[Unit]
Description=WPA supplicant for netplan %(iface)s
DefaultDependencies=no
@@ -91,6 +94,12 @@ Wants=network.target
Type=simple
ExecStart=/sbin/wpa_supplicant -c /run/netplan/wpa-%(iface)s.conf -i%(iface)s -D%(drivers)s
'''
+NM_MANAGED = 'SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_NAME}=="%s", ENV{NM_UNMANAGED}="0"\n'
+NM_UNMANAGED = 'SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_NAME}=="%s", ENV{NM_UNMANAGED}="1"\n'
+NM_MANAGED_MAC = 'SUBSYSTEM=="net", ACTION=="add|change|move", ATTR{address}=="%s", ENV{NM_UNMANAGED}="0"\n'
+NM_UNMANAGED_MAC = 'SUBSYSTEM=="net", ACTION=="add|change|move", ATTR{address}=="%s", ENV{NM_UNMANAGED}="1"\n'
+NM_MANAGED_DRIVER = 'SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_DRIVER}=="%s", ENV{NM_UNMANAGED}="0"\n'
+NM_UNMANAGED_DRIVER = 'SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_DRIVER}=="%s", ENV{NM_UNMANAGED}="1"\n'
class NetplanV2Normalizer():
@@ -431,7 +440,12 @@ class TestBase(unittest.TestCase):
self.assertFalse(os.path.exists(rule_path))
return
with open(rule_path) as f:
- self.assertEqual(f.read(), contents)
+ lines = []
+ for line in f.readlines():
+ # ignore any comment in udev rules.d file
+ if not line.startswith('#'):
+ lines.append(line)
+ self.assertEqual(''.join(lines), contents)
def assert_ovs(self, file_contents_map):
systemd_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system')
diff --git a/tests/generator/test_auth.py b/tests/generator/test_auth.py
index 3d20109..e8fd023 100644
--- a/tests/generator/test_auth.py
+++ b/tests/generator/test_auth.py
@@ -19,7 +19,7 @@
import os
import stat
-from .base import TestBase, ND_DHCP4, ND_WIFI_DHCP4, SD_WPA
+from .base import TestBase, ND_DHCP4, ND_WIFI_DHCP4, SD_WPA, NM_MANAGED, NM_UNMANAGED
class TestNetworkd(TestBase):
@@ -83,10 +83,8 @@ class TestNetworkd(TestBase):
''')
self.assert_networkd({'wl0.network': ND_WIFI_DHCP4 % 'wl0'})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:wl0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'wl0')
# generates wpa config and enables wpasupplicant unit
with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f:
@@ -201,10 +199,8 @@ network={
''')
self.assert_networkd({'eth0.network': ND_DHCP4 % 'eth0'})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:eth0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'eth0')
# generates wpa config and enables wpasupplicant unit
with open(os.path.join(self.workdir.name, 'run/netplan/wpa-eth0.conf')) as f:
@@ -458,7 +454,7 @@ method=ignore
ssid=peer2peer
mode=adhoc
'''})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'wl0')
def test_auth_wired(self):
self.generate('''network:
@@ -502,7 +498,7 @@ private-key=/etc/ssl/cust-key.pem
private-key-password=d3cryptPr1v4t3K3y
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eth0')
class TestConfigErrors(TestBase):
diff --git a/tests/generator/test_bonds.py b/tests/generator/test_bonds.py
index fea475e..ee3f53c 100644
--- a/tests/generator/test_bonds.py
+++ b/tests/generator/test_bonds.py
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from .base import TestBase
+from .base import TestBase, NM_MANAGED, NM_UNMANAGED
class TestNetworkd(TestBase):
@@ -79,10 +79,8 @@ ConfigureWithoutCarrier=yes
RouteMetric=100
UseMTU=true
'''})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:bn0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'bn0')
def test_bond_components(self):
self.generate('''network:
@@ -460,7 +458,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'bn0')
def test_bond_empty_params(self):
self.generate('''network:
@@ -521,7 +519,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'bn0')
def test_bond_with_params(self):
self.generate('''network:
@@ -625,7 +623,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'bn0')
def test_bond_primary_slave(self):
self.generate('''network:
@@ -692,7 +690,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'bn0')
class TestConfigErrors(TestBase):
diff --git a/tests/generator/test_bridges.py b/tests/generator/test_bridges.py
index 7898cbe..102d592 100644
--- a/tests/generator/test_bridges.py
+++ b/tests/generator/test_bridges.py
@@ -19,7 +19,7 @@
import os
import unittest
-from .base import TestBase
+from .base import TestBase, NM_UNMANAGED, NM_MANAGED
class TestNetworkd(TestBase):
@@ -108,10 +108,8 @@ ConfigureWithoutCarrier=yes
RouteMetric=100
UseMTU=true
'''})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:br0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'br0')
def test_bridge_type_renderer(self):
self.generate('''network:
@@ -135,10 +133,8 @@ ConfigureWithoutCarrier=yes
RouteMetric=100
UseMTU=true
'''})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:br0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'br0')
def test_bridge_def_renderer(self):
self.generate('''network:
@@ -165,10 +161,8 @@ ConfigureWithoutCarrier=yes
RouteMetric=100
UseMTU=true
'''})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:br0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'br0')
def test_bridge_forward_declaration(self):
self.generate('''network:
@@ -215,9 +209,8 @@ UseMTU=true
mybr:
interfaces: [ethbr]
dhcp4: yes''')
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:eth42,interface-name:eth43,interface-name:mybr,''')
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'eth42' + NM_UNMANAGED % 'eth43' + NM_UNMANAGED % 'mybr')
def test_bridge_components(self):
self.generate('''network:
@@ -322,7 +315,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'br0')
def test_bridge_type_renderer(self):
self.generate('''network:
@@ -345,7 +338,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'br0')
def test_bridge_set_mac(self):
self.generate('''network:
@@ -395,7 +388,7 @@ address1=1.2.3.4/12
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'br0')
def test_bridge_forward_declaration(self):
self.generate('''network:
@@ -456,7 +449,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'br0' + NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1')
def test_bridge_components(self):
self.generate('''network:
@@ -516,7 +509,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'br0')
def test_bridge_params(self):
self.generate('''network:
@@ -599,7 +592,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'br0')
class TestNetplanYAMLv2(TestBase):
diff --git a/tests/generator/test_common.py b/tests/generator/test_common.py
index f4bd7f4..18467ef 100644
--- a/tests/generator/test_common.py
+++ b/tests/generator/test_common.py
@@ -19,7 +19,7 @@
import os
import textwrap
-from .base import TestBase, ND_DHCP4, ND_DHCP6, ND_DHCPYES, ND_EMPTY
+from .base import TestBase, ND_DHCP4, ND_DHCP6, ND_DHCPYES, ND_EMPTY, NM_MANAGED, NM_UNMANAGED
class TestNetworkd(TestBase):
@@ -184,10 +184,8 @@ Bond=bond0
dhcp4: true''')
self.assert_networkd({'eth0.network': ND_DHCP4 % 'eth0'})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:eth0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'eth0')
# should not allow NM to manage everything
self.assertFalse(os.path.exists(self.nm_enable_all_conf))
@@ -201,12 +199,10 @@ unmanaged-devices+=interface-name:eth0,''')
dhcp4: true''')
self.assert_networkd({'eth0.network': ND_DHCP4 % 'eth0'})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:eth0,''')
+ self.assert_nm(None)
# should allow NM to manage everything else
self.assertTrue(os.path.exists(self.nm_enable_all_conf))
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'eth0')
def test_eth_def_renderer(self):
self.generate('''network:
@@ -220,10 +216,8 @@ unmanaged-devices+=interface-name:eth0,''')
self.assert_networkd({'eth0.network': ND_DHCP4 % 'eth0'})
self.assert_networkd_udev(None)
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:eth0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'eth0')
def test_eth_dhcp6(self):
self.generate('''network:
@@ -931,7 +925,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eth0')
def test_ipv6_mtu(self):
self.generate(textwrap.dedent("""
@@ -966,7 +960,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eth0')
def test_eth_type_renderer(self):
self.generate('''network:
@@ -992,7 +986,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eth0')
def test_eth_def_renderer(self):
self.generate('''network:
@@ -1018,7 +1012,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eth0')
def test_global_renderer_only(self):
self.generate(None, confs={'01-default-nm.yaml': 'network: {version: 2, renderer: NetworkManager}'})
@@ -1193,7 +1187,7 @@ address1=2001:FFfe::1/64
ip6-privacy=0
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'engreen')
def test_eth_manual_addresses_dhcp(self):
self.generate('''network:
@@ -1545,10 +1539,8 @@ class TestMerging(TestBase):
confs={'backend': 'network:\n renderer: networkd'})
self.assert_networkd({'engreen.network': ND_DHCP4 % 'engreen'})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:engreen,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'engreen')
def test_add_def(self):
self.generate('''network:
@@ -1568,10 +1560,8 @@ unmanaged-devices+=interface-name:engreen,''')
# releases, so we can't depend on the exact order.
# TODO: (cyphermox) turn this into an "assert_in_nm()" function.
if "CODECOV_TOKEN" not in os.environ: # pragma: nocover
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:engreen,interface-name:enblue,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'engreen' + NM_UNMANAGED % 'enblue')
def test_change_def(self):
self.generate('''network:
@@ -1608,10 +1598,8 @@ unmanaged-devices+=interface-name:engreen,interface-name:enblue,''')
engreen: {dhcp4: true}''')
self.assert_networkd({'engreen.network': ND_DHCP4 % 'engreen'})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:engreen,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'engreen')
def test_ref(self):
self.generate('''network:
diff --git a/tests/generator/test_errors.py b/tests/generator/test_errors.py
index b7bc949..0bc8157 100644
--- a/tests/generator/test_errors.py
+++ b/tests/generator/test_errors.py
@@ -149,7 +149,17 @@ class TestConfigErrors(TestBase):
match:
macaddress: 00:11:ZZ
dhcp4: true''', expect_fail=True)
- self.assertIn("Invalid MAC address '00:11:ZZ', must be XX:XX:XX:XX:XX:XX", err)
+ self.assertIn("Invalid MAC address '00:11:ZZ', must be XX:XX:XX:XX:XX:XX "
+ "or XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX", err)
+
+ def test_invalid_ipoib_mode(self):
+ err = self.generate('''network:
+ version: 2
+ ethernets:
+ ib0:
+ dhcp4: true
+ infiniband-mode: invalid''', expect_fail=True)
+ self.assertIn("Value of 'infiniband-mode' needs to be 'datagram' or 'connected'", err)
def test_glob_in_id(self):
err = self.generate('''network:
diff --git a/tests/generator/test_ethernets.py b/tests/generator/test_ethernets.py
index 46bf764..520ebdd 100644
--- a/tests/generator/test_ethernets.py
+++ b/tests/generator/test_ethernets.py
@@ -18,7 +18,9 @@
import os
-from .base import TestBase, ND_DHCP4, UDEV_MAC_RULE, UDEV_NO_MAC_RULE, UDEV_SRIOV_RULE
+from .base import TestBase, ND_DHCP4, UDEV_MAC_RULE, UDEV_NO_MAC_RULE, UDEV_SRIOV_RULE, \
+ NM_MANAGED, NM_UNMANAGED, NM_MANAGED_MAC, NM_UNMANAGED_MAC, \
+ NM_MANAGED_DRIVER, NM_UNMANAGED_DRIVER
class TestNetworkd(TestBase):
@@ -39,10 +41,8 @@ Name=eth0
LinkLocalAddressing=ipv6
'''})
self.assert_networkd_udev(None)
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:eth0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'eth0')
# should not allow NM to manage everything
self.assertFalse(os.path.exists(self.nm_enable_all_conf))
@@ -140,8 +140,8 @@ LinkLocalAddressing=ipv6
'''})
self.assert_networkd_udev({'def1.rules': (UDEV_NO_MAC_RULE % ('ixgbe', 'lom1'))})
# NM cannot match by driver, so blacklisting needs to happen via udev
- self.assert_nm(None, None)
- self.assert_nm_udev('ACTION=="add|change", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="ixgbe", ENV{NM_UNMANAGED}="1"\n')
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'lom1' + NM_UNMANAGED_DRIVER % 'ixgbe')
def test_eth_match_by_mac_rename(self):
self.generate('''network:
@@ -161,10 +161,36 @@ Name=lom1
LinkLocalAddressing=ipv6
'''})
self.assert_networkd_udev({'def1.rules': (UDEV_MAC_RULE % ('?*', '11:22:33:44:55:66', 'lom1'))})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=mac:11:22:33:44:55:66,interface-name:lom1,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'lom1' + NM_UNMANAGED_MAC % '11:22:33:44:55:66')
+
+ # https://bugs.launchpad.net/netplan/+bug/1848474
+ def test_eth_match_by_mac_infiniband(self):
+ self.generate('''network:
+ version: 2
+ ethernets:
+ ib0:
+ match:
+ macaddress: 11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00
+ dhcp4: true
+ infiniband-mode: connected''')
+
+ self.assert_networkd({'ib0.network': '''[Match]
+MACAddress=11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00
+
+[Network]
+DHCP=ipv4
+LinkLocalAddressing=ipv6
+
+[DHCP]
+RouteMetric=100
+UseMTU=true
+
+[IPoIB]
+Mode=connected
+'''})
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED_MAC % '11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00')
def test_eth_implicit_name_match_dhcp4(self):
self.generate('''network:
@@ -197,7 +223,7 @@ RouteMetric=100
UseMTU=true
'''})
self.assert_networkd_udev(None)
- self.assert_nm_udev('ACTION=="add|change", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="ixgbe", ENV{NM_UNMANAGED}="1"\n')
+ self.assert_nm_udev(NM_UNMANAGED_DRIVER % 'ixgbe')
def test_eth_match_name(self):
self.generate('''network:
@@ -210,10 +236,8 @@ UseMTU=true
self.assert_networkd({'def1.network': ND_DHCP4 % 'green'})
self.assert_networkd_udev(None)
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:green,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'green')
def test_eth_set_mac(self):
self.generate('''network:
@@ -247,9 +271,8 @@ unmanaged-devices+=interface-name:green,''')
# The udev rules engine does support renaming by name
self.assert_networkd_udev(None)
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:blue,''')
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'blue' + NM_UNMANAGED % 'green')
def test_eth_match_all_names(self):
self.generate('''network:
@@ -261,10 +284,8 @@ unmanaged-devices+=interface-name:blue,''')
self.assert_networkd({'def1.network': ND_DHCP4 % '*'})
self.assert_networkd_udev(None)
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:*,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % '*')
def test_eth_match_all(self):
self.generate('''network:
@@ -277,9 +298,9 @@ unmanaged-devices+=interface-name:*,''')
self.assert_networkd({'def1.network': '[Match]\n\n[Network]\nDHCP=ipv4\nLinkLocalAddressing=ipv6\n\n'
'[DHCP]\nRouteMetric=100\nUseMTU=true\n'})
self.assert_networkd_udev(None)
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=type:ethernet,''')
+ self.assert_nm(None, '''[device-netplan.ethernets.def1]
+match-device=type:ethernet
+managed=0\n\n''')
self.assert_nm_udev(None)
def test_match_multiple(self):
@@ -303,9 +324,9 @@ LinkLocalAddressing=ipv6
RouteMetric=100
UseMTU=true
'''})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=mac:00:11:22:33:44:55,interface-name:en1s*,''')
+ self.assert_nm(None)
+ self.assert_nm_udev('SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_NAME}=="en1s*", '
+ 'ATTR{address}=="00:11:22:33:44:55", ENV{NM_UNMANAGED}="1"\n')
class TestNetworkManager(TestBase):
@@ -335,7 +356,7 @@ method=ignore
# should allow NM to manage everything else
self.assertTrue(os.path.exists(self.nm_enable_all_conf))
self.assert_networkd({'eth0.link': '[Match]\nOriginalName=eth0\n\n[Link]\nWakeOnLan=magic\n'})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'eth0')
def test_eth_mtu(self):
self.generate('''network:
@@ -547,7 +568,7 @@ method=link-local
[ipv6]
method=ignore
'''})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'lom1' + NM_MANAGED_DRIVER % 'ixgbe')
def test_eth_match_by_mac_rename(self):
self.generate('''network:
@@ -575,7 +596,7 @@ method=link-local
[ipv6]
method=ignore
'''})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'lom1' + NM_MANAGED_MAC % '11:22:33:44:55:66')
def test_eth_implicit_name_match_dhcp4(self):
self.generate('''network:
@@ -652,7 +673,7 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'green')
def test_eth_match_name_rename(self):
self.generate('''network:
@@ -685,7 +706,7 @@ method=ignore
'''})
# ... while udev renames it
self.assert_networkd({'def1.link': '[Match]\nOriginalName=green\n\n[Link]\nName=blue\nWakeOnLan=off\n'})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'blue' + NM_MANAGED % 'green')
def test_eth_match_name_glob(self):
self.generate('''network:
@@ -735,7 +756,10 @@ method=auto
[ipv6]
method=ignore
-'''})
+'''}, '''[device-netplan.ethernets.def1]
+match-device=type:ethernet
+managed=1\n\n''')
+ self.assert_nm_udev(None)
self.assert_networkd({})
def test_match_multiple(self):
@@ -764,7 +788,8 @@ method=auto
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev('SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_NAME}=="engreen", '
+ 'ATTR{address}=="00:11:22:33:44:55", ENV{NM_UNMANAGED}="0"\n')
def test_offload(self):
self.generate('''network:
@@ -772,11 +797,11 @@ method=ignore
ethernets:
eth1:
receive-checksum-offload: true
- transmit-checksum-offload: true
+ transmit-checksum-offload: off
tcp-segmentation-offload: true
- tcp6-segmentation-offload: true
+ tcp6-segmentation-offload: false
generic-segmentation-offload: true
- generic-receive-offload: true
+ generic-receive-offload: no
large-receive-offload: true''')
self.assert_networkd({'eth1.link': '''[Match]
@@ -784,13 +809,13 @@ OriginalName=eth1
[Link]
WakeOnLan=off
-ReceiveChecksumOffload=1
-TransmitChecksumOffload=1
-TCPSegmentationOffload=1
-TCP6SegmentationOffload=1
-GenericSegmentationOffload=1
-GenericReceiveOffload=1
-LargeReceiveOffload=1
+ReceiveChecksumOffload=true
+TransmitChecksumOffload=false
+TCPSegmentationOffload=true
+TCP6SegmentationOffload=false
+GenericSegmentationOffload=true
+GenericReceiveOffload=false
+LargeReceiveOffload=true
''',
'eth1.network': '''[Match]
Name=eth1
@@ -799,3 +824,46 @@ Name=eth1
LinkLocalAddressing=ipv6
'''})
self.assert_networkd_udev(None)
+
+ def test_offload_invalid(self):
+ err = self.generate('''network:
+ version: 2
+ ethernets:
+ eth1:
+ generic-receive-offload: n
+ receive-checksum-offload: true
+ tcp-segmentation-offload: true
+ tcp6-segmentation-offload: false
+ generic-segmentation-offload: true
+ transmit-checksum-offload: xx
+ large-receive-offload: true''', expect_fail=True)
+ self.assertIn('invalid boolean value \'xx\'', err)
+
+ # https://bugs.launchpad.net/netplan/+bug/1848474
+ def test_eth_match_by_mac_infiniband(self):
+ self.generate('''network:
+ version: 2
+ renderer: NetworkManager
+ ethernets:
+ ib0:
+ match:
+ macaddress: 11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00
+ dhcp4: true
+ infiniband-mode: datagram''')
+
+ self.assert_networkd(None)
+ self.assert_nm({'ib0': '''[connection]
+id=netplan-ib0
+type=infiniband
+
+[infiniband]
+mac-address=11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00
+transport-mode=datagram
+
+[ipv4]
+method=auto
+
+[ipv6]
+method=ignore
+'''})
+ self.assert_nm_udev(NM_MANAGED_MAC % '11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00')
diff --git a/tests/generator/test_modems.py b/tests/generator/test_modems.py
index acffe87..b66144e 100644
--- a/tests/generator/test_modems.py
+++ b/tests/generator/test_modems.py
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from .base import TestBase
+from .base import TestBase, NM_MANAGED
class TestNetworkd(TestBase):
@@ -67,7 +67,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_auto_config(self):
self.generate('''network:
@@ -91,7 +91,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_auto_config_implicit(self):
self.generate('''network:
@@ -120,7 +120,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_apn(self):
self.generate('''network:
@@ -144,7 +144,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_apn_username_password(self):
self.generate('''network:
@@ -172,7 +172,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_device_id(self):
self.generate('''network:
@@ -197,7 +197,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_network_id(self):
self.generate('''network:
@@ -222,7 +222,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_pin(self):
self.generate('''network:
@@ -247,7 +247,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_sim_id(self):
self.generate('''network:
@@ -272,7 +272,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_sim_operator_id(self):
self.generate('''network:
@@ -297,7 +297,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_gsm_example(self):
self.generate('''network:
@@ -339,7 +339,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'cdc-wdm1')
def test_modem_nm_integration(self):
self.generate('''network:
@@ -366,7 +366,7 @@ method=link-local
method=ignore
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'mobilephone')
def test_modem_nm_integration_gsm_cdma(self):
self.generate('''network:
@@ -421,6 +421,8 @@ method=auto
[ipv6]
#Netplan: passthrough override
method=auto
-'''})
+'''}, '''[device-netplan.modems.NM-a08c5805-7cf5-43f7-afb9-12cb30f6eca3]
+match-device=type:gsm
+managed=1\n\n''')
self.assert_networkd({})
self.assert_nm_udev(None)
diff --git a/tests/generator/test_ovs.py b/tests/generator/test_ovs.py
index dff2989..49c98ed 100644
--- a/tests/generator/test_ovs.py
+++ b/tests/generator/test_ovs.py
@@ -29,6 +29,10 @@ class TestOpenVSwitch(TestBase):
def test_interface_external_ids_other_config(self):
self.generate('''network:
version: 2
+ bridges: # bridges first, to trigger multi-pass processing
+ ovs0:
+ interfaces: [eth0, eth1]
+ openvswitch: {}
ethernets:
eth0:
openvswitch:
@@ -41,15 +45,11 @@ class TestOpenVSwitch(TestBase):
dhcp4: true
openvswitch:
other-config:
- disable-in-band: false
- bridges:
- ovs0:
- interfaces: [eth0, eth1]
- openvswitch: {}
-''')
+ disable-in-band: false\n''')
self.assert_ovs({'ovs0.service': OVS_VIRTUAL % {'iface': 'ovs0', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br ovs0
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port ovs0 eth1
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port ovs0 eth0
@@ -60,6 +60,7 @@ After=netplan-ovs-ovs0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl set Interface eth0 external-ids:iface-id=myhostname
ExecStart=/usr/bin/ovs-vsctl set Interface eth0 external-ids:netplan/external-ids/iface-id=myhostname
ExecStart=/usr/bin/ovs-vsctl set Interface eth0 other-config:disable-in-band=true
@@ -71,6 +72,7 @@ After=netplan-ovs-ovs0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl set Interface eth1 other-config:disable-in-band=false
ExecStart=/usr/bin/ovs-vsctl set Interface eth1 external-ids:netplan/other-config/disable-in-band=false
'''},
@@ -109,6 +111,7 @@ ExecStart=/usr/bin/ovs-vsctl set Interface eth1 external-ids:netplan/other-confi
self.assert_ovs({'global.service': OVS_VIRTUAL % {'iface': 'global', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:iface-id=myhostname
ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:netplan/external-ids/iface-id=myhostname
ExecStart=/usr/bin/ovs-vsctl set open_vswitch . other-config:disable-in-band=true
@@ -129,6 +132,7 @@ ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:netplan/other-confi
self.assert_ovs({'ovs0.service': OVS_VIRTUAL % {'iface': 'ovs0', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br ovs0
''' + OVS_BR_DEFAULT % {'iface': 'ovs0'} + '''\
ExecStart=/usr/bin/ovs-vsctl set Bridge ovs0 protocols=OpenFlow10,OpenFlow11,OpenFlow12
@@ -185,6 +189,7 @@ After=netplan-ovs-br0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
@@ -253,6 +258,7 @@ After=netplan-ovs-br0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=active
@@ -318,6 +324,7 @@ After=netplan-ovs-br0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
@@ -357,6 +364,7 @@ After=netplan-ovs-br0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
@@ -408,6 +416,7 @@ ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan/bond_mode=activ
'''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 eth1
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 eth2
@@ -432,6 +441,7 @@ ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 eth2
self.assert_ovs({'br0.service': OVS_VIRTUAL % {'iface': 'br0', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
''' + OVS_BR_DEFAULT % {'iface': 'br0'} + '''\
ExecStart=/usr/bin/ovs-vsctl set Bridge br0 external-ids:iface-id=myhostname
@@ -462,6 +472,7 @@ ExecStart=/usr/bin/ovs-vsctl set Bridge br0 external-ids:netplan/other-config/di
'''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 eth1
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 eth2
@@ -521,6 +532,7 @@ ExecStart=/usr/bin/ovs-vsctl set Bridge br0 external-ids:netplan/rstp_enable=tru
'''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
''' + OVS_BR_DEFAULT % {'iface': 'br0'} + '''\
ExecStart=/usr/bin/ovs-vsctl set Bridge br0 protocols=OpenFlow10,OpenFlow11,OpenFlow15
@@ -570,6 +582,7 @@ ExecStart=/usr/bin/ovs-vsctl set Bridge br0 external-ids:netplan/protocols=OpenF
'''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
''' + OVS_BR_DEFAULT % {'iface': 'br0'} + '''\
ExecStart=/usr/bin/ovs-vsctl set-controller br0 ptcp: ptcp:1337 ptcp:1337:[fe80::1234%eth0] pssl:1337:[fe80::1] ssl:10.10.10.1 \
@@ -583,6 +596,7 @@ ExecStart=/usr/bin/ovs-vsctl set Controller br0 external-ids:netplan/connection-
'global.service': OVS_VIRTUAL % {'iface': 'global', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl set-ssl /key/path /some/path /another/path
ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:netplan/global/set-ssl=/key/path,/some/path,/another/path
'''},
@@ -680,6 +694,7 @@ ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:netplan/global/set-
self.assert_ovs({'global.service': OVS_VIRTUAL % {'iface': 'global', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl set-ssl /key/path /some/path /another/path
ExecStart=/usr/bin/ovs-vsctl set open_vswitch . external-ids:netplan/global/set-ssl=/key/path,/some/path,/another/path
'''},
@@ -784,6 +799,7 @@ After=netplan-ovs-br0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 eth1 eth2
ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
@@ -832,6 +848,7 @@ Bond=bond0
'br1.service': OVS_VIRTUAL % {'iface': 'br1', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br1
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br1 patchx -- set Interface patchx type=patch options:peer=patchy
''' + OVS_BR_DEFAULT % {'iface': 'br1'}},
@@ -841,6 +858,7 @@ After=netplan-ovs-br0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-bond br0 bond0 patchy eth0 -- set Interface patchy type=patch options:peer=patchx
ExecStart=/usr/bin/ovs-vsctl set Port bond0 external-ids:netplan=true
ExecStart=/usr/bin/ovs-vsctl set Port bond0 lacp=off
@@ -852,6 +870,7 @@ After=netplan-ovs-br1.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl set Port patchx external-ids:netplan=true
'''},
'patchy.service': OVS_VIRTUAL % {'iface': 'patchy', 'extra':
@@ -860,6 +879,7 @@ After=netplan-ovs-bond0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl set Interface patchy external-ids:netplan=true
'''},
'cleanup.service': OVS_CLEANUP % {'iface': 'cleanup'}})
@@ -887,12 +907,14 @@ ExecStart=/usr/bin/ovs-vsctl set Interface patchy external-ids:netplan=true
self.assert_ovs({'br0.service': OVS_VIRTUAL % {'iface': 'br0', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br0 patch0-1 -- set Interface patch0-1 type=patch options:peer=patch1-0
''' + OVS_BR_DEFAULT % {'iface': 'br0'}},
'br1.service': OVS_VIRTUAL % {'iface': 'br1', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br1
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port br1 patch1-0 -- set Interface patch1-0 type=patch options:peer=patch0-1
''' + OVS_BR_DEFAULT % {'iface': 'br1'}},
@@ -902,6 +924,7 @@ After=netplan-ovs-br0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl set Port patch0-1 external-ids:netplan=true
'''},
'patch1-0.service': OVS_VIRTUAL % {'iface': 'patch1-0', 'extra':
@@ -910,6 +933,7 @@ After=netplan-ovs-br1.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl set Port patch1-0 external-ids:netplan=true
'''},
'cleanup.service': OVS_CLEANUP % {'iface': 'cleanup'}})
@@ -934,6 +958,7 @@ ExecStart=/usr/bin/ovs-vsctl set Port patch1-0 external-ids:netplan=true
self.assert_ovs({'br0.service': OVS_VIRTUAL % {'iface': 'br0', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0
''' + OVS_BR_DEFAULT % {'iface': 'br0'}},
'br0.100.service': OVS_VIRTUAL % {'iface': 'br0.100', 'extra':
@@ -942,6 +967,7 @@ After=netplan-ovs-br0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0.100 br0 100
ExecStart=/usr/bin/ovs-vsctl set Interface br0.100 external-ids:netplan=true
'''},
@@ -971,6 +997,7 @@ After=netplan-ovs-br0.service
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br br0.100 br0 100
ExecStart=/usr/bin/ovs-vsctl set Interface br0.100 external-ids:netplan=true
'''},
@@ -1007,6 +1034,7 @@ ExecStart=/usr/bin/ovs-vsctl set Interface br0.100 external-ids:netplan=true
self.assert_ovs({'ovs-br.service': OVS_VIRTUAL % {'iface': 'ovs-br', 'extra': '''
[Service]
Type=oneshot
+TimeoutStartSec=10s
ExecStart=/usr/bin/ovs-vsctl --may-exist add-br ovs-br
ExecStart=/usr/bin/ovs-vsctl --may-exist add-port ovs-br non-ovs-bond
''' + OVS_BR_DEFAULT % {'iface': 'ovs-br'}},
diff --git a/tests/generator/test_passthrough.py b/tests/generator/test_passthrough.py
index 8d03c92..4af80d6 100644
--- a/tests/generator/test_passthrough.py
+++ b/tests/generator/test_passthrough.py
@@ -56,7 +56,9 @@ method=link-local
[ipv6]
method=ignore
-'''})
+'''}, '''[device-netplan.ethernets.NM-87749f1d-334f-40b2-98d4-55db58965f5f]
+match-device=type:ethernet
+managed=1\n\n''')
def test_passthrough_wifi(self):
self.generate('''network:
@@ -107,7 +109,9 @@ method=ignore
ssid=OTHER-SSID
mode=infrastructure
hidden=true
-'''})
+'''}, '''[device-netplan.wifis.NM-87749f1d-334f-40b2-98d4-55db58965f5f]
+match-device=type:wifi
+managed=1\n\n''')
def test_passthrough_type_nm_devices(self):
self.generate('''network:
@@ -132,7 +136,9 @@ method=link-local
[ipv6]
method=ignore
-'''})
+'''}, '''[device-netplan.nm-devices.NM-87749f1d-334f-40b2-98d4-55db58965f5f]
+match-device=type:dummy
+managed=1\n\n''')
def test_passthrough_dotted_group(self):
self.generate('''network:
@@ -159,7 +165,9 @@ method=ignore
[wireguard-peer.some-key]
#Netplan: passthrough setting
endpoint=1.2.3.4
-'''})
+'''}, '''[device-netplan.nm-devices.dotted-group-test]
+match-device=type:wireguard
+managed=1\n\n''')
def test_passthrough_dotted_key(self):
self.generate('''network:
@@ -193,7 +201,9 @@ qdisc.root=something
qdisc.fff1=:abc
#Netplan: passthrough setting
filters.test=test
-'''})
+'''}, '''[device-netplan.ethernets.dotted-key-test]
+match-device=type:ethernet
+managed=1\n\n''')
def test_passthrough_unsupported_setting(self):
self.generate('''network:
@@ -221,7 +231,9 @@ method=ignore
ssid=SOME-SSID
#Netplan: passthrough override
mode=mesh
-'''})
+'''}, '''[device-netplan.wifis.test]
+match-device=type:wifi
+managed=1\n\n''')
def test_passthrough_empty_group(self):
self.generate('''network:
@@ -247,7 +259,9 @@ method=link-local
method=ignore
[proxy]
-'''})
+'''}, '''[device-netplan.ethernets.test]
+match-device=type:ethernet
+managed=1\n\n''')
def test_passthrough_interface_rename_existing_id(self):
self.generate('''network:
diff --git a/tests/generator/test_routing.py b/tests/generator/test_routing.py
index c60e60c..eef17c8 100644
--- a/tests/generator/test_routing.py
+++ b/tests/generator/test_routing.py
@@ -176,7 +176,7 @@ Address=192.168.14.2/24
[Route]
Destination=10.10.10.0/24
Gateway=192.168.14.20
-GatewayOnlink=true
+GatewayOnLink=true
Metric=100
'''})
diff --git a/tests/generator/test_tunnels.py b/tests/generator/test_tunnels.py
index e772e9c..bdf9719 100644
--- a/tests/generator/test_tunnels.py
+++ b/tests/generator/test_tunnels.py
@@ -1,8 +1,11 @@
#
# Tests for tunnel devices config generated via netplan
#
-# Copyright (C) 2018 Canonical, Ltd.
+# Copyright (C) 2018-2022 Canonical, Ltd.
+# Copyright (C) 2022 Datto, Inc.
# Author: Mathieu Trudel-Lapierre <mathieu.trudel.lapierre@canonical.com>
+# Author: Anthony Timmins <atimmins@datto.com>
+# Author: Lukas Märdian <slyon@ubuntu.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,7 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from .base import TestBase, ND_WITHIPGW, ND_EMPTY, NM_WG, ND_WG
+import os
+import re
+
+from .base import TestBase, ND_WITHIPGW, ND_EMPTY, NM_WG, ND_WG, ND_VXLAN
def prepare_config_for_mode(renderer, mode, key=None, ttl=None):
@@ -328,6 +334,76 @@ must be X.X.X.X/NN or X:X:X:X:X:X:X:X/NN", out)
out = self.generate(config, expect_fail=True)
self.assertIn("Error in network definition: wg0: 'to' is required to define the allowed IPs.", out)
+ def test_vxlan_port_range_fail(self):
+ out = self.generate('''network:
+ tunnels:
+ vx0:
+ mode: vxlan
+ port-range: [1,2,3]''', expect_fail=True)
+ self.assertIn("Expected exactly two values for port-range", out)
+
+ def test_vxlan_port_min_wrong_type(self):
+ out = self.generate('''network:
+ tunnels:
+ vx0:
+ mode: vxlan
+ port-range: [a,10]''', expect_fail=True)
+ self.assertIn("invalid unsigned int value 'a'", out)
+
+ def test_vxlan_port_max_wrong_type(self):
+ out = self.generate('''network:
+ tunnels:
+ vx0:
+ mode: vxlan
+ port-range: [10,b]''', expect_fail=True)
+ self.assertIn("invalid unsigned int value 'b'", out)
+
+ def test_vxlan_flags_fail(self):
+ out = self.generate('''network:
+ tunnels:
+ vx0:
+ mode: vxlan
+ notifications: [INVALID]''', expect_fail=True)
+ self.assertIn("invalid value for notifications: 'INVALID'", out)
+
+ def test_vxlan_missing_vni(self):
+ out = self.generate('''network:
+ version: 2
+ tunnels:
+ vxlan1005:
+ mode: vxlan''', expect_fail=True)
+ self.assertIn('missing \'id\' property (VXLAN VNI)', out)
+
+ def test_vxlan_oob_vni(self):
+ out = self.generate('''network:
+ version: 2
+ tunnels:
+ vxlan1005:
+ mode: vxlan
+ id: 17000000''', expect_fail=True)
+ self.assertIn('VXLAN \'id\' (VNI) must be in range [1..16777215]', out)
+
+ def test_vxlan_oob_flow_label(self):
+ out = self.generate('''network:
+ version: 2
+ tunnels:
+ vxlan1005:
+ mode: vxlan
+ id: 1005
+ flow-label: 1111111''', expect_fail=True)
+ self.assertIn('VXLAN \'flow-label\' must be in range [0..1048575]', out)
+
+ def test_vxlan_local_remote_ip_family_mismatch(self):
+ out = self.generate('''network:
+ version: 2
+ tunnels:
+ vxlan1005:
+ mode: vxlan
+ id: 1005
+ local: 10.10.10.1
+ remote: fe80::3''', expect_fail=True)
+ self.assertIn('\'local\' and \'remote\' must be of same IP family type', out)
+
class _CommonTests():
@@ -508,6 +584,124 @@ persistent-keepalive=23
endpoint=[2001:fe:ad:de:ad:be:ef:11]:5
allowed-ips=0.0.0.0/0;2001:fe:ad:de:ad:be:ef:1/24;''')})
+ def test_vxlan(self):
+ out = self.generate('''network:
+ renderer: %(r)s
+ version: 2
+ tunnels:
+ vxlan1005:
+ link: br0
+ mode: vxlan
+ local: 10.10.10.1
+ remote: 224.0.0.5
+ id: 1005
+ port: 4789
+ ageing: 42
+ mac-learning: true
+ limit: 37
+ arp-proxy: true
+ short-circuit: true
+ type-of-service: 292
+ ttl: 128
+ flow-label: 0
+ do-not-fragment: true
+ notifications: [l2-miss, l3-miss]
+ checksums: [udp, zero-udp6-tx, zero-udp6-rx, remote-tx, remote-rx]
+ extensions: [group-policy, generic-protocol]
+ port-range: [42, 442]
+ neigh-suppress: false
+ bridges:
+ br0:
+ interfaces: [vxlan1005]''' % {'r': self.backend})
+
+ if self.backend == 'networkd':
+ self.assert_networkd({'vxlan1005.netdev': ND_VXLAN % ('vxlan1005', 1005) +
+ '''Group=224.0.0.5
+Local=10.10.10.1
+TOS=292
+TTL=128
+MacLearning=true
+FDBAgeingSec=42
+MaximumFDBEntries=37
+ReduceARPProxy=true
+L2MissNotification=true
+L3MissNotification=true
+RouteShortCircuit=true
+UDPChecksum=true
+UDP6ZeroChecksumTx=true
+UDP6ZeroChecksumRx=true
+RemoteChecksumTx=true
+RemoteChecksumRx=true
+GroupPolicyExtension=true
+GenericProtocolExtension=true
+DestinationPort=4789
+PortRange=42-442
+FlowLabel=0
+IPDoNotFragment=true\n''',
+ 'vxlan1005.network': '''[Match]
+Name=vxlan1005
+
+[Network]
+LinkLocalAddressing=no
+ConfigureWithoutCarrier=yes
+Bridge=br0
+
+[Bridge]
+NeighborSuppression=false\n''',
+ 'br0.network': '''[Match]
+Name=br0
+
+[Network]
+LinkLocalAddressing=ipv6
+ConfigureWithoutCarrier=yes
+VXLAN=vxlan1005\n''',
+ 'br0.netdev': '''[NetDev]
+Name=br0
+Kind=bridge\n'''})
+ elif self.backend == 'NetworkManager':
+ self.assertIn('checksums/extensions/flow-lable/do-not-fragment are '
+ 'not supported by NetworkManager', out)
+ self.assert_nm({'vxlan1005': '''[connection]
+id=netplan-vxlan1005
+type=vxlan
+interface-name=vxlan1005
+slave-type=bridge
+master=br0
+
+[vxlan]
+ageing=42
+destination-port=4789
+id=1005
+learning=true
+limit=37
+local=10.10.10.1
+remote=224.0.0.5
+proxy=true
+l2-miss=true
+l3-miss=true
+source-port-min=42
+source-port-max=442
+tos=292
+ttl=128
+rsc=true
+parent=br0
+
+[ipv4]
+method=disabled
+
+[ipv6]
+method=ignore\n''',
+ 'br0': '''[connection]
+id=netplan-br0
+type=bridge
+interface-name=br0
+
+[ipv4]
+method=link-local
+
+[ipv6]
+method=ignore\n'''})
+
# Execute the _CommonParserErrors only for one backend, to spare some test cycles
class TestNetworkd(TestBase, _CommonTests, _CommonParserErrors):
@@ -799,7 +993,7 @@ ConfigureWithoutCarrier=yes
def test_ip6gre(self):
"""[networkd] Validate generation of IP6GRE tunnels"""
- config = prepare_config_for_mode('networkd', 'ip6gre')
+ config = prepare_config_for_mode('networkd', 'ip6gre', '33490175')
self.generate(config)
self.assert_networkd({'tun0.netdev': '''[NetDev]
Name=tun0
@@ -809,6 +1003,8 @@ Kind=ip6gre
Independent=true
Local=fe80::dead:beef
Remote=2001:fe:ad:de:ad:be:ef:1
+InputKey=33490175
+OutputKey=33490175
''',
'tun0.network': '''[Match]
Name=tun0
@@ -866,6 +1062,34 @@ Gateway=20.20.20.21
ConfigureWithoutCarrier=yes
'''})
+ def test_vxlan_port_range_swap(self):
+ out = self.generate('''network:
+ version: 2
+ tunnels:
+ vx0:
+ mode: vxlan
+ id: 1005
+ remote: 10.0.0.5
+ port-range: [100, 10]''')
+ self.assertIn("swapped invalid port-range order [MIN, MAX]", out)
+
+ self.assert_networkd({'vx0.netdev': ND_VXLAN % ('vx0', 1005) +
+ 'Remote=10.0.0.5\nPortRange=10-100\nIndependent=true\n',
+ 'vx0.network': ND_EMPTY % ('vx0', 'ipv6')})
+
+ def test_vxlan_ip6_multicast(self):
+ self.generate('''network:
+ version: 2
+ tunnels:
+ vx0:
+ mode: vxlan
+ id: 1005
+ remote: "ff42::dead:beef"''')
+
+ self.assert_networkd({'vx0.netdev': ND_VXLAN % ('vx0', 1005) +
+ 'Group=ff42::dead:beef\nIndependent=true\n',
+ 'vx0.network': ND_EMPTY % ('vx0', 'ipv6')})
+
class TestNetworkManager(TestBase, _CommonTests):
backend = 'NetworkManager'
@@ -1189,6 +1413,56 @@ gateway=20.20.20.21
method=ignore
'''})
+ def test_vxlan_uuid(self):
+ self.generate('''network:
+ renderer: NetworkManager
+ tunnels:
+ vx0:
+ mode: vxlan
+ id: 42
+ link: id0
+ ethernets:
+ id0:
+ match: {name: 'someIface'}''')
+
+ self.assert_networkd({})
+
+ # get assigned UUID from id0 connection
+ with open(os.path.join(self.workdir.name, 'run/NetworkManager/system-connections/netplan-id0.nmconnection')) as f:
+ m = re.search('uuid=([0-9a-fA-F-]{36})\n', f.read())
+ self.assertTrue(m)
+ uuid = m.group(1)
+ self.assertNotEquals(uuid, "00000000-0000-0000-0000-000000000000")
+
+ self.assert_nm({'vx0': '''[connection]
+id=netplan-vx0
+type=vxlan
+interface-name=vx0
+
+[vxlan]
+id=42
+parent=%s
+
+[ipv4]
+method=disabled
+
+[ipv6]
+method=ignore\n''' % uuid,
+ 'id0': '''[connection]
+id=netplan-id0
+type=ethernet
+uuid=%s
+interface-name=someIface
+
+[ethernet]
+wake-on-lan=0
+
+[ipv4]
+method=link-local
+
+[ipv6]
+method=ignore\n''' % uuid})
+
class TestConfigErrors(TestBase):
diff --git a/tests/generator/test_vlans.py b/tests/generator/test_vlans.py
index f728d35..012295e 100644
--- a/tests/generator/test_vlans.py
+++ b/tests/generator/test_vlans.py
@@ -20,7 +20,8 @@ import os
import re
import unittest
-from .base import TestBase, ND_VLAN, ND_EMPTY, ND_WITHIP, ND_DHCP6_WOCARRIER
+from .base import TestBase, ND_VLAN, ND_EMPTY, ND_WITHIP, ND_DHCP6_WOCARRIER, \
+ NM_MANAGED, NM_UNMANAGED, NM_MANAGED_MAC, NM_UNMANAGED_MAC
class TestNetworkd(TestBase):
@@ -66,10 +67,9 @@ Id=3
.replace('[Network]', '[Link]\nMACAddress=aa:bb:cc:dd:ee:11\n\n[Network]'),
'engreen.network': (ND_DHCP6_WOCARRIER % 'engreen')})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:en1,interface-name:enblue,interface-name:enred,interface-name:engreen,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'en1' + NM_UNMANAGED % 'enblue' + NM_UNMANAGED % 'enred' +
+ NM_UNMANAGED_MAC % 'aa:bb:cc:dd:ee:11' + NM_UNMANAGED % 'engreen')
def test_vlan_sriov(self):
# we need to make sure renderer: sriov vlans are not saved as part of
@@ -95,10 +95,8 @@ VLAN=engreen
'engreen.netdev': ND_VLAN % ('engreen', 2),
'engreen.network': (ND_DHCP6_WOCARRIER % 'engreen')})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:en1,interface-name:enblue,interface-name:engreen,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'en1' + NM_UNMANAGED % 'enblue' + NM_UNMANAGED % 'engreen')
# see LP: #1888726
def test_vlan_parent_match(self):
@@ -137,10 +135,8 @@ MTUBytes=9000
'vlan20.network': ND_EMPTY % ('vlan20', 'ipv6'),
'vlan20.netdev': ND_VLAN % ('vlan20', 20)})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=mac:11:22:33:44:55:66,interface-name:lan,interface-name:vlan20,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'lan' + NM_UNMANAGED_MAC % '11:22:33:44:55:66' + NM_UNMANAGED % 'vlan20')
class TestNetworkManager(TestBase):
@@ -205,7 +201,7 @@ method=link-local
method=auto
ip6-privacy=0
'''})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'en1' + NM_MANAGED % 'enblue' + NM_MANAGED % 'engreen')
def test_vlan_parent_match(self):
self.generate('''network:
@@ -256,7 +252,7 @@ method=auto
[ipv6]
method=ignore
''' % uuid})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED_MAC % '11:22:33:44:55:66' + NM_MANAGED % 'engreen')
def test_vlan_sriov(self):
# we need to make sure renderer: sriov vlans are not saved as part of
@@ -305,4 +301,4 @@ method=link-local
method=auto
ip6-privacy=0
'''})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'en1' + NM_MANAGED % 'enblue' + NM_MANAGED % 'engreen')
diff --git a/tests/generator/test_vrfs.py b/tests/generator/test_vrfs.py
new file mode 100644
index 0000000..7747285
--- /dev/null
+++ b/tests/generator/test_vrfs.py
@@ -0,0 +1,188 @@
+#
+# Tests for bridge devices config generated via netplan
+#
+# Copyright (C) 2018 Canonical, Ltd.
+# Copyright (C) 2022 Datto, Inc.
+# Author: Anthony Timmins <atimmins@datto.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from .base import TestBase, ND_EMPTY, ND_DHCP, ND_VRF
+
+
+class NetworkManager(TestBase):
+
+ def test_vrf_set_table(self):
+ self.generate('''network:
+ version: 2
+ renderer: NetworkManager
+ ethernets:
+ eth0: { dhcp4: true }
+ vrfs:
+ vrf1005:
+ table: 1005
+ interfaces: [eth0]
+ routes:
+ - to: default
+ via: 1.2.3.4
+ routing-policy:
+ - from: 2.3.4.5''')
+
+ self.assert_nm({'eth0': '''[connection]
+id=netplan-eth0
+type=ethernet
+interface-name=eth0
+slave-type=vrf
+master=vrf1005
+
+[ethernet]
+wake-on-lan=0
+
+[ipv4]
+method=auto
+
+[ipv6]
+method=ignore
+''',
+ 'vrf1005': '''[connection]
+id=netplan-vrf1005
+type=vrf
+interface-name=vrf1005
+
+[vrf]
+table=1005
+
+[ipv4]
+route1=0.0.0.0/0,1.2.3.4
+route1_options=table=1005
+method=link-local
+
+[ipv6]
+method=ignore
+'''})
+
+
+class TestNetworkd(TestBase):
+
+ def test_vrf_set_table(self):
+ self.generate('''network:
+ version: 2
+ ethernets:
+ eth0: { dhcp4: true }
+ vrfs:
+ vrf1005:
+ table: 1005
+ interfaces: [eth0]
+ routes:
+ - to: default
+ via: 1.2.3.4
+ routing-policy:
+ - from: 2.3.4.5''')
+
+ self.assert_networkd({'eth0.network': ND_DHCP % ('eth0', 'ipv4', '\nVRF=vrf1005', 'true'),
+ 'vrf1005.network': ND_EMPTY % ('vrf1005', 'ipv6') + '''
+[Route]
+Destination=0.0.0.0/0
+Gateway=1.2.3.4
+Table=1005
+
+[RoutingPolicyRule]
+From=2.3.4.5
+Table=1005
+''',
+ 'vrf1005.netdev': ND_VRF % ('vrf1005', 1005)})
+
+
+class TestNetplanYAMLv2(TestBase):
+ '''No asserts are needed.
+
+ The generate() method implicitly checks the (re-)generated YAML.
+ '''
+
+ def test_vrf_table(self):
+ self.generate('''network:
+ version: 2
+ vrfs:
+ vrf1005:
+ table: 1005''')
+
+ def test_vrf_routes(self):
+ self.generate('''network:
+ version: 2
+ vrfs:
+ vrf1005:
+ table: 1005
+ routes:
+ - to: default
+ via: 1.2.3.4
+ routing-policy:
+ - from: 1.2.3.4''')
+
+
+class TestConfigErrors(TestBase):
+
+ def test_vrf_missing_table(self):
+ err = self.generate('''network:
+ version: 2
+ vrfs:
+ vrf1005: {}''', expect_fail=True)
+
+ self.assertIn("vrf1005: missing 'table' property", err)
+
+ def test_vrf_already_assigned(self):
+ err = self.generate('''network:
+ version: 2
+ vrfs:
+ vrf0:
+ table: 42
+ interfaces: [eno1]
+ vrf1:
+ table: 43
+ interfaces: [eno1]
+ ethernets:
+ eno1: {}''', expect_fail=True)
+ self.assertIn("vrf1: interface 'eno1' is already assigned to vrf vrf0", err)
+
+ def test_vrf_routes_table_mismatch(self):
+ err = self.generate('''network:
+ version: 2
+ vrfs:
+ vrf0:
+ table: 42
+ routes:
+ - table: 42 # pass
+ to: default
+ via: 1.2.3.4
+ - table: 43 # mismatch
+ to: 99.88.77.66
+ via: 2.3.4.5
+''', expect_fail=True)
+ self.assertIn("vrf0: VRF routes table mismatch (42 != 43)", err)
+
+ def test_vrf_policy_table_mismatch(self):
+ err = self.generate('''network:
+ version: 2
+ vrfs:
+ vrf0:
+ table: 45
+ routes:
+ - to: default
+ via: 3.4.5.6
+ routing-policy:
+ - table: 45 # pass
+ from: 1.2.3.4
+ - table: 46 # mismatch
+ from: 2.3.4.5
+''', expect_fail=True)
+ self.assertIn("vrf0: VRF routing-policy table mismatch (45 != 46)", err)
diff --git a/tests/generator/test_wifis.py b/tests/generator/test_wifis.py
index 1a4ead2..5e8050a 100644
--- a/tests/generator/test_wifis.py
+++ b/tests/generator/test_wifis.py
@@ -19,7 +19,7 @@
import os
import stat
-from .base import TestBase, ND_WIFI_DHCP4, SD_WPA
+from .base import TestBase, ND_WIFI_DHCP4, SD_WPA, NM_MANAGED, NM_UNMANAGED
class TestNetworkd(TestBase):
@@ -29,6 +29,7 @@ class TestNetworkd(TestBase):
version: 2
wifis:
wl0:
+ regulatory-domain: "DE"
access-points:
"Joe's Home":
password: "s0s3kr1t"
@@ -57,10 +58,8 @@ class TestNetworkd(TestBase):
dhcp4: yes''')
self.assert_networkd({'wl0.network': ND_WIFI_DHCP4 % 'wl0'})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:wl0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'wl0')
# generates wpa config and enables wpasupplicant unit
with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f:
@@ -131,6 +130,7 @@ network={
psk="s0s3kr1t"
}
''', new_config)
+ self.assertIn('country=DE\n', new_config)
self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o600)
self.assertTrue(os.path.isfile(os.path.join(
self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service')))
@@ -240,10 +240,8 @@ RouteMetric=600
UseMTU=true
'''})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:wl0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'wl0')
def test_wifi_match(self):
err = self.generate('''network:
@@ -292,10 +290,8 @@ Name=wl0
[Network]
LinkLocalAddressing=ipv6
'''})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:wl0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'wl0')
# generates wpa config and enables wpasupplicant unit
with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f:
@@ -328,10 +324,8 @@ Name=wl0
[Network]
LinkLocalAddressing=ipv6
'''})
- self.assert_nm(None, '''[keyfile]
-# devices managed by networkd
-unmanaged-devices+=interface-name:wl0,''')
- self.assert_nm_udev(None)
+ self.assert_nm(None)
+ self.assert_nm_udev(NM_UNMANAGED % 'wl0')
# generates wpa config and enables wpasupplicant unit
with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f:
@@ -495,7 +489,7 @@ mode=infrastructure
band=a
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'wl0')
def test_wifi_match_mac(self):
self.generate('''network:
@@ -547,7 +541,9 @@ method=ignore
[wifi]
ssid=workplace
mode=infrastructure
-'''})
+'''}, '''[device-netplan.wifis.all]
+match-device=type:wifi
+managed=1\n\n''')
def test_wifi_ap(self):
self.generate('''network:
@@ -580,7 +576,7 @@ key-mgmt=wpa-psk
psk=s0s3cret
'''})
self.assert_networkd({})
- self.assert_nm_udev(None)
+ self.assert_nm_udev(NM_MANAGED % 'wl0')
def test_wifi_adhoc(self):
self.generate('''network:
@@ -661,6 +657,29 @@ ssid=homenet
mode=infrastructure
'''})
+ def test_wifi_regdom(self):
+ out = self.generate('''network:
+ wifis:
+ wl0:
+ regulatory-domain: GB
+ access-points:
+ homenet: {mode: infrastructure}
+ wl1:
+ regulatory-domain: DE
+ access-points:
+ homenet2: {mode: infrastructure}''')
+
+ self.assertIn('wl1: Conflicting regulatory-domain (GB vs DE)', out)
+ with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f:
+ new_config = f.read()
+ self.assertIn('country=GB\n', new_config)
+ with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl1.conf')) as f:
+ new_config = f.read()
+ self.assertIn('country=DE\n', new_config)
+ with open(os.path.join(self.workdir.name, 'run/systemd/system/netplan-regdom.service')) as f:
+ new_config = f.read()
+ self.assertIn('ExecStart=/usr/sbin/iw reg set DE\n', new_config)
+
class TestConfigErrors(TestBase):
diff --git a/tests/integration/base.py b/tests/integration/base.py
index b086385..126bffc 100644
--- a/tests/integration/base.py
+++ b/tests/integration/base.py
@@ -65,17 +65,13 @@ class IntegrationTestsBase(unittest.TestCase):
# configured. It should be running all the time, independently of netplan
os.makedirs('/etc/systemd/network', exist_ok=True)
with open('/etc/systemd/network/20-wired.network', 'w') as f:
- f.write('[Match]\nName=eth0 en*\n\n[Network]\nDHCP=ipv4')
-
- # ensure NM can manage our fake eths
- os.makedirs('/run/udev/rules.d', exist_ok=True)
- with open('/run/udev/rules.d/99-nm-veth-test.rules', 'w') as f:
- f.write('ENV{ID_NET_DRIVER}=="veth", ENV{INTERFACE}=="eth42|eth43|iface1|iface2", ENV{NM_UNMANAGED}="0"\n')
- subprocess.check_call(['udevadm', 'control', '--reload'])
+ f.write('[Match]\nName=eth0 en*\n\n[Network]\nDHCP=yes\nKeepConfiguration=yes')
+ # ensure NM doesn't interfere with our test backend (fake eth endpoints & mgmt network)
os.makedirs('/etc/NetworkManager/conf.d', exist_ok=True)
with open('/etc/NetworkManager/conf.d/99-test-ignore.conf', 'w') as f:
- f.write('[keyfile]\nunmanaged-devices+=interface-name:eth0,interface-name:en*,interface-name:veth42,interface-name:veth43')
+ f.write('''[keyfile]
+unmanaged-devices+=interface-name:eth0,interface-name:en*,interface-name:veth42,interface-name:veth43''')
subprocess.check_call(['netplan', 'apply'])
subprocess.call(['/lib/systemd/systemd-networkd-wait-online', '--quiet', '--timeout=30'])
@@ -85,10 +81,6 @@ class IntegrationTestsBase(unittest.TestCase):
os.remove('/run/NetworkManager/conf.d/test-blacklist.conf')
except FileNotFoundError:
pass
- try:
- os.remove('/run/udev/rules.d/99-nm-veth-test.rules')
- except FileNotFoundError:
- pass
def tearDown(self):
subprocess.call(['systemctl', 'stop', 'NetworkManager', 'systemd-networkd', 'netplan-wpa-*',
@@ -104,7 +96,12 @@ class IntegrationTestsBase(unittest.TestCase):
os.remove(f)
for f in glob.glob('/run/systemd/system/**/netplan-*'):
os.remove(f)
+ for f in glob.glob('/run/udev/rules.d/*netplan*'):
+ os.remove(f)
subprocess.call(['systemctl', 'daemon-reload'])
+ subprocess.call(['udevadm', 'control', '--reload'])
+ subprocess.call(['udevadm', 'trigger', '--attr-match=subsystem=net'])
+ subprocess.call(['udevadm', 'settle'])
try:
os.remove('/run/systemd/generator/netplan.stamp')
except FileNotFoundError:
@@ -275,7 +272,7 @@ class IntegrationTestsBase(unittest.TestCase):
def assert_iface(self, iface, expected_ip_a=None, unexpected_ip_a=None):
'''Assert that client interface has been created'''
- out = subprocess.check_output(['ip', 'a', 'show', 'dev', iface],
+ out = subprocess.check_output(['ip', '-d', 'a', 'show', 'dev', iface],
universal_newlines=True)
if expected_ip_a:
for r in expected_ip_a:
@@ -300,7 +297,12 @@ class IntegrationTestsBase(unittest.TestCase):
cmd = ['netplan', 'apply']
if state_dir:
cmd = cmd + ['--state', state_dir]
- out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, universal_newlines=True)
+ out = ''
+ try:
+ out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, universal_newlines=True)
+ except subprocess.CalledProcessError as e:
+ self.assertTrue(False, 'netplan apply failed: {}'.format(e.output))
+
if 'Run \'systemctl daemon-reload\' to reload units.' in out:
self.fail('systemd units changed without reload')
# start NM so that we can verify that it does not manage anything
diff --git a/tests/integration/bonds.py b/tests/integration/bonds.py
index 763f7e5..cf853e6 100644
--- a/tests/integration/bonds.py
+++ b/tests/integration/bonds.py
@@ -301,7 +301,7 @@ class _CommonTests():
@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
+ "skipping as networkd backend tests are disabled")
class TestNetworkd(IntegrationTestsBase, _CommonTests):
backend = 'networkd'
@@ -315,14 +315,12 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
ethbn:
match:
name: %(ec)s
- macaddress: %(ec_mac)s
bonds:
mybond:
interfaces: [ethbn]
macaddress: 00:01:02:03:04:05
dhcp4: yes''' % {'r': self.backend,
- 'ec': self.dev_e_client,
- 'ec_mac': self.dev_e_client_mac})
+ 'ec': self.dev_e_client})
self.generate_and_settle([self.dev_e_client, self.state_dhcp4('mybond')])
self.assert_iface_up(self.dev_e_client, ['master mybond'], ['inet '])
self.assert_iface_up('mybond', ['inet 192.168.5.[0-9]+/24', '00:01:02:03:04:05'])
@@ -509,7 +507,7 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
@unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
+ "skipping as NetworkManager backend tests are disabled")
class TestNetworkManager(IntegrationTestsBase, _CommonTests):
backend = 'NetworkManager'
diff --git a/tests/integration/bridges.py b/tests/integration/bridges.py
index a391e4f..c75853d 100644
--- a/tests/integration/bridges.py
+++ b/tests/integration/bridges.py
@@ -249,7 +249,7 @@ class _CommonTests():
@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
+ "skipping as networkd backend tests are disabled")
class TestNetworkd(IntegrationTestsBase, _CommonTests):
backend = 'networkd'
@@ -310,7 +310,7 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
@unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
+ "skipping as NetworkManager backend tests are disabled")
class TestNetworkManager(IntegrationTestsBase, _CommonTests):
backend = 'NetworkManager'
diff --git a/tests/integration/ethernets.py b/tests/integration/ethernets.py
index 865c0d4..8197a66 100644
--- a/tests/integration/ethernets.py
+++ b/tests/integration/ethernets.py
@@ -71,7 +71,7 @@ class _CommonTests():
englob:
match: {name: "eth?2"}
addresses: ["172.16.42.99/18", "1234:FFFF::42/64"]
-''' % {'r': self.backend}) # globbing match on "eth42", i.e. self.dev_e_client
+''' % {'r': self.backend}) # globbing match on "eth42", i.e. self.dev_e_client
self.generate_and_settle([self.dev_e_client])
self.assert_iface_up(self.dev_e_client, ['inet 172.16.42.99/18', 'inet6 1234:ffff::42/64'])
@@ -117,7 +117,7 @@ class _CommonTests():
self.assertRegex(out, r'%s\s+(ethernet|bridge)\s+%s' % (i, expected_state))
with open('/etc/resolv.conf') as f:
- resolv_conf = f.read()
+ resolv_conf = f.read()
if self.backend == 'NetworkManager' and nm_uses_dnsmasq:
sys.stdout.write('[NM with dnsmasq] ')
@@ -236,9 +236,69 @@ class _CommonTests():
self.assert_iface_up('iface1', ['inet 10.10.10.11'])
self.assert_iface_up('iface2', ['inet 10.10.10.22'])
+ def test_link_offloading(self):
+ self.setup_eth(None, False)
+ # check kernel defaults
+ out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
+ self.assertIn(b'rx-checksumming: on', out)
+ self.assertIn(b'tx-checksumming: on', out)
+ self.assertIn(b'tcp-segmentation-offload: on', out)
+ self.assertIn(b'tx-tcp6-segmentation: on', out)
+ self.assertIn(b'generic-segmentation-offload: on', out)
+ # enabled for armhf on autopkgtest.u.c but 'off' elsewhere
+ # self.assertIn(b'generic-receive-offload: off', out)
+ # validate turning off
+ with open(self.config, 'w') as f:
+ f.write('''network:
+ renderer: %(r)s
+ ethernets:
+ %(ec)s:
+ addresses: [10.10.10.22/24]
+ receive-checksum-offload: off
+ transmit-checksum-offload: off
+ tcp-segmentation-offload: off
+ tcp6-segmentation-offload: off
+ generic-segmentation-offload: off
+ generic-receive-offload: off
+ #large-receive-offload: off # not possible on veth
+''' % {'r': self.backend, 'ec': self.dev_e_client})
+ self.generate_and_settle([self.dev_e_client])
+ self.assert_iface_up(self.dev_e_client, ['inet 10.10.10.22'])
+ out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
+ self.assertIn(b'rx-checksumming: off', out)
+ self.assertIn(b'tx-checksumming: off', out)
+ self.assertIn(b'tcp-segmentation-offload: off', out)
+ self.assertIn(b'tx-tcp6-segmentation: off', out)
+ self.assertIn(b'generic-segmentation-offload: off', out)
+ self.assertIn(b'generic-receive-offload: off', out)
+ # validate turning on
+ with open(self.config, 'w') as f:
+ f.write('''network:
+ renderer: %(r)s
+ ethernets:
+ %(ec)s:
+ addresses: [10.10.10.22/24]
+ receive-checksum-offload: true
+ transmit-checksum-offload: true
+ tcp-segmentation-offload: true
+ tcp6-segmentation-offload: true
+ generic-segmentation-offload: true
+ generic-receive-offload: true
+ #large-receive-offload: true # not possible on veth
+''' % {'r': self.backend, 'ec': self.dev_e_client})
+ self.generate_and_settle([self.dev_e_client])
+ self.assert_iface_up(self.dev_e_client, ['inet 10.10.10.22'])
+ out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
+ self.assertIn(b'rx-checksumming: on', out)
+ self.assertIn(b'tx-checksumming: on', out)
+ self.assertIn(b'tcp-segmentation-offload: on', out)
+ self.assertIn(b'tx-tcp6-segmentation: on', out)
+ self.assertIn(b'generic-segmentation-offload: on', out)
+ self.assertIn(b'generic-receive-offload: on', out)
+
@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
+ "skipping as networkd backend tests are disabled")
class TestNetworkd(IntegrationTestsBase, _CommonTests):
backend = 'networkd'
@@ -253,7 +313,7 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
dhcp6: no
accept-ra: yes
addresses: [ '192.168.1.100/24' ]''' % {'r': self.backend, 'ec': self.dev_e_client})
- self.generate_and_settle([self.dev_e_client])
+ self.generate_and_settle([self.state_dhcp6(self.dev_e_client)])
self.assert_iface_up(self.dev_e_client, ['inet6 2600:'], [])
def test_eth_dhcp6_off_no_accept_ra(self):
@@ -312,8 +372,9 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
['inet6 9876:bbbb::11/70', 'inet 172.16.5.3/20'],
['inet6 fe80:', 'inet 169.254.'])
+
@unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
+ "skipping as NetworkManager backend tests are disabled")
class TestNetworkManager(IntegrationTestsBase, _CommonTests):
backend = 'NetworkManager'
diff --git a/tests/integration/ovs.py b/tests/integration/ovs.py
index bfd6eb9..24c13bf 100644
--- a/tests/integration/ovs.py
+++ b/tests/integration/ovs.py
@@ -31,8 +31,8 @@ class _CommonTests():
def _collect_ovs_settings(self, bridge0):
d = {}
- d['show'] = subprocess.check_output(['ovs-vsctl', 'show'])
- d['ssl'] = subprocess.check_output(['ovs-vsctl', 'get-ssl'])
+ d['show'] = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
+ d['ssl'] = subprocess.check_output(['ovs-vsctl', '-t', '5', 'get-ssl'])
# Get external-ids
for tbl in ('Open_vSwitch', 'Controller', 'Bridge', 'Port', 'Interface'):
cols = 'name,external-ids'
@@ -40,37 +40,37 @@ class _CommonTests():
cols = 'external-ids'
elif tbl == 'Controller':
cols = '_uuid,external-ids'
- d['external-ids-%s' % tbl] = subprocess.check_output(['ovs-vsctl', '--columns=%s' % cols, '-f', 'csv', '-d',
- 'bare', '--no-headings', 'list', tbl])
+ d['external-ids-%s' % tbl] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=%s' % cols, '-f', 'csv',
+ '-d', 'bare', '--no-headings', 'list', tbl])
# Get other-config
for tbl in ('Open_vSwitch', 'Bridge', 'Port', 'Interface'):
cols = 'name,other-config'
if tbl == 'Open_vSwitch':
cols = 'other-config'
- d['other-config-%s' % tbl] = subprocess.check_output(['ovs-vsctl', '--columns=%s' % cols, '-f', 'csv', '-d',
- 'bare', '--no-headings', 'list', tbl])
+ d['other-config-%s' % tbl] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=%s' % cols, '-f', 'csv',
+ '-d', 'bare', '--no-headings', 'list', tbl])
# Get bond settings
for col in ('bond_mode', 'lacp'):
- d['%s-Bond' % col] = subprocess.check_output(['ovs-vsctl', '--columns=name,%s' % col, '-f', 'csv', '-d', 'bare',
- '--no-headings', 'list', 'Port'])
+ d['%s-Bond' % col] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=name,%s' % col, '-f', 'csv',
+ '-d', 'bare', '--no-headings', 'list', 'Port'])
# Get bridge settings
- d['set-fail-mode-Bridge'] = subprocess.check_output(['ovs-vsctl', 'get-fail-mode', bridge0])
+ d['set-fail-mode-Bridge'] = subprocess.check_output(['ovs-vsctl', '-t', '5', 'get-fail-mode', bridge0])
for col in ('mcast_snooping_enable', 'rstp_enable', 'protocols'):
- d['%s-Bridge' % col] = subprocess.check_output(['ovs-vsctl', '--columns=name,%s' % col, '-f', 'csv', '-d', 'bare',
- '--no-headings', 'list', 'Bridge'])
+ d['%s-Bridge' % col] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=name,%s' % col, '-f', 'csv',
+ '-d', 'bare', '--no-headings', 'list', 'Bridge'])
# Get controller settings
- d['set-controller-Bridge'] = subprocess.check_output(['ovs-vsctl', 'get-controller', bridge0])
+ d['set-controller-Bridge'] = subprocess.check_output(['ovs-vsctl', '-t', '5', 'get-controller', bridge0])
for col in ('connection_mode',):
- d['%s-Controller' % col] = subprocess.check_output(['ovs-vsctl', '--columns=_uuid,%s' % col, '-f', 'csv', '-d',
- 'bare', '--no-headings', 'list', 'Controller'])
+ d['%s-Controller' % col] = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=_uuid,%s' % col, '-f', 'csv',
+ '-d', 'bare', '--no-headings', 'list', 'Controller'])
return d
def test_cleanup_interfaces(self):
self.setup_eth(None, False)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs0'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs1'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch0-1'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch1-0'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs0'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs1'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch0-1'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch1-0'])
with open(self.config, 'w') as f:
f.write('''network:
openvswitch:
@@ -81,7 +81,7 @@ class _CommonTests():
ovs1: {interfaces: [patch1-0]}''')
self.generate_and_settle(['ovs0', 'ovs1'])
# Basic verification that the bridges/ports/interfaces are there in OVS
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertIn(b' Bridge ovs0', out)
self.assertIn(b' Port patch0-1', out)
self.assertIn(b' Interface patch0-1', out)
@@ -94,7 +94,7 @@ class _CommonTests():
%(ec)s: {addresses: ['1.2.3.4/24']}''' % {'ec': self.dev_e_client})
self.generate_and_settle([self.dev_e_client])
# Verify that the netplan=true tagged bridges/ports have been cleaned up
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertNotIn(b'Bridge ovs0', out)
self.assertNotIn(b'Port patch0-1', out)
self.assertNotIn(b'Interface patch0-1', out)
@@ -105,11 +105,11 @@ class _CommonTests():
def test_cleanup_patch_ports(self):
self.setup_eth(None, False)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs0'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs1'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch0-1'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patchy'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'bond0'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs0'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs1'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch0-1'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patchy'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'bond0'])
with open(self.config, 'w') as f:
f.write('''network:
ethernets:
@@ -122,7 +122,7 @@ class _CommonTests():
ovs0: {interfaces: [patch0-1, bond0]}''' % {'ec': self.dev_e_client})
self.generate_and_settle([self.dev_e_client, 'ovs0'])
# Basic verification that the bridges/ports/interfaces are there in OVS
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertIn(b' Bridge ovs0', out)
self.assertIn(b' Port patch0-1\n Interface patch0-1\n type: patch', out)
self.assertIn(b' Port bond0', out)
@@ -141,7 +141,7 @@ class _CommonTests():
self.generate_and_settle([self.dev_e_client, 'ovs1'])
# Verify that the netplan=true tagged patch ports have been cleaned up
# even though the containing bond0 port still exists (with new patch ports)
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertIn(b' Bridge ovs1', out)
self.assertIn(b' Port patchy\n Interface patchy\n type: patch', out)
self.assertIn(b' Port bond0', out)
@@ -154,10 +154,10 @@ class _CommonTests():
self.assertNotIn(b'Interface patch1-0', out)
def test_bridge_vlan(self):
- self.setup_eth(None, True)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-%s' % self.dev_e_client])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-data'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-%s.100' % self.dev_e_client])
+ self.setup_eth(None, False)
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-%s' % self.dev_e_client])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-data'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-%s.100' % self.dev_e_client])
with open(self.config, 'w') as f:
f.write('''network:
version: 2
@@ -166,7 +166,6 @@ class _CommonTests():
mtu: 9000
bridges:
br-%(ec)s:
- dhcp4: true
mtu: 9000
interfaces: [%(ec)s]
openvswitch: {}
@@ -178,12 +177,9 @@ class _CommonTests():
br-%(ec)s.100:
id: 100
link: br-%(ec)s''' % {'ec': self.dev_e_client})
- self.generate_and_settle([self.dev_e_client,
- self.state_dhcp4('br-eth42'),
- 'br-data',
- 'br-eth42.100'])
+ self.generate_and_settle([self.dev_e_client, 'br-eth42', 'br-data', 'br-eth42.100'])
# Basic verification that the interfaces/ports are set up in OVS
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertIn(b' Bridge br-%b' % self.dev_e_client.encode(), out)
self.assertIn(b''' Port %(ec)b
Interface %(ec)b''' % {b'ec': self.dev_e_client.encode()}, out)
@@ -192,20 +188,19 @@ class _CommonTests():
Interface br-%(ec)b.100
type: internal''' % {b'ec': self.dev_e_client.encode()}, out)
self.assertIn(b' Bridge br-data', out)
- self.assert_iface('br-%s' % self.dev_e_client,
- ['inet 192.168.5.[0-9]+/16', 'mtu 9000']) # from DHCP
+ self.assert_iface('br-%s' % self.dev_e_client, ['mtu 9000'])
self.assert_iface('br-data', ['inet 192.168.20.1/16'])
self.assert_iface(self.dev_e_client, ['mtu 9000', 'master ovs-system'])
- self.assertIn(b'100', subprocess.check_output(['ovs-vsctl', 'br-to-vlan',
+ self.assertIn(b'100', subprocess.check_output(['ovs-vsctl', '-t', '5', 'br-to-vlan',
'br-%s.100' % self.dev_e_client]))
self.assertIn(b'br-%b' % self.dev_e_client.encode(), subprocess.check_output(
- ['ovs-vsctl', 'br-to-parent', 'br-%s.100' % self.dev_e_client]))
+ ['ovs-vsctl', '-t', '5', 'br-to-parent', 'br-%s.100' % self.dev_e_client]))
self.assertIn(b'br-%b' % self.dev_e_client.encode(), out)
def test_bridge_vlan_deletion(self):
- self.setup_eth(None, True)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-%s' % self.dev_e_client])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br-%s.100' % self.dev_e_client])
+ self.setup_eth(None, False)
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-%s' % self.dev_e_client])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br-%s.100' % self.dev_e_client])
with open(self.config, 'w') as f:
f.write('''network:
version: 2
@@ -214,7 +209,6 @@ class _CommonTests():
mtu: 9000
bridges:
br-%(ec)s:
- dhcp4: true
mtu: 9000
interfaces: [%(ec)s]
openvswitch: {}
@@ -223,18 +217,16 @@ class _CommonTests():
br-%(ec)s.100:
id: 100
link: br-%(ec)s''' % {'ec': self.dev_e_client})
- self.generate_and_settle([self.dev_e_client,
- self.state_dhcp4('br-eth42'),
- 'br-eth42.100'])
+ self.generate_and_settle([self.dev_e_client, 'br-eth42', 'br-eth42.100'])
# Basic verification that the underlying bridge and vlan interface are configured
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertIn(b' Bridge br-%b' % self.dev_e_client.encode(), out)
self.assertIn(b''' Port br-%(ec)b.100
tag: 100
Interface br-%(ec)b.100
type: internal''' % {b'ec': self.dev_e_client.encode()}, out)
- self.assertIn(b'100', subprocess.check_output(['ovs-vsctl', 'br-to-vlan',
+ self.assertIn(b'100', subprocess.check_output(['ovs-vsctl', '-t', '5', 'br-to-vlan',
'br-%s.100' % self.dev_e_client]))
# Write a network configuration that has the .100 vlan interface removed
@@ -246,21 +238,20 @@ class _CommonTests():
mtu: 9000
bridges:
br-%(ec)s:
- dhcp4: true
mtu: 9000
interfaces: [%(ec)s]
openvswitch: {}''' % {'ec': self.dev_e_client})
- self.generate_and_settle([self.dev_e_client, self.state_dhcp4('br-eth42')])
+ self.generate_and_settle([self.dev_e_client, 'br-eth42'])
# Check that the underlying bridge is still present but the vlan interface is now absent
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertIn(b' Bridge br-%b' % self.dev_e_client.encode(), out)
self.assertNotIn(b'Port br-%(ec)b.100' % {b'ec': self.dev_e_client.encode()}, out)
def test_bridge_base(self):
self.setup_eth(None, False)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovsbr'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', 'del-ssl'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovsbr'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', 'del-ssl'])
with open(self.config, 'w') as f:
f.write('''network:
ethernets:
@@ -282,7 +273,7 @@ class _CommonTests():
''' % {'ec': self.dev_e_client, 'e2c': self.dev_e2_client})
self.generate_and_settle([self.dev_e_client, self.dev_e2_client, 'ovsbr'])
# Basic verification that the interfaces/ports are in OVS
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertIn(b' Bridge ovsbr', out)
self.assertIn(b' Controller "tcp:127.0.0.1"', out)
self.assertIn(b' Controller "pssl:1337:[::1]"', out)
@@ -291,15 +282,15 @@ class _CommonTests():
self.assertIn(b' Port %(ec)b\n Interface %(ec)b' % {b'ec': self.dev_e_client.encode()}, out)
self.assertIn(b' Port %(e2c)b\n Interface %(e2c)b' % {b'e2c': self.dev_e2_client.encode()}, out)
# Verify the bridge was tagged 'netplan:true' correctly
- out = subprocess.check_output(['ovs-vsctl', '--columns=name,external-ids', '-f', 'csv', '-d', 'bare',
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=name,external-ids', '-f', 'csv', '-d', 'bare',
'list', 'Bridge', 'ovsbr'])
self.assertIn(b'netplan=true', out)
self.assert_iface('ovsbr', ['inet 192.170.1.1/24'])
def test_bond_base(self):
self.setup_eth(None, False)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovsbr'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'mybond'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovsbr'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'mybond'])
with open(self.config, 'w') as f:
f.write('''network:
ethernets:
@@ -318,13 +309,14 @@ class _CommonTests():
interfaces: [mybond]''' % {'ec': self.dev_e_client, 'e2c': self.dev_e2_client})
self.generate_and_settle([self.dev_e_client, self.dev_e2_client, 'ovsbr'])
# Basic verification that the interfaces/ports are in OVS
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertIn(b' Bridge ovsbr', out)
self.assertIn(b' Port mybond', out)
self.assertIn(b' Interface %b' % self.dev_e_client.encode(), out)
self.assertIn(b' Interface %b' % self.dev_e2_client.encode(), out)
# Verify the bond was tagged 'netplan:true' correctly
- out = subprocess.check_output(['ovs-vsctl', '--columns=name,external-ids', '-f', 'csv', '-d', 'bare', 'list', 'Port'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', '--columns=name,external-ids', '-f', 'csv',
+ '-d', 'bare', 'list', 'Port'])
self.assertIn(b'mybond,netplan=true', out)
# Verify bond params
out = subprocess.check_output(['ovs-appctl', 'bond/show', 'mybond'])
@@ -337,10 +329,10 @@ class _CommonTests():
def test_bridge_patch_ports(self):
self.setup_eth(None)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br0'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'br1'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch0-1'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'patch1-0'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br0'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'br1'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch0-1'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'patch1-0'])
with open(self.config, 'w') as f:
f.write('''network:
openvswitch:
@@ -355,7 +347,7 @@ class _CommonTests():
interfaces: [patch1-0]''')
self.generate_and_settle(['br0', 'br1'])
# Basic verification that the interfaces/ports are set up in OVS
- out = subprocess.check_output(['ovs-vsctl', 'show'])
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'])
self.assertIn(b' Bridge br0', out)
self.assertIn(b''' Port patch0-1
Interface patch0-1
@@ -371,7 +363,7 @@ class _CommonTests():
def test_bridge_non_ovs_bond(self):
self.setup_eth(None, False)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs-br'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs-br'])
self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'non-ovs-bond'])
with open(self.config, 'w') as f:
f.write('''network:
@@ -388,7 +380,7 @@ class _CommonTests():
openvswitch: {}''' % {'ec': self.dev_e_client, 'e2c': self.dev_e2_client})
self.generate_and_settle([self.dev_e_client, self.dev_e2_client, 'ovs-br', 'non-ovs-bond'])
# Basic verification that the interfaces/ports are set up in OVS
- out = subprocess.check_output(['ovs-vsctl', 'show'], universal_newlines=True)
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'], universal_newlines=True)
self.assertIn(' Bridge ovs-br', out)
self.assertIn(''' Port non-ovs-bond
Interface non-ovs-bond''', out)
@@ -401,7 +393,7 @@ class _CommonTests():
def test_vlan_maas(self):
self.setup_eth(None, False)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs0'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs0'])
self.addCleanup(subprocess.call, ['ip', 'link', 'delete', '%s.21' % self.dev_e_client], stderr=subprocess.DEVNULL)
with open(self.config, 'w') as f:
f.write('''network:
@@ -434,7 +426,7 @@ class _CommonTests():
mtu: 1500''' % {'ec': self.dev_e_client})
self.generate_and_settle([self.dev_e_client, 'ovs0', 'eth42.21'])
# Basic verification that the interfaces/ports are set up in OVS
- out = subprocess.check_output(['ovs-vsctl', 'show'], universal_newlines=True)
+ out = subprocess.check_output(['ovs-vsctl', '-t', '5', 'show'], universal_newlines=True)
self.assertIn(' Bridge ovs0', out)
self.assertIn(''' Port %(ec)s.21
Interface %(ec)s.21''' % {'ec': self.dev_e_client}, out)
@@ -466,9 +458,9 @@ class _CommonTests():
def test_settings_tag_cleanup(self):
self.setup_eth(None, False)
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs0'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-br', 'ovs1'])
- self.addCleanup(subprocess.call, ['ovs-vsctl', '--if-exists', 'del-port', 'bond0'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs0'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-br', 'ovs1'])
+ self.addCleanup(subprocess.call, ['ovs-vsctl', '-t', '5', '--if-exists', 'del-port', 'bond0'])
with open(self.config, 'w') as f:
f.write('''network:
version: 2
@@ -604,7 +596,7 @@ class _CommonTests():
@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
+ "skipping as networkd backend tests are disabled")
class TestOVS(IntegrationTestsBase, _CommonTests):
backend = 'networkd'
diff --git a/tests/integration/regressions.py b/tests/integration/regressions.py
index 40aeeac..1a996b4 100644
--- a/tests/integration/regressions.py
+++ b/tests/integration/regressions.py
@@ -1,5 +1,5 @@
#!/usr/bin/python3
-#
+#
# Regression tests to catch previously-fixed issues.
#
# These need to be run in a VM and do change the system
@@ -39,7 +39,7 @@ class _CommonTests():
@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
+ "skipping as networkd backend tests are disabled")
class TestNetworkd(IntegrationTestsBase, _CommonTests):
backend = 'networkd'
@@ -96,9 +96,9 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
self.assertEqual('', err)
self.assertNotIn('An error occurred:', out)
self.assertRegex(out.strip(), r'Do you want to keep these settings\?\n\n\n'
-r'Press ENTER before the timeout to accept the new configuration\n\n\n'
-r'(Changes will revert in \d+ seconds\n)+'
-r'Configuration accepted\.')
+ r'Press ENTER before the timeout to accept the new configuration\n\n\n'
+ r'(Changes will revert in \d+ seconds\n)+'
+ r'Configuration accepted\.')
def test_try_reject_lp1949095(self):
with open(self.config, 'w') as f:
@@ -114,13 +114,30 @@ r'Configuration accepted\.')
self.assertEqual('', err)
self.assertNotIn('An error occurred:', out)
self.assertRegex(out.strip(), r'Do you want to keep these settings\?\n\n\n'
-r'Press ENTER before the timeout to accept the new configuration\n\n\n'
-r'(Changes will revert in \d+ seconds\n)+'
-r'Reverting\.')
+ r'Press ENTER before the timeout to accept the new configuration\n\n\n'
+ r'(Changes will revert in \d+ seconds\n)+'
+ r'Reverting\.')
+
+ def test_apply_networkd_inactive_lp1962095(self):
+ self.setup_eth(None)
+ with open(self.config, 'w') as f:
+ f.write('''network:
+ renderer: %(r)s
+ ethernets:
+ %(ec)s:
+ dhcp4: true
+ %(e2c)s:
+ dhcp4: true
+ version: 2''' % {'r': self.backend, 'ec': self.dev_e_client, 'e2c': self.dev_e2_client})
+ # stop networkd to simulate the failure case
+ subprocess.check_call(['systemctl', 'stop', 'systemd-networkd.service', 'systemd-networkd.socket'])
+ self.generate_and_settle([self.state_dhcp4(self.dev_e_client), self.state_dhcp4(self.dev_e2_client)])
+ self.assert_iface_up(self.dev_e_client, ['inet 192.168.5.[0-9]+/24'])
+ self.assert_iface_up(self.dev_e2_client, ['inet 192.168.6.[0-9]+/24'])
@unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
+ "skipping as NetworkManager backend tests are disabled")
class TestNetworkManager(IntegrationTestsBase, _CommonTests):
backend = 'NetworkManager'
diff --git a/tests/integration/routing.py b/tests/integration/routing.py
index 7cc291d..416268c 100644
--- a/tests/integration/routing.py
+++ b/tests/integration/routing.py
@@ -78,7 +78,7 @@ class _CommonTests():
# The table option was introduced as of NM 1.10+
def test_route_table(self):
self.setup_eth(None)
- table_id = '255' # This is the 'local' FIB of /etc/iproute2/rt_tables
+ table_id = '255' # This is the 'local' FIB of /etc/iproute2/rt_tables
with open(self.config, 'w') as f:
f.write('''network:
renderer: %(r)s
@@ -277,8 +277,54 @@ class _CommonTests():
self.assertIn(b'metric 99', # check metric from static route
subprocess.check_output(['ip', 'route', 'show', '10.10.10.0/24']))
+ def test_vrf_basic(self):
+ self.setup_eth('slaac')
+ self.addCleanup(subprocess.call, ['ip', 'link', 'delete', 'vrf0'], stderr=subprocess.DEVNULL)
+ self.addCleanup(subprocess.call, ['ip', 'route', 'flush', 'table', '1000'], stderr=subprocess.DEVNULL)
+ self.addCleanup(subprocess.call, ['ip', 'rule', 'del', 'from', '10.10.10.42', 'table', '1000'],
+ stderr=subprocess.DEVNULL)
+ with open(self.config, 'w') as f:
+ f.write('''network:
+ renderer: %(r)s
+ ethernets:
+ %(ec)s:
+ addresses: [10.10.10.22/24]
+ routes:
+ - to: 11.11.11.0/24
+ via: 10.10.10.2
+ table: 1000
+ vrfs:
+ vrf0:
+ table: 1000
+ interfaces: [%(ec)s]
+ routes:
+ - to: 10.10.0.0/16
+ via: 10.10.10.1
+ routing-policy:
+ - from: 10.10.10.42
+''' % {'r': self.backend, 'ec': self.dev_e_client})
+ self.generate_and_settle([self.dev_e_client])
+ self.assert_iface_up(self.dev_e_client, ['inet 10.10.10.22', 'master vrf0'])
+ self.assert_iface_up('vrf0', ['MASTER'])
+ # verify routes didn't leak into the main routing table
+ out = subprocess.check_output(['ip', 'route', 'show'], universal_newlines=True)
+ self.assertNotIn('10.10.0.0/16', out)
+ self.assertNotIn('11.11.11.0/24', out)
+ # verify routes were added to the VRF's routing table
+ out = subprocess.check_output(['ip', 'route', 'show', 'table', '1000'],
+ universal_newlines=True)
+ self.assertIn('10.10.0.0/16 via 10.10.10.1 dev vrf0', out)
+ self.assertIn('11.11.11.0/24 via 10.10.10.2 dev {}'.format(self.dev_e_client), out)
+
+ # verify routing policy was setup correctly to the VRF's table
+ # 'routing-policy' is not supported on NetworkManager
+ if self.backend == 'networkd':
+ out = subprocess.check_output(['ip', 'rule', 'show'], universal_newlines=True)
+ self.assertIn('from 10.10.10.42 lookup 1000', out)
+
+
@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
+ "skipping as networkd backend tests are disabled")
class TestNetworkd(IntegrationTestsBase, _CommonTests):
backend = 'networkd'
@@ -370,7 +416,7 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
@unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
+ "skipping as NetworkManager backend tests are disabled")
class TestNetworkManager(IntegrationTestsBase, _CommonTests):
backend = 'NetworkManager'
diff --git a/tests/integration/run.py b/tests/integration/run.py
index deb8e4b..dd6e1a4 100755
--- a/tests/integration/run.py
+++ b/tests/integration/run.py
@@ -29,8 +29,8 @@ import sys
tests_dir = os.path.dirname(os.path.abspath(__file__))
-default_backends = [ 'networkd', 'NetworkManager' ]
-fixtures = [ "__init__.py", "base.py", "run.py" ]
+default_backends = ['networkd', 'NetworkManager']
+fixtures = ["__init__.py", "base.py", "run.py"]
possible_tests = []
testfiles = glob.glob(os.path.join(tests_dir, "*.py"))
@@ -39,6 +39,7 @@ for pyfile in testfiles:
if filename not in fixtures:
possible_tests.append(filename.split('.')[0])
+
def dedupe(duped_list):
deduped = set()
for item in duped_list:
@@ -47,6 +48,7 @@ def dedupe(duped_list):
deduped.add(real_item)
return deduped
+
# XXX: omg, this is ugly :)
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent("""
diff --git a/tests/integration/scenarios.py b/tests/integration/scenarios.py
index e37dd18..fac978b 100644
--- a/tests/integration/scenarios.py
+++ b/tests/integration/scenarios.py
@@ -118,7 +118,7 @@ class _CommonTests():
confdir = os.path.join(tempdir, 'etc', 'netplan')
os.makedirs(confdir)
with open(self.config, 'w') as f:
- f.write('''network:
+ f.write('''network:
renderer: %(r)s
version: 2
bridges:
@@ -136,13 +136,13 @@ class _CommonTests():
@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
+ "skipping as networkd backend tests are disabled")
class TestNetworkd(IntegrationTestsBase, _CommonTests):
backend = 'networkd'
@unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
+ "skipping as NetworkManager backend tests are disabled")
class TestNetworkManager(IntegrationTestsBase, _CommonTests):
backend = 'NetworkManager'
diff --git a/tests/integration/tunnels.py b/tests/integration/tunnels.py
index c336954..2b780ba 100644
--- a/tests/integration/tunnels.py
+++ b/tests/integration/tunnels.py
@@ -23,11 +23,11 @@
import sys
import subprocess
-import time
import unittest
from base import IntegrationTestsBase, test_backends
+
class _CommonTests():
def test_tunnel_sit(self):
@@ -127,12 +127,6 @@ class _CommonTests():
self.assertRegex(out, r'transfer: \d+.*B received, \d+.*B sent')
self.assert_iface('wg1', ['inet 20.20.20.10/24'])
-
-@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
-class TestNetworkd(IntegrationTestsBase, _CommonTests):
- backend = 'networkd'
-
def test_tunnel_gre(self):
self.addCleanup(subprocess.call, ['ip', 'link', 'delete', 'tun0'], stderr=subprocess.DEVNULL)
with open(self.config, 'w') as f:
@@ -163,6 +157,88 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
self.generate_and_settle(['tun0'])
self.assert_iface('tun0', ['tun0@NONE', 'link.* fe80::1 brd 2001:dead:beef::2'])
+ def test_tunnel_gre_with_keys(self):
+ self.addCleanup(subprocess.call, ['ip', 'link', 'delete', 'tun0'], stderr=subprocess.DEVNULL)
+ with open(self.config, 'w') as f:
+ f.write('''network:
+ renderer: %(r)s
+ version: 2
+ tunnels:
+ tun0:
+ mode: gre
+ keys:
+ input: 1234
+ output: 5678
+ local: 192.168.5.1
+ remote: 99.99.99.99
+''' % {'r': self.backend})
+ self.generate_and_settle(['tun0'])
+ self.assert_iface('tun0', ['tun0@NONE', 'link.* 192.168.5.1 peer 99.99.99.99'])
+ out = subprocess.check_output(['ip', 'tunnel', 'show', 'tun0'], universal_newlines=True)
+ self.assertIn("ikey 1234 okey 5678", out)
+
+ def test_tunnel_gre6_with_keys(self):
+ self.addCleanup(subprocess.call, ['ip', 'link', 'delete', 'tun0'], stderr=subprocess.DEVNULL)
+ with open(self.config, 'w') as f:
+ f.write('''network:
+ renderer: %(r)s
+ version: 2
+ tunnels:
+ tun0:
+ mode: ip6gre
+ key: 1234
+ local: fe80::1
+ remote: 2001:dead:beef::2
+''' % {'r': self.backend})
+ self.generate_and_settle(['tun0'])
+ self.assert_iface('tun0', ['tun0@NONE', 'link.* fe80::1 brd 2001:dead:beef::2'])
+ out = subprocess.check_output(['ip', '-6', 'tunnel', 'show', 'tun0'], universal_newlines=True)
+ self.assertIn("key 1234", out)
+
+ def test_tunnel_vxlan(self):
+ self.addCleanup(subprocess.call, ['ip', 'link', 'delete', 'vx0'], stderr=subprocess.DEVNULL)
+ self.setup_eth(None, False)
+ with open(self.config, 'w') as f:
+ f.write('''network:
+ renderer: %(r)s
+ version: 2
+ tunnels:
+ vx0:
+ mode: vxlan
+ id: 1337
+ link: %(ec)s
+ local: 10.10.10.42
+ remote: 224.0.0.5 # multicast group
+ ttl: 64
+ aging: 100
+ port: 4567
+ port-range: [4000, 4200]
+ mac-learning: false
+ short-circuit: true
+ notifications: [l2-miss, l3-miss]
+ checksums: [udp, zero-udp6-tx, zero-udp6-rx, remote-tx, remote-rx] # sd-networkd only
+ ethernets:
+ %(ec)s:
+ addresses: [10.10.10.42/24]
+''' % {'r': self.backend, 'ec': self.dev_e_client})
+ self.generate_and_settle([self.dev_e_client, 'vx0'])
+ self.assert_iface('vx0', ['vxlan ', ' id 1337 ', ' group 224.0.0.5 ',
+ ' local 10.10.10.42 ', ' srcport 4000 4200 ',
+ ' dev %s ' % self.dev_e_client,
+ ' dstport 4567 ', ' rsc ', ' l2miss ',
+ ' l3miss ', ' ttl 64 ', ' ageing 100 '])
+ if self.backend == 'networkd':
+ # checksums are not supported on the NetworkManager backend
+ self.assert_iface('vx0', [' udpcsum ', ' udp6zerocsumtx ',
+ ' udp6zerocsumrx ', ' remcsumtx ',
+ ' remcsumrx '])
+
+
+@unittest.skipIf("networkd" not in test_backends,
+ "skipping as networkd backend tests are disabled")
+class TestNetworkd(IntegrationTestsBase, _CommonTests):
+ backend = 'networkd'
+
def test_tunnel_vti(self):
self.addCleanup(subprocess.call, ['ip', 'link', 'delete', 'tun0'], stderr=subprocess.DEVNULL)
with open(self.config, 'w') as f:
@@ -195,13 +271,7 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests):
self.generate_and_settle(['tun0'])
self.assert_iface('tun0', ['tun0@NONE', 'link.* fe80::1 brd 2001:dead:beef::2'])
-
-@unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
-class TestNetworkManager(IntegrationTestsBase, _CommonTests):
- backend = 'NetworkManager'
-
- def test_tunnel_gre(self):
+ def test_tunnel_gretap_with_keys(self):
self.addCleanup(subprocess.call, ['ip', 'link', 'delete', 'tun0'], stderr=subprocess.DEVNULL)
with open(self.config, 'w') as f:
f.write('''network:
@@ -209,13 +279,41 @@ class TestNetworkManager(IntegrationTestsBase, _CommonTests):
version: 2
tunnels:
tun0:
- mode: gre
- keys: 1234
+ mode: gretap
+ keys:
+ input: 1.2.3.4
+ output: 5.6.7.8
local: 192.168.5.1
remote: 99.99.99.99
''' % {'r': self.backend})
self.generate_and_settle(['tun0'])
- self.assert_iface('tun0', ['tun0@NONE', 'link.* 192.168.5.1 peer 99.99.99.99'])
+ out = subprocess.check_output(['ip', '-details', 'link', 'show', 'tun0'], universal_newlines=True)
+ self.assertIn("gretap remote 99.99.99.99 local 192.168.5.1", out)
+ self.assertIn("ikey 1.2.3.4 okey 5.6.7.8", out)
+
+ def test_tunnel_gretap6_with_keys(self):
+ self.addCleanup(subprocess.call, ['ip', 'link', 'delete', 'tun0'], stderr=subprocess.DEVNULL)
+ with open(self.config, 'w') as f:
+ f.write('''network:
+ renderer: %(r)s
+ version: 2
+ tunnels:
+ tun0:
+ mode: ip6gretap
+ keys: 1.2.3.4
+ local: fe80::1
+ remote: 2001:dead:beef::2
+''' % {'r': self.backend})
+ self.generate_and_settle(['tun0'])
+ out = subprocess.check_output(['ip', '-details', 'link', 'show', 'tun0'], universal_newlines=True)
+ self.assertIn("gretap remote 2001:dead:beef::2 local fe80::1", out)
+ self.assertIn("ikey 1.2.3.4 okey 1.2.3.4", out)
+
+
+@unittest.skipIf("NetworkManager" not in test_backends,
+ "skipping as NetworkManager backend tests are disabled")
+class TestNetworkManager(IntegrationTestsBase, _CommonTests):
+ backend = 'NetworkManager'
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))
diff --git a/tests/integration/vlans.py b/tests/integration/vlans.py
index e68f605..c8dce7c 100644
--- a/tests/integration/vlans.py
+++ b/tests/integration/vlans.py
@@ -89,13 +89,13 @@ class _CommonTests():
@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
+ "skipping as networkd backend tests are disabled")
class TestNetworkd(IntegrationTestsBase, _CommonTests):
backend = 'networkd'
@unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
+ "skipping as NetworkManager backend tests are disabled")
class TestNetworkManager(IntegrationTestsBase, _CommonTests):
backend = 'NetworkManager'
diff --git a/tests/integration/wifi.py b/tests/integration/wifi.py
index d09b5cf..6750796 100644
--- a/tests/integration/wifi.py
+++ b/tests/integration/wifi.py
@@ -111,15 +111,42 @@ wpa_passphrase=12345678
universal_newlines=True)
self.assertRegex(out, 'DNS.*192.168.5.1')
+ def test_wifi_regdom(self):
+ self.setup_ap('''hw_mode=g
+channel=1
+ssid=fake net
+wpa=1
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=TKIP
+wpa_passphrase=12345678
+''', None)
+
+ out = subprocess.check_output(['iw', 'reg', 'get'], universal_newlines=True)
+ self.assertNotIn('country GB', out)
+ with open(self.config, 'w') as f:
+ f.write('''network:
+ renderer: %(r)s
+ wifis:
+ %(wc)s:
+ addresses: ["192.168.1.42/24"]
+ regulatory-domain: GB
+ access-points:
+ "fake net":
+ password: 12345678''' % {'r': self.backend, 'wc': self.dev_w_client})
+ self.generate_and_settle([self.dev_w_client])
+ self.assert_iface_up(self.dev_w_client, ['inet 192.168.1.42/24'])
+ out = subprocess.check_output(['iw', 'reg', 'get'], universal_newlines=True)
+ self.assertIn('global\ncountry GB', out)
+
@unittest.skipIf("networkd" not in test_backends,
- "skipping as networkd backend tests are disabled")
+ "skipping as networkd backend tests are disabled")
class TestNetworkd(IntegrationTestsWifi, _CommonTests):
backend = 'networkd'
@unittest.skipIf("NetworkManager" not in test_backends,
- "skipping as NetworkManager backend tests are disabled")
+ "skipping as NetworkManager backend tests are disabled")
class TestNetworkManager(IntegrationTestsWifi, _CommonTests):
backend = 'NetworkManager'
diff --git a/tests/parser/base.py b/tests/parser/base.py
index 63e77d0..2859981 100644
--- a/tests/parser/base.py
+++ b/tests/parser/base.py
@@ -30,8 +30,9 @@ import ctypes.util
import contextlib
import subprocess
-exe_generate = os.path.join(os.path.dirname(os.path.dirname(
- os.path.dirname(os.path.abspath(__file__)))), 'generate')
+exe_generate = os.environ.get('NETPLAN_GENERATE_PATH',
+ os.path.join(os.path.dirname(os.path.dirname(
+ os.path.dirname(os.path.abspath(__file__)))), 'generate'))
# make sure we point to libnetplan properly.
os.environ.update({'LD_LIBRARY_PATH': '.:{}'.format(os.environ.get('LD_LIBRARY_PATH'))})
diff --git a/tests/parser/test_keyfile.py b/tests/parser/test_keyfile.py
index 809cfd8..6b5ccf5 100644
--- a/tests/parser/test_keyfile.py
+++ b/tests/parser/test_keyfile.py
@@ -25,8 +25,6 @@ from .base import TestKeyfileBase
rootdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
exe_cli = os.path.join(rootdir, 'src', 'netplan.script')
-# Make sure we can import our development netplan.
-os.environ.update({'PYTHONPATH': '.'})
lib = ctypes.CDLL(ctypes.util.find_library('netplan'))
lib.netplan_get_id_from_nm_filename.restype = ctypes.c_char_p
@@ -287,6 +285,7 @@ address1=1:2:3::9/128
gateway=6:6::6
route1=dead:beef::1/128,2001:1234::2
route1_options=unknown=invalid,
+route2=4:5:6:7:8:9:0:1/63,,5
[proxy]
'''.format(UUID))
@@ -306,10 +305,6 @@ route1_options=unknown=invalid,
- 9.8.7.6
- 5.4.3.2
- dead:beef::2
- search:
- - foo.local
- - bar.remote
- - bar.local
gateway4: 6.6.6.6
gateway6: 6:6::6
ipv6-address-generation: "stable-privacy"
@@ -328,6 +323,9 @@ route1_options=unknown=invalid,
via: "4.4.4.4"
- to: "dead:beef::1/128"
via: "2001:1234::2"
+ - scope: "link"
+ metric: 5
+ to: "4:5:6:7:8:9:0:1/63"
wakeonlan: true
networkmanager:
uuid: "{}"
@@ -600,7 +598,8 @@ mode=ap'''.format(UUID))
self._template_keyfile_type_wifi('infrastructure', 'mesh')
def test_keyfile_type_wifi_missing_ssid(self):
- err = self.generate_from_keyfile('''[connection]\ntype=wifi\nuuid={}\nid=myid with spaces'''.format(UUID), expect_fail=True)
+ err = self.generate_from_keyfile('''[connection]\ntype=wifi\nuuid={}\nid=myid with spaces'''
+ .format(UUID), expect_fail=True)
self.assertFalse(os.path.isfile(os.path.join(self.confdir, '90-NM-{}.yaml'.format(UUID))))
self.assertIn('netplan: Keyfile: cannot find SSID for WiFi connection', err)
@@ -1093,8 +1092,6 @@ route3=4:5:6:7:8:9:0:1/63,::,5
route4=5:6:7:8:9:0:1:2/62
[proxy]
-
-
'''.format(UUID))
self.assert_netplan({UUID: '''network:
version: 2
@@ -1114,8 +1111,6 @@ route4=5:6:7:8:9:0:1:2/62
- 4.2.2.2
- 1::cafe
- 2::cafe
- search:
- - wallaceandgromit.com
ipv6-address-generation: "stable-privacy"
mtu: 900
routes:
@@ -1242,3 +1237,117 @@ method=auto
passthrough:
ipv6.ip6-privacy: "-1"
'''.format(UUID, UUID)})
+
+ def test_keyfile_wpa3_sae(self):
+ self.generate_from_keyfile('''[connection]
+id=test2
+uuid={}
+type=wifi
+interface-name=wlan0
+
+[wifi]
+mode=infrastructure
+ssid=ubuntu-wpa2-wpa3-mixed
+
+[wifi-security]
+key-mgmt=sae
+psk=test1234
+
+[ipv4]
+method=auto
+
+[ipv6]
+addr-gen-mode=stable-privacy
+method=auto
+
+[proxy]
+'''.format(UUID))
+ self.assert_netplan({UUID: '''network:
+ version: 2
+ wifis:
+ NM-{}:
+ renderer: NetworkManager
+ match:
+ name: "wlan0"
+ dhcp4: true
+ dhcp6: true
+ ipv6-address-generation: "stable-privacy"
+ access-points:
+ "ubuntu-wpa2-wpa3-mixed":
+ auth:
+ key-management: "none"
+ password: "test1234"
+ networkmanager:
+ uuid: "ff9d6ebc-226d-4f82-a485-b7ff83b9607f"
+ name: "test2"
+ passthrough:
+ wifi-security.key-mgmt: "sae"
+ ipv6.ip6-privacy: "-1"
+ proxy._: ""
+ networkmanager:
+ uuid: "{}"
+ name: "test2"
+'''.format(UUID, UUID)})
+
+ def test_keyfile_dns_search_ip4_ip6_conflict(self):
+ self.generate_from_keyfile('''[connection]
+id=Work Wired
+type=ethernet
+uuid={}
+autoconnect=false
+timestamp=305419896
+
+[ethernet]
+wake-on-lan=1
+mac-address=99:88:77:66:55:44
+mtu=900
+
+[ipv4]
+method=manual
+address1=192.168.0.5/24,192.168.0.1
+address2=1.2.3.4/8
+dns=4.2.2.1;4.2.2.2;
+
+[ipv6]
+method=manual
+address1=abcd::beef/64
+address2=dcba::beef/56
+addr-gen-mode=1
+dns=1::cafe;2::cafe;
+dns-search=wallaceandgromit.com;
+
+[proxy]\n'''.format(UUID))
+ self.assert_netplan({UUID: '''network:
+ version: 2
+ ethernets:
+ NM-{}:
+ renderer: NetworkManager
+ match:
+ macaddress: "99:88:77:66:55:44"
+ addresses:
+ - "192.168.0.5/24"
+ - "1.2.3.4/8"
+ - "abcd::beef/64"
+ - "dcba::beef/56"
+ nameservers:
+ addresses:
+ - 4.2.2.1
+ - 4.2.2.2
+ - 1::cafe
+ - 2::cafe
+ mtu: 900
+ wakeonlan: true
+ networkmanager:
+ uuid: "{}"
+ name: "Work Wired"
+ passthrough:
+ connection.autoconnect: "false"
+ connection.timestamp: "305419896"
+ ethernet.wake-on-lan: "1"
+ ipv4.method: "manual"
+ ipv4.address1: "192.168.0.5/24,192.168.0.1"
+ ipv6.addr-gen-mode: "1"
+ ipv6.dns-search: "wallaceandgromit.com;"
+ ipv6.ip6-privacy: "-1"
+ proxy._: ""
+'''.format(UUID, UUID)})
diff --git a/tests/test_cli_get_set.py b/tests/test_cli_get_set.py
index 74675f6..110e167 100644
--- a/tests/test_cli_get_set.py
+++ b/tests/test_cli_get_set.py
@@ -48,53 +48,54 @@ class TestSet(unittest.TestCase):
self._set(['ethernets.eth0.dhcp4=true'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- self.assertIn('network:\n ethernets:\n eth0:\n dhcp4: true', f.read())
+ self.assertIs(True, yaml.safe_load(f)['network']['ethernets']['eth0']['dhcp4'])
def test_set_scalar2(self):
self._set(['ethernets.eth0.dhcp4="yes"'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- self.assertIn('network:\n ethernets:\n eth0:\n dhcp4: \'yes\'', f.read())
+ # XXX: the previous version using PyYAML would keep the "yes" variant but the round-trip
+ # through libnetplan doesn't keep formatting choices (yes is a keyword same as true)
+ self.assertIs(True, yaml.safe_load(f)['network']['ethernets']['eth0']['dhcp4'])
def test_set_global(self):
self._set([r'network={renderer: NetworkManager}'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- self.assertIn('network:\n renderer: NetworkManager', f.read())
+ self.assertEqual('NetworkManager', yaml.safe_load(f)['network']['renderer'])
def test_set_sequence(self):
self._set(['ethernets.eth0.addresses=[1.2.3.4/24, \'5.6.7.8/24\']'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- self.assertIn('''network:\n ethernets:\n eth0:
- addresses:
- - 1.2.3.4/24
- - 5.6.7.8/24''', f.read())
+ self.assertListEqual(
+ ['1.2.3.4/24', '5.6.7.8/24'],
+ yaml.safe_load(f)['network']['ethernets']['eth0']['addresses'])
def test_set_sequence2(self):
self._set(['ethernets.eth0.addresses=["1.2.3.4/24", 5.6.7.8/24]'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- self.assertIn('''network:\n ethernets:\n eth0:
- addresses:
- - 1.2.3.4/24
- - 5.6.7.8/24''', f.read())
+ self.assertListEqual(
+ ['1.2.3.4/24', '5.6.7.8/24'],
+ yaml.safe_load(f)['network']['ethernets']['eth0']['addresses'])
def test_set_mapping(self):
self._set(['ethernets.eth0={addresses: [1.2.3.4/24], dhcp4: true}'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- self.assertIn('''network:\n ethernets:\n eth0:
- addresses:
- - 1.2.3.4/24
- dhcp4: true''', f.read())
+ out = yaml.safe_load(f)
+ self.assertSequenceEqual(
+ ['1.2.3.4/24'],
+ out['network']['ethernets']['eth0']['addresses'])
+ self.assertIs(True, out['network']['ethernets']['eth0']['dhcp4'])
def test_set_origin_hint(self):
self._set(['ethernets.eth0.dhcp4=true', '--origin-hint=99_snapd'])
p = os.path.join(self.workdir.name, 'etc', 'netplan', '99_snapd.yaml')
self.assertTrue(os.path.isfile(p))
with open(p, 'r') as f:
- self.assertEquals('network:\n ethernets:\n eth0:\n dhcp4: true\n', f.read())
+ self.assertIs(True, yaml.safe_load(f)['network']['ethernets']['eth0']['dhcp4'])
def test_set_empty_origin_hint(self):
with self.assertRaises(Exception) as context:
@@ -107,7 +108,7 @@ class TestSet(unittest.TestCase):
self._set(['ethernets.eth0.dhcp4=true', '--origin-hint=00-empty'])
self.assertTrue(os.path.isfile(empty_file))
with open(empty_file, 'r') as f:
- self.assertIn('network:\n ethernets:\n eth0:\n dhcp4: true', f.read())
+ self.assertTrue(yaml.safe_load(f)['network']['ethernets']['eth0']['dhcp4'])
def test_set_empty_hint_file_whitespace(self):
empty_file = os.path.join(self.workdir.name, 'etc', 'netplan', '00-empty.yaml')
@@ -151,7 +152,7 @@ class TestSet(unittest.TestCase):
def test_set_invalid(self):
with self.assertRaises(Exception) as context:
self._set(['xxx.yyy=abc'])
- self.assertIn('unknown key \'xxx\'\n xxx:\n', str(context.exception))
+ self.assertIn('unknown key \'xxx\'', str(context.exception))
self.assertFalse(os.path.isfile(self.path))
def test_set_invalid_validation(self):
@@ -181,11 +182,12 @@ class TestSet(unittest.TestCase):
self._set(['ethernets.eth0.dhcp4=true'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- out = f.read()
- self.assertIn('network:\n ethernets:\n', out)
- self.assertIn(' ens3:\n dhcp4: true', out)
- self.assertIn(' eth0:\n dhcp4: true', out)
- self.assertIn(' version: 2', out)
+ out = yaml.safe_load(f)
+ self.assertIn('ens3', out['network']['ethernets'])
+ self.assertIn('eth0', out['network']['ethernets'])
+ self.assertIs(True, out['network']['ethernets']['ens3']['dhcp4'])
+ self.assertIs(True, out['network']['ethernets']['eth0']['dhcp4'])
+ self.assertEqual(2, out['network']['version'])
def test_set_overwrite_eq(self):
with open(self.path, 'w') as f:
@@ -195,39 +197,42 @@ class TestSet(unittest.TestCase):
self._set(['ethernets.ens3.dhcp4=yes'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- out = f.read()
- self.assertIn('network:\n ethernets:\n', out)
- self.assertIn(' ens3:\n dhcp4: true', out)
+ self.assertIs(
+ True,
+ yaml.safe_load(f)['network']['ethernets']['ens3']['dhcp4'])
def test_set_overwrite(self):
with open(self.path, 'w') as f:
f.write('''network:
ethernets:
- ens3: {dhcp4: "yes"}''')
+ ens3: {dhcp4: "no"}''')
self._set(['ethernets.ens3.dhcp4=true'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- out = f.read()
- self.assertIn('network:\n ethernets:\n', out)
- self.assertIn(' ens3:\n dhcp4: true', out)
+ self.assertIs(
+ True,
+ yaml.safe_load(f)['network']['ethernets']['ens3']['dhcp4'])
def test_set_delete(self):
with open(self.path, 'w') as f:
f.write('''network:\n version: 2\n renderer: NetworkManager
ethernets:
ens3: {dhcp4: yes, dhcp6: yes}
- eth0: {addresses: [1.2.3.4/24]}''')
+ eth0: {addresses: [1.2.3.4/24]}
+ eth1: {addresses: [2.3.4.5/24]}
+ ''')
self._set(['ethernets.eth0.addresses=NULL'])
self._set(['ethernets.ens3.dhcp6=null'])
+ self._set(['ethernets.eth1=NULL'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- out = f.read()
- self.assertIn('network:\n ethernets:\n', out)
- self.assertIn(' version: 2', out)
- self.assertIn(' ens3:\n dhcp4: true', out)
- self.assertNotIn('dhcp6: true', out)
- self.assertNotIn('addresses:', out)
- self.assertNotIn('eth0:', out)
+ out = yaml.safe_load(f)
+ self.assertIn('ethernets', out['network'])
+ self.assertEqual(2, out['network']['version'])
+ self.assertIs(True, out['network']['ethernets']['ens3']['dhcp4'])
+ self.assertNotIn('dhcp6', out['network']['ethernets']['ens3'])
+ self.assertNotIn('eth0', out['network']['ethernets'])
+ self.assertNotIn('eth1', out['network']['ethernets'])
def test_set_delete_subtree(self):
with open(self.path, 'w') as f:
@@ -237,11 +242,129 @@ class TestSet(unittest.TestCase):
self._set(['network.ethernets=null'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- out = f.read()
- self.assertIn('network:\n', out)
- self.assertIn(' version: 2\n', out)
- self.assertIn(' renderer: NetworkManager\n', out)
- self.assertNotIn('ethernets:', out)
+ out = yaml.safe_load(f)
+ self.assertIn('network', out)
+ self.assertEqual(2, out['network']['version'])
+ self.assertEqual('NetworkManager', out['network']['renderer'])
+ self.assertNotIn('ethernets', out['network'])
+
+ def test_set_global_ovs(self):
+ with open(self.path, 'w') as f:
+ f.write('''network:\n version: 2
+ ethernets:
+ eth0: {addresses: [1.2.3.4/24]}''')
+ self._set(['network.openvswitch={"ports": [[port1, port2]], "other-config": null}'])
+ self.assertTrue(os.path.isfile(self.path))
+ with open(self.path, 'r') as f:
+ out = yaml.safe_load(f)
+ self.assertIn('network', out)
+ self.assertEqual(2, out['network']['version'])
+ self.assertEqual('1.2.3.4/24', out['network']['ethernets']['eth0']['addresses'][0])
+ self.assertNotIn('other-config', out['network']['openvswitch'])
+ self.assertSequenceEqual(['port1', 'port2'], out['network']['openvswitch']['ports'][0])
+
+ def test_set_delete_access_point(self):
+ with open(self.path, 'w') as f:
+ f.write('''network:
+ version: 2
+ wifis:
+ wl0:
+ access-points:
+ "Joe's Home":
+ password: "s0s3kr1t"
+ bssid: 00:11:22:33:44:55
+ band: 2.4GHz
+ channel: 11
+ workplace:
+ password: "c0mpany1"
+ bssid: de:ad:be:ef:ca:fe
+ band: 5GHz
+ channel: 100
+ peer2peer:
+ mode: adhoc''')
+ self._set(['network.wifis.wl0.access-points.Joe\'s Home=null'])
+ with open(self.path, 'r') as f:
+ out = yaml.safe_load(f)
+ self.assertNotIn('Joe\'s Home', out['network']['wifis']['wl0']['access-points'])
+
+ def test_set_delete_nm_passthrough(self):
+ with open(self.path, 'w') as f:
+ f.write('''network:
+ version: 2
+ wifis:
+ wlan0:
+ renderer: NetworkManager
+ dhcp4: true
+ macaddress: "00:11:22:33:44:55"
+ access-points:
+ "SOME-SSID":
+ bssid: "de:ad:be:ef:ca:fe"
+ networkmanager:
+ name: "myid with spaces"
+ passthrough:
+ connection.permissions: ""
+ ipv4.dns-search: ""''')
+ ap_key = 'network.wifis.wlan0.access-points.SOME-SSID'
+ self._set([ap_key+'.networkmanager.passthrough.connection\\.permissions=null'])
+ with open(self.path, 'r') as f:
+ out = yaml.safe_load(f)
+ ap = out['network']['wifis']['wlan0']['access-points']['SOME-SSID']
+ self.assertNotIn('connection.permissions', ap['networkmanager']['passthrough'])
+ self.assertEqual('', ap['networkmanager']['passthrough']['ipv4.dns-search'])
+
+ def test_set_delete_bridge_subparams(self):
+ with open(self.path, 'w') as f:
+ f.write('''network:
+ version: 2
+ ethernets:
+ eno1: {}
+ eno2: {}
+ switchports:
+ match:
+ driver: yayroute
+ bridges:
+ br0:
+ interfaces: [eno1, eno2, switchports]
+ parameters:
+ path-cost:
+ eno1: 70
+ eno2: 80
+ port-priority:
+ eno1: 14
+ eno2: 15''')
+
+ self._set(['network.bridges.br0.parameters={path-cost: {eno1: null}, port-priority: {eno2: null}}'])
+
+ with open(self.path, 'r') as f:
+ out = yaml.safe_load(f)
+ self.assertNotIn('eno1', out['network']['bridges']['br0']['parameters']['path-cost'])
+ self.assertEqual(80, out['network']['bridges']['br0']['parameters']['path-cost']['eno2'])
+
+ self.assertNotIn('eno2', out['network']['bridges']['br0']['parameters']['port-priority'])
+ self.assertEqual(14, out['network']['bridges']['br0']['parameters']['port-priority']['eno1'])
+
+ def test_set_delete_ovs_other_config(self):
+ with open(self.path, 'w') as f:
+ f.write('''network:
+ version: 2
+ ethernets:
+ eth0:
+ openvswitch:
+ other-config:
+ bogus-option: bogus
+ disable-in-band: true
+ dhcp6: true
+ bridges:
+ ovs0:
+ interfaces: [eth0]
+ openvswitch: {}
+''')
+ self._set(['ethernets.eth0.openvswitch.other-config.bogus-option=null'])
+
+ with open(self.path, 'r') as f:
+ out = yaml.safe_load(f)
+ self.assertNotIn('bogus-option', out['network']['ethernets']['eth0']['openvswitch']['other-config'])
+ self.assertTrue(out['network']['ethernets']['eth0']['openvswitch']['other-config']['disable-in-band'])
def test_set_delete_file(self):
with open(self.path, 'w') as f:
@@ -269,13 +392,21 @@ class TestSet(unittest.TestCase):
eth0: {addresses: [1.2.3.4]}''')
with self.assertRaises(Exception) as context:
self._set(['ethernets.eth0.addresses'])
- self.assertEquals('Invalid value specified', str(context.exception))
+ self.assertEqual('Invalid value specified', str(context.exception))
def test_set_escaped_dot(self):
self._set([r'ethernets.eth0\.123.dhcp4=false'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- self.assertIn('network:\n ethernets:\n eth0.123:\n dhcp4: false', f.read())
+ out = yaml.safe_load(f)
+ self.assertIs(False, out['network']['ethernets']['eth0.123']['dhcp4'])
+
+ def test_set_invalid_input(self):
+ with self.assertRaises(Exception) as context:
+ self._set([r'ethernets.eth0={dhcp4:false}'])
+ self.assertIn(
+ "unknown key 'dhcp4:false'",
+ str(context.exception))
def test_set_override_existing_file(self):
override = os.path.join(self.workdir.name, 'etc', 'netplan', 'some-file.yaml')
@@ -285,9 +416,9 @@ class TestSet(unittest.TestCase):
self.assertFalse(os.path.isfile(self.path))
self.assertTrue(os.path.isfile(override))
with open(override, 'r') as f:
- out = f.read()
- self.assertIn('network:\n ethernets:\n eth0:\n dhcp4: false', out) # new
- self.assertIn('eth1:\n dhcp6: false', out) # old
+ out = yaml.safe_load(f)
+ self.assertIs(False, out['network']['ethernets']['eth0']['dhcp4'])
+ self.assertIs(False, out['network']['ethernets']['eth1']['dhcp6'])
def test_set_override_existing_file_escaped_dot(self):
override = os.path.join(self.workdir.name, 'etc', 'netplan', 'some-file.yaml')
@@ -297,7 +428,8 @@ class TestSet(unittest.TestCase):
self.assertFalse(os.path.isfile(self.path))
self.assertTrue(os.path.isfile(override))
with open(override, 'r') as f:
- self.assertIn('network:\n ethernets:\n eth0.123:\n dhcp4: false', f.read())
+ out = yaml.safe_load(f)
+ self.assertIs(False, out['network']['ethernets']['eth0.123']['dhcp4'])
def test_set_override_multiple_existing_files(self):
file1 = os.path.join(self.workdir.name, 'etc', 'netplan', 'eth0.yaml')
@@ -306,25 +438,25 @@ class TestSet(unittest.TestCase):
file2 = os.path.join(self.workdir.name, 'etc', 'netplan', 'eth1.yaml')
with open(file2, 'w') as f:
f.write(r'network: {ethernets: {eth1: {dhcp4: true}}}')
- self._set([(r'network={renderer: NetworkManager, version: 2,'
- r'ethernets:{'
- r'eth1:{dhcp4: false},'
- r'eth0.1:{dhcp4: false},'
- r'eth0.2:{dhcp4: false}},'
- r'bridges:{'
- r'br99:{dhcp4: false}}}')])
+ self._set([(r'network={"renderer": "NetworkManager", "version":2,'
+ r'"ethernets":{'
+ r'"eth1":{"dhcp4":false},'
+ r'"eth0.1":{"dhcp4":false},'
+ r'"eth0.2":{"dhcp4":false}},'
+ r'"bridges":{'
+ r'"br99":{"dhcp4":false}}}')])
self.assertTrue(os.path.isfile(file1))
with open(file1, 'r') as f:
- self.assertIn('network:\n ethernets:\n eth0.1:\n dhcp4: false', f.read())
+ self.assertIs(False, yaml.safe_load(f)['network']['ethernets']['eth0.1']['dhcp4'])
self.assertTrue(os.path.isfile(file2))
with open(file2, 'r') as f:
- self.assertIn('network:\n ethernets:\n eth1:\n dhcp4: false', f.read())
+ self.assertIs(False, yaml.safe_load(f)['network']['ethernets']['eth1']['dhcp4'])
self.assertTrue(os.path.isfile(self.path))
with open(self.path, 'r') as f:
- out = f.read()
- self.assertIn('network:\n bridges:\n br99:\n dhcp4: false', out)
- self.assertIn(' version: 2', out)
- self.assertIn(' renderer: NetworkManager', out)
+ out = yaml.safe_load(f)
+ self.assertIs(False, out['network']['bridges']['br99']['dhcp4'])
+ self.assertEqual(2, out['network']['version'])
+ self.assertEqual('NetworkManager', out['network']['renderer'])
class TestGet(unittest.TestCase):
diff --git a/tests/test_cli_units.py b/tests/test_cli_units.py
index 48fe2d9..f860965 100644
--- a/tests/test_cli_units.py
+++ b/tests/test_cli_units.py
@@ -34,6 +34,7 @@ class TestCLI(unittest.TestCase):
def setUp(self):
self.tmproot = tempfile.mkdtemp()
os.mkdir(os.path.join(self.tmproot, 'run'))
+ os.makedirs(os.path.join(self.tmproot, 'etc/netplan'))
os.environ['DBUS_TEST_NETPLAN_ROOT'] = self.tmproot
def tearDown(self):
@@ -103,3 +104,40 @@ class TestCLI(unittest.TestCase):
self.assertTrue(os.path.isfile(stamp_file))
self.assertTrue(cmd.clear_ready_stamp())
self.assertFalse(os.path.isfile(stamp_file))
+
+ def test_netplan_try_is_revertable(self):
+ with open(os.path.join(self.tmproot, 'etc/netplan/test.yaml'), 'w') as f:
+ f.write('''network:
+ bridges:
+ br54:
+ dhcp4: false
+''')
+ cmd = NetplanTry()
+ self.assertTrue(cmd.is_revertable())
+
+ def test_netplan_try_is_revertable_fail(self):
+ extra_config = os.path.join(self.tmproot, 'extra.yaml')
+ with open(extra_config, 'w') as f:
+ f.write('''network:
+ bridges:
+ br54:
+ INVALID: kaputt
+''')
+ cmd = NetplanTry()
+ cmd.config_file = extra_config
+ self.assertRaises(SystemExit, cmd.is_revertable)
+
+ def test_netplan_try_is_not_revertable(self):
+ with open(os.path.join(self.tmproot, 'etc/netplan/test.yaml'), 'w') as f:
+ f.write('''network:
+ ethernets:
+ eth0:
+ dhcp4: true
+ bonds:
+ bn0:
+ interfaces: [eth0]
+ parameters:
+ mode: balance-rr
+''')
+ cmd = NetplanTry()
+ self.assertFalse(cmd.is_revertable())
diff --git a/tests/test_configmanager.py b/tests/test_configmanager.py
index 9051b5e..e88b9ea 100644
--- a/tests/test_configmanager.py
+++ b/tests/test_configmanager.py
@@ -21,7 +21,7 @@ import shutil
import tempfile
import unittest
-from netplan.configmanager import ConfigManager
+from netplan.configmanager import ConfigManager, ConfigurationError
class TestConfigManager(unittest.TestCase):
@@ -44,6 +44,8 @@ class TestConfigManager(unittest.TestCase):
ethernets:
eth0:
dhcp6: on
+ eth42:
+ dhcp4: on
ethbr1:
dhcp4: on
''', file=fd)
@@ -59,10 +61,18 @@ class TestConfigManager(unittest.TestCase):
print('''network:
version: 2
openvswitch:
- ports: [[patchx, patcha], [patchy, patchb]]
+ ports: [[patchx, patchc], [patchy, patchd]]
bridges:
ovs0: {openvswitch: {}}
''', file=fd)
+ with open(os.path.join(self.workdir.name, "invalid.yaml"), 'w') as fd:
+ print('''network:
+ version: 2
+ vlans:
+ vlan78:
+ id: 78
+ link: ethinvalid
+''', file=fd)
with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"), 'w') as fd:
print('''network:
version: 2
@@ -72,6 +82,8 @@ class TestConfigManager(unittest.TestCase):
other-config:
disable-in-band: true
ethernets:
+ lo:
+ addresses: [ 192.168.10.10/32 ]
eth0:
dhcp4: false
ethbr1:
@@ -95,14 +107,53 @@ class TestConfigManager(unittest.TestCase):
vlans:
vlan2:
id: 2
- link: eth99
+ link: eth0
+ tunnels:
+ he-ipv6:
+ mode: sit
+ remote: 2.2.2.2
+ local: 1.1.1.1
+ addresses:
+ - "2001:dead:beef::2/64"
+ gateway6: "2001:dead:beef::1"
+ vxlan1005:
+ mode: vxlan
+ id: 1005
+ link: lo
+ mtu: 8950
+ accept-ra: no
+ neigh-suppress: true
+ mac-learning: false
+ port: 4789
+ local: 192.168.10.10
+ remote: 1.1.1.1
+ vxlan1:
+ mode: vxlan
+ id: 1
+ link: lo
+ mtu: 8950
+ accept-ra: no
+ neigh-suppress: true
+ mac-learning: false
+ port: 4789
+ local: 192.168.10.10
+ remote: 1.1.1.1
bridges:
br3:
- interfaces: [ ethbr1 ]
+ interfaces: [ ethbr1, vxlan1005 ]
br4:
- interfaces: [ ethbr2 ]
+ interfaces: [ ethbr2, vxlan1 ]
parameters:
stp: on
+ vrfs:
+ vrf1005:
+ table: 1005
+ interfaces:
+ - br3
+ - br4
+ vrf1006:
+ table: 1006
+ interfaces: []
bonds:
bond5:
interfaces: [ ethbond1 ]
@@ -110,14 +161,6 @@ class TestConfigManager(unittest.TestCase):
interfaces: [ ethbond2 ]
parameters:
mode: 802.3ad
- tunnels:
- he-ipv6:
- mode: sit
- remote: 2.2.2.2
- local: 1.1.1.1
- addresses:
- - "2001:dead:beef::2/64"
- gateway6: "2001:dead:beef::1"
nm-devices:
fallback:
renderer: NetworkManager
@@ -125,6 +168,7 @@ class TestConfigManager(unittest.TestCase):
passthrough:
connection.id: some-nm-id
connection.uuid: some-uuid
+ connection.type: ethernet
''', file=fd)
with open(os.path.join(self.workdir.name, "run/systemd/network/01-pretend.network"), 'w') as fd:
print("pretend .network", file=fd)
@@ -133,59 +177,65 @@ class TestConfigManager(unittest.TestCase):
def test_parse(self):
self.configmanager.parse()
- self.assertIn('eth0', self.configmanager.ethernets)
- self.assertIn('bond6', self.configmanager.bonds)
- self.assertIn('eth0', self.configmanager.physical_interfaces)
- self.assertNotIn('bond7', self.configmanager.interfaces)
+ state = self.configmanager.np_state
+ assert state
+ self.assertIn('lo', self.configmanager.ethernets)
+ self.assertIn('eth0', state.ethernets)
+ self.assertIn('bond6', state.bonds)
+ self.assertIn('eth0', self.configmanager.physical_interfaces)
+ self.assertNotIn('bond7', self.configmanager.all_defs)
self.assertNotIn('bond6', self.configmanager.physical_interfaces)
- self.assertNotIn('parameters', self.configmanager.bonds.get('bond5'))
- self.assertIn('parameters', self.configmanager.bonds.get('bond6'))
- self.assertIn('wwan0', self.configmanager.modems)
+ self.assertIn('wwan0', state.modems)
self.assertIn('wwan0', self.configmanager.physical_interfaces)
- self.assertIn('apn', self.configmanager.modems.get('wwan0'))
- self.assertIn('he-ipv6', self.configmanager.tunnels)
+ # self.assertIn('apn', self.configmanager.modems.get('wwan0'))
+ self.assertIn('he-ipv6', state.tunnels)
self.assertNotIn('he-ipv6', self.configmanager.physical_interfaces)
- self.assertIn('remote', self.configmanager.tunnels.get('he-ipv6'))
- self.assertIn('other-config', self.configmanager.openvswitch)
- self.assertIn('ports', self.configmanager.openvswitch)
- self.assertEquals(2, self.configmanager.version)
- self.assertEquals('networkd', self.configmanager.renderer)
- self.assertIn('fallback', self.configmanager.nm_devices)
- self.assertIn('vlan2', self.configmanager.virtual_interfaces)
- self.assertIn('br3', self.configmanager.virtual_interfaces)
- self.assertIn('br4', self.configmanager.virtual_interfaces)
- self.assertIn('bond5', self.configmanager.virtual_interfaces)
- self.assertIn('bond6', self.configmanager.virtual_interfaces)
+ # self.assertIn('remote', self.configmanager.tunnels.get('he-ipv6'))
+ self.assertIn('patcha', state.ovs_ports)
+ self.assertIn('patchb', state.ovs_ports)
+
+ self.assertEqual('networkd', state.backend)
+ self.assertIn('fallback', state.nm_devices)
+
+ self.assertIn('vrf1005', self.configmanager.virtual_interfaces)
+ self.assertIn('vlan2', self.configmanager.virtual_interfaces)
+ self.assertIn('br3', self.configmanager.virtual_interfaces)
+ self.assertIn('br4', self.configmanager.virtual_interfaces)
+ self.assertIn('vxlan1005', self.configmanager.virtual_interfaces)
+ self.assertIn('vxlan1', self.configmanager.virtual_interfaces)
+ self.assertIn('bond5', self.configmanager.virtual_interfaces)
+ self.assertIn('bond6', self.configmanager.virtual_interfaces)
self.assertIn('he-ipv6', self.configmanager.virtual_interfaces)
def test_parse_merging(self):
- self.configmanager.parse(extra_config=[os.path.join(self.workdir.name, "newfile_merging.yaml")])
- self.assertIn('eth0', self.configmanager.ethernets)
- self.assertIn('dhcp4', self.configmanager.ethernets['eth0'])
- self.assertEquals(True, self.configmanager.ethernets['eth0'].get('dhcp6'))
- self.assertEquals(True, self.configmanager.ethernets['ethbr1'].get('dhcp4'))
+ state = self.configmanager.parse(extra_config=[os.path.join(self.workdir.name, "newfile_merging.yaml")])
+ self.assertIn('eth0', state.ethernets)
+ self.assertIn('eth42', state.ethernets)
def test_parse_merging_ovs(self):
- self.configmanager.parse(extra_config=[os.path.join(self.workdir.name, "ovs_merging.yaml")])
- self.assertIn('eth0', self.configmanager.ethernets)
- self.assertIn('dhcp4', self.configmanager.ethernets['eth0'])
- self.assertIn('patchx', self.configmanager.ovs_ports)
- self.assertIn('patchy', self.configmanager.ovs_ports)
- self.assertIn('ovs0', self.configmanager.bridges)
- self.assertEqual({}, self.configmanager.ovs_ports['patchx'].get('openvswitch'))
- self.assertEqual({}, self.configmanager.ovs_ports['patchy'].get('openvswitch'))
- self.assertEqual({}, self.configmanager.bridges['ovs0'].get('openvswitch'))
+ state = self.configmanager.parse(extra_config=[os.path.join(self.workdir.name, "ovs_merging.yaml")])
+ self.assertIn('eth0', state.ethernets)
+ # self.assertIn('dhcp4', state.ethernets['eth0'])
+ self.assertIn('patchx', state.ovs_ports)
+ self.assertIn('patchy', state.ovs_ports)
+ self.assertIn('ovs0', state.bridges)
+ self.assertEqual('OpenVSwitch', state['ovs0'].backend)
+ self.assertEqual('OpenVSwitch', state['patchx'].backend)
+ self.assertEqual('OpenVSwitch', state['patchy'].backend)
def test_parse_emptydict(self):
- self.configmanager.parse(extra_config=[os.path.join(self.workdir.name, "newfile_emptydict.yaml")])
- self.assertIn('br666', self.configmanager.bridges)
- self.assertEquals(False, self.configmanager.ethernets['eth0'].get('dhcp4'))
- self.assertEquals(False, self.configmanager.ethernets['ethbr1'].get('dhcp4'))
+ state = self.configmanager.parse(extra_config=[os.path.join(self.workdir.name, "newfile_emptydict.yaml")])
+ self.assertIn('br666', state.bridges)
+ self.assertIn('eth0', state.ethernets)
+
+ def test_parse_invalid(self):
+ with self.assertRaises(ConfigurationError):
+ self.configmanager.parse(extra_config=[os.path.join(self.workdir.name, "invalid.yaml")])
def test_parse_extra_config(self):
- self.configmanager.parse(extra_config=[os.path.join(self.workdir.name, "newfile.yaml")])
- self.assertIn('ethtest', self.configmanager.ethernets)
- self.assertIn('bond6', self.configmanager.bonds)
+ state = self.configmanager.parse(extra_config=[os.path.join(self.workdir.name, "newfile.yaml")])
+ self.assertIn('ethtest', state.ethernets)
+ self.assertIn('bond6', state.bonds)
def test_add(self):
self.configmanager.add({os.path.join(self.workdir.name, "newfile.yaml"):
diff --git a/tests/test_libnetplan.py b/tests/test_libnetplan.py
index 14c74cb..8442b18 100644
--- a/tests/test_libnetplan.py
+++ b/tests/test_libnetplan.py
@@ -34,8 +34,6 @@ import netplan.libnetplan as libnetplan
lib = libnetplan.lib
rootdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
exe_cli = os.path.join(rootdir, 'src', 'netplan.script')
-# Make sure we can import our development netplan.
-os.environ.update({'PYTHONPATH': '.'})
class TestRawLibnetplan(TestBase):
@@ -132,7 +130,7 @@ class TestRawLibnetplan(TestBase):
self.assertTrue(os.path.isfile(orig))
# Verify the file still exists and still contains the other connection
with open(orig, 'r') as f:
- self.assertEqual(f.read(), 'network:\n ethernets:\n other-id:\n dhcp6: true\n')
+ self.assertEqual(f.read(), 'network:\n version: 2\n ethernets:\n other-id:\n dhcp6: true\n')
def test_write_netplan_conf(self):
netdef_id = 'some-netplan-id'
@@ -185,6 +183,23 @@ class TestNetdefIterator(TestBase):
self.assertSetEqual(set(["eth0", "eth1"]), set(d.id for d in libnetplan._NetdefIterator(state, "ethernets")))
+class TestParser(TestBase):
+ def test_load_yaml_from_fd_empty(self):
+ parser = libnetplan.Parser()
+ # We just don't want it to raise an exception
+ with tempfile.TemporaryFile() as f:
+ parser.load_yaml(f)
+
+ def test_load_yaml_from_fd_bad_yaml(self):
+ parser = libnetplan.Parser()
+ with tempfile.TemporaryFile() as f:
+ f.write(b'invalid: {]')
+ f.seek(0, io.SEEK_SET)
+ with self.assertRaises(libnetplan.LibNetplanException) as context:
+ parser.load_yaml(f)
+ self.assertIn('Invalid YAML', str(context.exception))
+
+
class TestState(TestBase):
def test_get_netdef(self):
state = state_from_yaml(self.confdir, '''network:
@@ -254,8 +269,77 @@ class TestState(TestBase):
f.flush()
self.assertEqual(0, f.seek(0, io.SEEK_END))
+ def test_write_yaml_file_unremovable_target(self):
+ state = state_from_yaml(self.confdir, '''network:
+ ethernets:
+ eth0:
+ dhcp4: false''', filename='target.yml')
+ target = os.path.join(self.confdir, 'target.yml')
+ os.remove(target)
+ os.makedirs(target)
+
+ with self.assertRaises(libnetplan.LibNetplanException):
+ state.write_yaml_file('target.yml', self.workdir.name)
+
+ def test_update_yaml_hierarchy_no_confdir(self):
+ state = state_from_yaml(self.confdir, '''network:
+ ethernets:
+ eth0:
+ dhcp4: false''')
+ shutil.rmtree(self.confdir)
+ with self.assertRaises(libnetplan.LibNetplanException) as context:
+ state.update_yaml_hierarchy("bogus", self.workdir.name)
+ self.assertIn('No such file or directory', str(context.exception))
+
+ def test_write_yaml_file_remove_directory(self):
+ state = libnetplan.State()
+ os.makedirs(self.confdir)
+ with tempfile.TemporaryDirectory(dir=self.confdir) as tmpdir:
+ hint = os.path.basename(tmpdir)
+ with self.assertRaises(libnetplan.LibNetplanException):
+ state.write_yaml_file(hint, self.workdir.name)
+
+ def test_write_yaml_file_file_no_confdir(self):
+ state = state_from_yaml(self.confdir, '''network:
+ ethernets:
+ eth0:
+ dhcp4: false''', filename='test.yml')
+ shutil.rmtree(self.confdir)
+ with self.assertRaises(libnetplan.LibNetplanException) as context:
+ state.write_yaml_file('test.yml', self.workdir.name)
+ self.assertIn('No such file or directory', str(context.exception))
+
class TestNetDefinition(TestBase):
+ def test_type(self):
+ state = state_from_yaml(self.confdir, '''network:
+ ethernets:
+ eth0:
+ dhcp4: false''')
+
+ self.assertEqual(state['eth0'].type, 'ethernets')
+
+ def test_backend(self):
+ state = state_from_yaml(self.confdir, '''network:
+ renderer: networkd
+ ethernets:
+ eth0:
+ dhcp4: false
+ eth1:
+ renderer: NetworkManager''')
+ self.assertEqual(state['eth0'].backend, 'networkd')
+ self.assertEqual(state['eth1'].backend, 'NetworkManager')
+
+ def test_critical(self):
+ state = state_from_yaml(self.confdir, '''network:
+ ethernets:
+ eth0:
+ critical: true
+ eth1: {}''')
+
+ self.assertTrue(state['eth0'].critical)
+ self.assertFalse(state['eth1'].critical)
+
def test_eq(self):
state = state_from_yaml(self.confdir, '''network:
ethernets:
@@ -272,77 +356,91 @@ class TestNetDefinition(TestBase):
# Test against a weird singleton to ensure consistency against other types
self.assertNotEqual(state['eth0'], True)
+ def test_filepath(self):
+ state = state_from_yaml(self.confdir, '''network:
+ ethernets:
+ eth0:
+ dhcp4: false''', filename="a.yaml")
+ netdef = state['eth0']
+ self.assertEqual(os.path.join(self.confdir, "a.yaml"), netdef.filepath)
+
+ def test_set_name(self):
+ state = state_from_yaml(self.confdir, '''network:
+ ethernets:
+ mac-match:
+ set-name: mymac0
+ match:
+ macaddress: 11:22:33:AA:BB:FF''')
+ self.assertEqual(state['mac-match'].set_name, 'mymac0')
-class TestFreeFunctions(TestBase):
- def setUp(self):
- super().setUp()
- os.makedirs(self.confdir)
+ def test_simple_matches(self):
+ state = state_from_yaml(self.confdir, '''network:
+ ethernets:
+ witness: {}
+ name-match:
+ match:
+ name: "eth42"
+ driver-match:
+ match:
+ driver: "e10*"
+ mac-match:
+ match:
+ macaddress: 11:22:33:AA:BB:FF''')
+ self.assertFalse(state['witness'].has_match)
+ self.assertTrue(state['name-match'].has_match)
+ self.assertTrue(state['name-match'].match_interface(itf_name="eth42"))
+ self.assertFalse(state['name-match'].match_interface(itf_name="eth32"))
+ self.assertTrue(state['driver-match'].match_interface(itf_driver="e1000"))
+ self.assertFalse(state['name-match'].match_interface(itf_driver="ixgbe"))
+ self.assertFalse(state['driver-match'].match_interface(itf_name="eth42"))
+ self.assertTrue(state['mac-match'].match_interface(itf_mac="11:22:33:AA:BB:FF"))
+ self.assertFalse(state['mac-match'].match_interface(itf_mac="11:22:33:AA:BB:CC"))
+
+ def test_match_without_match_block(self):
+ state = state_from_yaml(self.confdir, '''network:
+ ethernets:
+ eth0:
+ dhcp4: false''')
- def tearDown(self):
- shutil.rmtree(self.workdir.name)
- super().tearDown()
+ netdef = state['eth0']
+ self.assertTrue(netdef.match_interface('eth0'))
+ self.assertFalse(netdef.match_interface('eth000'))
- def test_netplan_get_filename_by_id(self):
- file_a = os.path.join(self.workdir.name, 'etc/netplan/a.yaml')
- file_b = os.path.join(self.workdir.name, 'etc/netplan/b.yaml')
- with open(file_a, 'w') as f:
- f.write('network:\n ethernets:\n id_a:\n dhcp4: true')
- with open(file_b, 'w') as f:
- f.write('network:\n ethernets:\n id_b:\n dhcp4: true\n id_a:\n dhcp4: true')
- # netdef:b can only be found in b.yaml
- basename = os.path.basename(libnetplan.netplan_get_filename_by_id('id_b', self.workdir.name))
- self.assertEqual(basename, 'b.yaml')
- # netdef:a is defined in a.yaml, overriden by b.yaml
- basename = os.path.basename(libnetplan.netplan_get_filename_by_id('id_a', self.workdir.name))
- self.assertEqual(basename, 'b.yaml')
-
- def test_netplan_get_filename_by_id_no_files(self):
- self.assertIsNone(libnetplan.netplan_get_filename_by_id('some-id', self.workdir.name))
-
- def test_netplan_get_filename_by_id_invalid(self):
- file = os.path.join(self.workdir.name, 'etc/netplan/a.yaml')
- with open(file, 'w') as f:
- f.write('''network:
- tunnels:
- id_a:
- mode: sit
- local: 0.0.0.0
- remote: 0.0.0.0
- key: 0.0.0.0''')
- self.assertIsNone(libnetplan.netplan_get_filename_by_id('some-id', self.workdir.name))
-
- def test_netplan_get_ids_for_devtype(self):
- path = os.path.join(self.workdir.name, 'etc/netplan/a.yaml')
- with open(path, 'w') as f:
- f.write('''network:
+ def test_vlan_props_without_vlan(self):
+ state = state_from_yaml(self.confdir, '''network:
ethernets:
- id_b:
- dhcp4: true
- id_a:
- dhcp4: true
- vlans:
- en-intra:
- id: 3
- link: id_b
- dhcp4: true''')
- self.assertSetEqual(
- set(libnetplan.netplan_get_ids_for_devtype("ethernets", self.workdir.name)),
- set(["id_a", "id_b"]))
+ eth0:
+ dhcp4: false''')
- def test_netplan_get_ids_for_devtype_no_dev(self):
- path = os.path.join(self.workdir.name, 'etc/netplan/a.yaml')
- with open(path, 'w') as f:
- f.write('''network:
+ self.assertIsNone(state['eth0'].vlan_id)
+ self.assertIsNone(state['eth0'].vlan_link)
+
+ def test_is_trivial_compound_itf(self):
+ state = state_from_yaml(self.confdir, '''network:
ethernets:
- id_b:
- dhcp4: true''')
- self.assertSetEqual(
- set(libnetplan.netplan_get_ids_for_devtype("tunnels", self.workdir.name)),
- set([]))
+ eth0:
+ dhcp4: false
+ bridges:
+ br0:
+ dhcp4: false
+ br1:
+ parameters:
+ priority: 42
+ ''')
- def test_NetdefIterator_with_clear_netplan(self):
- state = libnetplan.State()
- self.assertSequenceEqual(list(libnetplan._NetdefIterator(state, "ethernets")), [])
+ self.assertFalse(state['eth0'].is_trivial_compound_itf)
+ self.assertTrue(state['br0'].is_trivial_compound_itf)
+ self.assertFalse(state['br1'].is_trivial_compound_itf)
+
+
+class TestFreeFunctions(TestBase):
+ def test_create_yaml_patch_bad_syntax(self):
+ with tempfile.TemporaryFile() as patchfile:
+ with self.assertRaises(libnetplan.LibNetplanException) as context:
+ libnetplan.create_yaml_patch(['network'], '{invalid_yaml]', patchfile)
+ self.assertIn('Error parsing YAML', str(context.exception))
+ patchfile.seek(0, io.SEEK_END)
+ self.assertEqual(patchfile.tell(), 0)
def test_dump_yaml_subtree_bad_file_perms(self):
input_file = os.path.join(self.workdir.name, 'input.yaml')
diff --git a/tests/test_ovs.py b/tests/test_ovs.py
index 393061d..ec33aaa 100644
--- a/tests/test_ovs.py
+++ b/tests/test_ovs.py
@@ -22,6 +22,9 @@ from netplan.cli.ovs import OPENVSWITCH_OVS_VSCTL as OVS
import netplan.cli.ovs as ovs
+from utils import state_from_yaml
+import tempfile
+
class TestOVS(unittest.TestCase):
@@ -118,31 +121,36 @@ Bootstrap: false'''
mock.mock_calls
def test_is_ovs_interface(self):
- interfaces = dict()
- interfaces['ovs0'] = {'openvswitch': {'set-fail-mode': 'secure'}}
- self.assertTrue(ovs.is_ovs_interface('ovs0', interfaces))
+ with tempfile.TemporaryDirectory() as root:
+ state = state_from_yaml(root, '''network:
+ ethernets:
+ ovs0:
+ openvswitch: {}''')
+ self.assertTrue(ovs.is_ovs_interface('ovs0', state.all_defs))
def test_is_ovs_interface_false(self):
- interfaces = dict()
- interfaces['br0'] = {'interfaces': ['eth0', 'eth1']}
- interfaces['eth0'] = {}
- interfaces['eth1'] = {}
- self.assertFalse(ovs.is_ovs_interface('br0', interfaces))
+ with tempfile.TemporaryDirectory() as root:
+ state = state_from_yaml(root, '''network:
+ ethernets:
+ eth0: {}
+ eth1: {}
+ bridges:
+ br0:
+ interfaces:
+ - eth0
+ - eth1''')
+ self.assertFalse(ovs.is_ovs_interface('br0', state.all_defs))
def test_is_ovs_interface_recursive(self):
- interfaces = dict()
- interfaces['patchx'] = {'peer': 'patchy', 'openvswitch': {}}
- interfaces['patchy'] = {'peer': 'patchx', 'openvswitch': {}}
- interfaces['ovs0'] = {'interfaces': ['bond0']}
- interfaces['bond0'] = {'interfaces': ['patchx', 'patchy']}
- self.assertTrue(ovs.is_ovs_interface('ovs0', interfaces))
-
- def test_is_ovs_interface_invalid_key(self):
- interfaces = dict()
- interfaces['ovs0'] = {'openvswitch': {'set-fail-mode': 'secure'}}
- self.assertFalse(ovs.is_ovs_interface('gretap1', interfaces))
-
- def test_is_ovs_interface_special_key(self):
- interfaces = dict()
- interfaces['renderer'] = 'NetworkManager'
- self.assertFalse(ovs.is_ovs_interface('renderer', interfaces))
+ with tempfile.TemporaryDirectory() as root:
+ state = state_from_yaml(root, '''network:
+ version: 2
+ openvswitch:
+ ports:
+ - [patch0-1, patch1-0]
+ ethernets:
+ eth0: {}
+ bonds:
+ bond0:
+ interfaces: [patch1-0, eth0]''')
+ self.assertTrue(ovs.is_ovs_interface('bond0', state.all_defs))
diff --git a/tests/test_sriov.py b/tests/test_sriov.py
index 80b1cd9..12a3d84 100644
--- a/tests/test_sriov.py
+++ b/tests/test_sriov.py
@@ -198,7 +198,7 @@ class TestSRIOV(unittest.TestCase):
pfs = {}
# call the function under test
- sriov.get_vf_count_and_functions(interfaces, self.configmanager,
+ sriov.get_vf_count_and_functions(interfaces, self.configmanager.np_state,
vf_counts, vfs, pfs)
# check if the right vf counts have been recorded in vf_counts
self.assertDictEqual(
@@ -247,7 +247,7 @@ class TestSRIOV(unittest.TestCase):
pfs = {}
# call the function under test
- sriov.get_vf_count_and_functions(interfaces, self.configmanager,
+ sriov.get_vf_count_and_functions(interfaces, self.configmanager.np_state,
vf_counts, vfs, pfs)
# check if the right vf counts have been recorded in vf_counts -
# we expect netplan to take into consideration the renamed interface
@@ -291,7 +291,7 @@ class TestSRIOV(unittest.TestCase):
# call the function under test
with self.assertRaises(ConfigurationError) as e:
- sriov.get_vf_count_and_functions(interfaces, self.configmanager,
+ sriov.get_vf_count_and_functions(interfaces, self.configmanager.np_state,
vf_counts, vfs, pfs)
self.assertIn('matched more than one interface for a PF device: enpx',
@@ -328,7 +328,7 @@ class TestSRIOV(unittest.TestCase):
# call the function under test
with self.assertRaises(ConfigurationError) as e:
- sriov.get_vf_count_and_functions(interfaces, self.configmanager,
+ sriov.get_vf_count_and_functions(interfaces, self.configmanager.np_state,
vf_counts, vfs, pfs)
self.assertIn('more VFs allocated than the explicit size declared: 3 > 2',
@@ -522,7 +522,7 @@ class TestSRIOV(unittest.TestCase):
sriov.apply_sriov_config(self.configmanager, rootdir=self.workdir.name)
# make sure config_manager.parse() has been called
- self.assertTrue(self.configmanager.config)
+ self.assertTrue(self.configmanager.np_state)
# check if the config got applied as expected
# we had 2 PFs, one having two VFs and the other only one
self.assertEqual(set_numvfs.call_count, 2)
@@ -748,7 +748,7 @@ MODALIAS=pci:v00008086d0000156Fsv000017AAsd00002245bc02sc00i00
if args[0].endswith('mlx5_core/bind') or args[0].endswith('mlx5_core/unbind'):
return handle(*args, **kwargs)
# unpatched version for every other path
- return builtin_open(*args, **kwargs)
+ return builtin_open(*args, **kwargs) # pragma: nocover
# set up the mock sysfs environment
self._prepare_sysfs_dir_structure(pf=('enp1', '0000:03:00.0'),
diff --git a/tests/test_utils.py b/tests/test_utils.py
index b958d58..7cf1efc 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -26,6 +26,7 @@ import netifaces
from contextlib import redirect_stdout
from netplan.cli.core import Netplan
import netplan.cli.utils as utils
+import netplan.libnetplan as libnetplan
from unittest.mock import patch
@@ -70,11 +71,11 @@ printf '\\0' >> %(log)s
def set_output(self, output):
with open(self.path, "a") as fp:
- fp.write("cat << EOF\n%s\nEOF" % output)
+ fp.write("\ncat << EOF\n%s\nEOF" % output)
def touch(self, stamp_path):
with open(self.path, "a") as fp:
- fp.write("touch %s\n" % stamp_path)
+ fp.write("\ntouch %s\n" % stamp_path)
def set_timeout(self, timeout_dsec=10):
with open(self.path, "a") as fp:
@@ -94,7 +95,7 @@ fi
def set_returncode(self, returncode):
with open(self.path, "a") as fp:
- fp.write("exit %d" % returncode)
+ fp.write("\nexit %d" % returncode)
def call_cli(args):
@@ -113,10 +114,21 @@ class TestUtils(unittest.TestCase):
def setUp(self):
self.workdir = tempfile.TemporaryDirectory()
- os.makedirs(os.path.join(self.workdir.name, 'etc/netplan'))
+ self.confdir = os.path.join(self.workdir.name, 'etc/netplan')
+ self.default_conf = os.path.join(self.confdir, 'a.yaml')
+ os.makedirs(self.confdir)
os.makedirs(os.path.join(self.workdir.name,
'run/NetworkManager/system-connections'))
+ def load_conf(self, conf_txt):
+ with open(self.default_conf, 'w') as f:
+ f.write(conf_txt)
+ parser = libnetplan.Parser()
+ parser.load_yaml_hierarchy(rootdir=self.workdir.name)
+ state = libnetplan.State()
+ state.import_parser_results(parser)
+ return state
+
def _create_nm_keyfile(self, filename, ifname):
with open(os.path.join(self.workdir.name,
'run/NetworkManager/system-connections/', filename), 'w') as f:
@@ -155,36 +167,70 @@ class TestUtils(unittest.TestCase):
self.assertTrue('ens4' in ifaces)
self.assertTrue(len(ifaces) == 4)
- def test_find_matching_iface_too_many(self):
+ # For the matching tests, we mock out the functions querying extra data
+ @patch('netplan.cli.utils.get_interface_driver_name')
+ @patch('netplan.cli.utils.get_interface_macaddress')
+ def test_find_matching_iface_too_many(self, gim, gidn):
+ gidn.side_effect = lambda x: 'foo' if x == 'ens4' else 'bar'
+ gim.side_effect = lambda x: '00:01:02:03:04:05' if x == 'eth1' else '00:00:00:00:00:00'
+
+ state = self.load_conf('''network:
+ ethernets:
+ netplan-id:
+ match:
+ name: "e*"''')
# too many matches
- iface = utils.find_matching_iface(DEVICES, {'name': 'e*'})
+ iface = utils.find_matching_iface(DEVICES, state['netplan-id'])
self.assertEqual(iface, None)
+ @patch('netplan.cli.utils.get_interface_driver_name')
@patch('netplan.cli.utils.get_interface_macaddress')
- def test_find_matching_iface(self, gim):
+ def test_find_matching_iface(self, gim, gidn):
# we mock-out get_interface_macaddress to return useful values for the test
+ gidn.side_effect = lambda x: 'foo' if x == 'ens4' else 'bar'
gim.side_effect = lambda x: '00:01:02:03:04:05' if x == 'eth1' else '00:00:00:00:00:00'
- match = {'name': 'e*', 'macaddress': '00:01:02:03:04:05'}
- iface = utils.find_matching_iface(DEVICES, match)
+ state = self.load_conf('''network:
+ ethernets:
+ netplan-id:
+ match:
+ name: "e*"
+ macaddress: "00:01:02:03:04:05"''')
+
+ iface = utils.find_matching_iface(DEVICES, state['netplan-id'])
self.assertEqual(iface, 'eth1')
@patch('netplan.cli.utils.get_interface_driver_name')
- def test_find_matching_iface_name_and_driver(self, gidn):
- # we mock-out get_interface_driver_name to return useful values for the test
+ @patch('netplan.cli.utils.get_interface_macaddress')
+ def test_find_matching_iface_name_and_driver(self, gim, gidn):
gidn.side_effect = lambda x: 'foo' if x == 'ens4' else 'bar'
+ gim.side_effect = lambda x: '00:01:02:03:04:05' if x == 'eth1' else '00:00:00:00:00:00'
+
+ state = self.load_conf('''network:
+ ethernets:
+ netplan-id:
+ match:
+ name: "ens?"
+ driver: "f*"''')
- match = {'name': 'ens?', 'driver': 'f*'}
- iface = utils.find_matching_iface(DEVICES, match)
+ iface = utils.find_matching_iface(DEVICES, state['netplan-id'])
self.assertEqual(iface, 'ens4')
@patch('netplan.cli.utils.get_interface_driver_name')
- def test_find_matching_iface_name_and_drivers(self, gidn):
+ @patch('netplan.cli.utils.get_interface_macaddress')
+ def test_find_matching_iface_name_and_drivers(self, gim, gidn):
# we mock-out get_interface_driver_name to return useful values for the test
gidn.side_effect = lambda x: 'foo' if x == 'ens4' else 'bar'
+ gim.side_effect = lambda x: '00:01:02:03:04:05'
- match = {'name': 'ens?', 'driver': ['baz', 'f*', 'quux']}
- iface = utils.find_matching_iface(DEVICES, match)
+ state = self.load_conf('''network:
+ ethernets:
+ netplan-id:
+ match:
+ name: "ens?"
+ driver: ["baz", "f*", "quux"]''')
+
+ iface = utils.find_matching_iface(DEVICES, state['netplan-id'])
self.assertEqual(iface, 'ens4')
@patch('netifaces.ifaddresses')
@@ -215,17 +261,25 @@ class TestUtils(unittest.TestCase):
174 wwan0 wwan off linger''')
res = utils.networkd_interfaces()
self.assertEquals(self.mock_networkctl.calls(), [['networkctl', '--no-pager', '--no-legend']])
- self.assertIn('wlan0', res)
- self.assertIn('ens3', res)
+ self.assertIn('2', res)
+ self.assertIn('3', res)
+
+ def test_networkctl_reload(self):
+ self.mock_networkctl = MockCmd('networkctl')
+ path_env = os.environ['PATH']
+ os.environ['PATH'] = os.path.dirname(self.mock_networkctl.path) + os.pathsep + path_env
+ utils.networkctl_reload()
+ self.assertEquals(self.mock_networkctl.calls(), [
+ ['networkctl', 'reload']
+ ])
def test_networkctl_reconfigure(self):
self.mock_networkctl = MockCmd('networkctl')
path_env = os.environ['PATH']
os.environ['PATH'] = os.path.dirname(self.mock_networkctl.path) + os.pathsep + path_env
- utils.networkctl_reconfigure(['eth0', 'eth1'])
+ utils.networkctl_reconfigure(['3', '5'])
self.assertEquals(self.mock_networkctl.calls(), [
- ['networkctl', 'reload'],
- ['networkctl', 'reconfigure', 'eth0', 'eth1']
+ ['networkctl', 'reconfigure', '3', '5']
])
def test_is_nm_snap_enabled(self):
diff --git a/tests/validate_docs.sh b/tests/validate_docs.sh
index d5ee07a..81cef67 100755
--- a/tests/validate_docs.sh
+++ b/tests/validate_docs.sh
@@ -6,7 +6,7 @@
# sanity check: make sure none have disappeared, as might happen from a reformat.
count=$(sed -n 's/[ ]\+{"\([a-z0-9-]\+\)", YAML_[A-Z]\+_NODE.*/\1/p' src/parse.c | sort | wc -l)
# 144 is based on 0.99+da6f776 definitions, and should be updated periodically.
-if [ $count -lt 144 ]; then
+if [ $count -lt 202 ]; then
echo "ERROR: fewer YAML keys defined in src/parse.c than expected!"
echo " Has the file been reformatted or refactored? If so, modify"
echo " validate_docs.sh appropriately."
@@ -16,13 +16,13 @@ fi
# iterate through the keys
for term in $(sed -n 's/[ ]\+{"\([a-z0-9-]\+\)", YAML_[A-Z]\+_NODE.*/\1/p' src/parse.c | sort | uniq); do
# it can be documented in the following ways.
- # 1. "Properties for device type ``blah:``
- if egrep "## Properties for device type \`\`$term:\`\`" doc/netplan.md > /dev/null; then
+ # 1. "Properties for device type `blah:`
+ if egrep "## Properties for device type \`$term:\`" doc/netplan-yaml.md > /dev/null; then
continue
fi
- # 2. "[blah, ]``blah``[, ``blah2``]: (scalar|bool|...)
- if egrep "\`\`$term\`\`.*\((scalar|bool|mapping|sequence of scalars|sequence of mappings|sequence of sequence of scalars)" doc/netplan.md > /dev/null; then
+ # 2. "[blah, ]**blah**[, **blah2**]: (scalar|bool|...)
+ if egrep "\*\*$term\*\*.*\((scalar|bool|mapping|sequence of scalars|sequence of mappings|sequence of sequence of scalars)" doc/netplan-yaml.md > /dev/null; then
continue
fi