diff options
-rw-r--r-- | cloudinit/distros/bsd.py | 6 | ||||
-rw-r--r-- | cloudinit/distros/netbsd.py | 13 | ||||
-rw-r--r-- | cloudinit/distros/openbsd.py | 47 | ||||
-rw-r--r-- | cloudinit/net/__init__.py | 23 | ||||
-rw-r--r-- | cloudinit/net/openbsd.py | 44 | ||||
-rw-r--r-- | cloudinit/net/renderers.py | 5 | ||||
-rw-r--r-- | cloudinit/sources/DataSourceConfigDrive.py | 2 | ||||
-rw-r--r-- | cloudinit/util.py | 31 | ||||
-rw-r--r-- | config/cloud.cfg.tmpl | 16 | ||||
-rw-r--r-- | doc/rtd/topics/network-config.rst | 2 | ||||
-rwxr-xr-x | setup.py | 5 | ||||
-rw-r--r-- | tests/unittests/test_util.py | 19 | ||||
-rwxr-xr-x | tools/build-on-openbsd | 27 | ||||
-rwxr-xr-x | tools/render-cloudcfg | 5 |
14 files changed, 228 insertions, 17 deletions
diff --git a/cloudinit/distros/bsd.py b/cloudinit/distros/bsd.py index c58f897c..efc73d5d 100644 --- a/cloudinit/distros/bsd.py +++ b/cloudinit/distros/bsd.py @@ -114,3 +114,9 @@ class BSD(distros.Distro): def set_timezone(self, tz): distros.set_etc_timezone(tz=tz, tz_file=self._find_tz_file(tz)) + + def apply_locale(self, locale, out_fn=None): + LOG.debug('Cannot set the locale.') + + def apply_network_config_names(self, netconfig): + LOG.debug('Cannot rename network interface.') diff --git a/cloudinit/distros/netbsd.py b/cloudinit/distros/netbsd.py index 96794d76..69d07846 100644 --- a/cloudinit/distros/netbsd.py +++ b/cloudinit/distros/netbsd.py @@ -13,7 +13,13 @@ from cloudinit import util LOG = logging.getLogger(__name__) -class Distro(cloudinit.distros.bsd.BSD): +class NetBSD(cloudinit.distros.bsd.BSD): + """ + Distro subclass for NetBSD. + + (N.B. OpenBSD inherits from this class.) + """ + ci_sudoers_fn = '/usr/pkg/etc/sudoers.d/90-cloud-init-users' group_add_cmd_prefix = ["groupadd"] @@ -115,7 +121,7 @@ class Distro(cloudinit.distros.bsd.BSD): LOG.debug('NetBSD cannot rename network interface.') def _get_pkg_cmd_environ(self): - """Return environment vars used in *BSD package_command operations""" + """Return env vars used in NetBSD package_command operations""" os_release = platform.release() os_arch = platform.machine() e = os.environ.copy() @@ -128,4 +134,7 @@ class Distro(cloudinit.distros.bsd.BSD): pass +class Distro(NetBSD): + pass + # vi: ts=4 expandtab diff --git a/cloudinit/distros/openbsd.py b/cloudinit/distros/openbsd.py new file mode 100644 index 00000000..dbe1f069 --- /dev/null +++ b/cloudinit/distros/openbsd.py @@ -0,0 +1,47 @@ +# Copyright (C) 2019-2020 Gonéri Le Bouder +# +# This file is part of cloud-init. See LICENSE file for license information. + +import os +import platform + +import cloudinit.distros.netbsd +from cloudinit import log as logging +from cloudinit import util + +LOG = logging.getLogger(__name__) + + +class Distro(cloudinit.distros.netbsd.NetBSD): + hostname_conf_fn = '/etc/myname' + + def _read_hostname(self, filename, default=None): + return util.load_file(self.hostname_conf_fn) + + def _write_hostname(self, hostname, filename): + content = hostname + '\n' + util.write_file(self.hostname_conf_fn, content) + + def _get_add_member_to_group_cmd(self, member_name, group_name): + return ['usermod', '-G', group_name, member_name] + + def lock_passwd(self, name): + try: + util.subp(['usermod', '-p', '*', name]) + except Exception: + util.logexc(LOG, "Failed to lock user %s", name) + raise + + def _get_pkg_cmd_environ(self): + """Return env vars used in OpenBSD package_command operations""" + os_release = platform.release() + os_arch = platform.machine() + e = os.environ.copy() + e['PKG_PATH'] = ( + 'ftp://ftp.openbsd.org/pub/OpenBSD/{os_release}/' + 'packages/{os_arch}/').format( + os_arch=os_arch, os_release=os_release) + return e + + +# vi: ts=4 expandtab diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py index 400d7870..67e3d578 100644 --- a/cloudinit/net/__init__.py +++ b/cloudinit/net/__init__.py @@ -334,13 +334,13 @@ def find_fallback_nic(blacklist_drivers=None): """Return the name of the 'fallback' network device.""" if util.is_FreeBSD(): return find_fallback_nic_on_freebsd(blacklist_drivers) - elif util.is_NetBSD(): - return find_fallback_nic_on_netbsd(blacklist_drivers) + elif util.is_NetBSD() or util.is_OpenBSD(): + return find_fallback_nic_on_netbsd_or_openbsd(blacklist_drivers) else: return find_fallback_nic_on_linux(blacklist_drivers) -def find_fallback_nic_on_netbsd(blacklist_drivers=None): +def find_fallback_nic_on_netbsd_or_openbsd(blacklist_drivers=None): values = list(sorted( get_interfaces_by_mac().values(), key=natural_sort_key)) @@ -811,6 +811,8 @@ def get_interfaces_by_mac(): return get_interfaces_by_mac_on_freebsd() elif util.is_NetBSD(): return get_interfaces_by_mac_on_netbsd() + elif util.is_OpenBSD(): + return get_interfaces_by_mac_on_openbsd() else: return get_interfaces_by_mac_on_linux() @@ -857,6 +859,21 @@ def get_interfaces_by_mac_on_netbsd(): return ret +def get_interfaces_by_mac_on_openbsd(): + ret = {} + re_field_match = ( + r"(?P<ifname>\w+).*lladdr\s" + r"(?P<mac>([\da-f]{2}[:-]){5}([\da-f]{2})).*") + (out, _) = util.subp(['ifconfig', '-a']) + if_lines = re.sub(r'\n\s+', ' ', out).splitlines() + for line in if_lines: + m = re.match(re_field_match, line) + if m: + fields = m.groupdict() + ret[fields['mac']] = fields['ifname'] + return ret + + def get_interfaces_by_mac_on_linux(): """Build a dictionary of tuples {mac: name}. diff --git a/cloudinit/net/openbsd.py b/cloudinit/net/openbsd.py new file mode 100644 index 00000000..b9897e90 --- /dev/null +++ b/cloudinit/net/openbsd.py @@ -0,0 +1,44 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +from cloudinit import log as logging +from cloudinit import util +import cloudinit.net.bsd + +LOG = logging.getLogger(__name__) + + +class Renderer(cloudinit.net.bsd.BSDRenderer): + + def write_config(self): + for device_name, v in self.interface_configurations.items(): + if_file = 'etc/hostname.{}'.format(device_name) + fn = util.target_path(self.target, if_file) + if device_name in self.dhcp_interfaces(): + content = 'dhcp\n' + elif isinstance(v, dict): + try: + content = "inet {address} {netmask}\n".format( + address=v['address'], + netmask=v['netmask']) + except KeyError: + LOG.error( + "Invalid static configuration for %s", + device_name) + util.write_file(fn, content) + + def start_services(self, run=False): + if not self._postcmds: + LOG.debug("openbsd generate postcmd disabled") + return + util.subp(['sh', '/etc/netstart'], capture=True) + + def set_route(self, network, netmask, gateway): + if network == '0.0.0.0': + if_file = 'etc/mygate' + fn = util.target_path(self.target, if_file) + content = gateway + '\n' + util.write_file(fn, content) + + +def available(target=None): + return util.is_OpenBSD() diff --git a/cloudinit/net/renderers.py b/cloudinit/net/renderers.py index e4bcae9d..e2de4d55 100644 --- a/cloudinit/net/renderers.py +++ b/cloudinit/net/renderers.py @@ -5,6 +5,7 @@ from . import freebsd from . import netbsd from . import netplan from . import RendererNotFoundError +from . import openbsd from . import sysconfig NAME_TO_RENDERER = { @@ -12,10 +13,12 @@ NAME_TO_RENDERER = { "freebsd": freebsd, "netbsd": netbsd, "netplan": netplan, + "openbsd": openbsd, "sysconfig": sysconfig, } -DEFAULT_PRIORITY = ["eni", "sysconfig", "netplan", "freebsd", "netbsd"] +DEFAULT_PRIORITY = ["eni", "sysconfig", "netplan", "freebsd", + "netbsd", "openbsd"] def search(priority=None, target=None, first=False): diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py index dee8cde4..ae31934b 100644 --- a/cloudinit/sources/DataSourceConfigDrive.py +++ b/cloudinit/sources/DataSourceConfigDrive.py @@ -72,7 +72,7 @@ class DataSourceConfigDrive(openstack.SourceMixin, sources.DataSource): dslist = self.sys_cfg.get('datasource_list') for dev in find_candidate_devs(dslist=dslist): mtype = None - if (util.is_FreeBSD() or util.is_NetBSD()): + if util.is_BSD(): if dev.startswith("/dev/cd"): mtype = "cd9660" try: diff --git a/cloudinit/util.py b/cloudinit/util.py index db60b9d2..541a486b 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -557,6 +557,11 @@ def is_NetBSD(): return system_info()['variant'] == "netbsd" +@lru_cache() +def is_OpenBSD(): + return system_info()['variant'] == "openbsd" + + def get_cfg_option_bool(yobj, key, default=False): if key not in yobj: return default @@ -679,7 +684,7 @@ def system_info(): var = 'suse' else: var = 'linux' - elif system in ('windows', 'darwin', "freebsd", "netbsd"): + elif system in ('windows', 'darwin', "freebsd", "netbsd", "openbsd"): var = system info['variant'] = var @@ -1279,6 +1284,27 @@ def find_devs_with_netbsd(criteria=None, oformat='device', return [] +def find_devs_with_openbsd(criteria=None, oformat='device', + tag=None, no_cache=False, path=None): + out, _err = subp(['sysctl', '-n', 'hw.disknames'], rcs=[0]) + devlist = [] + for entry in out.split(','): + if not entry.endswith(':'): + # ffs partition with a serial, not a config-drive + continue + if entry == 'fd0:': + continue + part_id = 'a' if entry.startswith('cd') else 'i' + devlist.append(entry[:-1] + part_id) + if criteria == "TYPE=iso9660": + devlist = [i for i in devlist if i.startswith('cd')] + elif criteria in ["LABEL=CONFIG-2", "TYPE=vfat"]: + devlist = [i for i in devlist if not i.startswith('cd')] + elif criteria: + LOG.debug("Unexpected criteria: %s", criteria) + return ['/dev/' + i for i in devlist] + + def find_devs_with(criteria=None, oformat='device', tag=None, no_cache=False, path=None): """ @@ -1291,6 +1317,9 @@ def find_devs_with(criteria=None, oformat='device', if is_NetBSD(): return find_devs_with_netbsd(criteria, oformat, tag, no_cache, path) + elif is_OpenBSD(): + return find_devs_with_openbsd(criteria, oformat, + tag, no_cache, path) blk_id_cmd = ['blkid'] options = [] diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl index 50cfbb2f..2b052c0f 100644 --- a/config/cloud.cfg.tmpl +++ b/config/cloud.cfg.tmpl @@ -2,7 +2,7 @@ # The top level settings are used as module # and system configuration. -{% if variant in ["freebsd", "netbsd"] %} +{% if variant.endswith("bsd") %} syslog_fix_perms: root:wheel {% elif variant in ["suse"] %} syslog_fix_perms: root:root @@ -33,7 +33,7 @@ ssh_pwauth: 0 # This will cause the set+update hostname module to not operate (if true) preserve_hostname: false -{% if variant in ["freebsd", "netbsd"] %} +{% if variant.endswith("bsd") %} # This should not be required, but leave it in place until the real cause of # not finding -any- datasources is resolved. datasource_list: ['NoCloud', 'ConfigDrive', 'Azure', 'OpenStack', 'Ec2'] @@ -147,7 +147,7 @@ cloud_final_modules: # (not accessible to handlers/transforms) system_info: # This will affect which distro class gets used -{% if variant in ["amazon", "arch", "centos", "debian", "fedora", "freebsd", "netbsd", "rhel", "suse", "ubuntu"] %} +{% if variant in ["amazon", "arch", "centos", "debian", "fedora", "freebsd", "netbsd", "openbsd", "rhel", "suse", "ubuntu"] %} distro: {{ variant }} {% else %} # Unknown/fallback distro. @@ -238,8 +238,16 @@ system_info: groups: [wheel] sudo: ["ALL=(ALL) NOPASSWD:ALL"] shell: /bin/sh +{% elif variant in ["openbsd"] %} + default_user: + name: openbsd + lock_passwd: True + gecos: OpenBSD + groups: [wheel] + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/ksh {% endif %} -{% if variant in ["freebsd", "netbsd"] %} +{% if variant in ["freebsd", "netbsd", "openbsd"] %} network: renderers: ['{{ variant }}'] {% endif %} diff --git a/doc/rtd/topics/network-config.rst b/doc/rtd/topics/network-config.rst index ba00a889..8eeadebf 100644 --- a/doc/rtd/topics/network-config.rst +++ b/doc/rtd/topics/network-config.rst @@ -197,7 +197,7 @@ supplying an updated configuration in cloud-config. :: system_info: network: - renderers: ['netplan', 'eni', 'sysconfig', 'freebsd', 'netbsd'] + renderers: ['netplan', 'eni', 'sysconfig', 'freebsd', 'netbsd', 'openbsd'] Network Configuration Tools @@ -15,6 +15,7 @@ import os import shutil import sys import tempfile +import platform import setuptools from setuptools.command.install import install @@ -230,7 +231,7 @@ class InitsysInstallData(install): if self.init_system and isinstance(self.init_system, str): self.init_system = self.init_system.split(",") - if len(self.init_system) == 0: + if len(self.init_system) == 0 and not platform.system().endswith('BSD'): self.init_system = ['systemd'] bad = [f for f in self.init_system if f not in INITSYS_TYPES] @@ -274,7 +275,7 @@ data_files = [ (USR + '/share/doc/cloud-init/examples/seed', [f for f in glob('doc/examples/seed/*') if is_f(f)]), ] -if os.uname()[0] not in ['FreeBSD', 'NetBSD']: +if not platform.system().endswith('BSD'): data_files.extend([ (ETC + '/NetworkManager/dispatcher.d/', ['tools/hook-network-manager']), diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py index 5b2eaa69..2900997b 100644 --- a/tests/unittests/test_util.py +++ b/tests/unittests/test_util.py @@ -1171,4 +1171,23 @@ class TestGetProcEnv(helpers.TestCase): my_ppid = os.getppid() self.assertEqual(my_ppid, util.get_proc_ppid(my_pid)) + +@mock.patch('cloudinit.util.subp') +def test_find_devs_with_openbsd(m_subp): + m_subp.return_value = ( + 'cd0:,sd0:630d98d32b5d3759,sd1:,fd0:', '' + ) + devlist = util.find_devs_with_openbsd() + assert devlist == ['/dev/cd0a', '/dev/sd1i'] + + +@mock.patch('cloudinit.util.subp') +def test_find_devs_with_openbsd_with_criteria(m_subp): + m_subp.return_value = ( + 'cd0:,sd0:630d98d32b5d3759,sd1:,fd0:', '' + ) + devlist = util.find_devs_with_openbsd(criteria="TYPE=iso9660") + assert devlist == ['/dev/cd0a'] + + # vi: ts=4 expandtab diff --git a/tools/build-on-openbsd b/tools/build-on-openbsd new file mode 100755 index 00000000..ca028606 --- /dev/null +++ b/tools/build-on-openbsd @@ -0,0 +1,27 @@ +#!/bin/sh + +fail() { echo "FAILED:" "$@" 1>&2; exit 1; } + +# Check dependencies: +depschecked=/tmp/c-i.dependencieschecked +pkgs=" + bash + dmidecode + py3-configobj + py3-jinja2 + py3-jsonschema + py3-oauthlib + py3-requests + py3-setuptools + py3-six + py3-yaml + sudo-- +" +[ -f "$depschecked" ] || pkg_add ${pkgs} || fail "install packages" + +touch $depschecked + +python3 setup.py build +python3 setup.py install -O1 --distro openbsd --skip-build + +echo "Installation completed." diff --git a/tools/render-cloudcfg b/tools/render-cloudcfg index 50f85369..9322b2c3 100755 --- a/tools/render-cloudcfg +++ b/tools/render-cloudcfg @@ -4,8 +4,9 @@ import argparse import os import sys -VARIANTS = ["amazon", "arch", "centos", "debian", "fedora", "freebsd", "netbsd", - "rhel", "suse", "ubuntu", "unknown"] +VARIANTS = ["amazon", "arch", "centos", "debian", "fedora", "freebsd", + "netbsd", "openbsd", "rhel", "suse", "ubuntu", "unknown"] + if "avoid-pep8-E402-import-not-top-of-file": _tdir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) |