From 53f1938a1c33b4d9e333101d1d614803373a6bc5 Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Fri, 6 Dec 2013 21:25:04 +0000 Subject: new: FreeBSD module to support cloud-init on the FBSD10 platform. In its current form its still missing some modules though. Supported: -SSH-keys -growpart -growfs -adduser -powerstate --- cloudinit/config/cc_growpart.py | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'cloudinit/config/cc_growpart.py') diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py index 0dd92a46..81de4c37 100644 --- a/cloudinit/config/cc_growpart.py +++ b/cloudinit/config/cc_growpart.py @@ -22,6 +22,7 @@ import os import os.path import re import stat +import sys from cloudinit import log as logging from cloudinit.settings import PER_ALWAYS @@ -137,6 +138,35 @@ class ResizeGrowPart(object): return (before, get_size(partdev)) +class ResizeGpart(object): + def available(self): + if not os.path.exists('/usr/local/sbin/gpart'): + return False + return True + + def resize(self, diskdev, partnum, partdev): + """ + GPT disks store metadata at the beginning (primary) and at the + end (secondary) of the disk. When launching an image with a + larger disk compared to the original image, the secondary copy + is lost. Thus, the metadata will be marked CORRUPT, and need to + be recovered. + """ + try: + util.subp(["gpart", "recover", diskdev]) + except util.ProcessExecutionError as e: + if e.exit_code != 0: + util.logexc(LOG, "Failed: gpart recover %s", diskdev) + raise ResizeFailedException(e) + + before = get_size(partdev) + try: + util.subp(["gpart", "resize", "-i", partnum, diskdev]) + except util.ProcessExecutionError as e: + util.logexc(LOG, "Failed: gpart resize -i %s %s", partnum, diskdev) + raise ResizeFailedException(e) + + return (before, get_size(partdev)) def get_size(filename): fd = os.open(filename, os.O_RDONLY) @@ -156,6 +186,12 @@ def device_part_info(devpath): bname = os.path.basename(rpath) syspath = "/sys/class/block/%s" % bname + # FreeBSD doesn't know of sysfs so just get everything we need from + # the device, like /dev/vtbd0p2. + if sys.platform.startswith('freebsd'): + m = re.search('^(/dev/.+)p([0-9])$', devpath) + return (m.group(1), m.group(2)) + if not os.path.exists(syspath): raise ValueError("%s had no syspath (%s)" % (devpath, syspath)) @@ -206,7 +242,7 @@ def resize_devices(resizer, devices): "stat of '%s' failed: %s" % (blockdev, e),)) continue - if not stat.S_ISBLK(statret.st_mode): + if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode): info.append((devent, RESIZE.SKIPPED, "device '%s' not a block device" % blockdev,)) continue @@ -281,4 +317,4 @@ def handle(_name, cfg, _cloud, log, _args): # LP: 1212444 FIXME re-order and favor ResizeParted #RESIZERS = (('growpart', ResizeGrowPart),) -RESIZERS = (('growpart', ResizeGrowPart), ('parted', ResizeParted)) +RESIZERS = (('growpart', ResizeGrowPart), ('parted', ResizeParted), ('gpart', ResizeGpart)) -- cgit v1.2.3 From 43b88392a14f6ab9395313353e28e60acc85ab75 Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Sat, 14 Dec 2013 19:11:47 +0000 Subject: change: Use util.system_info(). --- cloudinit/config/cc_growpart.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'cloudinit/config/cc_growpart.py') diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py index 81de4c37..07556f03 100644 --- a/cloudinit/config/cc_growpart.py +++ b/cloudinit/config/cc_growpart.py @@ -22,7 +22,6 @@ import os import os.path import re import stat -import sys from cloudinit import log as logging from cloudinit.settings import PER_ALWAYS @@ -188,7 +187,7 @@ def device_part_info(devpath): # FreeBSD doesn't know of sysfs so just get everything we need from # the device, like /dev/vtbd0p2. - if sys.platform.startswith('freebsd'): + if util.system_info()["platform"].startswith('FreeBSD'): m = re.search('^(/dev/.+)p([0-9])$', devpath) return (m.group(1), m.group(2)) -- cgit v1.2.3 From bd96af406f268e3fe41537125be4cf2dfc9ea5bc Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Sat, 14 Dec 2013 19:14:18 +0000 Subject: new: Touch a reboot-required file to make clear we want a reboot after resizing the partition. --- cloudinit/config/cc_growpart.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'cloudinit/config/cc_growpart.py') diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py index 07556f03..29d8b49b 100644 --- a/cloudinit/config/cc_growpart.py +++ b/cloudinit/config/cc_growpart.py @@ -165,6 +165,10 @@ class ResizeGpart(object): util.logexc(LOG, "Failed: gpart resize -i %s %s", partnum, diskdev) raise ResizeFailedException(e) + # Since growing the FS requires a reboot, make sure we reboot + # first when this module has finished. + open('/var/run/reboot-required', 'a').close() + return (before, get_size(partdev)) def get_size(filename): -- cgit v1.2.3 From 5323320a7251b124c231ba6be25b8583535f1b62 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 23 Jan 2014 16:13:09 -0500 Subject: pep8, use which rather than hard coded path --- cloudinit/config/cc_growpart.py | 6 ++++-- cloudinit/config/cc_power_state_change.py | 7 +++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'cloudinit/config/cc_growpart.py') diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py index 1d3a4412..b81951ad 100644 --- a/cloudinit/config/cc_growpart.py +++ b/cloudinit/config/cc_growpart.py @@ -113,9 +113,10 @@ class ResizeGrowPart(object): return (before, get_size(partdev)) + class ResizeGpart(object): def available(self): - if not os.path.exists('/usr/local/sbin/gpart'): + if not util.which('gpart'): return False return True @@ -138,7 +139,7 @@ class ResizeGpart(object): try: util.subp(["gpart", "resize", "-i", partnum, diskdev]) except util.ProcessExecutionError as e: - util.logexc(LOG, "Failed: gpart resize -i %s %s", partnum, diskdev) + util.logexc(LOG, "Failed: gpart resize -i %s %s", partnum, diskdev) raise ResizeFailedException(e) # Since growing the FS requires a reboot, make sure we reboot @@ -147,6 +148,7 @@ class ResizeGpart(object): return (before, get_size(partdev)) + def get_size(filename): fd = os.open(filename, os.O_RDONLY) try: diff --git a/cloudinit/config/cc_power_state_change.py b/cloudinit/config/cc_power_state_change.py index 6bd14b7e..561c5abd 100644 --- a/cloudinit/config/cc_power_state_change.py +++ b/cloudinit/config/cc_power_state_change.py @@ -30,12 +30,10 @@ frequency = PER_INSTANCE EXIT_FAIL = 254 -# -# Returns the cmdline for the given process id. In Linux we can use procfs for -# this but on BSD there is /usr/bin/procstat. -# def givecmdline(pid): + # Returns the cmdline for the given process id. In Linux we can use procfs + # for this but on BSD there is /usr/bin/procstat. try: # Example output from procstat -c 1 # PID COMM ARGS @@ -50,6 +48,7 @@ def givecmdline(pid): except IOError: return None + def handle(_name, cfg, _cloud, log, _args): try: -- cgit v1.2.3 From 4919cd124e57e82ecfcdaa9bfcbc051c719708e6 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Fri, 24 Jan 2014 15:29:09 -0500 Subject: pylint and long line fixes. This fixes up many long lines to be < 80 chars and some other pylint issues. pylint 1.1 (in trusty) is now complaining about the lazy logging, so I'll clean that up when I touch things. --- cloudinit/config/cc_growpart.py | 3 +- cloudinit/distros/freebsd.py | 47 +++++++++++----------- cloudinit/netinfo.py | 30 ++++++-------- cloudinit/user_data.py | 3 +- cloudinit/util.py | 12 +++--- .../unittests/test_datasource/test_configdrive.py | 2 +- 6 files changed, 49 insertions(+), 48 deletions(-) (limited to 'cloudinit/config/cc_growpart.py') diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py index b81951ad..f52c41f0 100644 --- a/cloudinit/config/cc_growpart.py +++ b/cloudinit/config/cc_growpart.py @@ -223,7 +223,8 @@ def resize_devices(resizer, devices): "stat of '%s' failed: %s" % (blockdev, e),)) continue - if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode): + if (not stat.S_ISBLK(statret.st_mode) and + not stat.S_ISCHR(statret.st_mode)): info.append((devent, RESIZE.SKIPPED, "device '%s' not a block device" % blockdev,)) continue diff --git a/cloudinit/distros/freebsd.py b/cloudinit/distros/freebsd.py index d28860eb..4c0c6d29 100644 --- a/cloudinit/distros/freebsd.py +++ b/cloudinit/distros/freebsd.py @@ -16,15 +16,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import re + from cloudinit import distros from cloudinit import helpers from cloudinit import log as logging -from cloudinit import netinfo from cloudinit import ssh_util from cloudinit import util -from cloudinit.settings import PER_INSTANCE - LOG = logging.getLogger(__name__) @@ -39,26 +38,27 @@ class Distro(distros.Distro): # Updates a key in /etc/rc.conf. def updatercconf(self, key, value): - LOG.debug("updatercconf: %s => %s" % (key, value)) + LOG.debug("updatercconf: %s => %s", key, value) conf = self.loadrcconf() configchanged = False for item in conf: if item == key and conf[item] != value: conf[item] = value - LOG.debug("[rc.conf]: Value %s for key %s needs to be changed" % (value, key)) + LOG.debug("[rc.conf]: Value %s for key %s needs to be changed", + value, key) configchanged = True if configchanged: LOG.debug("Writing new /etc/rc.conf file") - with open('/etc/rc.conf', 'w') as file: + with open('/etc/rc.conf', 'w') as fp: for keyval in conf.items(): - file.write("%s=%s\n" % keyval) + fp.write("%s=%s\n" % keyval) # Load the contents of /etc/rc.conf and store all keys in a dict. def loadrcconf(self): conf = {} - with open("/etc/rc.conf") as file: - for line in file: + with open("/etc/rc.conf") as fp: + for line in fp: tok = line.split('=') conf[tok[0]] = tok[1].rstrip() return conf @@ -75,7 +75,7 @@ class Distro(distros.Distro): sys_hostname = self._read_hostname() return ('rc.conf', sys_hostname) - def _read_hostname(self, default=None): + def _read_hostname(self, filename, default=None): hostname = None try: hostname = self.readrcconf('hostname') @@ -90,17 +90,17 @@ class Distro(distros.Distro): return fqdn return hostname - def _write_hostname(self, your_hostname, out_fn): - self.updatercconf('hostname', your_hostname) + def _write_hostname(self, hostname, filename): + self.updatercconf('hostname', hostname) def create_group(self, name, members): group_add_cmd = ['pw', '-n', name] if util.is_group(name): - LOG.warn("Skipping creation of existing group '%s'" % name) + LOG.warn("Skipping creation of existing group '%s'", name) else: try: util.subp(group_add_cmd) - LOG.info("Created new group %s" % name) + LOG.info("Created new group %s", name) except Exception: util.logexc("Failed to create group %s", name) @@ -111,11 +111,11 @@ class Distro(distros.Distro): "; user does not exist.", member, name) continue util.subp(['pw', 'usermod', '-n', name, '-G', member]) - LOG.info("Added user '%s' to group '%s'" % (member, name)) + LOG.info("Added user '%s' to group '%s'", member, name) def add_user(self, name, **kwargs): if util.is_user(name): - LOG.info("User %s already exists, skipping." % name) + LOG.info("User %s already exists, skipping.", name) return False adduser_cmd = ['pw', 'useradd', '-n', name] @@ -170,7 +170,7 @@ class Distro(distros.Distro): raise e # TODO: - def set_passwd(self, name, **kwargs): + def set_passwd(self, user, passwd, hashed=False): return False def lock_passwd(self, name): @@ -182,7 +182,7 @@ class Distro(distros.Distro): # TODO: def write_sudo_rules(self, name, rules, sudo_file=None): - LOG.debug("[write_sudo_rules] Name: %s" % name) + LOG.debug("[write_sudo_rules] Name: %s", name) def create_user(self, name, **kwargs): self.add_user(name, **kwargs) @@ -217,7 +217,8 @@ class Distro(distros.Distro): origconf = open(loginconf, 'r') for line in origconf: - newconf.write(re.sub('^default:', r'default:lang=%s:' % locale, line)) + newconf.write(re.sub(r'^default:', + r'default:lang=%s:' % locale, line)) newconf.close() origconf.close() # Make a backup of login.conf. @@ -233,14 +234,14 @@ class Distro(distros.Distro): util.logexc("Failed to apply locale %s", locale) copyfile(backupconf, loginconf) - def install_packages(): + def install_packages(self, pkglist): return - def package_command(): + def package_command(self, cmd, args=None, pkgs=None): return - def set_timezone(): + def set_timezone(self, tz): return - def update_package_sources(): + def update_package_sources(self): return diff --git a/cloudinit/netinfo.py b/cloudinit/netinfo.py index 63f720e4..ac3c011f 100644 --- a/cloudinit/netinfo.py +++ b/cloudinit/netinfo.py @@ -44,7 +44,7 @@ def netdev_info(empty=""): # If the output of ifconfig doesn't contain the required info in the # obvious place, use a regex filter to be sure. elif len(toks) > 1: - if re.search("flags=\d+ %r" % (link, source)) + LOG.debug("Creating symbolic link from %r => %r", link, source) os.symlink(source, link) @@ -1444,7 +1445,8 @@ def uptime(): size = ctypes.c_size_t() buf = ctypes.c_int() size.value = ctypes.sizeof(buf) - libc.sysctlbyname("kern.boottime", ctypes.byref(buf), ctypes.byref(size), None, 0) + libc.sysctlbyname("kern.boottime", ctypes.byref(buf), + ctypes.byref(size), None, 0) now = time.time() bootup = buf.value uptime_str = now - bootup @@ -1793,7 +1795,7 @@ def parse_mount(path): (mountoutput, _err) = subp("mount") mount_locs = mountoutput.splitlines() for line in mount_locs: - m = re.search('^(/dev/[\S]+) on (/.*) \((.+), .+, (.+)\)$', line) + m = re.search(r'^(/dev/[\S]+) on (/.*) \((.+), .+, (.+)\)$', line) devpth = m.group(1) mount_point = m.group(2) fs_type = m.group(3) diff --git a/tests/unittests/test_datasource/test_configdrive.py b/tests/unittests/test_datasource/test_configdrive.py index 3c1e8add..1f4a0a0b 100644 --- a/tests/unittests/test_datasource/test_configdrive.py +++ b/tests/unittests/test_datasource/test_configdrive.py @@ -285,7 +285,7 @@ class TestConfigDriveDataSource(MockerTestCase): self.assertEqual(["/dev/vdb", "/dev/zdd"], ds.find_candidate_devs()) - # verify that partitions are considered, but only if they have a label. + # verify that partitions are considered, that have correct label. devs_with_answers = {"TYPE=vfat": ["/dev/sda1"], "TYPE=iso9660": [], "LABEL=config-2": ["/dev/vdb3"]} self.assertEqual(["/dev/vdb3"], -- cgit v1.2.3