stubbed out rest of storage things

This commit is contained in:
brent s 2019-11-06 03:47:08 -05:00
parent d4de31dd67
commit 7e6736f6a2
8 changed files with 169 additions and 59 deletions

2
README
View File

@ -1,3 +1,5 @@
AIF-NG (Arch Installation Framework, Next Generation) is a means to install Arch Linux (https://www.archlinux.org/) in an unattended and automated fashion. Think of it as something akin to RedHat's Kickstart or Debian's Preseed for Arch Linux.

Be sure to import "aif" rather than importing any submodules directly, as deterministic logic is used to set up virtual names.

See https://aif-ng.io/ for more information about this project.

View File

@ -1,3 +1,4 @@
import re
import uuid
##
import parted # https://www.gnu.org/software/parted/api/index.html
@ -267,3 +268,16 @@ MSDOS_FSTYPE_IDS = ((1, 'Empty', b'\x00'),
(98, 'Linux raid autodetect', b'\xFD'),
(99, 'LANstep', b'\xFE'),
(100, 'BBT', b'\xFF'))
MDADM_SUPPORTED_LEVELS = (0, 1, 4, 5, 6, 10)
MDADM_SUPPORTED_METADATA = ('0', '0.90', '1', '1.0', '1.1', '1.2', 'default', 'ddf', 'imsm')
MDADM_SUPPORTED_LAYOUTS = {5: (re.compile(r'^((left|right)-a?symmetric|[lr][as]|'
r'parity-(fir|la)st|'
r'ddf-(N|zero)-restart|ddf-N-continue)$'),
'left-symmetric'),
6: (re.compile(r'^((left|right)-a?symmetric(-6)?|[lr][as]|'
r'parity-(fir|la)st|'
r'ddf-(N|zero)-restart|ddf-N-continue|'
r'parity-first-6)$'),
None),
10: (re.compile(r'^[nof][0-9]+$'),
None)}

View File

@ -1,3 +1,13 @@
from . import _common
import aif.disk.block as block
import aif.disk.lvm as lvm
import aif.disk.mdadm as mdadm


BlockDev = _common.BlockDev


class LUKS(object):
def __init__(self, partobj):
self.devpath = None
pass

View File

@ -1,3 +1,24 @@
from . import _common
import aif.disk.block as block
import aif.disk.luks as luks
import aif.disk.mdadm as mdadm

BlockDev = _common.BlockDev

_BlockDev = _common.BlockDev


class PV(object):
def __init__(self, partobj):
self.devpath = None
pass


class VG(object):
def __init__(self, vg_xml, lv_objs):
self.devpath = None
pass


class LV(object):
def __init__(self, lv_xml, pv_objs):
pass

View File

@ -1,13 +1,4 @@
try:
import gi
gi.require_version('BlockDev', '2.0')
from gi.repository import BlockDev, GLib
has_mod = True
except ImportError:
# This is ineffecient; the native gobject-introspection module is preferred.
# In Arch, this can be installed via the "extra" repository packages "libblockdev" and "python-gobject".
import subprocess
has_mod = False
import subprocess
##
import aif.disk.block_fallback as block
import aif.disk.luks_fallback as luks

View File

@ -1,3 +1,81 @@
import re
##
import aif.utils
import aif.constants
from . import _common
import aif.disk.block as block
import aif.disk.luks as luks
import aif.disk.lvm as lvm

BlockDev = _common.BlockDev

_BlockDev = _common.BlockDev


_mdblock_size_re = re.compile(r'^(?P<sectors>[0-9]+)\s+'
r'\((?P<GiB>[0-9.]+)\s+GiB\s+'
r'(?P<GB>[0-9.]+)\s+GB\)')
_mdblock_unused_re = re.compile(r'^before=(?P<before>[0-9]+)\s+sectors,'
r'\s+after=(?P<after>[0-9]+)\s+sectors$')
_mdblock_badblock_re = re.compile(r'^(?P<entries>[0-9]+)\s+entries'
r'[A-Za-z\s]+'
r'(?P<offset>[0-9]+)\s+sectors$')


class Member(object):
def __init__(self, member_xml, partobj):
self.xml = member_xml
self.device = partobj
if not isinstance(self.device, (block.Partition,
block.Disk,
Array,
lvm.LV,
luks.LUKS)):
raise ValueError(('partobj must be of type '
'aif.disk.block.Disk, '
'aif.disk.block.Partition, '
'aif.disk.luks.LUKS, '
'aif.disk.lvm.LV, or'
'aif.disk.mdadm.Array'))
self.devpath = self.device.devpath
self.is_superblocked = None
self.superblock = None
self._parseDeviceBlock()

def _parseDeviceBlock(self):
pass

def prepare(self):
pass


class Array(object):
def __init__(self, array_xml, homehost, devpath = None):
self.xml = array_xml
self.id = array_xml.attrib['id']
self.level = int(self.xml.attrib['level'])
if self.level not in aif.constants.MDADM_SUPPORTED_LEVELS:
raise ValueError('RAID level must be one of: {0}'.format(', '.join([str(i)
for i in
aif.constants.MDADM_SUPPORTED_LEVELS])))
self.metadata = self.xml.attrib.get('meta', '1.2')
if self.metadata not in aif.constants.MDADM_SUPPORTED_METADATA:
raise ValueError('Metadata version must be one of: {0}'.format(', '.join(
aif.constants.MDADM_SUPPORTED_METADATA)))

def addMember(self, memberobj):
pass

def create(self):
pass

def start(self, scan = False):
pass

def stop(self):
pass

def updateStatus(self):
pass

def writeConf(self):
pass

View File

@ -10,22 +10,10 @@ import mdstat
import aif.disk.block_fallback as block
import aif.disk.luks_fallback as luks
import aif.disk.lvm_fallback as lvm
import aif.utils
import aif.constants


SUPPORTED_LEVELS = (0, 1, 4, 5, 6, 10)
SUPPORTED_METADATA = ('0', '0.90', '1', '1.0', '1.1', '1.2', 'default', 'ddf', 'imsm')
SUPPORTED_LAYOUTS = {5: (re.compile(r'^((left|right)-a?symmetric|[lr][as]|'
r'parity-(fir|la)st|'
r'ddf-(N|zero)-restart|ddf-N-continue)$'),
'left-symmetric'),
6: (re.compile(r'^((left|right)-a?symmetric(-6)?|[lr][as]|'
r'parity-(fir|la)st|'
r'ddf-(N|zero)-restart|ddf-N-continue|'
r'parity-first-6)$'),
None),
10: (re.compile(r'^[nof][0-9]+$'),
None)}

_mdblock_size_re = re.compile(r'^(?P<sectors>[0-9]+)\s+'
r'\((?P<GiB>[0-9.]+)\s+GiB\s+'
r'(?P<GB>[0-9.]+)\s+GB\)')
@ -35,16 +23,6 @@ _mdblock_badblock_re = re.compile(r'^(?P<entries>[0-9]+)\s+entries'
r'[A-Za-z\s]+'
r'(?P<offset>[0-9]+)\s+sectors$')

def _itTakesTwo(n):
# So dumb.
isPowerOf2 = math.ceil(math.log(n, 2)) == math.floor(math.log(n, 2))
return(isPowerOf2)

def _safeChunks(n):
if (n % 4) != 0:
return(False)
return(True)


class Member(object):
def __init__(self, member_xml, partobj):
@ -151,23 +129,26 @@ class Array(object):
self.xml = array_xml
self.id = array_xml.attrib['id']
self.level = int(self.xml.attrib['level'])
if self.level not in SUPPORTED_LEVELS:
raise ValueError('RAID level must be one of: {0}'.format(', '.join([str(i) for i in SUPPORTED_LEVELS])))
if self.level not in aif.constants.MDADM_SUPPORTED_LEVELS:
raise ValueError('RAID level must be one of: {0}'.format(', '.join([str(i)
for i in
aif.constants.MDADM_SUPPORTED_LEVELS])))
self.metadata = self.xml.attrib.get('meta', '1.2')
if self.metadata not in SUPPORTED_METADATA:
raise ValueError('Metadata version must be one of: {0}'.format(', '.join(SUPPORTED_METADATA)))
if self.metadata not in aif.constants.MDADM_SUPPORTED_METADATA:
raise ValueError('Metadata version must be one of: {0}'.format(', '.join(
aif.constants.MDADM_SUPPORTED_METADATA)))
self.chunksize = int(self.xml.attrib.get('chunkSize', 512))
if self.level in (4, 5, 6, 10):
if not _itTakesTwo(self.chunksize):
if not aif.utils.isPowerofTwo(self.chunksize):
# TODO: log.warn instead of raise exception? Will mdadm lose its marbles if it *isn't* a proper number?
raise ValueError('chunksize must be a power of 2 for the RAID level you specified')
if self.level in (0, 4, 5, 6, 10):
if not _safeChunks(self.chunksize):
if not aif.utils.hasSafeChunks(self.chunksize):
# TODO: log.warn instead of raise exception? Will mdadm lose its marbles if it *isn't* a proper number?
raise ValueError('chunksize must be divisible by 4 for the RAID level you specified')
self.layout = self.xml.attrib.get('layout', 'none')
if self.level in SUPPORTED_LAYOUTS.keys():
matcher, layout_default = SUPPORTED_LAYOUTS[self.level]
if self.level in aif.constants.MDADM_SUPPORTED_LAYOUTS.keys():
matcher, layout_default = aif.constants.MDADM_SUPPORTED_LAYOUTS[self.level]
if not matcher.search(self.layout):
if layout_default:
self.layout = layout_default
@ -190,20 +171,6 @@ class Array(object):
self.members.append(memberobj)
return()

def start(self, scan = False):
if not any((self.members, self.devpath)):
raise RuntimeError('Cannot assemble an array with no members (for hints) or device path')
cmd = ['mdadm', '--assemble', self.devpath]
if not scan:
for m in self.members:
cmd.append(m.devpath)
else:
cmd.append('--scan')
# TODO: logging!
subprocess.run(cmd)
self.state = 'assembled'
return()

def create(self):
if not self.members:
raise RuntimeError('Cannot create an array with no members')
@ -224,6 +191,20 @@ class Array(object):
self.state = 'new'
return()

def start(self, scan = False):
if not any((self.members, self.devpath)):
raise RuntimeError('Cannot assemble an array with no members (for hints) or device path')
cmd = ['mdadm', '--assemble', self.devpath]
if not scan:
for m in self.members:
cmd.append(m.devpath)
else:
cmd.append('--scan')
# TODO: logging!
subprocess.run(cmd)
self.state = 'assembled'
return()

def stop(self):
# TODO: logging
subprocess.run(['mdadm', '--stop', self.devpath])

View File

@ -1,3 +1,4 @@
import math
import os
import re
import subprocess
@ -42,6 +43,18 @@ def hasBin(binary_name):
return(False)


def hasSafeChunks(n):
if (n % 4) != 0:
return(False)
return(True)


def isPowerofTwo(n):
# So dumb.
isPowerOf2 = math.ceil(math.log(n, 2)) == math.floor(math.log(n, 2))
return(isPowerOf2)


def kernelFilesystems():
# I wish there was a better way of doing this.
# https://unix.stackexchange.com/a/98680