i *think* i'm done the gi version of disk.lvm. LVM is such a mess.

This commit is contained in:
brent s 2019-11-06 16:58:58 -05:00
parent fbd1d4b0f3
commit 00e9c546d7
6 changed files with 174 additions and 36 deletions

10
aif.xsd
View File

@ -357,7 +357,7 @@
<xs:complexType> <xs:complexType>
<xs:all> <xs:all>
<!-- BEGIN BLOCKDEVICES --> <!-- BEGIN BLOCKDEVICES -->
<xs:element name="blockDevices" minOccurs="1"> <xs:element name="blockDevices" minOccurs="0" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded"> <xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:element name="disk" minOccurs="1" maxOccurs="unbounded"> <xs:element name="disk" minOccurs="1" maxOccurs="unbounded">
@ -670,7 +670,7 @@
<xs:element name="system" maxOccurs="1" minOccurs="1"> <xs:element name="system" maxOccurs="1" minOccurs="1">
<xs:complexType> <xs:complexType>
<xs:all> <xs:all>
<xs:element name="rootPassword" minOccurs="1" maxOccurs="1" <xs:element name="rootPassword" minOccurs="0" maxOccurs="1"
type="aif:t_nixpass"/> type="aif:t_nixpass"/>
<xs:element name="locales" minOccurs="1" maxOccurs="1"> <xs:element name="locales" minOccurs="1" maxOccurs="1">
<xs:complexType> <xs:complexType>
@ -687,10 +687,10 @@
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="users" minOccurs="1" maxOccurs="1"> <xs:element name="users" minOccurs="0" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element name="user" minOccurs="0" maxOccurs="unbounded"> <xs:element name="user" minOccurs="1" maxOccurs="unbounded">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element name="password" minOccurs="0" maxOccurs="1" <xs:element name="password" minOccurs="0" maxOccurs="1"
@ -728,7 +728,7 @@
<xs:element name="services" minOccurs="0" maxOccurs="1"> <xs:element name="services" minOccurs="0" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element name="service" minOccurs="0" maxOccurs="unbounded"> <xs:element name="service" minOccurs="1" maxOccurs="unbounded">
<xs:complexType> <xs:complexType>
<xs:simpleContent> <xs:simpleContent>
<xs:extension base="aif:t_nonempty"> <xs:extension base="aif:t_nonempty">

View File

@ -107,12 +107,13 @@ class VG(object):
self.name = self.xml.attrib('name') self.name = self.xml.attrib('name')
self.lvs = [] self.lvs = []
self.pvs = [] self.pvs = []
self.tags = [] # self.tags = []
for te in self.xml.findall('tags/tag'): # for te in self.xml.findall('tags/tag'):
self.tags.append(te.text) # self.tags.append(te.text)
_common.addBDPlugin('lvm') _common.addBDPlugin('lvm')
self.devpath = self.name self.devpath = self.name
self.info = None self.info = None
self.created = False


def addPV(self, pvobj): def addPV(self, pvobj):
if not isinstance(pvobj, PV): if not isinstance(pvobj, PV):
@ -128,26 +129,31 @@ class VG(object):
# FUCK. LVM. You can't *specify* a UUID. # FUCK. LVM. You can't *specify* a UUID.
# u = uuid.uuid4() # u = uuid.uuid4()
# opts.append(_BlockDev.ExtraArg.new('--uuid', str(u))) # opts.append(_BlockDev.ExtraArg.new('--uuid', str(u)))
for t in self.tags: # for t in self.tags:
opts.append(_BlockDev.ExtraArg.new('--addtag', t)) # opts.append(_BlockDev.ExtraArg.new('--addtag', t))
_BlockDev.lvm.vgcreate(self.name, _BlockDev.lvm.vgcreate(self.name,
[p.devpath for p in self.pvs], [p.devpath for p in self.pvs],
0, 0,
opts) opts)
for p in self.pvs: for p in self.pvs:
p._parseMeta() p._parseMeta()
self.created = True
self.updateInfo() self.updateInfo()
return() return()


