making a LOT of headway on the gi stuff but hitting a namespace issue

This commit is contained in:
brent s 2019-11-04 23:18:28 -05:00
parent 0ad8314d0b
commit 27978786b8
8 changed files with 247 additions and 64 deletions

View File

@ -1,13 +1,20 @@
try:
from . import constants
except ImportError:
from . import constants_fallback as constants

from . import disk
from . import system
from . import config
from . import constants
from . import envsetup
from . import log
from . import network
from . import pacman
from . import utils




class AIF(object):
def __init__(self):
pass

View File

@ -1,13 +1,22 @@
arch_releng_key = '4AA4767BBC9C4B1D18AE28B77F2D434B9741E8AC'
version = '0.2.0'
external_deps = ['blkinfo',
'gpg',
'lxml',
'mdstat',
'passlib',
'psutil',
'pyparted',
'pyroute2',
'pytz',
'requests',
'validators']
from .constants_fallback import *
##
import aif.disk._common
_BlockDev = aif.disk._common.BlockDev
aif.disk._common.addBDPlugin('part')


# LIBBLOCKDEV FLAG INDEXING / PARTED <=> LIBBLOCKDEV FLAG CONVERSION
BD_PART_FLAGS = _BlockDev.PartFlag(-1)
BD_PART_FLAGS_FRIENDLY = dict(zip(BD_PART_FLAGS.value_nicks, BD_PART_FLAGS.value_names))
BD_PARTED_MAP = {'apple_tv_recovery': 'atvrecv',
'cpalo': 'palo',
'gpt_hidden': None, # ???
'gpt_no_automount': None, # ???
'gpt_read_only': None, # ???
'gpt_system_part': None, # ???
'hpservice': 'hp-service',
'msft_data': 'msftdata',
'msft_reserved': 'msftres'}
PARTED_BD_MAP = {v: k for k, v in BD_PARTED_MAP.items() if v is not None}
BD_PART_FLAGS_IDX_FLAG = {k: v.value_nicks[0] for k, v in BD_PART_FLAGS.__flags_values__.items()}
BD_PART_FLAGS_FLAG_IDX = {v: k for k, v in BD_PART_FLAGS_IDX_FLAG.items()}

21
aif/constants_fallback.py Normal file
View File

@ -0,0 +1,21 @@
import parted # https://www.gnu.org/software/parted/api/index.html


ARCH_RELENG_KEY = '4AA4767BBC9C4B1D18AE28B77F2D434B9741E8AC'
VERSION = '0.2.0'
EXTERNAL_DEPS = ['blkinfo',
'gpg',
'lxml',
'mdstat',
'passlib',
'psutil',
'pyparted',
'pyroute2',
'pytz',
'requests',
'validators']
# PARTED FLAG INDEXING
PARTED_FSTYPES = sorted(list(dict(vars(parted.filesystem))['fileSystemType'].keys()))
PARTED_FLAGS = sorted(list(parted.partition.partitionFlag.values()))
PARTED_IDX_FLAG = dict(parted.partition.partitionFlag)
PARTED_FLAG_IDX = {v: k for k, v in PARTED_IDX_FLAG.items()}

View File

@ -21,3 +21,8 @@ try:
from . import mdadm_fallback
except ImportError:
from . import mdadm_fallback as mdadm

try:
from . import _common
except ImportError:
pass # GI isn't supported, so we don't even use a fallback.

View File

@ -2,8 +2,12 @@ import gi
gi.require_version('BlockDev', '2.0')
from gi.repository import BlockDev, GLib

ps = BlockDev.PluginSpec()
ps.name = BlockDev.Plugin.LVM
ps.so_name = "libbd_lvm.so"
BlockDev.ensure_init([None])

BlockDev.init([ps])

def addBDPlugin(plugin_name):
plugins = BlockDev.get_available_plugin_names()
plugins.append(plugin_name)
plugins = list(set(plugins)) # Deduplicate
spec = BlockDev.plugin_specs_from_names(plugins)
return(BlockDev.ensure_init(spec))

View File

@ -1,4 +1,133 @@
from . import _common
import re
##
import parted
import psutil
##
import aif.constants
import aif.disk._common
import aif.utils

_BlockDev = _common.BlockDev
_BlockDev = aif.disk._common.BlockDev


class Partition(object):
def __init__(self, part_xml, diskobj, start_sector, partnum, tbltype, part_type = None):
# Belive it or not, dear reader, but this *entire method* is just to set attributes.
if tbltype not in ('gpt', 'msdos'):
raise ValueError('{0} must be one of gpt or msdos'.format(tbltype))
if tbltype == 'msdos' and part_type not in ('primary', 'extended', 'logical'):
raise ValueError(('You must specify if this is a '
'primary, extended, or logical partition for msdos partition tables'))
aif.disk._common.addBDPlugin('part')
self.xml = part_xml
self.id = part_xml.attrib['id']
self.table_type = getattr(_BlockDev.PartTableType, tbltype.upper())
if tbltype == 'msdos':
# Could technically be _BlockDev.PartTypeReq.NEXT buuuut that doesn't *quite* work
# with our project's structure.
if part_type == 'primary':
self.part_type = _BlockDev.PartTypeReq.NORMAL
elif part_type == 'extended':
self.part_type = _BlockDev.PartTypeReq.EXTENDED
elif part_type == 'logical':
self.part_type = _BlockDev.PartTypeReq.LOGICAL
self.flags = []
for f in self.xml.findall('partitionFlag'):
# *Technically* we could use e.g. getattr(_BlockDev.PartFlag, f.text.upper()), *but* we lose compat
# with parted's flags if we do that. :| So we do some funky logic both here and in the constants.
if f.text in aif.constants.PARTED_BD_MAP:
flag_id = aif.constants.BD_PART_FLAGS_FLAG_IDX[aif.constants.PARTED_BD_MAP[f.text]]
elif f.text in aif.constants.BD_PART_FLAGS_FRIENDLY:
flag_id = aif.constants.BD_PART_FLAGS_FLAG_IDX[aif.constants.BD_PART_FLAGS_FRIENDLY[f.text]]
else:
continue
self.flags.append(_BlockDev.PartFlag(flag_id))
self.partnum = partnum
self.fstype = self.xml.attrib['fsType'].lower()
if self.fstype not in aif.constants.PARTED_FSTYPES: # There isn't a way to do this with BlockDev? :|
raise ValueError(('{0} is not a valid partition filesystem type; '
'must be one of: {1}').format(self.xml.attrib['fsType'],
', '.join(sorted(aif.constants.PARTED_FSTYPES))))
self.disk = diskobj
self.device = self.disk.device # TODO: how to get this in libblockdev?
self.devpath = '{0}{1}'.format(self.device.path, partnum)
self.is_hiformatted = False
sizes = {}
for s in ('start', 'stop'):
x = dict(zip(('from_bgn', 'size', 'type'),
aif.utils.convertSizeUnit(self.xml.attrib[s])))
sectors = x['size']
if x['type'] == '%':
sectors = int(self.device.getLength() / x['size'])
else:
sectors = int(aif.utils.size.convertStorage(x['size'],
x['type'],
target = 'B') / self.device.sectorSize)
sizes[s] = (sectors, x['from_bgn'])
if sizes['start'][1] is not None:
if sizes['start'][1]:
self.begin = sizes['start'][0] + 0
else:
self.begin = self.device.getLength() - sizes['start'][0] # TODO: is there a way to get this in BD?
else:
self.begin = sizes['start'][0] + start_sector
if sizes['stop'][1] is not None:
if sizes['stop'][1]:
self.end = sizes['stop'][0] + 0
else:
# This *technically* should be - 34, at least for gpt, but the alignment optimizer fixes it for us.
self.end = (self.device.getLength() - 1) - sizes['stop'][0] # TODO: is there a way to get this in BD?
else:
self.end = self.begin + sizes['stop'][0]
# TECHNICALLY we could craft the Geometry object with "length = ...", but it doesn't let us be explicit
# in configs. So we manually crunch the numbers and do it all at the end.
# TODO: switch parted objects to BlockDev
# self.geometry = parted.Geometry(device = self.device,
# start = self.begin,
# end = self.end)
# self.filesystem = parted.FileSystem(type = self.fstype,
# geometry = self.geometry)
# self.partition = parted.Partition(disk = diskobj,
# type = self.part_type,
# geometry = self.geometry,
# fs = self.filesystem)
self.part_name = self.xml.attrib.get('name')

#
# def detect(self):
# pass # TODO; blkinfo?


class Disk(object):
def __init__(self, disk_xml):
# TODO: BlockDev.part.set_disk_flag(<disk>,
# BlockDev.PartDiskFlag(1),
# True) ??
# https://lazka.github.io/pgi-docs/BlockDev-2.0/enums.html#BlockDev.PartDiskFlag
# https://unix.stackexchange.com/questions/325886/bios-gpt-do-we-need-a-boot-flag
self.xml = disk_xml
self.devpath = self.xml.attrib['device']
self.is_lowformatted = None
self.is_hiformatted = None
self.is_partitioned = None
self.partitions = None
self._initDisk()
aif.disk._common.addBDPlugin('part')

def _initDisk(self):
pass

def diskFormat(self):
pass

def getPartitions(self):
pass

def partFormat(self):
pass


class Mount(object):
def __init__(self, mount_xml, partobj):
pass
_common.addBDPlugin('fs')

View File

@ -15,42 +15,12 @@ except ImportError:
import parted # https://www.gnu.org/software/parted/api/index.html
import psutil
##
from aif.utils import xmlBool, size
import aif.constants
import aif.utils

# TODO: https://serverfault.com/questions/356534/ssd-erase-block-size-lvm-pv-on-raw-device-alignment


PARTED_FSTYPES = sorted(list(dict(vars(parted.filesystem))['fileSystemType'].keys()))
PARTED_FLAGS = sorted(list(parted.partition.partitionFlag.values()))
IDX_FLAG = dict(parted.partition.partitionFlag)
FLAG_IDX = {v: k for k, v in IDX_FLAG.items()}

# parted lib can do SI or IEC. So can we.
_pos_re = re.compile((r'^(?P<pos_or_neg>-|\+)?\s*'
r'(?P<size>[0-9]+)\s*'
# empty means size in sectors
r'(?P<pct_unit_or_sct>%|{0}|)\s*$'.format('|'.join(size.valid_storage))
))


def convertSizeUnit(pos):
orig_pos = pos
pos = _pos_re.search(pos)
if pos:
pos_or_neg = (pos.group('pos_or_neg') if pos.group('pos_or_neg') else None)
if pos_or_neg == '+':
from_beginning = True
elif pos_or_neg == '-':
from_beginning = False
else:
from_beginning = pos_or_neg
_size = int(pos.group('size'))
amt_type = pos.group('pct_unit_or_sct').strip()
else:
raise ValueError('Invalid size specified: {0}'.format(orig_pos))
return((from_beginning, _size, amt_type))


class Partition(object):
def __init__(self, part_xml, diskobj, start_sector, partnum, tbltype, part_type = None):
if tbltype not in ('gpt', 'msdos'):
@ -59,10 +29,10 @@ class Partition(object):
raise ValueError(('You must specify if this is a '
'primary, extended, or logical partition for msdos partition tables'))
self.xml = part_xml
self.id = part_xml.attrib['id']
self.id = self.xml.attrib['id']
self.flags = set()
for f in self.xml.findall('partitionFlag'):
if f.text in PARTED_FLAGS:
if f.text in aif.constants.PARTED_FLAGS:
self.flags.add(f.text)
self.flags = sorted(list(self.flags))
self.partnum = partnum
@ -77,10 +47,10 @@ class Partition(object):
else:
self.part_type = parted.PARTITION_NORMAL
self.fstype = self.xml.attrib['fsType'].lower()
if self.fstype not in PARTED_FSTYPES:
if self.fstype not in aif.constants.PARTED_FSTYPES:
raise ValueError(('{0} is not a valid partition filesystem type; '
'must be one of: {1}').format(self.xml.attrib['fsType'],
', '.join(sorted(PARTED_FSTYPES))))
', '.join(sorted(aif.constants.PARTED_FSTYPES))))
self.disk = diskobj
self.device = self.disk.device
self.devpath = '{0}{1}'.format(self.device.path, partnum)
@ -88,12 +58,14 @@ class Partition(object):
sizes = {}
for s in ('start', 'stop'):
x = dict(zip(('from_bgn', 'size', 'type'),
convertSizeUnit(self.xml.attrib[s])))
aif.utils.convertSizeUnit(self.xml.attrib[s])))
sectors = x['size']
if x['type'] == '%':
sectors = int(self.device.getLength() / x['size'])
else:
sectors = int(size.convertStorage(x['size'], x['type'], target = 'B') / self.device.sectorSize)
sectors = int(aif.utils.size.convertStorage(x['size'],
x['type'],
target = 'B') / self.device.sectorSize)
sizes[s] = (sectors, x['from_bgn'])
if sizes['start'][1] is not None:
if sizes['start'][1]:
@ -122,7 +94,7 @@ class Partition(object):
geometry = self.geometry,
fs = self.filesystem)
for f in self.flags[:]:
flag_id = FLAG_IDX[f]
flag_id = aif.constants.PARTED_FLAG_IDX[f]
if self.partition.isFlagAvailable(flag_id):
self.partition.setFlag(flag_id)
else:
@ -133,16 +105,20 @@ class Partition(object):
# https://github.com/dcantrell/pyparted/issues/65
# self.partition.name = self.xml.attrib.get('name')
_pedpart = self.partition.getPedPartition()
_pedpart.set_name(self.xml.attrib.get('name'))

def detect(self):
pass
_pedpart.set_name(self.xml.attrib['name'])
#
# def detect(self):
# pass # TODO; blkinfo?


class Disk(object):
def __init__(self, disk_xml):
self.xml = disk_xml
self.devpath = self.xml.attrib['device']
self.is_lowformatted = None
self.is_hiformatted = None
self.is_partitioned = None
self.partitions = None
self._initDisk()

def _initDisk(self):
@ -221,6 +197,7 @@ class Disk(object):
self.is_partitioned = True
return()


class Mount(object):
def __init__(self, mount_xml, partobj):
self.xml = mount_xml

View File

@ -1,9 +1,13 @@
import os
import re


def hasBin(binary_name):
paths = []
for p in os.environ.get('PATH', '/usr/bin:/bin').split(':'):
os.listdir(os.path.realpath(p))
if binary_name in os.listdir(os.path.realpath(p)):
return(os.path.join(p, binary_name))
return(False)


def xmlBool(xmlobj):
@ -181,3 +185,30 @@ class _Sizer(object):


size = _Sizer()


# We do this as base level so they aren't compiled on every invocation/instantiation.
# parted lib can do SI or IEC. So can we.
_pos_re = re.compile((r'^(?P<pos_or_neg>-|\+)?\s*'
r'(?P<size>[0-9]+)\s*'
# empty means size in sectors
r'(?P<pct_unit_or_sct>%|{0}|)\s*$'.format('|'.join(size.valid_storage))
))


def convertSizeUnit(pos):
orig_pos = pos
pos = _pos_re.search(pos)
if pos:
pos_or_neg = (pos.group('pos_or_neg') if pos.group('pos_or_neg') else None)
if pos_or_neg == '+':
from_beginning = True
elif pos_or_neg == '-':
from_beginning = False
else:
from_beginning = pos_or_neg
_size = int(pos.group('size'))
amt_type = pos.group('pct_unit_or_sct').strip()
else:
raise ValueError('Invalid size specified: {0}'.format(orig_pos))
return((from_beginning, _size, amt_type))