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. 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. See https://aif-ng.io/ for more information about this project.

View File

@ -1,3 +1,4 @@
import re
import uuid import uuid
## ##
import parted # https://www.gnu.org/software/parted/api/index.html 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'), (98, 'Linux raid autodetect', b'\xFD'),
(99, 'LANstep', b'\xFE'), (99, 'LANstep', b'\xFE'),
(100, 'BBT', b'\xFF')) (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 from . import _common
import aif.disk.block as block
import aif.disk.lvm as lvm
import aif.disk.mdadm as mdadm



BlockDev = _common.BlockDev BlockDev = _common.BlockDev


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

View File

@ -1,3 +1,24 @@
from . import _common 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 import subprocess
has_mod = False
## ##
import aif.disk.block_fallback as block import aif.disk.block_fallback as block
import aif.disk.luks_fallback as luks import aif.disk.luks_fallback as luks

View File

@ -1,3 +1,81 @@
import re
##
import aif.utils
import aif.constants
from . import _common 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.block_fallback as block
import aif.disk.luks_fallback as luks import aif.disk.luks_fallback as luks
import aif.disk.lvm_fallback as lvm 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+' _mdblock_size_re = re.compile(r'^(?P<sectors>[0-9]+)\s+'
r'\((?P<GiB>[0-9.]+)\s+GiB\s+' r'\((?P<GiB>[0-9.]+)\s+GiB\s+'
r'(?P<GB>[0-9.]+)\s+GB\)') 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'[A-Za-z\s]+'
r'(?P<offset>[0-9]+)\s+sectors$') 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): class Member(object):
def __init__(self, member_xml, partobj): def __init__(self, member_xml, partobj):
@ -151,23 +129,26 @@ class Array(object):
self.xml = array_xml self.xml = array_xml
self.id = array_xml.attrib['id'] self.id = array_xml.attrib['id']
self.level = int(self.xml.attrib['level']) self.level = int(self.xml.attrib['level'])
if self.level not 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 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') self.metadata = self.xml.attrib.get('meta', '1.2')
if self.metadata not in SUPPORTED_METADATA: if self.metadata not in aif.constants.MDADM_SUPPORTED_METADATA:
raise ValueError('Metadata version must be one of: {0}'.format(', '.join(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)) self.chunksize = int(self.xml.attrib.get('chunkSize', 512))
if self.level in (4, 5, 6, 10): 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? # 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') 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 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? # 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') raise ValueError('chunksize must be divisible by 4 for the RAID level you specified')
self.layout = self.xml.attrib.get('layout', 'none') self.layout = self.xml.attrib.get('layout', 'none')
if self.level in SUPPORTED_LAYOUTS.keys(): if self.level in aif.constants.MDADM_SUPPORTED_LAYOUTS.keys():
matcher, layout_default = SUPPORTED_LAYOUTS[self.level] matcher, layout_default = aif.constants.MDADM_SUPPORTED_LAYOUTS[self.level]
if not matcher.search(self.layout): if not matcher.search(self.layout):
if layout_default: if layout_default:
self.layout = layout_default self.layout = layout_default
@ -190,20 +171,6 @@ class Array(object):
self.members.append(memberobj) self.members.append(memberobj)
return() 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): def create(self):
if not self.members: if not self.members:
raise RuntimeError('Cannot create an array with no members') raise RuntimeError('Cannot create an array with no members')
@ -224,6 +191,20 @@ class Array(object):
self.state = 'new' self.state = 'new'
return() 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): def stop(self):
# TODO: logging # TODO: logging
subprocess.run(['mdadm', '--stop', self.devpath]) subprocess.run(['mdadm', '--stop', self.devpath])

View File

@ -1,3 +1,4 @@
import math
import os import os
import re import re
import subprocess import subprocess
@ -42,6 +43,18 @@ def hasBin(binary_name):
return(False) 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(): def kernelFilesystems():
# I wish there was a better way of doing this. # I wish there was a better way of doing this.
# https://unix.stackexchange.com/a/98680 # https://unix.stackexchange.com/a/98680