summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Williams <codehelp@debian.org>2015-12-31 12:59:01 +0000
committerNeil Williams <codehelp@debian.org>2015-12-31 12:59:01 +0000
commit78f8e6657ba4fef04919dad889257d7f341c035b (patch)
tree9a3f4911d0dfb0f95e6843b9a50fb16b9bc368fd
parentb7c3af89b3cd5c64717f008bb5e59d15752c5725 (diff)
downloadvmdebootstrap-78f8e6657ba4fef04919dad889257d7f341c035b.tar.gz
Add support for systemd-networkd
When masking udev/systemd predictable network interface names, the initramfs must be updated or the mask will not be effective. Add support for systemd-networkd using predictable network interface names - can be extended using customisation scripts.
-rwxr-xr-xbin/vmdebootstrap53
-rw-r--r--vmdebootstrap/base.py11
-rw-r--r--vmdebootstrap/filesystem.py8
-rw-r--r--vmdebootstrap/network.py105
-rw-r--r--yarns/200-fast-tests.yarn7
5 files changed, 138 insertions, 46 deletions
diff --git a/bin/vmdebootstrap b/bin/vmdebootstrap
index 0a08eb9..805b347 100755
--- a/bin/vmdebootstrap
+++ b/bin/vmdebootstrap
@@ -35,8 +35,9 @@ from vmdebootstrap.extlinux import ExtLinux
from vmdebootstrap.codenames import Codenames
from vmdebootstrap.filesystem import Filesystem
from vmdebootstrap.uefi import Uefi
+from vmdebootstrap.network import Networking
-__version__ = '1.3'
+__version__ = '1.4'
# pylint: disable=invalid-name,line-too-long,missing-docstring
@@ -56,6 +57,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
GrubHandler.name: GrubHandler(),
ExtLinux.name: ExtLinux(),
Filesystem.name: Filesystem(),
+ Networking.name: Networking(),
}
def add_settings(self):
@@ -123,8 +125,11 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
self.settings.boolean(['pkglist'], 'Create a list of package names '
'included in the image.')
self.settings.boolean(['no-acpid'], 'do not install the acpid package', default=False)
- self.settings.boolean(['no-update-initramfs'], 'Skip running update-initramfs', default=False)
+ self.settings.boolean(['update-initramfs'],
+ 'Run update-initramfs after customisation', default=True)
self.settings.boolean(['convert-qcow2'], 'Convert final image to qcow2', default=False)
+ self.settings.boolean(['systemd-networkd'], 'Use Predictable Network '
+ 'Interface Names', default=True)
self.settings.boolean(['dry-run'], 'do not build, just test the options', default=False)
def process_args(self, args): # pylint: disable=too-many-branches,too-many-statements
@@ -161,6 +166,13 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
if self.settings['squash'] and self.settings['tarball']:
raise cliapp.AppException(
'Use --squash or --tarball, not both.')
+ if not distro.was_oldstable(datetime.date(2015, 4, 26)):
+ if not self.settings['systemd-networkd'] and\
+ self.settings['no-update-initramfs']:
+ raise cliapp.AppException(
+ 'Disabling systemd-networkd for jessie and later '
+ 'requires updating the initramfs.')
+
uefi = self.handlers[Uefi.name]
oldstable = distro.was_oldstable(datetime.date(2015, 4, 26))
uefi.check_settings(oldstable=oldstable)
@@ -234,7 +246,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
# only append for wheezy (which became oldstable on 2015.04.25)
if distro.was_oldstable(datetime.date(2015, 4, 26)):
base.append_serial_console(rootdir)
- elif self.settings['serial-console']:
+ elif self.settings['serial-console'] and not self.settings['grub']:
base.message("Skipping setting serial console- wheezy only.")
self.optimize_image(rootdir)
@@ -258,7 +270,11 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
base.set_root_password(rootdir)
base.create_users(rootdir)
filesystem.remove_udev_persistent_rules()
- self.setup_networking(rootdir)
+ if distro.was_oldstable(datetime.date(2015, 4, 26)):
+ network.setup_wheezy_networking(rootdir)
+ else:
+ network.setup_networking(rootdir)
+ network.systemd_support(rootdir)
filesystem.configure_apt()
base.customize(rootdir)
cleanup_apt_cache(rootdir)
@@ -462,35 +478,6 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
self.runcmd_unchecked(['dd', 'if=/dev/zero', 'of=' + zeros, 'bs=1M'])
runcmd(['rm', '-f', zeros])
- def setup_networking(self, rootdir):
- base = self.handlers[Base.name]
- base.message('Setting up networking')
- distro = self.handlers[Codenames.name]
- ifc_file = os.path.join(rootdir, 'etc', 'network', 'interfaces')
- ifc_d = os.path.join(rootdir, 'etc', 'network', 'interfaces.d')
-
- # unconditionally write for wheezy (which became oldstable on 2015.04.25)
- if distro.was_oldstable(datetime.date(2015, 4, 26)):
- with open(ifc_file, 'w') as netfile:
- netfile.write('source /etc/network/interfaces.d/*\n')
- elif not os.path.exists(ifc_file):
- with open(ifc_file, 'a') as netfile:
- netfile.write('source-directory /etc/network/interfaces.d\n')
-
- if not os.path.exists(ifc_d):
- os.mkdir(ifc_d)
- ethpath = os.path.join(ifc_d, 'setup')
- with open(ethpath, 'w') as eth:
- eth.write('auto lo\n')
- eth.write('iface lo inet loopback\n')
-
- if self.settings['enable-dhcp']:
- eth.write('\n')
- eth.write('auto eth0\n')
- eth.write('iface eth0 inet dhcp\n')
- # force predictable interface names
- base.mask_udev_predictable_rules(rootdir)
-
def cleanup_system(self):
base = self.handlers[Base.name]
# Clean up after any errors.
diff --git a/vmdebootstrap/base.py b/vmdebootstrap/base.py
index 4ce5236..43d5a3e 100644
--- a/vmdebootstrap/base.py
+++ b/vmdebootstrap/base.py
@@ -204,14 +204,3 @@ class Base(object):
if self.settings['kernel-package']:
packages.append(self.settings['kernel-package'])
return packages
-
- def mask_udev_predictable_rules(self, rootdir):
- """
- This can be reset later but to get networking using immediately
- on boot, the interface we're going to use must be known
- http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/
- """
- self.message('Disabling systemd predictable interface names')
- udev_path = os.path.join(
- 'etc', 'udev', 'rules.d', '80-net-setup-link.rules')
- runcmd(['chroot', rootdir, 'ln', '-s', '/dev/null', udev_path])
diff --git a/vmdebootstrap/filesystem.py b/vmdebootstrap/filesystem.py
index 106fa56..ad7575c 100644
--- a/vmdebootstrap/filesystem.py
+++ b/vmdebootstrap/filesystem.py
@@ -70,11 +70,15 @@ class Filesystem(Base):
runcmd(["chown", "-R", self.settings["owner"], filename])
def update_initramfs(self):
+ if not rootdir:
+ raise cliapp.AppException("rootdir not set")
+ if not os.path.exists(
+ os.path.join(rootdir, 'usr', 'sbin', 'update-initramfs')):
+ self.message("Error: Unable to run update-initramfs.")
+ return
if not self.settings['no-update-initramfs']:
return
rootdir = self.devices['rootdir']
- if not rootdir:
- raise cliapp.AppException("rootdir not set")
cmd = os.path.join('usr', 'sbin', 'update-initramfs')
if os.path.exists(os.path.join(str(rootdir), cmd)):
self.message("Updating the initramfs")
diff --git a/vmdebootstrap/network.py b/vmdebootstrap/network.py
new file mode 100644
index 0000000..740dede
--- /dev/null
+++ b/vmdebootstrap/network.py
@@ -0,0 +1,105 @@
+"""
+ Wrapper for network support
+"""
+# -*- coding: utf-8 -*-
+#
+# network.py
+#
+# Copyright 2015 Neil Williams <codehelp@debian.org>
+#
+# 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; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+#
+
+import os
+from vmdebootstrap.base import (
+ Base,
+ runcmd,
+)
+
+# pylint: disable=missing-docstring
+
+
+class Networking(Base):
+
+ name = 'networking'
+
+ def _write_network_interfaces(self, rootdir, line):
+ self.message('Setting up networking')
+ ifc_d = os.path.join(rootdir, 'etc', 'network', 'interfaces.d')
+ ifc_file = os.path.join(rootdir, 'etc', 'network', 'interfaces')
+ with open(ifc_file, 'w') as netfile:
+ netfile.write(line)
+ if not os.path.exists(ifc_d):
+ os.mkdir(ifc_d)
+ with open(ethpath, 'w') as eth:
+ eth.write('auto lo\n')
+ eth.write('iface lo inet loopback\n')
+ if self.settings['enable-dhcp']:
+ eth.write('\n')
+ eth.write('auto eth0\n')
+ eth.write('iface eth0 inet dhcp\n')
+
+ def setup_wheezy_networking(self, rootdir):
+ """
+ unconditionally write for wheezy
+ (which became oldstable on 2015.04.25)
+ """
+ self._write_network_interfaces(
+ rootdir, 'source /etc/network/interfaces.d/*\n')
+
+ def setup_networking(self, rootdir):
+ self._write_network_interfaces(
+ rootdir, 'source-directory /etc/network/interfaces.d\n')
+
+ def systemd_support(self, rootdir):
+ """
+ Handle the systemd-networkd setting
+ """
+ if self.settings['systemd-networkd']:
+ self.enable_systemd_networkd(rootdir)
+ else:
+ self.mask_udev_predictable_rules(rootdir)
+
+ def mask_udev_predictable_rules(self, rootdir):
+ """
+ This can be reset later but to get networking using eth0
+ immediately upon boot, the interface we're going to use must
+ be known and must update the initramfs after setting up the
+ mask.
+ """
+ self.message('Disabling systemd predictable interface names')
+ udev_path = os.path.join(
+ 'etc', 'udev', 'rules.d', '80-net-setup-link.rules')
+ runcmd(['chroot', rootdir, 'ln', '-s', '/dev/null', udev_path])
+
+ def enable_systemd_networkd(self, rootdir):
+ """
+ Get networking working immediately on boot, allow any en* interface
+ to be enabled by systemd-networkd using DHCP
+ https://coreos.com/os/docs/latest/network-config-with-networkd.html
+ http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/
+ """
+ if not self.settings['enable-dhcp']:
+ return
+ self.message('Enabling systemd-networkd for DHCP')
+ ethpath = os.path.join(rootdir, 'etc', 'systemd', 'network', '99-dhcp.network')
+ with open(ethpath, 'w') as eth:
+ eth.write('[Match]\n')
+ eth.write('Name=en*\n')
+ eth.write('\n[Network]\n')
+ eth.write('DHCP=yes\n')
+ runcmd(['chroot', rootdir, 'systemctl', 'enable', 'systemd-networkd'])
diff --git a/yarns/200-fast-tests.yarn b/yarns/200-fast-tests.yarn
index 796535e..803d09d 100644
--- a/yarns/200-fast-tests.yarn
+++ b/yarns/200-fast-tests.yarn
@@ -167,3 +167,10 @@ verify that vmdebootstrap parses the command line correctly.
... --squash=FOO --tarball=FOO --arch=amd64 --dry-run
THEN vmdebootstrap exited with a non-zero exit code
AND vmdebootstrap wrote an error message matching not both
+
+ SCENARIO masking systemd-networkd without updating initramfs
+ ASSUMING fast tests are requested
+ WHEN user attempts to run vmdebootstrap
+ ... --image=FOO --no-systemd-networkd --no-update-initramfs --dry-run
+ THEN vmdebootstrap exited with a non-zero exit code
+ AND vmdebootstrap wrote an error message matching requires updating the