def createLV(self, lv_xml = None): def createLV(self, lv_xml = None):
if not self.created:
raise RuntimeError('VG must be created before LVs can be added')
# If lv_xml is None, we loop through our own XML. # If lv_xml is None, we loop through our own XML.
if lv_xml: if lv_xml:
lv = LV(lv_xml, self) lv = LV(lv_xml, self)
lv.create() lv.create()
self.lvs.append(lv) # self.lvs.append(lv)
else: else:
for le in self.xml.findall('logicalVolumes/lv'): for le in self.xml.findall('logicalVolumes/lv'):
pass lv = LV(le, self)
lv.create()
# self.lvs.append(lv)
self.updateInfo() self.updateInfo()
return() return()


@ -162,6 +168,8 @@ class VG(object):
return() return()


def updateInfo(self): def updateInfo(self):
if not self.created:
return()
_info = _BlockDev.lvm.vginfo(self.name) _info = _BlockDev.lvm.vginfo(self.name)
# TODO: parity with lvm_fallback.VG.updateInfo # TODO: parity with lvm_fallback.VG.updateInfo
# key names currently (probably) don't match and need to confirm the information's all present # key names currently (probably) don't match and need to confirm the information's all present
@ -182,11 +190,78 @@ class LV(object):
self.xml = lv_xml self.xml = lv_xml
self.id = self.xml.attrib('id') self.id = self.xml.attrib('id')
self.name = self.xml.attrib('name') self.name = self.xml.attrib('name')
self.size = self.xml.attrib('size') # Convert to bytes. Can get max from _BlockDev.lvm.vginfo(<VG>).free self.size = self.xml.attrib('size') # Convert to bytes. Can get max from _BlockDev.lvm.vginfo(<VG>).free TODO
self.vg = vg_obj self.vg = vgobj
self.pvs = []
if not isinstance(self.vg, VG): if not isinstance(self.vg, VG):
raise ValueError('vg_obj must be of type aif.disk.lvm.VG') raise ValueError('vgobj must be of type aif.disk.lvm.VG')
_common.addBDPlugin('lvm') _common.addBDPlugin('lvm')
self.info = None
self.devpath = '/dev/{0}/{1}'.format(self.vg.name, self.name)
self.created = False
self.updateInfo()
self._initLV()


self.devpath = None def _initLV(self):
pass self.pvs = []
_indexed_pvs = {i.id: i for i in self.vg.pvs}
for pe in self.xml.findall('pvMember'):
pv_id = pe.attrib('source')
if pv_id in _indexed_pvs.keys():
self.pvs.append(_indexed_pvs[pv_id])
if not self.pvs: # We get all in the VG instead since none were explicitly assigned
self.pvs = self.vg.pvs
return()

def create(self):
if not self.pvs:
raise RuntimeError('Cannot create LV with no associated LVs')
opts = [_BlockDev.ExtraArg.new('--reportformat', 'json')]
# FUCK. LVM. You can't *specify* a UUID.
# u = uuid.uuid4()
# opts.append(_BlockDev.ExtraArg.new('--uuid', str(u)))
# for t in self.tags:
# opts.append(_BlockDev.ExtraArg.new('--addtag', t))
_BlockDev.lvm.lvcreate(self.vg.name,
self.name,
self.size,
None,
[i.devpath for i in self.pvs],
opts)
self.vg.lvs.append(self)
self.created = True
self.updateInfo()
self.vg.updateInfo()
return()

def start(self):
_BlockDev.lvm.lvactivate(self.vg.name,
self.name,
True,
None)
self.updateInfo()
return()

def stop(self):
_BlockDev.lvm.lvdeactivate(self.vg.name,
self.name,
None)
self.updateInfo()
return()

def updateInfo(self):
if not self.created:
return()
_info = _BlockDev.lvm.lvinfo(self.vg.name, self.name)
# TODO: parity with lvm_fallback.LV.updateInfo
# key names currently (probably) don't match and need to confirm the information's all present
info = {}
for k in dir(_info):
if k.startswith('_'):
continue
elif k in ('copy',):
continue
v = getattr(_info, k)
info[k] = v
self.info = info
return()

View File

