1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
import logging
import platform
import re
from typing import List, Optional
import cloudinit.net.netops.bsd_netops as bsd_netops
from cloudinit import distros, helpers, net, subp, util
from cloudinit.distros import PackageList, bsd_utils
from cloudinit.distros.networking import BSDNetworking
LOG = logging.getLogger(__name__)
class BSD(distros.Distro):
networking_cls = BSDNetworking
hostname_conf_fn = "/etc/rc.conf"
rc_conf_fn = "/etc/rc.conf"
shadow_fn = "/etc/master.passwd"
default_owner = "root:wheel"
# This differs from the parent Distro class, which has -P for
# poweroff.
shutdown_options_map = {"halt": "-H", "poweroff": "-p", "reboot": "-r"}
# Set in BSD distro subclasses
group_add_cmd_prefix: List[str] = []
pkg_cmd_install_prefix: List[str] = []
pkg_cmd_remove_prefix: List[str] = []
# There is no update/upgrade on OpenBSD
pkg_cmd_update_prefix: Optional[List[str]] = None
pkg_cmd_upgrade_prefix: Optional[List[str]] = None
net_ops = bsd_netops.BsdNetOps
def __init__(self, name, cfg, paths):
super().__init__(name, cfg, paths)
# This will be used to restrict certain
# calls from repeatedly happening (when they
# should only happen say once per instance...)
self._runner = helpers.Runners(paths)
cfg["ssh_svcname"] = "sshd"
cfg["rsyslog_svcname"] = "rsyslogd"
self.osfamily = platform.system().lower()
self.net_ops = bsd_netops.BsdNetOps
self.is_linux = False
def _unpickle(self, ci_pkl_version: int) -> None:
super()._unpickle(ci_pkl_version)
# this needs to be after the super class _unpickle to override it
self.is_linux = False
def _read_system_hostname(self):
sys_hostname = self._read_hostname(self.hostname_conf_fn)
return (self.hostname_conf_fn, sys_hostname)
def _read_hostname(self, filename, default=None):
return bsd_utils.get_rc_config_value("hostname")
def _get_add_member_to_group_cmd(self, member_name, group_name):
raise NotImplementedError("Return list cmd to add member to group")
def _write_hostname(self, hostname, filename):
bsd_utils.set_rc_config_value("hostname", hostname, fn="/etc/rc.conf")
def create_group(self, name, members=None):
if util.is_group(name):
LOG.warning("Skipping creation of existing group '%s'", name)
else:
group_add_cmd = self.group_add_cmd_prefix + [name]
try:
subp.subp(group_add_cmd)
LOG.info("Created new group %s", name)
except Exception:
util.logexc(LOG, "Failed to create group %s", name)
if not members:
members = []
for member in members:
if not util.is_user(member):
LOG.warning(
"Unable to add group member '%s' to group '%s'"
"; user does not exist.",
member,
name,
)
continue
try:
subp.subp(self._get_add_member_to_group_cmd(member, name))
LOG.info("Added user '%s' to group '%s'", member, name)
except Exception:
util.logexc(
LOG, "Failed to add user '%s' to group '%s'", member, name
)
def generate_fallback_config(self):
nconf = {"config": [], "version": 1}
for mac, name in net.get_interfaces_by_mac().items():
nconf["config"].append(
{
"type": "physical",
"name": name,
"mac_address": mac,
"subnets": [{"type": "dhcp"}],
}
)
return nconf
def install_packages(self, pkglist: PackageList):
self.update_package_sources()
self.package_command("install", pkgs=pkglist)
def _get_pkg_cmd_environ(self):
"""Return environment vars used in *BSD package_command operations"""
raise NotImplementedError("BSD subclasses return a dict of env vars")
def package_command(self, command, args=None, pkgs=None):
if pkgs is None:
pkgs = []
if command == "install":
cmd = self.pkg_cmd_install_prefix
elif command == "remove":
cmd = self.pkg_cmd_remove_prefix
elif command == "update":
if not self.pkg_cmd_update_prefix:
return
cmd = self.pkg_cmd_update_prefix
elif command == "upgrade":
if not self.pkg_cmd_upgrade_prefix:
return
cmd = self.pkg_cmd_upgrade_prefix
else:
cmd = []
if args and isinstance(args, str):
cmd.append(args)
elif args and isinstance(args, list):
cmd.extend(args)
pkglist = util.expand_package_list("%s-%s", pkgs)
cmd.extend(pkglist)
# Allow the output of this to flow outwards (ie not be captured)
subp.subp(cmd, update_env=self._get_pkg_cmd_environ(), capture=False)
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 chpasswd(self, plist_in: list, hashed: bool):
for name, password in plist_in:
self.set_passwd(name, password, hashed=hashed)
@staticmethod
def get_proc_ppid(pid):
"""
Return the parent pid of a process by checking ps
"""
ppid, _ = subp.subp(["ps", "-oppid=", "-p", str(pid)])
return int(ppid.strip())
@staticmethod
def get_mapped_device(blockdev: str) -> Optional[str]:
return None
@staticmethod
def device_part_info(devpath: str) -> tuple:
# FreeBSD doesn't know of sysfs so just get everything we need from
# the device, like /dev/vtbd0p2.
part = util.find_freebsd_part(devpath)
if part:
fpart = f"/dev/{part}"
# Handle both GPT partitions and MBR slices with partitions
m = re.search(
r"^(?P<dev>/dev/.+)[sp](?P<part_slice>\d+[a-z]*)$", fpart
)
if m:
return m["dev"], m["part_slice"]
# the input is bogus and we need to bail
raise ValueError(f"Invalid value for devpath: '{devpath}'")
|