Skip to content

Instantly share code, notes, and snippets.

@flaf
Last active October 28, 2021 22:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save flaf/c2402cdbae190a35809f786e8c5044a0 to your computer and use it in GitHub Desktop.
Save flaf/c2402cdbae190a35809f786e8c5044a0 to your computer and use it in GitHub Desktop.
cloud-init: bad support of "networking config version 2"

TL;DR

  1. It seems that the device ID (id0 in my example) is always used in the rendered configuration, but a udev rule is added to rename this interface to the device ID. The only way where the device ID is not used for me it's when I use set-name: <ifname> to force another name for the interface.
  2. A match: rule with the name property doesn't work for me, in any case, with a fixed name or with globbing in name. The doc says that globbing in name can work only with networkd renderer (ie --output-kind networkd), but for me name has never worked in any case (the device ID is used and no udev rule is present).

Here is my environment:

root@flaf-ubuntu-tmp:~# lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.3 LTS
Release:	20.04
Codename:	focal

root@flaf-ubuntu-tmp:~# cloud-init --version
/usr/bin/cloud-init 21.3-1-g6803368d-0ubuntu1~20.04.4

root@flaf-ubuntu-tmp:~# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 00:50:56:a4:a2:c0 brd ff:ff:ff:ff:ff:ff

I use this metadata file to test a basic network configuration:

network:
  version: 2
  ethernets:
    id0: # because `match:` is present, id0 is just an internal ID.
      match:
        name: ens*
      dhcp4: yes

I use match: but this config just says "set ens192 in dhcp configuration".

If I test this metadata for centos/sysconfig or debian/eni, the config is bad because cloud-init thinks (wrongly) that id0 is the name of an interface:

root@flaf-ubuntu-tmp:~# cat /tmp/network-data.yml 
network:
  version: 2
  ethernets:
    id0:
      match:
        name: ens*
      dhcp4: yes

root@flaf-ubuntu-tmp:~# rm -rf /tmp/network-conf/ && \
    python3 /usr/lib/python3/dist-packages/cloudinit/cmd/devel/net_convert.py \
    --network-data /tmp/network-data.yml --kind yaml --directory /tmp/network-conf/ \
    --distro centos --output-kind sysconfig

Read input format 'yaml' from '/tmp/network-data.yml'.
Wrote output format 'sysconfig' to '/tmp/network-conf/'

root@flaf-ubuntu-tmp:~# LC_ALL=C tree /tmp/network-conf/
/tmp/network-conf/
`-- etc
    |-- sysconfig
    |   |-- network
    |   `-- network-scripts
    |       `-- ifcfg-id0
    `-- udev
        `-- rules.d
            `-- 70-persistent-net.rules

5 directories, 3 files
root@flaf-ubuntu-tmp:~# cat /tmp/network-conf/etc/sysconfig/network-scripts/ifcfg-id0 
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=dhcp
DEVICE=id0
NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no


root@flaf-ubuntu-tmp:~# rm -rf /tmp/network-conf/ && \
    python3 /usr/lib/python3/dist-packages/cloudinit/cmd/devel/net_convert.py
    --network-data /tmp/network-data.yml --kind yaml --directory /tmp/network-conf/ \
    --distro debian --output-kind eni

Read input format 'yaml' from '/tmp/network-data.yml'.
Wrote output format 'eni' to '/tmp/network-conf/'

root@flaf-ubuntu-tmp:~# LC_ALL=C tree /tmp/network-conf/
/tmp/network-conf/
`-- etc
    |-- network
    |   `-- interfaces.d
    |       `-- 50-cloud-init
    `-- udev
        `-- rules.d
            `-- 70-persistent-net.rules

5 directories, 2 files
root@flaf-ubuntu-tmp:~# cat /tmp/network-conf/etc/network/interfaces.d/50-cloud-init 
# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
auto lo
iface lo inet loopback

auto id0
iface id0 inet dhcp

Have I missed something or is it a bug of cloud-init concerning the support of the "networking config version 2" support?

I have just tried to follow this doc.

Thanks for you help.

@flaf
Copy link
Author