@ -104,11 +104,11 @@ class Array(object):
self.layout = None # TODO: log.warn? self.layout = None # TODO: log.warn?
else: else:
self.layout = None self.layout = None
self.devname = self.xml.attrib['name'] self.name = self.xml.attrib['name']
self.fulldevname = '{0}:{1}'.format(self.homehost, self.devname) self.fullname = '{0}:{1}'.format(self.homehost, self.name)
self.devpath = devpath self.devpath = devpath
if not self.devpath: if not self.devpath:
self.devpath = '/dev/md/{0}'.format(self.devname) self.devpath = '/dev/md/{0}'.format(self.name)
self.updateStatus() self.updateStatus()
self.homehost = homehost self.homehost = homehost
self.members = [] self.members = []
@ -128,11 +128,11 @@ class Array(object):
opts = [_BlockDev.ExtraArg.new('--homehost', opts = [_BlockDev.ExtraArg.new('--homehost',
self.homehost), self.homehost),
_BlockDev.ExtraArg.new('--name', _BlockDev.ExtraArg.new('--name',
self.devname)] self.name)]
if self.layout: if self.layout:
opts.append(_BlockDev.ExtraArg.new('--layout', opts.append(_BlockDev.ExtraArg.new('--layout',
self.layout)) self.layout))
_BlockDev.md.create(self.devname, _BlockDev.md.create(self.name,
str(self.level), str(self.level),
[i.devpath for i in self.members], [i.devpath for i in self.members],
0, 0,
@ -154,7 +154,7 @@ class Array(object):
if scan: if scan:
target = None target = None
else: else:
target = self.devname target = self.name
_BlockDev.md.activate(target, _BlockDev.md.activate(target,
[i.devpath for i in self.members], # Ignored if scan mode enabled [i.devpath for i in self.members], # Ignored if scan mode enabled
None, None,
@ -164,12 +164,12 @@ class Array(object):
return() return()


def stop(self): def stop(self):
_BlockDev.md.deactivate(self.devname) _BlockDev.md.deactivate(self.name)
self.state = 'disassembled' self.state = 'disassembled'
return() return()


def updateStatus(self): def updateStatus(self):
_status = _BlockDev.md.detail(self.devname) _status = _BlockDev.md.detail(self.name)
# TODO: parity with mdadm_fallback.Array.updateStatus # TODO: parity with mdadm_fallback.Array.updateStatus
# key names currently (probably) don't match and need to confirm the information's all present # key names currently (probably) don't match and need to confirm the information's all present
info = {} info = {}

View File

@ -158,10 +158,10 @@ class Array(object):
self.layout = None # TODO: log.warn? self.layout = None # TODO: log.warn?
else: else:
self.layout = None self.layout = None
self.devname = self.xml.attrib['name'] self.name = self.xml.attrib['name']
self.devpath = devpath self.devpath = devpath
if not self.devpath: if not self.devpath:
self.devpath = '/dev/md/{0}'.format(self.devname) self.devpath = '/dev/md/{0}'.format(self.name)
self.updateStatus() self.updateStatus()
self.homehost = homehost self.homehost = homehost
self.members = [] self.members = []
@ -179,7 +179,7 @@ class Array(object):
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')
cmd = ['mdadm', '--create', cmd = ['mdadm', '--create',
'--name={0}'.format(self.devname), '--name={0}'.format(self.name),
'--bitmap=internal', '--bitmap=internal',
'--level={0}'.format(self.level), '--level={0}'.format(self.level),
'--metadata={0}'.format(self.metadata), '--metadata={0}'.format(self.metadata),
@ -224,7 +224,7 @@ class Array(object):
def updateStatus(self): def updateStatus(self):
_info = mdstat.parse() _info = mdstat.parse()
for k, v in _info['devices'].items(): for k, v in _info['devices'].items():
if k != self.devname: if k != self.name:
del(_info['devices'][k]) del(_info['devices'][k])
self.info = copy.deepcopy(_info) self.info = copy.deepcopy(_info)
return() return()

View File

@ -31,15 +31,15 @@
</disk> </disk>
</blockDevices> </blockDevices>
<!-- "Special" devices are processed *in the order they are specified*. This is important if you wish to <!-- "Special" devices are processed *in the order they are specified*. This is important if you wish to
e.g. layer LUKS on top of LVM - you would specify <lvm> before <luks> and reference the e.g. layer LVM on top of LUKS - you would specify <lvm> before <luks> and reference the
<luksDev id="SOMETHING" ... > as <lvmLogical source="SOMETHING" ... />. <luksDev id="SOMETHING" ... > as <pv source="SOMETHING" ... />.
Of course, a limitation of this is you cannot e.g. first assemble a LUKS volume, then an LVM Of course, a limitation of this is you cannot e.g. first assemble a LUKS volume, then an LVM
group, and then another LUKS volume - so plan accordingly and/or perform this in a <post> script. --> group, and then another LUKS volume - so plan accordingly and/or perform that in
a <post> script instead. -->
<luks> <luks>
<luksDev id="luks_secrets" name="secrets" source="secrets1"> <luksDev id="luks_secrets" name="secrets" source="secrets1">
<!-- You can assign multiple secrets (or "keys") to a LUKS volume. --> <!-- You can assign multiple secrets (or "keys") to a LUKS volume. -->
<secrets <secrets>
>
<!-- A simple passphrase. --> <!-- A simple passphrase. -->
<passphrase>secrets1</passphrase> <passphrase>secrets1</passphrase>
</secrets> </secrets>
@ -63,7 +63,7 @@
<logicalVolumes> <logicalVolumes>
<!-- Default is to add all available PVs in PhysicalVolumes... --> <!-- Default is to add all available PVs in PhysicalVolumes... -->
<lv id="lv1" name="logical1" size="80%"/> <lv id="lv1" name="logical1" size="80%"/>
<!-- But you can also explicitly designate them. --> <!-- But you can also explicitly designate them. They have to still be in the same volumeGroup though. -->
<lv id="lv2" name="logical2" size="20%"> <lv id="lv2" name="logical2" size="20%">
<pvMember source="pv1"/> <pvMember source="pv1"/>
</lv> </lv>

63
examples/most_minimal.xml Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" ?>
<aif xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://aif-ng.io/"
xsi:schemaLocation="http://aif-ng.io/ http://aif-ng.io/aif.xsd"
version="v2_rewrite">
<storage>
<blockDevices>
<disk id="sda" device="auto" diskFormat="gpt">
<part id="boot" name="BOOT" start="0%" stop="10%" fsType="fat32">
<partitionFlag>esp</partitionFlag>
</part>
<part id="root" name="root" start="10%" stop="100%" fsType="ext4">
<partitionFlag>root</partitionFlag>
</part>
</disk>
</blockDevices>
<fileSystems>
<fs id="esp" source="boot" type="vfat">
<opt name="-F">32</opt>
</fs>
<fs id="rootfs" type="ext4" source="root"/>
</fileSystems>
<mountPoints>
<mount source="rootfs" target="/mnt/aif/"/>
<mount source="esp" target="/mnt/aif/boot"/>
</mountPoints>
</storage>
<network hostname="aiftest.square-r00t.net">
<iface device="auto">
<addresses>
<ipv4>
<address>dhcp</address>
</ipv4>
<ipv6>
<address>slaac</address>
</ipv6>
</addresses>
<resolvers>
<resolver>4.2.2.1</resolver>
<resolver>4.2.2.2</resolver>
<resolver>4.2.2.3</resolver>
</resolvers>
</iface>
</network>
<system timezone="UTC" chrootPath="/mnt/aif" reboot="1">
<locales>
<locale name="LANG">en_US.UTF-8</locale>
</locales>
</system>
<pacman>
<repos>
<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="community" enabled="true" sigLevel="default" mirror="file:///etc/pacman.d/mirrorlist"/>
<repo name="multilib" enabled="true" sigLevel="default" mirror="file:///etc/pacman.d/mirrorlist"/>
</repos>
<mirrorList>
<mirror>http://arch.mirror.square-r00t.net/$repo/os/$arch</mirror>
</mirrorList>
</pacman>
<bootloader type="grub" target="/boot" efi="true"/>
</aif>