checking in

This commit is contained in:
brent s. 2018-05-10 00:30:50 -04:00
parent a315468ff8
commit 7819b5edc4
8 changed files with 269 additions and 24 deletions

0
COPYING Normal file
View File

1
LICENSE Symbolic link
View File

@ -0,0 +1 @@
COPYING

3
TODO
View File

@ -1,7 +1,8 @@
- write classes/functions - write classes/functions
- XML-based config - XML-based config
- ensure we use docstrings in a Sphinx-compatible manner. - ensure we use docstrings in a Sphinx-compatible manner?
https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html
at the very least document all the functions and such so pydoc's happy.


- package for PyPI: - package for PyPI:
# https://packaging.python.org/tutorials/distributing-packages/ # https://packaging.python.org/tutorials/distributing-packages/

View File

@ -2,6 +2,9 @@ import os
import platform import platform
import sys import sys


"""
BDisk - An easy liveCD creator built in python.
"""


# BDisk is only supported on Python 3.4 and up. # BDisk is only supported on Python 3.4 and up.
if sys.version_info.major != 3: if sys.version_info.major != 3:

0
bdisk/bdisk.xsd Normal file
View File

View File

@ -1,29 +1,158 @@
import _io
import copy
import os
import validators import validators
from urllib.parse import urlparse from urllib.parse import urlparse
try: import lxml.etree
from lxml import etree import lxml.objectify as objectify
has_lxml = True
except ImportError:
import xml.etree.ElementTree as etree
has_lxml = False


"""Read a configuration file, parse it, and make it available to the rest of etree = lxml.etree
BDisk."""
_profile_specifiers = ('id', 'name', 'uuid')

def _detect_cfg(cfg):
if isinstance(cfg, str):
# check for path or string
try:
etree.fromstring(cfg)
except lxml.etree.XMLSyntaxError:
path = os.path.abspath(os.path.expanduser(cfg))
try:
with open(path, 'r') as f:
cfg = f.read()
except FileNotFoundError:
raise ValueError('Could not open {0}'.format(path))
elif isinstance(cfg, _io.TextIOWrapper):
_cfg = cfg.read()
cfg.close()
cfg = _cfg
elif isinstance(self.cfg, _io.BufferedReader):
_cfg = cfg.read().decode('utf-8')
cfg.close()
cfg = _cfg
elif isinstance(cfg, bytes):
cfg = cfg.decode('utf-8')
else:
raise TypeError('Could not determine the object type.')
return(cfg)

def _profile_xpath_gen(selector):
xpath = ''
for i in selector.items():
if i[1] and i[0] in _profile_specifiers:
xpath += '[@{0}="{1}"]'.format(*i)
return(xpath)


class Conf(object): class Conf(object):
def __init__(self, cfg, profile = None, id_type = 'name'): def __init__(self, cfg, profile = None):
"""Conf classes accept the following parameters: """
cfg - The configuration. Can be a filesystem path, a string, bytes, A configuration object.
or a stream


profile (optional) - A sub-profile in the configuration. If None is Read a configuration file, parse it, and make it available to the rest
provided, we'll first look for a profile named of BDisk.
'default'. If one isn't found, then the first
profile found will be used
id_type (optional) - The type of identifer to use for profile=.
Valid values are:


id Args:
name
uuid""" cfg The configuration. Can be a filesystem path, a string,
bytes, or a stream. If bytes or a bytestream, it must be
in UTF-8 format.

profile (optional) A sub-profile in the configuration. If None
is provided, we'll first look for the first profile
named 'default' (case-insensitive). If one isn't found,
then the first profile found will be used. Can be a
string (in which we'll automatically search for the
given value in the "name" attribute) or a dict for more
fine-grained profile identification, such as:

{'name': 'PROFILE_NAME',
'id': 1,
'uuid': '00000000-0000-0000-0000-000000000000'}

You can provide any combination of these
(e.g. "profile={'id': 2, 'name' = 'some_profile'}").
"""
self.raw = _detect_cfg(cfg)
self.profile = profile
self.xml = None
self.profile = None
self.xml = etree.from_string(self.cfg)
self.xsd = None
#if not self.validate(): # Need to write the XSD
# raise ValueError('The configuration did not pass XSD/schema '
# 'validation')
self.get_profile()
self.max_recurse = int(self.profile.xpath('//meta/'
'max_recurse')[0].text)