flaf commented Oct 28, 2021

Another example without globbing:

root@flaf-ubuntu-tmp:~# cat /tmp/network-data.yml 
network:
  version: 2
  ethernets:
    id0:
      match:
        name: ens192
      dhcp4: yes

root@flaf-ubuntu-tmp:~# rm -rf /tmp/network-conf/ && python3 \
    /usr/lib/python3/dist-packages/cloudinit/cmd/devel/net_convert.py \
    --network-data /tmp/network-data.yml --kind yaml --directory /tmp/network-conf/ \
--distro debian --output-kind eni

Read input format 'yaml' from '/tmp/network-data.yml'.
Wrote output format 'eni' to '/tmp/network-conf/'

root@flaf-ubuntu-tmp:~# LC_ALL=C tree /tmp/network-conf/
/tmp/network-conf/
`-- etc
    |-- network
    |   `-- interfaces.d
    |       `-- 50-cloud-init
    `-- udev
        `-- rules.d
            `-- 70-persistent-net.rules

5 directories, 2 files

root@flaf-ubuntu-tmp:~# cat /tmp/network-conf/etc/network/interfaces.d/50-cloud-init 
# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
auto lo
iface lo inet loopback

auto id0
iface id0 inet dhcp

@flaf
Copy link
Author

flaf commented Oct 28, 2021

Another example with --output-kind networkd:

root@flaf-ubuntu-tmp:~# cat /tmp/network-data.yml 
network:
  version: 2
  ethernets:
    id0:
      match:
        name: ens*
      dhcp4: yes

root@flaf-ubuntu-tmp:~# rm -rf /tmp/network-conf/ && \
    python3 /usr/lib/python3/dist-packages/cloudinit/cmd/devel/net_convert.py \
    --network-data /tmp/network-data.yml --kind yaml --directory /tmp/network-conf/ \
    --distro ubuntu --output-kind networkd
Read input format 'yaml' from '/tmp/network-data.yml'.
Wrote output format 'networkd' to '/tmp/network-conf/'

root@flaf-ubuntu-tmp:~# LC_ALL=C tree /tmp/network-conf/
/tmp/network-conf/
`-- etc
    `-- systemd
        `-- network
            `-- 10-cloud-init-id0.network

3 directories, 1 file
root@flaf-ubuntu-tmp:~# cat /tmp/network-conf/etc/systemd/network/10-cloud-init-id0.network 
[Match]
Name=id0

[Network]
DHCP=ipv4

@flaf
Copy link
Author

flaf commented Oct 28, 2021

Another example with match: via macaddress and with debian/eni renderer:

root@flaf-ubuntu-tmp:~# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 00:50:56:a4:a2:c0 brd ff:ff:ff:ff:ff:ff

root@flaf-ubuntu-tmp:~# cat /tmp/network-data.yml 
network:
  version: 2
  ethernets:
    id0:
      match:
        #name: ens*
        macaddress: "00:50:56:A4:A2:C0"
      dhcp4: yes

root@flaf-ubuntu-tmp:~# rm -rf /tmp/network-conf/ && python3 /usr/lib/python3/dist-packages/cloudinit/cmd/devel/net_convert.py\
    --network-data /tmp/network-data.yml --kind yaml --directory /tmp/network-conf/ \
    --distro debian --output-kind eni
Read input format 'yaml' from '/tmp/network-data.yml'.
Wrote output format 'eni' to '/tmp/network-conf/'

root@flaf-ubuntu-tmp:~# LC_ALL=C tree /tmp/network-conf/
/tmp/network-conf/
`-- etc
    |-- network
    |   `-- interfaces.d
    |       `-- 50-cloud-init
    `-- udev
        `-- rules.d
            `-- 70-persistent-net.rules

5 directories, 2 files

root@flaf-ubuntu-tmp:~# cat /tmp/network-conf/etc/network/interfaces.d/50-cloud-init 
# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
auto lo
iface lo inet loopback

auto id0
iface id0 inet dhcp

@flaf
Copy link
Author

