def freeze_unfreeze_xfs(self): """ Freeze and unfreeze xfs, as hack for grub(2) installing """ if not os.path.exists("/usr/bin/xfs_freeze"): return xfs_boot = False xfs_root = False call(["sync"]) with open("/proc/mounts") as mounts_file: mounts = mounts_file.readlines() # We leave a blank space in the end as we want to search # exactly for this mount points boot_mount_point = self.dest_dir + "/boot " root_mount_point = self.dest_dir + " " for line in mounts: if " xfs " in line: if boot_mount_point in line: xfs_boot = True elif root_mount_point in line: xfs_root = True if xfs_boot: boot_mount_point = boot_mount_point.rstrip() call(["xfs_freeze", "-f", boot_mount_point]) call(["xfs_freeze", "-u", boot_mount_point]) if xfs_root: call(["xfs_freeze", "-f", self.dest_dir]) call(["xfs_freeze", "-u", self.dest_dir])
def unmount_all_in_device(device): """ Unmounts all partitions from device """ # Unmount all swap cmd = ["swapon", "--show=NAME", "--noheadings"] swaps = call(cmd) swaps = swaps.split("\n") for name in filter(None, swaps): if "/dev/zram" not in name: call(["swapoff", name]) # Get all mounted devices mount_result = call(["mount"]) mount_result = mount_result.split("\n") # Umount all partitions of device dirs = [] for mount in mount_result: if device in mount: try: directory = mount.split()[0] dirs.append(directory) except IndexError: pass for directory in dirs: unmount(directory)
def unmount_all_in_directory(dest_dir): """ Unmounts all devices that are mounted inside dest_dir """ # Unmount all swap devices cmd = ["swapon", "--show=NAME", "--noheadings"] swaps = call(cmd) if swaps: swaps = swaps.split("\n") for name in filter(None, swaps): if "/dev/zram" not in name: call(["swapoff", name]) # Get all mounted devices mount_result = call(["mount"]).split("\n") # Umount all devices mounted inside dest_dir (if any) dirs = [] for mount in mount_result: if dest_dir in mount: try: directory = mount.split()[2] # Do not unmount dest_dir now (we will do it later) if directory != dest_dir: dirs.append(directory) except IndexError: pass for directory in dirs: unmount(directory) # Now is the time to unmount the device that is mounted in dest_dir (if any) unmount(dest_dir)
def label_fs(fstype, part, label): """ Set filesystem label """ ladic = { 'ext2': 'e2label %(part)s %(label)s', 'ext3': 'e2label %(part)s %(label)s', 'ext4': 'e2label %(part)s %(label)s', 'f2fs': 'blkid -s LABEL -o value %(part)s %(label)s', 'fat': 'mlabel -i %(part)s ::%(label)s', 'fat16': 'mlabel -i %(part)s ::%(label)s', 'fat32': 'mlabel -i %(part)s ::%(label)s', 'ntfs': 'ntfslabel %(part)s %(label)s', 'jfs': 'jfs_tune -L %(label)s %(part)s', 'reiserfs': 'reiserfstune -l %(label)s %(part)s', 'xfs': 'xfs_admin -l %(label)s %(part)s', 'btrfs': 'btrfs filesystem label %(part)s %(label)s', 'swap': 'swaplabel -L %(label)s %(part)s' } fstype = fstype.lower() # OK, the below is a quick cheat. vars() returns all variables # in a dictionary. So 'part' and 'label' will be defined # and replaced in above dic if fstype in ladic: cmd = shlex.split(ladic[fstype] % vars()) call(cmd) else: # Not being able to label a partition shouldn't worry us much logging.warning("Can't label %s (%s) with label %s", part, fstype, label)
def label_fs(fstype, part, label): """ Set filesystem label """ ladic = {'ext2': 'e2label %(part)s %(label)s', 'ext3': 'e2label %(part)s %(label)s', 'ext4': 'e2label %(part)s %(label)s', 'f2fs': 'blkid -s LABEL -o value %(part)s %(label)s', 'fat': 'mlabel -i %(part)s ::%(label)s', 'fat16': 'mlabel -i %(part)s ::%(label)s', 'fat32': 'mlabel -i %(part)s ::%(label)s', 'ntfs': 'ntfslabel %(part)s %(label)s', 'jfs': 'jfs_tune -L %(label)s %(part)s', 'reiserfs': 'reiserfstune -l %(label)s %(part)s', 'xfs': 'xfs_admin -l %(label)s %(part)s', 'btrfs': 'btrfs filesystem label %(part)s %(label)s', 'swap': 'swaplabel -L %(label)s %(part)s'} fstype = fstype.lower() # OK, the below is a quick cheat. vars() returns all variables # in a dictionary. So 'part' and 'label' will be defined # and replaced in above dic if fstype in ladic: cmd = shlex.split(ladic[fstype] % vars()) call(cmd) else: # Not being able to label a partition shouldn't worry us much logging.warning("Can't label %s (%s) with label %s", part, fstype, label)
def get_os_dict(): """ Returns all detected OSes in a dict """ oses = {} tmp_dir = tempfile.mkdtemp() with open("/proc/partitions", 'r') as partitions_file: for line in partitions_file: line_split = line.split() if len(line_split) > 0: device = line_split[3] if "sd" in device and re.search(r'\d+$', device): # ok, it has sd and ends with a number device = "/dev/" + device call(["mount", device, tmp_dir]) oses[device] = _get_os(tmp_dir) call(["umount", "-l", tmp_dir]) if oses[device] == _("unknown"): # As a last resort, try reading partition info # with hexdump oses[device] = _get_partition_info(device) try: os.rmdir(tmp_dir) except OSError: pass return oses
def create_zfs_vol(self, pool_name, vol_name, swap_size=None): """ Creates zfs vol inside the pool if size is given, it should be in GB. If vol_name is "swap" it will be setup as a swap space """ cmd = ["zfs", "create"] if swap_size: # If size is given, mountpoint cannot be set (zfs) # Round up swap_size = math.ceil(swap_size) logging.debug("Creating a zfs vol %s/%s of size %dGB", pool_name, vol_name, swap_size) cmd.extend(["-V", "{0}G".format(swap_size)]) else: logging.debug("Creating a zfs vol %s/%s", pool_name, vol_name) if vol_name == "swap": cmd.extend(["-o", "mountpoint=none"]) else: cmd.extend( ["-o", "mountpoint={0}/{1}".format(DEST_DIR, vol_name)]) cmd.append("{0}/{1}".format(pool_name, vol_name)) call(cmd, fatal=True) if vol_name == "swap": self.create_swap(pool_name, vol_name)
def run_mkconfig(self): """ Create grub.cfg file using grub-mkconfig """ logging.debug("Generating grub.cfg...") # Make sure that /dev and others are mounted (binded). special_dirs.mount(self.dest_dir) # if self.settings.get("zfs"): # # grub-mkconfig does not properly detect the ZFS filesystem, # # so it is necessary to edit grub.cfg manually. # zfs_pool_name = self.settings.get("zfs_pool_name") # grub_cfg_path = os.path.join(self.dest_dir, "boot/grub/grub.cfg") # with open(grub_cfg_path, "w") as grub_cfg: # grub_cfg.write('set timeout=2\n') # grub_cfg.write('set default=0\n\n') # grub_cfg.write('# (0) Antergos Linux\n') # grub_cfg.write('\tmenuentry "Antergos Linux (zfs)" {\n') # # grub_cfg.write('\tsearch --no-floppy --label --set=root {0}\n'.format(zfs_pool_name)) # grub_cfg.write('\tlinux /vmlinuz-linux zfs={0} rw\n'.format(zfs_pool_name)) # grub_cfg.write('\tinitrd /initramfs-linux.img\n') # grub_cfg.write('}\n') # else: # Add -l option to os-prober's umount call so that it does not hang self.apply_osprober_patch() logging.debug("Running grub-mkconfig...") locale = self.settings.get("locale") cmd = 'LANG={0} grub-mkconfig -o /boot/grub/grub.cfg'.format(locale) cmd_sh = ['sh', '-c', cmd] if not chroot_call(cmd_sh, self.dest_dir, timeout=300): msg = ("grub-mkconfig does not respond. Killing grub-mount and" "os-prober so we can continue.") logging.error(msg) call(['killall', 'grub-mount']) call(['killall', 'os-prober'])
def create_zfs_vol(self, pool_name, vol_name, swap_size=None): """ Creates zfs vol inside the pool if size is given, it should be in GB. If vol_name is "swap" it will be setup as a swap space """ cmd = ["zfs", "create"] if swap_size: # If size is given, mountpoint cannot be set (zfs) # Round up swap_size = math.ceil(swap_size) logging.debug("Creating a zfs vol %s/%s of size %dGB", pool_name, vol_name, swap_size) cmd.extend(["-V", "{0}G".format(swap_size)]) else: logging.debug("Creating a zfs vol %s/%s", pool_name, vol_name) if vol_name == "swap": cmd.extend(["-o", "mountpoint=none"]) else: cmd.extend(["-o", "mountpoint={0}/{1}".format(DEST_DIR, vol_name)]) cmd.append("{0}/{1}".format(pool_name, vol_name)) call(cmd, fatal=True) if vol_name == "swap": self.create_swap(pool_name, vol_name)
def setup_luks(luks_device, luks_name, luks_pass=None, luks_key=None): """ Setups a luks device """ if (luks_pass is None or luks_pass == "") and luks_key is None: txt = "Can't setup LUKS in device {0}. A password or a key file are needed".format( luks_device) logging.error(txt) return # For now, we we'll use the same password for root and /home # If instead user wants to use a key file, we'll have two different key files. logging.debug("Cnchi will setup LUKS on device %s", luks_device) # Wipe LUKS header (just in case we're installing on a pre LUKS setup) # For 512 bit key length the header is 2MiB # If in doubt, just be generous and overwrite the first 10MiB or so wrapper.dd("/dev/zero", luks_device, bs=512, count=20480) err_msg = "Can't format and open the LUKS device {0}".format(luks_device) if luks_pass is None or luks_pass == "": # No key password given, let's create a random keyfile wrapper.dd("/dev/urandom", luks_key, bs=1024, count=4) # Set up luks with a keyfile cmd = [ "cryptsetup", "luksFormat", "-q", "-c", "aes-xts-plain", "-s", "512", luks_device, luks_key ] call(cmd, msg=err_msg, fatal=True) cmd = [ "cryptsetup", "luksOpen", luks_device, luks_name, "-q", "--key-file", luks_key ] call(cmd, msg=err_msg, fatal=True) else: # Set up luks with a password key luks_pass_bytes = bytes(luks_pass, 'UTF-8') # https://code.google.com/p/cryptsetup/wiki/Cryptsetup160 # aes-xts-plain # aes-cbc-essiv:sha256 cmd = [ "cryptsetup", "luksFormat", "-q", "-c", "aes-xts-plain64", "-s", "512", "--key-file=-", luks_device ] proc = popen(cmd, msg=err_msg, fatal=True) proc.communicate(input=luks_pass_bytes) cmd = [ "cryptsetup", "luksOpen", luks_device, luks_name, "-q", "--key-file=-" ] proc = popen(cmd, msg=err_msg, fatal=True) proc.communicate(input=luks_pass_bytes)
def do_destroy_zfs_pools(self): """ Try to destroy existing antergos zfs pools """ self.load_existing_pools() for pool_name in self.existing_pools: if "antergos" in pool_name.lower(): pool_id, pool_state = self.existing_pools[pool_name] destroy_cmd = ['zpool', 'destroy', '-f', pool_name] if not call(destroy_cmd, warning=False): destroy_cmd = ['zfs', 'destroy', '-R', '-f', pool_name] call(destroy_cmd, warning=False)
def close_antergos_luks_devices(): """ Close LUKS devices (they may have been left open because of a previous failed installation) """ volumes = ["/dev/mapper/cryptAntergos", "/dev/mapper/cryptAntergosHome"] err_msg = "Can't close already opened LUKS devices" for volume in volumes: if os.path.exists(volume): cmd = ["cryptsetup", "luksClose", volume] call(cmd, msg=err_msg)
def setup_luks(luks_device, luks_name, luks_pass=None, luks_key=None): """ Setups a luks device """ if (luks_pass is None or luks_pass == "") and luks_key is None: txt = "Can't setup LUKS in device {0}. A password or a key file are needed".format(luks_device) logging.error(txt) return # For now, we we'll use the same password for root and /home # If instead user wants to use a key file, we'll have two different key files. logging.debug("Cnchi will setup LUKS on device %s", luks_device) # Wipe LUKS header (just in case we're installing on a pre LUKS setup) # For 512 bit key length the header is 2MiB # If in doubt, just be generous and overwrite the first 10MiB or so wrapper.dd("/dev/zero", luks_device, bs=512, count=20480) err_msg = "Can't format and open the LUKS device {0}".format(luks_device) if luks_pass is None or luks_pass == "": # No key password given, let's create a random keyfile wrapper.dd("/dev/urandom", luks_key, bs=1024, count=4) # Set up luks with a keyfile cmd = [ "cryptsetup", "luksFormat", "-q", "-c", "aes-xts-plain", "-s", "512", luks_device, luks_key] call(cmd, msg=err_msg, fatal=True) cmd = [ "cryptsetup", "luksOpen", luks_device, luks_name, "-q", "--key-file", luks_key] call(cmd, msg=err_msg, fatal=True) else: # Set up luks with a password key luks_pass_bytes = bytes(luks_pass, 'UTF-8') # https://code.google.com/p/cryptsetup/wiki/Cryptsetup160 # aes-xts-plain # aes-cbc-essiv:sha256 cmd = [ "cryptsetup", "luksFormat", "-q", "-c", "aes-xts-plain64", "-s", "512", "--key-file=-", luks_device] proc = popen(cmd, msg=err_msg, fatal=True) proc.communicate(input=luks_pass_bytes) cmd = [ "cryptsetup", "luksOpen", luks_device, luks_name, "-q", "--key-file=-"] proc = popen(cmd, msg=err_msg, fatal=True) proc.communicate(input=luks_pass_bytes)
def close_reborn_luks_devices(): """ Close LUKS devices (they may have been left open because of a previous failed installation) """ volumes = ["/dev/mapper/cryptReborn", "/dev/mapper/cryptRebornHome"] err_msg = "Can't close already opened LUKS devices" for volume in volumes: if os.path.exists(volume): cmd = ["cryptsetup", "luksClose", volume] call(cmd, msg=err_msg)
def init_device(self, device_path, scheme="GPT"): """ Initialize device """ logging.debug("Zapping device %s...", device_path) offset = 20480 # Zero out all GPT and MBR data structures wrapper.sgdisk("zap-all", device_path) # Clear all magic strings/signatures # Wipe out first "offset" sectors wrapper.dd("/dev/zero", device_path, bs=512, count=offset) # Clear the end "offset" sectors of the disk, too. try: seek = int(call(["blockdev", "--getsz", device_path])) - offset wrapper.dd("/dev/zero", device_path, bs=512, count=offset, seek=seek) except ValueError as ex: logging.warning(ex) wrapper.wipefs(device_path, fatal=True) if scheme == "GPT": # Create fresh GPT table wrapper.sgdisk("clear", device_path) # Inform the kernel of the partition change. # Needed if the hard disk had a MBR partition table. call(["partprobe", device_path]) else: # Create fresh MBR table wrapper.parted_mklabel(device_path, "msdos") """ if self.zfs_options["encrypt_disk"]: from installation import auto_partition as ap vol_name = device_path.split("/")[-1] ap.setup_luks( luks_device=device_path, luks_name=vol_name, luks_pass=self.zfs_options["encrypt_password"]) self.settings.set("use_luks", True) """ call(["sync"])
def _hexdump8081(partition): """ Runs hexdump on partition to try to identify the boot sector """ cmd = [ "hexdump", "-v", "-n", "2", "-s", "0x80", "-e", '2/1 "%02x"', partition ] hexdump = call(cmd) return hexdump
def get_part_sizes(self, disk_size, start_part_sizes=1): part_sizes = {'disk': disk_size, 'boot': 256, 'efi': 0} if self.gpt and self.bootloader == "grub2": part_sizes['efi'] = 200 cmd = ["grep", "MemTotal", "/proc/meminfo"] mem_total = call(cmd) mem_total = int(mem_total.split()[1]) mem = mem_total / 1024 # Suggested sizes from Anaconda installer if mem < 2048: part_sizes['swap'] = 2 * mem elif 2048 <= mem < 8192: part_sizes['swap'] = mem elif 8192 <= mem < 65536: part_sizes['swap'] = mem // 2 else: part_sizes['swap'] = 4096 # Max swap size is 10% of all available disk size max_swap = disk_size * 0.1 if part_sizes['swap'] > max_swap: part_sizes['swap'] = max_swap part_sizes['swap'] = math.ceil(part_sizes['swap']) other_than_root_size = start_part_sizes + part_sizes['efi'] + part_sizes['boot'] + part_sizes['swap'] part_sizes['root'] = disk_size - other_than_root_size if self.home: # Decide how much we leave to root and how much we leave to /home # Question: why 5? new_root_part_size = part_sizes['root'] // 5 if new_root_part_size > MAX_ROOT_SIZE: new_root_part_size = MAX_ROOT_SIZE elif new_root_part_size < MIN_ROOT_SIZE: new_root_part_size = MIN_ROOT_SIZE if new_root_part_size >= part_sizes['root']: # new_root_part_size can't be bigger than part_sizes['root'] ! # this could happen if new_root_part_size == MIN_ROOT_SIZE but # our harddisk is smaller (detected using vbox) # Should we fail here or install without a separated /home partition? logging.warning("There's not enough free space to have a separate /home partition") self.home = False part_sizes['home'] = 0 else: part_sizes['home'] = part_sizes['root'] - new_root_part_size part_sizes['root'] = new_root_part_size else: part_sizes['home'] = 0 part_sizes['lvm_pv'] = part_sizes['swap'] + part_sizes['root'] + part_sizes['home'] for part in part_sizes: part_sizes[part] = int(part_sizes[part]) return part_sizes
def get_part_sizes(self, disk_size, start_part_sizes=1): part_sizes = {"disk": disk_size, "boot": 256, "efi": 0} if self.gpt and self.bootloader == "grub2": part_sizes["efi"] = 200 cmd = ["grep", "MemTotal", "/proc/meminfo"] mem_total = call(cmd) mem_total = int(mem_total.split()[1]) mem = mem_total / 1024 # Suggested sizes from Anaconda installer if mem < 2048: part_sizes["swap"] = 2 * mem elif 2048 <= mem < 8192: part_sizes["swap"] = mem elif 8192 <= mem < 65536: part_sizes["swap"] = mem // 2 else: part_sizes["swap"] = 4096 # Max swap size is 10% of all available disk size max_swap = disk_size * 0.1 if part_sizes["swap"] > max_swap: part_sizes["swap"] = max_swap part_sizes["swap"] = math.ceil(part_sizes["swap"]) other_than_root_size = start_part_sizes + part_sizes["efi"] + part_sizes["boot"] + part_sizes["swap"] part_sizes["root"] = disk_size - other_than_root_size if self.home: # Decide how much we leave to root and how much we leave to /home # Question: why 5? new_root_part_size = part_sizes["root"] // 5 if new_root_part_size > MAX_ROOT_SIZE: new_root_part_size = MAX_ROOT_SIZE elif new_root_part_size < MIN_ROOT_SIZE: new_root_part_size = MIN_ROOT_SIZE if new_root_part_size >= part_sizes["root"]: # new_root_part_size can't be bigger than part_sizes['root'] ! # this could happen if new_root_part_size == MIN_ROOT_SIZE but # our harddisk is smaller (detected using vbox) # Should we fail here or install without a separated /home partition? logging.warning("There's not enough free space to have a separate /home partition") self.home = False part_sizes["home"] = 0 else: part_sizes["home"] = part_sizes["root"] - new_root_part_size part_sizes["root"] = new_root_part_size else: part_sizes["home"] = 0 part_sizes["lvm_pv"] = part_sizes["swap"] + part_sizes["root"] + part_sizes["home"] for part in part_sizes: part_sizes[part] = int(part_sizes[part]) return part_sizes
def init_device(self, device_path, scheme="GPT"): """ Initialize device """ logging.debug("Zapping device %s...", device_path) offset = 20480 # Zero out all GPT and MBR data structures wrapper.sgdisk("zap-all", device_path) # Clear all magic strings/signatures # Wipe out first "offset" sectors wrapper.dd("/dev/zero", device_path, bs=512, count=offset) # Clear the end "offset" sectors of the disk, too. try: seek = int(call(["blockdev", "--getsz", device_path])) - offset wrapper.dd("/dev/zero", device_path, bs=512, count=offset, seek=seek) except ValueError as ex: logging.warning(ex) if not wrapper.wipefs(device_path, fatal=False): pname, pid, _n = self.get_pool_id('_', include_offline=True) if self.do_destroy_zfs_pool(): call(["udevadm", "settle"]) call(["sync"]) wrapper.wipefs(device_path, fatal=True) if scheme == "GPT": # Create fresh GPT table wrapper.sgdisk("clear", device_path) # Inform the kernel of the partition change. # Needed if the hard disk had a MBR partition table. call(["partprobe", device_path]) else: # Create fresh MBR table wrapper.parted_mklabel(device_path, "msdos") """ if self.zfs_options["encrypt_disk"]: from installation import auto_partition as ap vol_name = device_path.split("/")[-1] ap.setup_luks( luks_device=device_path, luks_name=vol_name, luks_pass=self.zfs_options["encrypt_password"]) self.settings.set("use_luks", True) """ call(["sync"])
def get_info(part): """ Get partition info using blkid """ ret = '' partdic = {} # Do not try to get extended partition info if part and not misc.is_partition_extended(part): # -c /dev/null means no cache cmd = ['blkid', '-c', '/dev/null', part] call(cmd) for info in ret.split(): if '=' in info: info = info.split('=') partdic[info[0]] = info[1].strip('"') return partdic
def get_type(part): """ Get filesystem type using blkid """ ret = '' if part and not misc.is_partition_extended(part): cmd = ['blkid', '-o', 'value', '-s', 'TYPE', part] ret = call(cmd) return ret
def run_mkconfig(self): """ Create grub.cfg file using grub-mkconfig """ logging.debug("Generating grub.cfg...") # Make sure that /dev and others are mounted (binded). special_dirs.mount(self.dest_dir) # Add -l option to os-prober's umount call so that it does not hang self.apply_osprober_patch() logging.debug("Running grub-mkconfig...") locale = self.settings.get("locale") cmd = "LANG={0} grub-mkconfig -o /boot/grub/grub.cfg".format(locale) cmd_sh = ["sh", "-c", cmd] if not chroot_call(cmd_sh, self.dest_dir, timeout=300): msg = "grub-mkconfig does not respond. Killing grub-mount and" "os-prober so we can continue." logging.error(msg) call(["killall", "grub-mount"]) call(["killall", "os-prober"])
def run_mkconfig(self): """ Create grub.cfg file using grub-mkconfig """ logging.debug("Generating grub.cfg...") # Make sure that /dev and others are mounted (binded). special_dirs.mount(self.dest_dir) # Add -l option to os-prober's umount call so that it does not hang self.apply_osprober_patch() logging.debug("Running grub-mkconfig...") locale = self.settings.get("locale") cmd = 'LANG={0} grub-mkconfig -o /boot/grub/grub.cfg'.format(locale) cmd_sh = ['sh', '-c', cmd] if not chroot_call(cmd_sh, self.dest_dir, timeout=300): msg = ("grub-mkconfig does not respond. Killing grub-mount and" "os-prober so we can continue.") logging.error(msg) call(['killall', 'grub-mount']) call(['killall', 'os-prober'])
def set_keymap(self, layout, variant=None): self.settings.keyboard_layout = layout self.settings.keyboard_variant = variant # setxkbmap sets the keyboard layout for the current X session only cmd = ['setxkbmap', '-layout', layout] if variant: cmd.extend(['-variant', variant]) txt = 'Set keyboard to "{0}" ({1}), variant "{2}" ({3})' txt = txt.format('', layout, '', variant) else: txt = 'Set keyboard to "{0}" ({1})'.format('', layout) try: call(cmd) self.logger.debug(txt) except (OSError, subprocess.CalledProcessError) as setxkbmap_error: self.logger.warning(setxkbmap_error)
def create_zfs_vol(self, pool_name, vol_name, size): """ Creates zfs vol inside the pool size is in GB """ # Round up size = math.ceil(size) logging.debug( "Creating a zfs vol %s/%s of size %dGB", pool_name, vol_name, size) cmd = [ "zfs", "create", "-V", "{0}G".format(size), "-b", str(os.sysconf("SC_PAGE_SIZE")), "-o", "primarycache=metadata", "-o", "checksum=off", "-o", "com.sun:auto-snapshot=false", "{0}/{1}".format(pool_name, vol_name)] call(cmd, fatal=True)
def create_swap(self, pool_name, vol_name): """ mkswap on a zfs zvol """ zvol = "{0}/{1}".format(pool_name, vol_name) cmd = ["zfs", "set", "com.sun:auto-snapshot=false", zvol] call(cmd) cmd = ["zfs", "set", "sync=always", zvol] call(cmd) path = "/dev/zvol/{0}/swap".format(pool_name) if os.path.exists(path): logging.debug("Formatting swap (%s)", path) cmd = ["mkswap", "-f", path] if call(cmd): self.devices["swap"] = path self.fs_devices[path] = "swap" self.mount_devices["swap"] = path else: logging.warning("Can't find %s to create swap on it", path)
def get_info(part): """ Get partition info using blkid """ ret = '' partdic = {} # Do not try to get extended partition info if part and not misc.is_partition_extended(part): # -c /dev/null means no cache cmd = ['blkid', '-c', '/dev/null', part] call(cmd) try: ret = subprocess.check_output(cmd).decode().strip() except subprocess.CalledProcessError as err: logging.warning("Error running %s: %s", err.cmd, err.output) for info in ret.split(): if '=' in info: info = info.split('=') partdic[info[0]] = info[1].strip('"') return partdic
def init_device(self, device_path, scheme="GPT"): """ Initialize device """ logging.debug("Zapping device %s...", device_path) offset = 20480 # Zero out all GPT and MBR data structures wrapper.sgdisk("zap-all", device_path) # Clear all magic strings/signatures # Wipe out first "offset" sectors wrapper.dd("/dev/zero", device_path, bs=512, count=offset) # Clear the end "offset" sectors of the disk, too. try: seek = int(call(["blockdev", "--getsz", device_path])) - offset wrapper.dd("/dev/zero", device_path, bs=512, count=offset, seek=seek) except ValueError as ex: logging.warning(ex) wrapper.wipefs(device_path, fatal=True) if scheme == "GPT": # Create fresh GPT table wrapper.sgdisk("clear", device_path) # Inform the kernel of the partition change. # Needed if the hard disk had a MBR partition table. call(["partprobe", device_path]) else: # Create fresh MBR table wrapper.parted_mklabel(device_path, "msdos") call(["sync"])
def prepare_pacman_keyring(): """ Add gnupg pacman files to installed system """ dirs = ["var/cache/pacman/pkg", "var/lib/pacman"] for pacman_dir in dirs: mydir = os.path.join(DEST_DIR, pacman_dir) os.makedirs(mydir, mode=0o755, exist_ok=True) # Be sure that haveged is running (liveCD) # haveged is a daemon that generates system entropy; this speeds up # critical operations in cryptographic programs such as gnupg # (including the generation of new keyrings) cmd = ["systemctl", "start", "haveged"] call(cmd) # Delete old gnupg files dest_path = os.path.join(DEST_DIR, "etc/pacman.d/gnupg") cmd = ["rm", "-rf", dest_path] call(cmd) os.mkdir(dest_path) # Tell pacman-key to regenerate gnupg files # Initialize the pacman keyring cmd = ["pacman-key", "--init", "--gpgdir", dest_path] call(cmd) # Load the signature keys cmd = ["pacman-key", "--populate", "--gpgdir", dest_path, "archlinux", "antergos"] call(cmd) # path = os.path.join(DEST_DIR, "root/.gnupg/dirmngr_ldapservers.conf") # Run dirmngr # https://bbs.archlinux.org/viewtopic.php?id=190380 with open(os.devnull, "r") as dev_null: cmd = ["dirmngr"] call(cmd, stdin=dev_null)
def load_codes(self): """ Load keyboard codes """ if self.layout is None: return cmd = [ "/usr/share/cnchi/scripts/ckbcomp", "-model", "pc106", "-layout", self.layout] if self.variant: cmd.extend(["-variant", self.variant]) cmd.append("-compact") try: with raised_privileges() as privileged: cfile = call(cmd).split('\n') except subprocess.CalledProcessError as process_error: logging.error( "Error running command %s: %s", process_error.cmd, process_error) return # Clear current codes del self.codes[:] for line in cfile: if line[:7] != "keycode": continue codes = line.split('=')[1].strip().split(' ') plain = unicode_to_string(codes[0]) shift = unicode_to_string(codes[1]) ctrl = unicode_to_string(codes[2]) alt = unicode_to_string(codes[3]) if ctrl == plain: ctrl = "" if alt == plain: alt = "" self.codes.append((plain, shift, ctrl, alt))
def clear_dest_dir(): """ Empties /install """ # Check that /install/boot and /install are not mounted call(["umount", "{0}/boot".format(DEST_DIR)], warning=False) call(["umount", "{0}".format(DEST_DIR)], warning=False) call(["zfs", "umount", "{0}/boot".format(DEST_DIR)], warning=False) call(["zfs", "umount", "{0}".format(DEST_DIR)], warning=False) # Delete /install contents for file_name in os.listdir(DEST_DIR): file_path = os.path.join(DEST_DIR, file_name) try: if os.path.isfile(file_path): os.unlink(file_path) elif os.path.isdir(file_path): shutil.rmtree(file_path) except OSError as err: logging.warning(err)
def has_enough_space(): """ Check that we have a disk or partition with enough space """ output = call(cmd=["lsblk", "-lnb"], debug=False).split("\n") max_size = 0 for item in output: col = item.split() if len(col) >= 5: if col[5] == "disk" or col[5] == "part": size = int(col[3]) if size > max_size: max_size = size if max_size >= MIN_ROOT_SIZE: return True return False
def get_pool_id(pool_name, include_offline=False): """ Get zpool id number """ output = call(["zpool", "import"]) if not output: return None name = identifier = state = None lines = output.split("\n") for line in lines: if "pool:" in line: name = line.split(": ")[1] elif "id:" in line: identifier = line.split(": ")[1] elif "state:" in line: state = line.split(": ")[1] if not name or (name == pool_name and state != "ONLINE" and not include_offline): name = identifier = state = None return name, identifier, state
def get_pool_id(pool_name): """ Get zpool id number """ output = call(["zpool", "import"]) if not output: return None name = identifier = state = None lines = output.split("\n") for line in lines: if "pool:" in line: name = line.split(": ")[1] elif "id:" in line: identifier = line.split(": ")[1] elif "state:" in line: state = line.split(": ")[1] if name == pool_name and state == "ONLINE": return identifier else: name = identifier = state = None return None
def load_existing_pools(self): """ Fills existing_pools dict with pool's name, identifier and status """ output = call(["zpool", "import"]) if not output: return self.existing_pools = {} name = identifier = state = None lines = output.split("\n") for line in lines: if "pool:" in line: # Pool info starts name = line.split(": ")[1] identifier = state = None elif "id:" in line: identifier = line.split(": ")[1] elif "state:" in line: state = line.split(": ")[1] self.existing_pools[name] = (identifier, state)
def get_pknames(): """ PKNAME: internal parent kernel device name """ pknames = {} info = None cmd = ['lsblk', '-o', 'NAME,PKNAME', '-l'] info = call(cmd) if info: info = info.split('\n') # skip header info = info[1:] skip_list = ["disk", "rom", "loop", "arch_root-image"] for line in info: skip_line = False for skip in skip_list: if skip in line: skip_line = True break if not skip_line: line = line.split() try: pknames[line[0]] = line[1] except IndexError as err: pass return pknames
def unmount(directory): """ Unmount """ logging.debug("Unmounting %s", directory) call(["umount", "-l", directory])
def create_zfs_pool(self, pool_name, pool_type, device_paths): """ Create zpool """ if pool_type not in self.pool_types.values(): raise InstallError("Unknown pool type: {0}".format(pool_type)) #for device_path in device_paths: # cmd = ["zpool", "labelclear", device_path] # call(cmd) cmd = ["zpool", "create"] if self.zfs_options["force_4k"]: cmd.extend(["-o", "ashift=12"]) cmd.extend(["-m", DEST_DIR, pool_name]) pool_type = pool_type.lower().replace("-", "") if pool_type in ["none", "stripe"]: # Add first device cmd.append(device_paths[0]) elif pool_type == "mirror": if len(device_paths) > 2 and len(device_paths) % 2 == 0: # Try to mirror pair of devices # (mirrors of two devices each) for i,k in zip(device_paths[0::2], device_paths[1::2]): cmd.append(pool_type) cmd.extend([i, k]) else: cmd.append(pool_type) cmd.extend(device_paths) else: cmd.append(pool_type) cmd.extend(device_paths) # Wait until /dev initialized correct devices call(["udevadm", "settle"]) call(["sync"]) logging.debug("Creating zfs pool %s...", pool_name) if call(cmd, warning=False) is False: # Try again, now with -f cmd.insert(2, "-f") if call(cmd, warning=False) is False: # Wait 10 seconds more and try again. # (Waiting a bit sometimes works) time.sleep(10) call(cmd, fatal=True) # Wait until /dev initialized correct devices call(["udevadm", "settle"]) call(["sync"]) if pool_type == "stripe": # Add the other devices that were left out cmd = ["zpool", "add", pool_name] cmd.extend(device_paths[1:]) call(cmd, fatal=True) logging.debug("Pool %s created.", pool_name)
def install_efi(self): """ Install Grub2 bootloader in a UEFI system """ uefi_arch = "x86_64" spec_uefi_arch = "x64" spec_uefi_arch_caps = "X64" fpath = '/install/boot/efi/EFI/grub_reborn' bootloader_id = 'grub_reborn' if not os.path.exists(fpath) else \ 'grub_reborn_{0}'.format(random_generator()) # grub2 in efi needs efibootmgr if not os.path.exists("/usr/bin/efibootmgr"): txt = _( "Please install efibootmgr package to install Grub2 for x86_64-efi platform." ) logging.warning(txt) txt = _("GRUB(2) will NOT be installed") logging.warning(txt) self.settings.set('bootloader_installation_successful', False) return txt = _("Installing GRUB(2) UEFI {0} boot loader").format(uefi_arch) logging.info(txt) grub_install = [ 'grub-install', '--target={0}-efi'.format(uefi_arch), '--efi-directory=/install/boot/efi', '--bootloader-id={0}'.format(bootloader_id), '--boot-directory=/install/boot', '--recheck' ] load_module = ['modprobe', '-a', 'efivarfs'] call(load_module, timeout=15) call(grub_install, timeout=120) self.install_locales() # Copy grub into dirs known to be used as default by some OEMs # if they do not exist yet. grub_defaults = [ os.path.join(self.dest_dir, "boot/efi/EFI/BOOT", "BOOT{0}.efi".format(spec_uefi_arch_caps)), os.path.join(self.dest_dir, "boot/efi/EFI/Microsoft/Boot", "bootmgfw.efi") ] grub_path = os.path.join(self.dest_dir, "boot/efi/EFI/antergos_grub", "grub{0}.efi".format(spec_uefi_arch)) for grub_default in grub_defaults: path = grub_default.split()[0] if not os.path.exists(path): msg = _("No OEM loader found in %s. Copying Grub(2) into dir.") logging.info(msg, path) os.makedirs(path, mode=0o755) msg_failed = _("Copying Grub(2) into OEM dir failed: %s") try: shutil.copy(grub_path, grub_default) except FileNotFoundError: logging.warning(msg_failed, _("File not found.")) except FileExistsError: logging.warning(msg_failed, _("File already exists.")) except Exception as ex: template = "An exception of type {0} occured. Arguments:\n{1!r}" message = template.format(type(ex).__name__, ex.args) logging.error(message) self.run_mkconfig() paths = [ os.path.join(self.dest_dir, "boot/grub/x86_64-efi/core.efi"), os.path.join(self.dest_dir, "boot/efi/EFI/{0}".format(bootloader_id), "grub{0}.efi".format(spec_uefi_arch)) ] exists = True for path in paths: if not os.path.exists(path): exists = False logging.debug("Path '%s' doesn't exist, when it should", path) if exists: logging.info("GRUB(2) UEFI install completed successfully") self.settings.set('bootloader_installation_successful', True) else: logging.warning( "GRUB(2) UEFI install may not have completed successfully.") self.settings.set('bootloader_installation_successful', False)
def run_format(self): """ Create partitions and file systems """ # https://wiki.archlinux.org/index.php/Installing_Arch_Linux_on_ZFS # https://wiki.archlinux.org/index.php/ZFS#GRUB-compatible_pool_creation device_paths = self.zfs_options["device_paths"] logging.debug("Configuring ZFS in %s", ",".join(device_paths)) # Read all preexisting zfs pools. If there's an antergos one, delete it. self.do_destroy_zfs_pools() # Wipe all disks that will be part of the installation. # This cannot be undone! self.init_device(device_paths[0], self.zfs_options["scheme"]) for device_path in device_paths[1:]: self.init_device(device_path, "GPT") device_path = device_paths[0] solaris_partition_number = -1 self.settings.set('bootloader_device', device_path) if self.zfs_options["scheme"] == "GPT": part_num = 1 if not self.uefi: # BIOS and GPT # Create BIOS Boot Partition # GPT GUID: 21686148-6449-6E6F-744E-656564454649 # This partition is not required if the system is UEFI based, # as there is no such embedding of the second-stage code in that case wrapper.sgdisk_new(device_path, part_num, "BIOS_BOOT", 2, "EF02") part_num += 1 # Create BOOT partition wrapper.sgdisk_new(device_path, part_num, "ANTERGOS_BOOT", 512, "8300") fs.create_fs(device_path + str(part_num), "ext4", "ANTERGOS_BOOT") self.devices['boot'] = "{0}{1}".format(device_path, part_num) self.fs_devices[self.devices['boot']] = "ext4" self.mount_devices['/boot'] = self.devices['boot'] part_num += 1 else: # UEFI and GPT if self.bootloader == "grub2": # Create EFI System Partition (ESP) # GPT GUID: C12A7328-F81F-11D2-BA4B-00A0C93EC93B wrapper.sgdisk_new(device_path, part_num, "UEFI_SYSTEM", 200, "EF00") self.devices['efi'] = "{0}{1}".format( device_path, part_num) self.fs_devices[self.devices['efi']] = "vfat" self.mount_devices['/boot/efi'] = self.devices['efi'] part_num += 1 # Create BOOT partition wrapper.sgdisk_new(device_path, part_num, "ANTERGOS_BOOT", 512, "8300") fs.create_fs(device_path + str(part_num), "ext4", "ANTERGOS_BOOT") self.devices['boot'] = "{0}{1}".format( device_path, part_num) self.fs_devices[self.devices['boot']] = "ext4" self.mount_devices['/boot'] = self.devices['boot'] part_num += 1 else: # systemd-boot, refind # Create BOOT partition wrapper.sgdisk_new(device_path, part_num, "ANTERGOS_BOOT", 512, "EF00") fs.create_fs(device_path + str(part_num), "vfat", "ANTERGOS_BOOT") self.devices['boot'] = "{0}{1}".format( device_path, part_num) self.fs_devices[self.devices['boot']] = "vfat" self.mount_devices['/boot'] = self.devices['boot'] part_num += 1 # The rest of the disk will be of solaris type wrapper.sgdisk_new(device_path, part_num, "ANTERGOS_ZFS", 0, "BF00") solaris_partition_number = part_num self.devices['root'] = "{0}{1}".format(device_path, part_num) # self.fs_devices[self.devices['root']] = "zfs" self.mount_devices['/'] = self.devices['root'] else: # MBR # Create boot partition (all sizes are in MiB) # if start is -1 wrapper.parted_mkpart assumes that our partition # starts at 1 (first partition in disk) start = -1 end = 512 part = "1" wrapper.parted_mkpart(device_path, "primary", start, end) # Set boot partition as bootable wrapper.parted_set(device_path, part, "boot", "on") # Format the boot partition as well as any other system partitions. # Do not do anything to the Solaris partition nor to the BIOS boot # partition. ZFS will manage the first, and the bootloader the # second. if self.uefi: fs_boot = "vfat" else: fs_boot = "ext4" fs.create_fs(device_path + part, fs_boot, "ANTERGOS_BOOT") self.devices['boot'] = "{0}{1}".format(device_path, part) self.fs_devices[self.devices['boot']] = fs_boot self.mount_devices['/boot'] = self.devices['boot'] # The rest of the disk will be of solaris type start = end wrapper.parted_mkpart(device_path, "primary", start, "-1s") solaris_partition_number = 2 self.devices['root'] = "{0}{1}".format(device_path, 2) # self.fs_devices[self.devices['root']] = "zfs" self.mount_devices['/'] = self.devices['root'] # Wait until /dev initialized correct devices call(["udevadm", "settle"]) call(["sync"]) ''' if self.zfs_options["encrypt_disk"]: vol_name = self.devices['root'].split("/")[-1] password = self.zfs_options["encrypt_password"] ap.setup_luks( luks_device=self.devices['root'], luks_name=vol_name, luks_pass=password) self.settings.set("use_luks", True) self.settings.set("luks_root_password", password) self.settings.set("luks_root_device", self.devices['root']) # Encrypt the rest of drives for device_path in device_paths[1:]: vol_name = device_path.split("/")[-1] ap.setup_luks( luks_device=device_path, luks_name=vol_name, luks_pass=password) ''' self.create_zfs(solaris_partition_number)
def wipefs(device, fatal=True): """ Wipe fs from device """ err_msg = "Cannot wipe the filesystem of device {0}".format(device) cmd = ["wipefs", "-a", device] call(cmd, msg=err_msg, fatal=fatal)
def create_zfs(self, solaris_partition_number): """ Setup ZFS system """ # Empty DEST_DIR or zfs pool will fail to mount on it # (this will delete preexisting installing attempts, too) if os.path.exists(DEST_DIR): self.clear_dest_dir() device_paths = self.zfs_options["device_paths"] if not device_paths: txt = _("No devices were selected for the ZFS pool") raise InstallError(txt) # Make sure the ZFS modules are loaded call(["modprobe", "zfs"]) # Using by-id (recommended) does not work atm # https://github.com/zfsonlinux/zfs/issues/3708 # Can't use the whole first disk, just the dedicated zfs partition device_paths[0] = self.get_partition_path(device_paths[0], solaris_partition_number) line = ", ".join(device_paths) logging.debug("Cnchi will create a ZFS pool using %s devices", line) # Just in case... if os.path.exists("/etc/zfs/zpool.cache"): os.remove("/etc/zfs/zpool.cache") try: os.mkdir(DEST_DIR, mode=0o755) except OSError: pass pool_name = self.zfs_options["pool_name"] pool_type = self.zfs_options["pool_type"] if not self.pool_name_is_valid(pool_name): txt = _( "Pool name is invalid. It must contain only alphanumeric characters (a-zA-Z0-9_), " "hyphens (-), colons (:), and/or spaces ( ). Names starting with the letter 'c' " "followed by a number (c[0-9]) are not allowed. The following names are also not " "allowed: 'mirror', 'raidz', 'spare', 'log'.") raise InstallError(txt) # Create zpool self.create_zfs_pool(pool_name, pool_type, device_paths) # Set the mount point of the root filesystem self.set_zfs_mountpoint(pool_name, "/") # Set the bootfs property on the descendant root filesystem so the # boot loader knows where to find the operating system. cmd = ["zpool", "set", "bootfs={0}".format(pool_name), pool_name] call(cmd, fatal=True) # Create zpool.cache file cmd = ["zpool", "set", "cachefile=/etc/zfs/zpool.cache", pool_name] call(cmd, fatal=True) if self.settings.get('use_home'): # Create home zvol logging.debug("Creating zfs subvolume 'home'") self.create_zfs_vol(pool_name, "home") self.set_zfs_mountpoint("{0}/home".format(pool_name), "/home") # ZFS automounts, we have to unmount /install/home and delete it, # otherwise we won't be able to import the zfs pool home_path = "{0}/home".format(DEST_DIR) call(["zfs", "umount", home_path], warning=False) shutil.rmtree(path=home_path, ignore_errors=True) # Create swap zvol (it has to be named "swap") swap_size = self.get_swap_size(pool_name) self.create_zfs_vol(pool_name, "swap", swap_size) # Wait until /dev initialized correct devices call(["udevadm", "settle"]) call(["sync"]) # Export the pool # Makes the kernel to flush all pending data to disk, writes data to # the disk acknowledging that the export was done, and removes all # knowledge that the storage pool existed in the system logging.debug("Exporting pool %s...", pool_name) cmd = ["zpool", "export", "-f", pool_name] call(cmd, fatal=True) # Let's get the id of the pool (to import it) pool_id, _status = self.get_pool_id(pool_name) if not pool_id: # Something bad has happened. Will use the pool name instead. logging.warning("Can't get %s zpool id", pool_name) pool_id = pool_name # Save pool id self.settings.set("zfs_pool_id", pool_id) # Finally, re-import the pool by-id # DEST_DIR must be empty or importing will fail! logging.debug("Importing pool %s (%s)...", pool_name, pool_id) cmd = [ "zpool", "import", "-f", "-d", "/dev/disk/by-id", "-R", DEST_DIR, pool_id ] call(cmd, fatal=True) # Copy created cache file to destination try: dst_dir = os.path.join(DEST_DIR, "etc/zfs") os.makedirs(dst_dir, mode=0o755, exist_ok=True) src = "/etc/zfs/zpool.cache" dst = os.path.join(dst_dir, "zpool.cache") shutil.copyfile(src, dst) except OSError as copy_error: logging.warning(copy_error) # Store hostid hostid = call(["hostid"]) if hostid: hostid_path = os.path.join(DEST_DIR, "etc/hostid") with open(hostid_path, "w") as hostid_file: hostid_file.write("{0}\n".format(hostid))
def install_efi(self): """ Install Grub2 bootloader in a UEFI system """ uefi_arch = "x86_64" spec_uefi_arch = "x64" spec_uefi_arch_caps = "X64" fpath = '/install/boot/efi/EFI/antergos_grub' bootloader_id = 'antergos_grub' if not os.path.exists(fpath) else \ 'antergos_grub_{0}'.format(random_generator()) txt = _("Installing GRUB(2) UEFI {0} boot loader").format(uefi_arch) logging.info(txt) grub_install = [ 'grub-install', '--target={0}-efi'.format(uefi_arch), '--efi-directory=/install/boot/efi', '--bootloader-id={0}'.format(bootloader_id), '--boot-directory=/install/boot', '--recheck'] load_module = ['modprobe', '-a', 'efivarfs'] call(load_module, timeout=15) call(grub_install, timeout=120) self.install_locales() # Copy grub into dirs known to be used as default by some OEMs # if they do not exist yet. grub_defaults = [ os.path.join( self.dest_dir, "boot/efi/EFI/BOOT", "BOOT{0}.efi".format(spec_uefi_arch_caps)), os.path.join( self.dest_dir, "boot/efi/EFI/Microsoft/Boot", "bootmgfw.efi")] grub_path = os.path.join( self.dest_dir, "boot/efi/EFI/antergos_grub", "grub{0}.efi".format(spec_uefi_arch)) for grub_default in grub_defaults: path = grub_default.split()[0] if not os.path.exists(path): msg = _("No OEM loader found in %s. Copying Grub(2) into dir.") logging.info(msg, path) os.makedirs(path, mode=0o755) msg_failed = _("Copying Grub(2) into OEM dir failed: %s") try: shutil.copy(grub_path, grub_default) except FileNotFoundError: logging.warning(msg_failed, _("File not found.")) except FileExistsError: logging.warning(msg_failed, _("File already exists.")) except Exception as ex: template = "An exception of type {0} occured. Arguments:\n{1!r}" message = template.format(type(ex).__name__, ex.args) logging.error(message) self.run_mkconfig() paths = [ os.path.join(self.dest_dir, "boot/grub/x86_64-efi/core.efi"), os.path.join( self.dest_dir, "boot/efi/EFI/{0}".format(bootloader_id), "grub{0}.efi".format(spec_uefi_arch))] exists = True for path in paths: if not os.path.exists(path): exists = False logging.debug("Path '%s' doesn't exist, when it should", path) if exists: logging.info("GRUB(2) UEFI install completed successfully") self.settings.set('bootloader_installation_successful', True) else: logging.warning("GRUB(2) UEFI install may not have completed successfully.") self.settings.set('bootloader_installation_successful', False)
def create_zfs_pool(self, pool_name, pool_type, device_paths): """ Create zpool """ if pool_type not in self.pool_types.values(): raise InstallError("Unknown pool type: {0}".format(pool_type)) #for device_path in device_paths: # cmd = ["zpool", "labelclear", device_path] # call(cmd) cmd = ["zpool", "create"] if self.zfs_options["force_4k"]: cmd.extend(["-o", "ashift=12"]) cmd.extend(["-m", DEST_DIR, pool_name]) pool_type = pool_type.lower().replace("-", "") if pool_type in ["none", "stripe"]: # Add first device cmd.append(device_paths[0]) elif pool_type == "mirror": if len(device_paths) > 2 and len(device_paths) % 2 == 0: # Try to mirror pair of devices # (mirrors of two devices each) for i, k in zip(device_paths[0::2], device_paths[1::2]): cmd.append(pool_type) cmd.extend([i, k]) else: cmd.append(pool_type) cmd.extend(device_paths) else: cmd.append(pool_type) cmd.extend(device_paths) # Wait until /dev initialized correct devices call(["udevadm", "settle"]) call(["sync"]) logging.debug("Creating zfs pool %s...", pool_name) if call(cmd, warning=False) is False: # Try again, now with -f cmd.insert(2, "-f") if call(cmd, warning=False) is False: # Wait 10 seconds more and try again. # (Waiting a bit sometimes works) time.sleep(10) call(cmd, fatal=True) # Wait until /dev initialized correct devices call(["udevadm", "settle"]) call(["sync"]) if pool_type == "stripe": # Add the other devices that were left out cmd = ["zpool", "add", pool_name] cmd.extend(device_paths[1:]) call(cmd, fatal=True) logging.debug("Pool %s created.", pool_name)
def create_fs(part, fstype, label='', other_opts=''): """ Create filesystem using mkfs Set some default options -m 1 reserves one percent for root, because I think five percent is too much on newer bigger drives. Also turn on dir_index for ext. Not sure about other fs opts The return value is tuple. (failed, msg) First arg is False for success, True for fail Second arg is either output from call if successful or exception message error if failure """ if not fstype: logging.error("Cannot make a filesystem of type None in partition %s", part) return False fstype = fstype.lower() comdic = {'ext2': 'mkfs.ext2 -F -q', 'ext3': 'mkfs.ext3 -F -q', 'ext4': 'mkfs.ext4 -F -q', 'f2fs': 'mkfs.f2fs', 'fat': 'mkfs.vfat -F 32', 'fat16': 'mkfs.vfat -F 16', 'fat32': 'mkfs.vfat -F 32', 'vfat': 'mkfs.vfat -F 32', 'ntfs': 'mkfs.ntfs', 'jfs': 'mkfs.jfs -q', 'reiserfs': 'mkfs.reiserfs -q', 'xfs': 'mkfs.xfs -f', 'btrfs': 'mkfs.btrfs -f', 'swap': 'mkswap'} if fstype not in comdic.keys(): logging.error("Unknown filesystem %s for partition %s", fstype, part) return False cmd = comdic[fstype] if label: lbldic = {'ext2': '-L "%(label)s"', 'ext3': '-L "%(label)s"', 'ext4': '-L "%(label)s"', 'f2fs': '-l "%(label)s"', 'fat': '-n "%(label)s"', 'fat16': '-n "%(label)s"', 'fat32': '-n "%(label)s"', 'vfat': '-n "%(label)s"', 'ntfs': '-L "%(label)s"', 'jfs': '-L "%(label)s"', 'reiserfs': '-l "%(label)s"', 'xfs': '-L "%(label)s"', 'btrfs': '-L "%(label)s"', 'swap': '-L "%(label)s"'} cmd += " " + lbldic[fstype] if not other_opts: default_opts = {'ext2': '-m 1', 'ext3': '-m 1 -O dir_index', 'ext4': '-m 1 -O dir_index', 'f2fs': '', 'fat': '', 'fat16': '', 'fat32': '', 'vfat': '', 'ntfs': '', 'jfs': '', 'reiserfs': '', 'btrfs': '', 'xfs': '', 'swap': ''} other_opts = default_opts[fstype] if other_opts: cmd += " %(other_opts)s" cmd += " %(part)s" cmd = shlex.split(cmd % vars()) return call(cmd)