def get_xsd(self):
path = os.path.join(os.path.dirname(__file__),
'bdisk.xsd')
with open(path, 'r') as f:
xsd = f.read()
return(xsd)

def validate(self):
self.xsd = etree.XMLSchema(self.get_xsd())
return(self.xsd.validate(self.xml))

def get_profile(self):
"""Get a configuration profile.

Get a configuration profile from the XML object and set that as a
profile object. If a profile is specified, attempt to find it. If not,
follow the default rules as specified in __init__.
"""
if self.profile:
# A profile identifier was provided
if isinstance(self.profile, str):
_profile_name = self.profile
self.profile = {}
for i in _profile_specifiers:
self.profile[i] = None
self.profile['name'] = _profile_name
elif isinstance(self.profile, dict):
for k in _profile_specifiers:
if k not in self.profile.keys():
self.profile[k] = None
else:
raise TypeError('profile must be a string (name of profile), '
'a dictionary, or None')
xpath = ('/bdisk/'
'profile{0}').format(_profile_xpath_gen(self.profile))
self.profile = self.xml.xpath(xpath)
if not self.profile:
raise RuntimeError('Could not find the profile specified in '
'the given configuration')
else:
# We need to find the default.
profiles = []
for p in self.xml.xpath('/bdisk/profile'):
profiles.append(p)
# Look for one named "default" or "DEFAULT" etc.
for idx, value in enumerate([e.attrib['name'].lower() \
for e in profiles]):
if value == 'default':
self.profile = copy.deepcopy(profiles[idx])
break
# We couldn't find a profile with a default name. Try to grab the
# first profile.
if not self.profile:
# Grab the first profile.
if profiles:
self.profile = profile[0]
else:
# No profiles found.
raise RuntimeError('Could not find any usable '
'configuration profiles')
return()

def parse_profile(self):
pass pass

def _xpath_ref(self, element):
data = None
# This is incremented each recursive call until we reach
# self.max_recurse
recurse_cnt = 1
return(data)

View File

@ -1,15 +1,30 @@
#!/usr/bin/env python3.6 #!/usr/bin/env python3.6


import argparse import argparse
import confparse


"""The primary user interface for BDisk. If we are running interactively, """The primary user interface for BDisk. If we are running interactively,
parse arguments first, then initiate a BDisk session.""" parse arguments first, then initiate a BDisk session."""


def parseArgs(): def parseArgs():
pass args = argparse.ArgumentParser(description = ('An easy liveCD creator '
'built in python. Supports '
'hybrid ISOs/USB, iPXE, and '
'UEFI.'),
epilog = ('https://git.square-r00t.net'))
return(args)


def run(): def run():
pass pass


def run_interactive(): def run_interactive():
pass args = vars(parseArgs().parse_args())
args['profile'] = {}
for i in ('name', 'id', 'uuid'):
args['profile'][i] = args[i]
del(args[i])
run(args)
return()

if __name__ == '__main__':
main()

View File

@ -110,4 +110,100 @@
</sync> </sync>
</build> </build>
</profile> </profile>
<profile name="alternate" id="2" uuid="4f09e014-0827-41f8-b7bd-4faf9fdcf58f">
<meta>
<names>
<name>Another Disk</name>
<uxname>livecd2</uxname>
<pname><xpath-ref select="../name" /></pname>
</names>
<desc>Some other rescue/restore live environment.</desc>
<dev>
<author>Another Dev Eloper</author>
<email>dev2@domain.tld</email>
<website>https://domain.tld/~dev2</website>
</dev>
<uri>https://domain.tld/projname</uri>
<ver>0.0.1</ver>
<max_recurse>3</max_recurse>
</meta>
<accounts>
<!-- Yep, you guessed it; "test" -->
<rootpass hashed="yes"
salt="sha512">
$6$yR0lsi68GZ.8oAuV$juLOanZ6IGD6caxJFo5knnXwFZRi65Q58a1XfSWBX7R97EpHrVgpzdXfA3ysAfAg4bs1d6wBv7su2rURkg2rn.
</rootpass>
</accounts>
<sources>
<source arch="x86_64">
<mirror>http://archlinux.mirror.domain.tld</mirror>
<webroot>/iso/latest</webroot>
<tarball flags="glob,latest">
<xpath-ref select="../mirror" />/<xpath-ref select="../webroot" />/archlinux-bootstrap-*-x86_64.tar.gz
</tarball>
<checksum hash="sha1">
<xpath-ref select="../mirror" />/<xpath-ref select="../webroot" />/sha1sums.txt
</checksum>
<sig keys="7F2D434B9741E8AC"
keyserver="hkp://pool.sks-keyservers.net">
<xpath-ref select="../tarball" />.sig
</sig>
</source>
<source arch="i686">
<mirror>http://archlinux32.mirror.domain.tld</mirror>
<webroot>/iso/latest</webroot>
<tarball flags="glob,latest">
<xpath-ref select="../mirror" />/<xpath-ref select="../webroot" />/archlinux-bootstrap-*-i686.tar.gz
</tarball>
<checksum hash="sha512">
<xpath-ref select="../mirror" />/<xpath-ref select="../webroot" />/sha512sums.txt
</checksum>
<sig keys="248BF41F9BDD61D41D060AE774EDA3C6B06D0506"
keyserver="hkp://pool.sks-keyservers.net">
<xpath-ref select="../tarball" />.sig
</sig>
</source>
</sources>
<build its_full_of_stars="yes">
<paths>
<cache>/var/tmp/<xpath-ref select="//meta/names/uxname" /></cache>
<chroot>/var/tmp/chroots/<xpath-ref select="//meta/names/uxname" /></chroot>
<templates>~/<xpath-ref select="//meta/names/uxname" />/templates</templates>
<mount>/mnt/<xpath-ref select="//meta/names/uxname" /></mount>
<distros>~/<xpath-ref select="//meta/names/uxname" />/distros</distros>
<dest>~/<xpath-ref select="//meta/names/uxname" />/results</dest>
<iso><xpath-ref select="../dest" />/iso</iso>
<http><xpath-ref select="../dest" />/http</http>
<tftp><xpath-ref select="../dest" />/tftp</tftp>
<ssl><xpath-ref select="../dest" />/pki</ssl>
</paths>
<basedistro>archlinux</basedistro>
<iso sign="yes" sync="yes" multiarch="yes" rsync="yes"/>
<ipxe sign="yes" sync="yes" iso="yes" rsync="yes">
<ssl>
<ca><xpath-ref select="build/paths/ssl" />/ca.crt</ca>
<ca_key><xpath-ref select="build/paths/ssl" />/ca.key</ca_key>
<crt>
<xpath-ref select="build/paths/ssl" />/<xpath-ref select="//meta/names/uxname" />.crt
</crt>
<key>
<xpath-ref select="build/paths/ssl" />/<xpath-ref select="//meta/names/uxname" />.key
</key>
</ssl>
<uri><xpath-ref select="meta/dev/website" />/ipxe</uri>
</ipxe>
<gpg keyid="none" gnupghome="none" publish="no" sync="yes" />
<sync>
<http enabled="yes" rsync="yes" />
<tftp enabled="yes" rsync="yes" />
<rsync enabled="yes">
<user>root</user>
<path>/srv/http/<xpath-ref select="//meta/names/uxname" /></path>
<host>mirror.domain.tld</host>
<port>22</port>
<pubkey>~/.ssh/id_ed25519</pubkey>
</rsync>
</sync>
</build>
</profile>
</bdisk> </bdisk>