flaf commented Oct 28, 2021

  1. It seems that the device ID is always used but a udev rule is added to rename this interface. The only way where the device ID is not used for me it's when I use set-name: <ifname>.
  2. A match: rule with name property doesn't work for me, with a fixed name or with globbing. The doc says that globbing in name can work only with networkd renderer, but for me name has never worked in any case.
root@flaf-ubuntu-tmp:~# cat /tmp/network-data.yml && rm -rf /tmp/network-conf/ && python3 /usr/lib/python3/dist-packages/cloudinit/cmd/devel/net_convert.py --network-data /tmp/network-data.yml --kind yaml --directory /tmp/network-conf/ --distro rhel --output-kind sysconfig; tree -s /tmp/network-conf/; for f in $(find /tmp/network-conf/ -type f); do echo ==== "$f" ====; cat $f; done
network:
  version: 2
  ethernets:
    eth0:
      match:
        macaddress: "00:50:56:A4:A2:C0"
      dhcp4: yes
Read input format 'yaml' from '/tmp/network-data.yml'.
Wrote output format 'sysconfig' to '/tmp/network-conf/'

/tmp/network-conf/
└── [       4096]  etc
    ├── [       4096]  sysconfig
    │   ├── [         86]  network
    │   └── [       4096]  network-scripts
    │       └── [        176]  ifcfg-eth0
    └── [       4096]  udev
        └── [       4096]  rules.d
            └── [         96]  70-persistent-net.rules

5 directories, 3 files
==== /tmp/network-conf/etc/sysconfig/network-scripts/ifcfg-eth0 ====
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=dhcp
DEVICE=eth0
HWADDR=00:50:56:A4:A2:C0
NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
==== /tmp/network-conf/etc/sysconfig/network ====
# Created by cloud-init on instance boot automatically, do not edit.
#
NETWORKING=yes
==== /tmp/network-conf/etc/udev/rules.d/70-persistent-net.rules ====
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:50:56:A4:A2:C0", NAME="eth0"
root@flaf-ubuntu-tmp:~# cat /tmp/network-data.yml && echo ====== && rm -rf /tmp/network-conf/ && python3 /usr/lib/python3/dist-packages/cloudinit/cmd/devel/net_convert.py --network-data /tmp/network-data.yml --kind yaml --directory /tmp/network-conf/ --distro rhel --output-kind sysconfig; tree -s /tmp/network-conf/; for f in $(find /tmp/network-conf/ -type f); do echo ==== "$f" ====; cat $f; done
root@flaf-ubuntu-tmp:~# cat /tmp/network-data.yml && echo ====== && rm -rf /tmp/network-conf/ && python3 /usr/lib/python3/dist-packages/cloudinit/cmd/devel/net_convert.py --network-data /tmp/network-data.yml --kind yaml --directory /tmp/network-conf/ --distro rhel --output-kind sysconfig; tree -s /tmp/network-conf/; for f in $(find /tmp/network-conf/ -type f); do echo ==== "$f" ====; cat $f; done
network:
  version: 2
  ethernets:
    eth0:
      match:
        macaddress: "00:50:56:A4:A2:C0"
      dhcp4: yes
======
Read input format 'yaml' from '/tmp/network-data.yml'.
Wrote output format 'sysconfig' to '/tmp/network-conf/'

/tmp/network-conf/
└── [       4096]  etc
    ├── [       4096]  sysconfig
    │   ├── [         86]  network
    │   └── [       4096]  network-scripts
    │       └── [        176]  ifcfg-eth0
    └── [       4096]  udev
        └── [       4096]  rules.d
            └── [         96]  70-persistent-net.rules

5 directories, 3 files
==== /tmp/network-conf/etc/sysconfig/network-scripts/ifcfg-eth0 ====
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=dhcp
DEVICE=eth0
HWADDR=00:50:56:A4:A2:C0
NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
==== /tmp/network-conf/etc/sysconfig/network ====
# Created by cloud-init on instance boot automatically, do not edit.
#
NETWORKING=yes
==== /tmp/network-conf/etc/udev/rules.d/70-persistent-net.rules ====
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:50:56:A4:A2:C0", NAME="eth0"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment