still doing some work but checking in what i have so far
This commit is contained in:
parent
3ca56d7b5c
commit
108588827a
72
aif.xsd
72
aif.xsd
@ -1,8 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||||
targetNamespace="http://aif.square-r00t.net"
|
targetNamespace="http://aif-ng.io/"
|
||||||
xmlns="http://aif.square-r00t.net"
|
xmlns="http://aif-ng.io/"
|
||||||
elementFormDefault="qualified">
|
xmlns:aif="http://aif-ng.io/"
|
||||||
|
elementFormDefault="qualified"
|
||||||
|
attributeFormDefault="unqualified">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>
|
<xs:documentation>
|
||||||
See https://aif.square-r00t.net/ for more information about this project.
|
See https://aif.square-r00t.net/ for more information about this project.
|
||||||
@ -148,22 +150,24 @@
|
|||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="part" minOccurs="1" maxOccurs="unbounded">
|
<xs:element name="part" minOccurs="1" maxOccurs="unbounded">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
|
<!-- num should not be required since it's a sequence; it's inherently
|
||||||
|
ordered! -->
|
||||||
<xs:attribute name="num" type="xs:positiveInteger" use="required"/>
|
<xs:attribute name="num" type="xs:positiveInteger" use="required"/>
|
||||||
<xs:attribute name="start" type="disksize" use="required"/>
|
<xs:attribute name="start" type="aif:disksize" use="required"/>
|
||||||
<xs:attribute name="stop" type="disksize" use="required"/>
|
<xs:attribute name="stop" type="aif:disksize" use="required"/>
|
||||||
<xs:attribute name="fstype" type="fstype" use="required"/>
|
<xs:attribute name="fstype" type="aif:fstype" use="required"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="unique-partnum">
|
<xs:unique name="unique-partnum">
|
||||||
<xs:selector xpath="part"/>
|
<xs:selector xpath="aif:part"/>
|
||||||
<xs:field xpath="@num"/>
|
<xs:field xpath="@num"/>
|
||||||
</xs:unique>
|
</xs:unique>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="device" type="diskdev" use="required"/>
|
<xs:attribute name="device" type="aif:diskdev" use="required"/>
|
||||||
<xs:attribute name="diskfmt" type="diskfmt" use="required"/>
|
<xs:attribute name="diskfmt" type="aif:diskfmt" use="required"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="unique-diskdev">
|
<xs:unique name="unique-diskdev">
|
||||||
<xs:selector xpath="disk"/>
|
<xs:selector xpath="aif:disk"/>
|
||||||
<xs:field xpath="@device"/>
|
<xs:field xpath="@device"/>
|
||||||
</xs:unique>
|
</xs:unique>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
@ -171,13 +175,13 @@
|
|||||||
<xs:element name="mount" minOccurs="1" maxOccurs="unbounded">
|
<xs:element name="mount" minOccurs="1" maxOccurs="unbounded">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:attribute name="order" type="xs:integer" use="required"/>
|
<xs:attribute name="order" type="xs:integer" use="required"/>
|
||||||
<xs:attribute name="source" type="diskdev" use="required"/>
|
<xs:attribute name="source" type="aif:diskdev" use="required"/>
|
||||||
<xs:attribute name="target" type="xs:token" use="required"/>
|
<xs:attribute name="target" type="xs:token" use="required"/>
|
||||||
<xs:attribute name="fstype" type="fstype"/>
|
<xs:attribute name="fstype" type="aif:fstype"/>
|
||||||
<xs:attribute name="opts" type="mntopts"/>
|
<xs:attribute name="opts" type="aif:mntopts"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="unique-mnts">
|
<xs:unique name="unique-mnts">
|
||||||
<xs:selector xpath="mount"/>
|
<xs:selector xpath="aif:mount"/>
|
||||||
<xs:field xpath="@order"/>
|
<xs:field xpath="@order"/>
|
||||||
<xs:field xpath="@source"/>
|
<xs:field xpath="@source"/>
|
||||||
<xs:field xpath="@target"/>
|
<xs:field xpath="@target"/>
|
||||||
@ -194,10 +198,10 @@
|
|||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="iface" minOccurs="1" maxOccurs="unbounded">
|
<xs:element name="iface" minOccurs="1" maxOccurs="unbounded">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:attribute name="device" type="iface" use="required"/>
|
<xs:attribute name="device" type="aif:iface" use="required"/>
|
||||||
<xs:attribute name="address" type="netaddress" use="required"/>
|
<xs:attribute name="address" type="aif:netaddress" use="required"/>
|
||||||
<xs:attribute name="netproto" type="netproto" use="required"/>
|
<xs:attribute name="netproto" type="aif:netproto" use="required"/>
|
||||||
<xs:attribute name="gateway" type="netaddress"/>
|
<xs:attribute name="gateway" type="aif:netaddress"/>
|
||||||
<xs:attribute name="resolvers" type="xs:string"/>
|
<xs:attribute name="resolvers" type="xs:string"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
@ -205,7 +209,7 @@
|
|||||||
<xs:attribute name="hostname" type="xs:token" use="required"/>
|
<xs:attribute name="hostname" type="xs:token" use="required"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="unique-iface">
|
<xs:unique name="unique-iface">
|
||||||
<xs:selector xpath="iface"/>
|
<xs:selector xpath="aif:iface"/>
|
||||||
<xs:field xpath="@address"/>
|
<xs:field xpath="@address"/>
|
||||||
<xs:field xpath="@netproto"/>
|
<xs:field xpath="@netproto"/>
|
||||||
</xs:unique>
|
</xs:unique>
|
||||||
@ -229,30 +233,30 @@
|
|||||||
</xs:element>
|
</xs:element>
|
||||||
<xs:element name="xgroup" minOccurs="0" maxOccurs="unbounded">
|
<xs:element name="xgroup" minOccurs="0" maxOccurs="unbounded">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:attribute name="name" type="nixgroup" use="required"/>
|
<xs:attribute name="name" type="aif:nixgroup" use="required"/>
|
||||||
<xs:attribute name="create" type="xs:boolean"/>
|
<xs:attribute name="create" type="xs:boolean"/>
|
||||||
<xs:attribute name="gid" type="xs:boolean"/>
|
<xs:attribute name="gid" type="xs:boolean"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="unique-grp">
|
<xs:unique name="unique-grp">
|
||||||
<xs:selector xpath="xgroup"/>
|
<xs:selector xpath="aif:xgroup"/>
|
||||||
<xs:field xpath="@name"/>
|
<xs:field xpath="@name"/>
|
||||||
</xs:unique>
|
</xs:unique>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="name" type="xs:token" use="required"/>
|
<xs:attribute name="name" type="xs:token" use="required"/>
|
||||||
<xs:attribute name="uid" type="xs:token"/>
|
<xs:attribute name="uid" type="xs:token"/>
|
||||||
<xs:attribute name="group" type="nixgroup"/>
|
<xs:attribute name="group" type="aif:nixgroup"/>
|
||||||
<xs:attribute name="gid" type="xs:token"/>
|
<xs:attribute name="gid" type="xs:token"/>
|
||||||
<xs:attribute name="password" type="nixpass"/>
|
<xs:attribute name="password" type="aif:nixpass"/>
|
||||||
<xs:attribute name="comment" type="xs:token"/>
|
<xs:attribute name="comment" type="xs:token"/>
|
||||||
<xs:attribute name="sudo" type="xs:boolean"/>
|
<xs:attribute name="sudo" type="xs:boolean"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="rootpass" type="nixpass"/>
|
<xs:attribute name="rootpass" type="aif:nixpass"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="unique-usr">
|
<xs:unique name="unique-usr">
|
||||||
<xs:selector xpath="user"/>
|
<xs:selector xpath="aif:user"/>
|
||||||
<xs:field xpath="@name"/>
|
<xs:field xpath="@name"/>
|
||||||
</xs:unique>
|
</xs:unique>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
@ -262,7 +266,7 @@
|
|||||||
<xs:attribute name="status" type="xs:boolean" use="required"/>
|
<xs:attribute name="status" type="xs:boolean" use="required"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="unique-svc">
|
<xs:unique name="unique-svc">
|
||||||
<xs:selector xpath="service"/>
|
<xs:selector xpath="aif:service"/>
|
||||||
<xs:field xpath="@name"/>
|
<xs:field xpath="@name"/>
|
||||||
<xs:field xpath="@status"/>
|
<xs:field xpath="@status"/>
|
||||||
</xs:unique>
|
</xs:unique>
|
||||||
@ -288,7 +292,7 @@
|
|||||||
<xs:attribute name="name" type="xs:token" use="required"/>
|
<xs:attribute name="name" type="xs:token" use="required"/>
|
||||||
<xs:attribute name="enabled" type="xs:boolean" use="required"/>
|
<xs:attribute name="enabled" type="xs:boolean" use="required"/>
|
||||||
<xs:attribute name="siglevel" type="xs:token" use="required"/>
|
<xs:attribute name="siglevel" type="xs:token" use="required"/>
|
||||||
<xs:attribute name="mirror" type="pacuri" use="required"/>
|
<xs:attribute name="mirror" type="aif:pacuri" use="required"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
@ -297,11 +301,11 @@
|
|||||||
<xs:element name="mirrorlist" maxOccurs="1" minOccurs="0">
|
<xs:element name="mirrorlist" maxOccurs="1" minOccurs="0">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="mirror" type="pacuri" maxOccurs="unbounded" minOccurs="1"/>
|
<xs:element name="mirror" type="aif:pacuri" maxOccurs="unbounded" minOccurs="1"/>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="unique-mirrors">
|
<xs:unique name="unique-mirrors">
|
||||||
<xs:selector xpath="mirror"/>
|
<xs:selector xpath="aif:mirror"/>
|
||||||
<xs:field xpath="."/>
|
<xs:field xpath="."/>
|
||||||
</xs:unique>
|
</xs:unique>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
@ -325,7 +329,7 @@
|
|||||||
<!-- BEGIN BOOTLOADER -->
|
<!-- BEGIN BOOTLOADER -->
|
||||||
<xs:element name="bootloader" maxOccurs="1" minOccurs="1">
|
<xs:element name="bootloader" maxOccurs="1" minOccurs="1">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:attribute name="type" type="bootloaders" use="required"/>
|
<xs:attribute name="type" type="aif:bootloaders" use="required"/>
|
||||||
<xs:attribute name="target" type="xs:token" use="required"/>
|
<xs:attribute name="target" type="xs:token" use="required"/>
|
||||||
<xs:attribute name="efi" type="xs:boolean"/>
|
<xs:attribute name="efi" type="xs:boolean"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
@ -337,19 +341,19 @@
|
|||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="script" minOccurs="1" maxOccurs="unbounded">
|
<xs:element name="script" minOccurs="1" maxOccurs="unbounded">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:attribute name="uri" type="scripturi" use="required"/>
|
<xs:attribute name="uri" type="aif:scripturi" use="required"/>
|
||||||
<xs:attribute name="order" type="xs:integer" use="required"/>
|
<xs:attribute name="order" type="xs:integer" use="required"/>
|
||||||
<xs:attribute name="execution" type="scripttype" use="required"/>
|
<xs:attribute name="execution" type="aif:scripttype" use="required"/>
|
||||||
<xs:attribute name="user" type="xs:string"/>
|
<xs:attribute name="user" type="xs:string"/>
|
||||||
<xs:attribute name="password" type="xs:string"/>
|
<xs:attribute name="password" type="xs:string"/>
|
||||||
<xs:attribute name="realm" type="xs:string"/>
|
<xs:attribute name="realm" type="xs:string"/>
|
||||||
<xs:attribute name="authtype" type="authselect"/>
|
<xs:attribute name="authtype" type="aif:authselect"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="unique-script">
|
<xs:unique name="unique-script">
|
||||||
<xs:selector xpath="script"/>
|
<xs:selector xpath="aif:script"/>
|
||||||
<xs:field xpath="@order"/>
|
<xs:field xpath="@order"/>
|
||||||
</xs:unique>
|
</xs:unique>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
9
aif/aif_util.py
Normal file
9
aif/aif_util.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
def xmlBool(xmlobj):
|
||||||
|
if isinstance(xmlobj, bool):
|
||||||
|
return (xmlobj)
|
||||||
|
if xmlobj.lower() in ('1', 'true'):
|
||||||
|
return(True)
|
||||||
|
elif xmlobj.lower() in ('0', 'false'):
|
||||||
|
return(False)
|
||||||
|
else:
|
||||||
|
return(None)
|
150
aif/disk.py
150
aif/disk.py
@ -3,6 +3,9 @@
|
|||||||
# https://github.com/dcantrell/pyparted/blob/master/examples/query_device_capacity.py
|
# https://github.com/dcantrell/pyparted/blob/master/examples/query_device_capacity.py
|
||||||
# TODO: Remember to replicate genfstab behaviour.
|
# TODO: Remember to replicate genfstab behaviour.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
try:
|
try:
|
||||||
# https://stackoverflow.com/a/34812552/733214
|
# https://stackoverflow.com/a/34812552/733214
|
||||||
# https://github.com/karelzak/util-linux/blob/master/libmount/python/test_mount_context.py#L6
|
# https://github.com/karelzak/util-linux/blob/master/libmount/python/test_mount_context.py#L6
|
||||||
@ -11,5 +14,150 @@ except ImportError:
|
|||||||
# We should never get here. util-linux is part of core (base) in Arch and uses "libmount".
|
# We should never get here. util-linux is part of core (base) in Arch and uses "libmount".
|
||||||
import pylibmount as mount
|
import pylibmount as mount
|
||||||
##
|
##
|
||||||
import parted
|
import blkinfo
|
||||||
|
import parted # https://www.gnu.org/software/parted/api/index.html
|
||||||
import psutil
|
import psutil
|
||||||
|
##
|
||||||
|
from .aif_util import xmlBool
|
||||||
|
|
||||||
|
|
||||||
|
# parted lib can do SI or IEC (see table to right at https://en.wikipedia.org/wiki/Binary_prefix)
|
||||||
|
# We bit-shift to do conversions:
|
||||||
|
# https://stackoverflow.com/a/12912296/733214
|
||||||
|
# https://stackoverflow.com/a/52684562/733214
|
||||||
|
_units = {'B': 0,
|
||||||
|
'kB': 7,
|
||||||
|
'MB': 17,
|
||||||
|
'GB': 27,
|
||||||
|
'TB': 37,
|
||||||
|
'KiB': 10,
|
||||||
|
'MiB': 20,
|
||||||
|
'GiB': 30,
|
||||||
|
'TiB': 40}
|
||||||
|
_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(list(_units.keys())))),
|
||||||
|
re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
|
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, disk_xml, diskobj, start_sector):
|
||||||
|
self.xml = disk_xml
|
||||||
|
device = diskobj.device
|
||||||
|
sizes = {}
|
||||||
|
for s in ('start', 'stop'):
|
||||||
|
x = dict(zip(('from_bgn', 'size', 'type'),
|
||||||
|
convertSizeUnit(self.xml.attrib[s])))
|
||||||
|
sectors = x['size']
|
||||||
|
if x['type'] == '%':
|
||||||
|
sectors = int(device.getLength() / x['size'])
|
||||||
|
elif x['type'] in _units.keys():
|
||||||
|
sectors = int(x['size'] << _units[x['type']] / 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 = device.getLength() - sizes['start'][0]
|
||||||
|
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:
|
||||||
|
self.end = device.getLength() - sizes['stop'][0]
|
||||||
|
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.
|
||||||
|
self.geometry = parted.Geometry(device = device,
|
||||||
|
start = self.begin,
|
||||||
|
end = self.end)
|
||||||
|
self.filesystem = parted.FileSystem(type = self.xml.attrib['fsType'],
|
||||||
|
)
|
||||||
|
self.partition = parted.Partition(disk = diskobj,
|
||||||
|
type = parted.PARTITION_NORMAL,
|
||||||
|
geometry = self.geometry,
|
||||||
|
fs = )
|
||||||
|
|
||||||
|
class Disk(object):
|
||||||
|
def __init__(self, disk_xml):
|
||||||
|
self.xml = disk_xml
|
||||||
|
self.devpath = self.xml.attrib['device']
|
||||||
|
self._initDisk()
|
||||||
|
|
||||||
|
def _initDisk(self):
|
||||||
|
self.device = parted.getDevice(self.devpath)
|
||||||
|
try:
|
||||||
|
self.disk = parted.newDisk(self.device)
|
||||||
|
if xmlBool(self.xml.attrib.get('forceReformat')):
|
||||||
|
self.is_lowformatted = False
|
||||||
|
self.is_hiformatted = False
|
||||||
|
else:
|
||||||
|
self.is_lowformatted = True
|
||||||
|
self.is_hiformatted = False
|
||||||
|
for d in blkinfo.BlkDiskInfo().get_disks(filters = {'group': 'disk',
|
||||||
|
'name': os.path.basename(self.devpath),
|
||||||
|
'kname': os.path.basename(self.devpath)}):
|
||||||
|
if d.get('fstype', '').strip() != '':
|
||||||
|
self.is_hiformatted = True
|
||||||
|
break
|
||||||
|
self.is_partitioned = True
|
||||||
|
except parted._ped.DiskException:
|
||||||
|
self.disk = None
|
||||||
|
self.is_lowformatted = False
|
||||||
|
self.is_hiformatted = False
|
||||||
|
self.is_partitioned = False
|
||||||
|
return()
|
||||||
|
|
||||||
|
def diskformat(self):
|
||||||
|
if self.is_lowformatted:
|
||||||
|
return()
|
||||||
|
# This is a safeguard. We do *not* want to low-format a disk that is mounted.
|
||||||
|
for p in psutil.disk_partitions(all = True):
|
||||||
|
if self.devpath in p:
|
||||||
|
raise RuntimeError('{0} is mounted; we are cowardly refusing to low-format it'.format(self.devpath))
|
||||||
|
self.disk.deleteAllPartitions()
|
||||||
|
tabletype = self.xml.attrib.get('diskFormat', 'gpt').lower()
|
||||||
|
if tabletype in ('bios', 'mbr'):
|
||||||
|
tabletype = 'msdos'
|
||||||
|
validlabels = parted.getLabels()
|
||||||
|
if tabletype not in validlabels:
|
||||||
|
raise ValueError(('Disk format {0} is not valid for this architecture;'
|
||||||
|
'must be one of: {1}'.format(tabletype, ', '.join(list(validlabels)))))
|
||||||
|
self.disk = parted.freshDisk(self.device, tabletype)
|
||||||
|
|
||||||
|
pass
|
||||||
|
self.is_lowformatted = True
|
||||||
|
self.is_partitioned = True
|
||||||
|
return()
|
||||||
|
|
||||||
|
def fsformat(self):
|
||||||
|
if self.is_hiformatted:
|
||||||
|
return()
|
||||||
|
# This is a safeguard. We do *not* want to high-format a disk that is mounted.
|
||||||
|
for p in psutil.disk_partitions(all = True):
|
||||||
|
if self.devpath in p:
|
||||||
|
raise RuntimeError('{0} is mounted; we are cowardly refusing to high-format it'.format(self.devpath))
|
||||||
|
|
||||||
|
pass
|
||||||
|
return()
|
||||||
|
@ -7,13 +7,14 @@
|
|||||||
import ensurepip
|
import ensurepip
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import tempfile
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import venv
|
import venv
|
||||||
|
|
||||||
# TODO: a more consistent way of managing deps?
|
# TODO: a more consistent way of managing deps?
|
||||||
depmods = ['gpg', 'requests', 'lxml', 'psutil', 'pyparted', 'pytz', 'passlib', 'validators']
|
depmods = ['blkinfo', 'gpg', 'lxml', 'passlib', 'psutil',
|
||||||
|
'pyparted', 'pytz', 'requests', 'validators']
|
||||||
|
|
||||||
class EnvBuilder(object):
|
class EnvBuilder(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -205,7 +205,7 @@ The `/aif/storage/disk` element holds information about disks on the system, and
|
|||||||
|======================
|
|======================
|
||||||
^|Attribute ^|Value
|
^|Attribute ^|Value
|
||||||
^m|device |The disk to format (e.g. `/dev/sda`)
|
^m|device |The disk to format (e.g. `/dev/sda`)
|
||||||
^m|diskfmt |https://en.wikipedia.org/wiki/GUID_Partition_Table[`gpt`^] or https://en.wikipedia.org/wiki/Master_boot_record[`bios`^]
|
^m|diskfmt |https://en.wikipedia.org/wiki/GUID_Partition_Table[`gpt`^] or https://en.wikipedia.org/wiki/Master_boot_record[`msdos`^]
|
||||||
|======================
|
|======================
|
||||||
|
|
||||||
===== `<part>`
|
===== `<part>`
|
||||||
@ -223,10 +223,11 @@ The `/aif/storage/disk/part` element holds information on partitioning that it's
|
|||||||
[[specialsize]]
|
[[specialsize]]
|
||||||
The `start` and `stop` attributes can be in the form of:
|
The `start` and `stop` attributes can be in the form of:
|
||||||
|
|
||||||
* A percentage, indicated by a percentage sign (`"10%"`)
|
* A percentage of the total disk size, indicated by a percentage sign (`"10%"`)
|
||||||
* A size, indicated by the abbreviation (`"300K"`, `"30G"`, etc.)
|
* A size, indicated by the abbreviation (`"300KiB"`, `"10GB"`, etc.)
|
||||||
** Accepts *K* (Kilobytes), *M* (Megabytes), *G* (Gigabytes), *T* (Terabytes), or *P* (Petabytes -- I know, I know.)
|
** Accepts notation in https://en.wikipedia.org/wiki/Binary_prefix[SI or IEC formats^]
|
||||||
** Can also accept modifiers for this form (`"+500G"`, `"-400M"`)
|
* A raw sector size, if no suffix is provided (sector sizes are *typically* 512 bytes but this can vary depending on disk) (`1024`)
|
||||||
|
* One can also specify modifiers (`"+10%"`, `"-400MB"`, etc.). A positive modifier indicates from the beginning of the *start of the disk* and a negative modifier specifies from the *end of the disk* (the default, if none is specified, is to use the _previously defined partition's end_ as the *start* for the new partition, or to use the _beginning of the usable disk space_ as the *start* if no previous partition is specified, and to *add* the size to the *start* until the *stop* is reached)
|
||||||
|
|
||||||
[[fstypes]]
|
[[fstypes]]
|
||||||
NOTE: The following is a table for your reference of partition types. Note that it may be out of date, so reference the link above for the most up-to-date table.
|
NOTE: The following is a table for your reference of partition types. Note that it may be out of date, so reference the link above for the most up-to-date table.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
- make disk partitioning/table formatting OPTIONAL (so it can be installed on an already formatted disk)
|
||||||
- support Arch Linux ARM?
|
- support Arch Linux ARM?
|
||||||
- support multiple explicit locales via comma-separated list (see how i handle resolvers)
|
- support multiple explicit locales via comma-separated list (see how i handle resolvers)
|
||||||
- config layout
|
- config layout
|
||||||
@ -25,6 +26,7 @@
|
|||||||
shm on /mnt/aif/dev/shm type tmpfs (rw,nosuid,nodev,relatime)
|
shm on /mnt/aif/dev/shm type tmpfs (rw,nosuid,nodev,relatime)
|
||||||
run on /mnt/aif/run type tmpfs (rw,nosuid,nodev,relatime,mode=755)
|
run on /mnt/aif/run type tmpfs (rw,nosuid,nodev,relatime,mode=755)
|
||||||
tmp on /mnt/aif/tmp type tmpfs (rw,nosuid,nodev)
|
tmp on /mnt/aif/tmp type tmpfs (rw,nosuid,nodev)
|
||||||
|
OR just use pyalpm
|
||||||
|
|
||||||
DOCUMENTATION: aif-config.py (and note sample json as well)
|
DOCUMENTATION: aif-config.py (and note sample json as well)
|
||||||
|
|
||||||
|
@ -1,54 +1,77 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<aif xmlns:aif="https://aif.square-r00t.net"
|
<aif xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://aif-ng.io/"
|
||||||
xsi:schemaLocation="https://aif.square-r00t.net aif.xsd">
|
xsi:schemaLocation="http://aif-ng.io/aif.xsd">
|
||||||
<storage>
|
<storage>
|
||||||
<disk device="/dev/sda" diskfmt="gpt">
|
<disk device="/dev/sda" diskFormat="gpt" forceReformat="true">
|
||||||
<part num="1" start="0%" stop="10%" fstype="ef00" />
|
<!-- Partitions are numbered *in the order they are specified*. -->
|
||||||
<part num="2" start="10%" stop="80%" fstype="8300" />
|
<part id="boot" label="/boot" start="0%" stop="10%" fsType="ef00" /><!-- e.g. this would be /dev/sda1 -->
|
||||||
<part num="3" start="80%" stop="100%" fstype="8200" />
|
<part id="secrets1" label="shh" start="10%" stop="20%" fsType="8300"/>
|
||||||
|
<part id="lvm_member1" label="dynamic" start="20%" stop="30%" fsType="8300"/>
|
||||||
|
<part id="raid1_d1" start="30%" stop="55%" fsType="fd00"/>
|
||||||
|
<part id="raid1_d2" start="55%" stop="80%" fsType="fd00"/>
|
||||||
|
<part id="swap" start="80%" stop="100%" fsType="8200" />
|
||||||
</disk>
|
</disk>
|
||||||
<mount source="/dev/sda2" target="/mnt/aif" order="1" />
|
<!-- "Special" devices are processed *in the order they are specified*. This is important if you wish to
|
||||||
<mount source="/dev/sda1" target="/mnt/aif/boot" order="2" />
|
e.g. layer LUKS on top of LVM - you would specify <lvm> before <luks> and reference the
|
||||||
<mount source="/dev/sda3" target="swap" order="3" />
|
<luksDev id="SOMETHING" ... > as <lvmLogical source="SOMETHING" ... />. -->
|
||||||
|
<luks>
|
||||||
|
<luksDev id="luks_secrets" name="secrets" source="secrets1" secret="superSeekritPassword"/>
|
||||||
|
</luks>
|
||||||
|
<lvm>
|
||||||
|
<lvmGroup id="vg1" name="GroupName">
|
||||||
|
<lvmLogical id="lv1" name="LogicalName" source="lvm_member1"/>
|
||||||
|
</lvmGroup>
|
||||||
|
</lvm>
|
||||||
|
<mdadm>
|
||||||
|
<array id="mdadm1" name="extra_data" meta="1.2" level="1">
|
||||||
|
<member source="raid1_d1"/>
|
||||||
|
<member source="raid1_d2"/>
|
||||||
|
</array>
|
||||||
|
</mdadm>
|
||||||
|
<!-- And you use the id to reference mountpoints as well. -->
|
||||||
|
<mount source="luks_secrets" target="/mnt/aif" order="1" />
|
||||||
|
<mount source="boot" target="/mnt/aif/boot" order="2" />
|
||||||
|
<mount source="swap" target="swap" order="3" />
|
||||||
|
<mount source="vg1" target="/mnt/aif/mnt/pool" order="4" />
|
||||||
|
<mount source="mdadm1" target="/mnt/aif/mnt/raid" order="5" />
|
||||||
</storage>
|
</storage>
|
||||||
<network hostname="aiftest.square-r00t.net">
|
<network hostname="aiftest.square-r00t.net">
|
||||||
<iface device="auto" address="auto" netproto="ipv4" />
|
<iface device="auto" address="auto" netProto="ipv4" />
|
||||||
</network>
|
</network>
|
||||||
<system timezone="EST5EDT" locale="en_US.UTF-8" chrootpath="/mnt/aif" reboot="0">
|
<system timezone="EST5EDT" locale="en_US.UTF-8" chrootPath="/mnt/aif" reboot="0">
|
||||||
<!-- note: all password hashes below are "test"; don't waste your time trying to crack. :) -->
|
<!-- note: all password hashes below are "test"; don't waste your time trying to crack. :) -->
|
||||||
<users rootpass="$6$3YPpiS.l3SQC6ELe$NQ4qMvcDpv5j1cCM6AGNc5Hyg.rsvtzCt2VWlSbuZXCGg2GB21CMUN8TMGS35tdUezZ/n9y3UFGlmLRVWXvZR.">
|
<users rootPass="$6$3YPpiS.l3SQC6ELe$NQ4qMvcDpv5j1cCM6AGNc5Hyg.rsvtzCt2VWlSbuZXCGg2GB21CMUN8TMGS35tdUezZ/n9y3UFGlmLRVWXvZR.">
|
||||||
<user name="aifusr"
|
<user name="aifusr"
|
||||||
sudo="true"
|
sudo="true"
|
||||||
password="$6$WtxZKOyaahvvWQRG$TUys60kQhF0ffBdnDSJVTA.PovwCOajjMz8HEHL2H0ZMi0bFpDTQvKA7BqzM3nA.ZMAUxNjpJP1dG/eA78Zgw0"
|
password="$6$WtxZKOyaahvvWQRG$TUys60kQhF0ffBdnDSJVTA.PovwCOajjMz8HEHL2H0ZMi0bFpDTQvKA7BqzM3nA.ZMAUxNjpJP1dG/eA78Zgw0"
|
||||||
comment="A test user for AIF.">
|
comment="A test user for AIF.">
|
||||||
<home path="/opt/aifusr" create="true" />
|
<home path="/opt/aifusr" create="true" />
|
||||||
<xgroup name="admins" create="true" />
|
<xGroup name="admins" create="true" />
|
||||||
<xgroup name="wheel" />
|
<xGroup name="wheel" />
|
||||||
<xgroup name="users" />
|
<xGroup name="users" />
|
||||||
</user>
|
</user>
|
||||||
</users>
|
</users>
|
||||||
<service name="sshd" status="0" />
|
<service name="sshd" status="0" />
|
||||||
</system>
|
</system>
|
||||||
<pacman>
|
<pacman>
|
||||||
<repos>
|
<repos>
|
||||||
<repo name="core" enabled="true" siglevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
<repo name="core" enabled="true" sigLevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
||||||
<repo name="extra" enabled="true" siglevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
<repo name="extra" enabled="true" sigLevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
||||||
<repo name="community" enabled="true" siglevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
<repo name="community" enabled="true" sigLevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
||||||
<repo name="multilib" enabled="true" siglevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
<repo name="multilib" enabled="true" sigLevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
||||||
<repo name="testing" enabled="false" siglevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
<repo name="testing" enabled="false" sigLevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
||||||
<repo name="multilib-testing" enabled="false" siglevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
<repo name="multilib-testing" enabled="false" sigLevel="default" mirror="file:///etc/pacman.d/mirrorlist" />
|
||||||
<repo name="archlinuxfr" enabled="false" siglevel="Optional TrustedOnly" mirror="http://repo.archlinux.fr/$arch" />
|
<repo name="archlinuxfr" enabled="false" sigLevel="Optional TrustedOnly" mirror="http://repo.archlinux.fr/$arch" />
|
||||||
</repos>
|
</repos>
|
||||||
<mirrorlist>
|
<mirrorList>
|
||||||
<mirror>http://mirrors.advancedhosters.com/archlinux/$repo/os/$arch</mirror>
|
<mirror>http://arch.mirror.square-r00t.net/$repo/os/$arch</mirror>
|
||||||
<mirror>http://mirrors.advancedhosters.com/archlinux/$repo/os/$arch</mirror>
|
|
||||||
<mirror>http://mirror.us.leaseweb.net/archlinux/$repo/os/$arch</mirror>
|
<mirror>http://mirror.us.leaseweb.net/archlinux/$repo/os/$arch</mirror>
|
||||||
<mirror>http://ftp.osuosl.org/pub/archlinux/$repo/os/$arch</mirror>
|
<mirror>http://ftp.osuosl.org/pub/archlinux/$repo/os/$arch</mirror>
|
||||||
<mirror>http://arch.mirrors.ionfish.org/$repo/os/$arch</mirror>
|
<mirror>http://arch.mirrors.ionfish.org/$repo/os/$arch</mirror>
|
||||||
<mirror>http://mirrors.gigenet.com/archlinux/$repo/os/$arch</mirror>
|
<mirror>http://mirrors.gigenet.com/archlinux/$repo/os/$arch</mirror>
|
||||||
<mirror>http://mirror.jmu.edu/pub/archlinux/$repo/os/$arch</mirror>
|
<mirror>http://mirror.jmu.edu/pub/archlinux/$repo/os/$arch</mirror>
|
||||||
</mirrorlist>
|
</mirrorList>
|
||||||
<software>
|
<software>
|
||||||
<package name="sed" repo="core" />
|
<package name="sed" repo="core" />
|
||||||
<package name="python" />
|
<package name="python" />
|
||||||
|
3
setup.py
3
setup.py
@ -31,5 +31,6 @@ setuptools.setup(
|
|||||||
project_urls = {'Documentation': 'https://aif-ng.io/',
|
project_urls = {'Documentation': 'https://aif-ng.io/',
|
||||||
'Source': 'https://git.square-r00t.net/AIF-NG/',
|
'Source': 'https://git.square-r00t.net/AIF-NG/',
|
||||||
'Tracker': 'https://bugs.square-r00t.net/index.php?project=9'},
|
'Tracker': 'https://bugs.square-r00t.net/index.php?project=9'},
|
||||||
install_requires = ['gpg', 'requests', 'lxml', 'psutil', 'pyparted', 'pytz', 'passlib', 'validators']
|
install_requires = ['blkinfo', 'gpg', 'lxml', 'passlib', 'psutil',
|
||||||
|
'pyparted', 'pytz', 'requests', 'validators']
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user