i should commit this.

This commit is contained in:
brent s. 2018-05-24 08:24:46 -04:00
parent b134ee67bd
commit 9f74e97c45
7 changed files with 1076 additions and 369 deletions

View File

@ -3,9 +3,247 @@
targetNamespace="http://bdisk.square-r00t.net/"
xmlns="http://bdisk.square-r00t.net/"
elementFormDefault="qualified">

<!-- CUSTOM TYPES -->
<!-- t_btag_uri: a string that will allow btags (xpath or variable only) or a URI string (but NOT a URN). -->
<!-- We can't use xs:anyURI because it is too loose (allows things like relative paths, etc.) -->
<!-- but ALSO too restrictive in that btags fail validation ({ and } are invalid for anyURI, -->
<!-- ironically). -->
<xs:simpleType name="t_btag_uri">
<xs:restriction base="xs:string">
<xs:pattern value="\w+:(/?/?)[^\s]+"/>
<xs:pattern value=".*\{variable%[A-Za-z0-9_]\}.*"/>
<xs:pattern value=".*\{xpath%[A-Za-z0-9_/\(\)\.\*@\-]+\}.*"/>
</xs:restriction>
</xs:simpleType>
<!-- END t_btag_uri -->

<!-- t_filename: a POSIX fully-portable filename. -->
<xs:simpleType name="t_filename">
<xs:restriction base="xs:string">
<xs:pattern value="([a-z0-9._-]+){1,255}"/>
<xs:pattern value=".*\{variable%[A-Za-z0-9_]\}.*"/>
<xs:pattern value=".*\{xpath%[A-Za-z0-9_/\(\)\.\*@\-]+\}.*"/>
<!-- We don't allow (string)(regex) or (regex)(string) or (string)(regex)(string) or multiple regexes -->
<!-- because that's just... not feasible to manage from a parsing perspective. -->
<xs:pattern value="\{regex%.+\}"/>
</xs:restriction>
</xs:simpleType>
<!-- END t_filename -->

<!-- t_gpg_keyid: a set of various patterns that match GPG key IDs. -->
<xs:simpleType name="t_gpg_keyid">
<xs:restriction base="xs:string">
<xs:pattern value="(none|new)"/>
<xs:pattern value="(auto|default)"/>
<xs:pattern value="(0x)?[0-9A-Fa-f]{40}"/>
<xs:pattern value="(0x)?[0-9A-Fa-f]{16}"/>
<xs:pattern value="(0x)?[0-9A-Fa-f]{8}"/>
<xs:pattern value="([0-9A-Fa-f ]{4}){5} ?([0-9A-Fa-f ]{4}){4}[0-9A-Fa-f]{4}"/>
</xs:restriction>
</xs:simpleType>
<!-- END t_gpg_keyid -->

<!-- t_gpg_keyid_list: a type for a list of key IDs. -->
<xs:simpleType name="t_gpg_keyid_list">
<xs:list itemType="t_gpg_keyid"/>
</xs:simpleType>
<!-- END t_gpg_key_list -->

<!-- t_net_loc: a remote host. Used for PKI Subject's commonName and host for rsync. -->
<xs:simpleType name="t_net_loc">
<xs:restriction base="xs:string">
<xs:pattern
value="(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])"/>
</xs:restriction>
</xs:simpleType>
<!-- END t_net_loc -->

<!-- t_pass_hash_algo: used for t_password. -->
<xs:simpleType name="t_pass_hash_algo">
<xs:restriction base="xs:string">
<xs:enumeration value="des"/>
<xs:enumeration value="md5"/>
<xs:enumeration value="sha256"/>
<xs:enumeration value="sha512"/>
</xs:restriction>
</xs:simpleType>
<!-- END t_pass_hash_algo -->

<!-- t_pass_salt: used for t_password. -->
<xs:simpleType name="t_pass_salt">
<xs:restriction base="xs:string">
<xs:pattern value="($[156]($rounds=[0-9]+)?$[a-zA-Z0-9./]{1,16}$?|auto|)"/>
<xs:pattern value="\{variable%[A-Za-z0-9_]\}"/>
<xs:pattern value="\{xpath%[A-Za-z0-9_\(\)\.\*\-/]+\}"/>
</xs:restriction>
</xs:simpleType>
<!-- END t_pass_salt -->

<!-- t_password: used for rootpass and user/password elements. -->
<xs:complexType name="t_password">
<!-- The below will need some fleshing out and testing. It may not be possible strictly via XSD. -->
<!-- TODO: restrict the value further with a union or multi-group regex that checks for a valid length? -->
<!-- des: ????? -->
<!-- md5: "[a-zA-Z0-9./]{22}" -->
<!-- sha256: "[a-zA-Z0-9./]{43}" -->
<!-- sha512: "[a-zA-Z0-9./]{86}" -->
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="hash_algo" type="t_pass_hash_algo"/>
<xs:attribute name="hashed" type="xs:boolean" use="required"/>
<xs:attribute name="salt" type="t_pass_salt"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- END t_password -->

<!-- t_path: for specifying subdirectories (either local filesystem or remote paths). -->
<xs:simpleType name="t_path">
<xs:restriction base="xs:string">
<!-- We include blank to operate on default actions (or default filepaths). -->
<xs:pattern value=""/>
<xs:pattern value="(.+)/([^/]+)"/>
<xs:pattern value="((.+)/([^/]+))?\{variable%[A-Za-z0-9_]\}((.+)/([^/]+))?"/>
<xs:pattern value="((.+)/([^/]+))?\{xpath%[A-Za-z0-9_\(\)\.\*\-/]+\}((.+)/([^/]+))?"/>
</xs:restriction>
</xs:simpleType>
<!-- END t_path -->

<!-- t_pki_cert: used for pki/ca/cert and pki/client/cert. -->
<xs:complexType name="t_pki_cert">
<xs:simpleContent>
<xs:extension base="t_path">
<xs:attribute name="hash_algo" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="blake2b512"/>
<xs:enumeration value="blake2s256"/>
<xs:enumeration value="gost"/>
<xs:enumeration value="md4"/>
<xs:enumeration value="md5"/>
<xs:enumeration value="mdc2"/>
<xs:enumeration value="rmd160"/>
<xs:enumeration value="sha1"/>
<xs:enumeration value="sha224"/>
<xs:enumeration value="sha256"/>
<xs:enumeration value="sha384"/>
<xs:enumeration value="sha512"/>
<xs:enumeration value="none"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- END t_pki_cert -->

<!-- t_pki_key: used for pki/ca/key and pki/client/key -->
<xs:complexType name="t_pki_key">
<xs:simpleContent>
<xs:extension base="t_path">
<xs:attribute name="cipher" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="aes128"/>
<xs:enumeration value="aes192"/>
<xs:enumeration value="bf"/>
<xs:enumeration value="blowfish"/>
<xs:enumeration value="camellia128"/>
<xs:enumeration value="camellia192"/>
<xs:enumeration value="camellia256"/>
<xs:enumeration value="des"/>
<xs:enumeration value="rc2"/>
<xs:enumeration value="seed"/>
<xs:enumeration value="none"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="passphrase" type="xs:string"/>
<xs:attribute name="keysize"
type="xs:positiveInteger"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- END t_pki_key -->

<!-- t_pki_subject: used for pki/ca/subject and pki/client/subject -->
<xs:complexType name="t_pki_subject">
<xs:all>
<!-- .../SUBJECT/COMMONNAME -->
<xs:element name="commonName" type="t_net_loc"/>
<!-- END .../SUBJECT/COMMONNAME -->
<!-- .../SUBJECT/COUNTRYNAME -->
<xs:element name="countryName">
<xs:simpleType>
<xs:restriction base="xs:string">
<!-- We can't validate an actual ISO-3166 ALPHA-2 code, but we can validate the format. -->
<!-- TODO: maybe cron the generation of an external namespace? -->
<xs:pattern value="[A-Z]{2}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<!-- END .../SUBJECT/COUNTRYNAME -->
<!-- .../SUBJECT/LOCALITYNAME -->
<xs:element name="localityName" type="xs:string"/>
<!-- END .../SUBJECT/LOCALITYNAME -->
<!-- .../SUBJECT/STATEORPROVINCENAME -->
<xs:element name="stateOrProvinceName"
type="xs:string"/>
<!-- END .../SUBJECT/STATEORPROVINCENAME -->
<!-- .../SUBJECT/ORGANIZATION -->
<xs:element name="organization" type="xs:string"/>
<!-- END .../SUBJECT/ORGANIZATION -->
<!-- .../SUBJECT/ORGANIZATIONALUNITNAME -->
<xs:element name="organizationalUnitName"
type="xs:string"/>
<!-- END .../SUBJECT/ORGANIZATIONALUNITNAME -->
<!-- .../SUBJECT/EMAILADDRESS -->
<xs:element name="emailAddress" type="xs:string"/>
<!-- END .../SUBJECT/EMAILADDRESS -->
</xs:all>
</xs:complexType>
<!-- END t_pki_subject -->

<!-- t_remote_file: an element that lets us define both a file pattern for remote content and flags attribute. -->
<xs:complexType name="t_remote_file">
<xs:simpleContent>
<xs:extension base="t_filename">
<xs:attribute name="flags" type="t_remote_file_flags" use="optional"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- END t_remote_file -->

<!-- t_remote_file_flags: a type to match a list of known flags. -->
<xs:simpleType name="t_remote_file_flags">
<xs:list>
<xs:simpleType>
<xs:restriction base="xs:string">
<!-- Currently we only support two flags. -->
<xs:enumeration value="regex"/>
<xs:enumeration value="latest"/>
</xs:restriction>
</xs:simpleType>
</xs:list>
</xs:simpleType>
<!-- END t_remote_file_flags -->

<!-- t_username: enforce a POSIX-compliant username. Used for user/username elements. -->
<xs:simpleType name="t_username">
<xs:restriction base="xs:string">
<xs:pattern value="[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}$)"/>
<xs:pattern value="\{variable%[A-Za-z0-9_]\}"/>
<xs:pattern value="\{xpath%[A-Za-z0-9_\(\)\.\*\-/]+\}"/>
</xs:restriction>
</xs:simpleType>
<!-- END t_username -->
<!-- END CUSTOM TYPES -->

<!-- ROOT ELEMENT ("BDISK") -->
<xs:element name="bdisk">
<xs:complexType>
<!-- Should this be xs:sequence instead? -->
<xs:choice>
<!-- BDISK/PROFILE -->
<xs:element name="profile" maxOccurs="unbounded" minOccurs="1">
@ -23,8 +261,9 @@
<xs:element name="name" maxOccurs="1" minOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern
value="(\{(xpath|variable)%[A-Za-z0-9_]\}|[A-Z0-9]{1,8})"/>
<xs:pattern value="[A-Z0-9]{1,8}"/>
<xs:pattern value="\{variable%[A-Za-z0-9_]\}"/>
<xs:pattern value="\{xpath%[A-Za-z0-9_\(\)\.\*\-/]+\}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
@ -33,8 +272,12 @@
<xs:element name="uxname" maxOccurs="1" minOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern
value="(\{(xpath|variable)%[A-Za-z0-9_]+\}|[A-Za-z0-9]{1,255})"/>
<!-- refer to the 2009 POSIX spec, "3.282 Portable Filename Character Set" -->
<!-- http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282 -->
<!-- (We use this string to name some files.) -->
<xs:pattern value="([a-z0-9._-]+){1,255}"/>
<xs:pattern value="\{variable%[A-Za-z0-9_]\}"/>
<xs:pattern value="\{xpath%[A-Za-z0-9_\(\)\.\*\-/]+\}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
@ -43,6 +286,7 @@
<xs:element name="pname" maxOccurs="1" minOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<!-- TODO: Can I use UTF-8 instead? -->
<!-- https://stackoverflow.com/a/9805789/733214 -->
<xs:pattern value="\p{IsBasicLatin}*"/>
</xs:restriction>
@ -57,10 +301,30 @@
<xs:element name="desc" maxOccurs="1" minOccurs="1" type="xs:string"/>
<!-- END BDISK/PROFILE/META/DESC -->
<!-- BDISK/PROFILE/META/DEV -->
<xs:element name="dev" maxOccurs="1" minOccurs="1" type="xs:string"/>
<xs:element name="dev" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:all>
<!-- BDISK/PROFILE/META/DEV/AUTHOR -->
<xs:element name="author" maxOccurs="1" minOccurs="1"
type="xs:string"/>
<!-- END BDISK/PROFILE/META/DEV/AUTHOR -->
<!-- BDISK/PROFILE/META/DEV/EMAIL -->
<!-- The following does NOT WORK. Shame, really. -->
<!-- It seems to be an invalid pattern per my XSD validator (xmllint). -->
<!--<xs:pattern value="([!#-&apos;*+/-9=?A-Z^-~-]+(\.[!#-&apos;*+/-9=?A-Z^-~-]+)*|&quot;([]!#-[^-~ \t]|(\\[\t -~]))+&quot;)@([!#-&apos;*+/-9=?A-Z^-~-]+(\.[!#-&apos;*+/-9=?A-Z^-~-]+)*|\[[\t -Z^-~]*])"/>-->
<xs:element name="email" maxOccurs="1" minOccurs="1"
type="xs:string"/>
<!-- END BDISK/PROFILE/META/DEV/EMAIL -->
<!-- BDISK/PROFILE/META/DEV/WEBSITE -->
<xs:element name="website" maxOccurs="1" minOccurs="1"
type="t_btag_uri"/>
<!-- END BDISK/PROFILE/META/DEV/WEBSITE -->
</xs:all>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/META/DEV -->
<!-- BDISK/PROFILE/META/URI -->
<xs:element name="uri" maxOccurs="1" minOccurs="1" type="xs:anyURI"/>
<xs:element name="uri" maxOccurs="1" minOccurs="1" type="t_btag_uri"/>
<!-- END BDISK/PROFILE/META/URI -->
<!-- BDISK/PROFILE/META/VER -->
<xs:element name="ver" maxOccurs="1" minOccurs="1" type="xs:string"/>
@ -114,29 +378,427 @@
</xs:element>
<!-- END BDISK/PROFILE/META -->
<!-- BDISK/PROFILE/ACCOUNTS -->
<xs:element name="accounts" maxOccurs="1" minOccurs="1"/>
<xs:element name="accounts" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:sequence>
<!-- BDISK/PROFILE/ACCOUNTS/ROOTPASS -->
<xs:element name="rootpass" maxOccurs="1" minOccurs="1" type="t_password"/>
<!-- END BDISK/PROFILE/ACCOUNTS/ROOTPASS -->
<!-- BDISK/PROFILE/ACCOUNTS/USER -->
<xs:element name="user" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:all>
<!-- BDISK/PROFILE/ACCOUNTS/USER/USERNAME -->
<xs:element name="username" type="t_username" minOccurs="1"
maxOccurs="1"/>
<!-- END BDISK/PROFILE/ACCOUNTS/USER/USERNAME -->
<!-- BDISK/PROFILE/ACCOUNTS/USER/COMMENT -->
<xs:element name="comment" type="xs:string" maxOccurs="1"
minOccurs="0"/>
<!-- END BDISK/PROFILE/ACCOUNTS/USER/COMMENT -->
<!-- BDISK/PROFILE/ACCOUNTS/USER/PASSWORD -->
<xs:element name="password" type="t_password" maxOccurs="1"
minOccurs="1"/>
<!-- END BDISK/PROFILE/ACCOUNTS/USER/PASSWORD -->
</xs:all>
<xs:attribute name="sudo" type="xs:boolean" use="optional"/>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/ACCOUNTS/USER -->
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/ACCOUNTS -->
<!-- BDISK/PROFILE/BUILD-->
<xs:element name="build" maxOccurs="1" minOccurs="1"/>
<!-- BDISK/PROFILE/SOURCES -->
<xs:element name="sources" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:sequence>
<!-- BDisk only supports two different architectures (x86/i686 and x86_64, respectively) currently. -->
<!-- TODO: future improvements may let us include e.g. two different x86_64 environments (e.g. CentOS and Debian on the same media), but this is like, still in development stages. -->
<!-- BDISK/PROFILE/SOURCES/SOURCE -->
<xs:element name="source" minOccurs="1" maxOccurs="2">
<xs:complexType>
<xs:all>
<!-- We cheat here. TECHNICALLY it should ONLY be scheme://location (no /path...), but there isn't a data type for that. -->
<!-- Currently we enforce only one item. Future BDisk versions may be able to make use of multiple <mirror>s and select best one based on speed. -->
<!-- BDISK/PROFILE/SOURCES/SOURCE/MIRROR -->
<xs:element name="mirror" type="t_btag_uri" maxOccurs="1"
minOccurs="1"/>
<!-- END BDISK/PROFILE/SOURCES/SOURCE/MIRROR -->
<!-- BDISK/PROFILE/SOURCES/SOURCE/ROOTPATH -->
<xs:element name="rootpath" maxOccurs="1" minOccurs="1"
type="t_path"/>
<!-- END BDISK/PROFILE/SOURCES/SOURCE/ROOTPATH -->
<!-- BDISK/PROFILE/SOURCES/SOURCE/TARBALL -->
<xs:element name="tarball" maxOccurs="1" minOccurs="1"
type="t_remote_file"/>
<!-- END BDISK/PROFILE/SOURCES/SOURCE/TARBALL -->
<!-- BDISK/PROFILE/SOURCES/SOURCE/CHECKSUM -->
<xs:element name="checksum" maxOccurs="1" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="t_remote_file">
<!-- There is NO way we can validate this, because it will vary based on the algorithms supported by the build host. -->
<xs:attribute name="hash_algo" type="xs:string" use="required"/>
<xs:attribute name="explicit" type="xs:boolean" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SOURCES/SOURCE/CHECKSUM -->
<!-- BDISK/PROFILE/SOURCES/SOURCE/SIG -->
<xs:element name="sig" maxOccurs="1" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="t_remote_file">
<!-- Required; otherwise there's no point using it. -->
<xs:attribute name="keys" type="t_gpg_keyid_list" use="required"/>
<xs:attribute name="keyserver" type="t_btag_uri"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SOURCES/SOURCE/SIG-->
</xs:all>
<xs:attribute name="arch">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="(i686|x86(_64)?|32|64)"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SOURCES/SOURCE -->
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SOURCES -->
<!-- BDISK/PROFILE/BUILD -->
<xs:element name="build" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:all>
<!-- BDISK/PROFILE/BUILD/PATHS -->
<xs:element name="paths">
<xs:complexType>
<xs:all>
<!-- BDISK/PROFILE/BUILD/PATHS/BASE -->
<xs:element name="base" maxOccurs="1" minOccurs="1" type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/BASE -->
<!-- BDISK/PROFILE/BUILD/PATHS/CACHE -->
<xs:element name="cache" maxOccurs="1" minOccurs="1" type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/CACHE -->
<!-- BDISK/PROFILE/BUILD/PATHS/CHROOT -->
<xs:element name="chroot" maxOccurs="1" minOccurs="1"
type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/CHROOT -->
<!-- BDISK/PROFILE/BUILD/PATHS/OVERLAY -->
<xs:element name="overlay" maxOccurs="1" minOccurs="1"
type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/OVERLAY -->
<!-- BDISK/PROFILE/BUILD/PATHS/TEMPLATES -->
<xs:element name="templates" maxOccurs="1" minOccurs="1"
type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/TEMPLATES -->
<!-- BDISK/PROFILE/BUILD/PATHS/MOUNT -->
<xs:element name="mount" maxOccurs="1" minOccurs="1" type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/MOUNT -->
<!-- BDISK/PROFILE/BUILD/PATHS/DISTROS -->
<xs:element name="distros" maxOccurs="1" minOccurs="1"
type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/DISTROS -->
<!-- BDISK/PROFILE/BUILD/PATHS/DEST -->
<xs:element name="dest" maxOccurs="1" minOccurs="1" type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/DEST -->
<!-- BDISK/PROFILE/BUILD/PATHS/ISO -->
<xs:element name="iso" maxOccurs="1" minOccurs="1" type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/ISO -->
<!-- BDISK/PROFILE/BUILD/PATHS/HTTP -->
<xs:element name="http" maxOccurs="1" minOccurs="1" type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/HTTP -->
<!-- BDISK/PROFILE/BUILD/PATHS/TFTP -->
<xs:element name="tftp" maxOccurs="1" minOccurs="1" type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/TFTP -->
<!-- EBDISK/PROFILE/BUILD/PATHS/PKI -->
<xs:element name="pki" maxOccurs="1" minOccurs="1" type="t_path"/>
<!-- END BDISK/PROFILE/BUILD/PATHS/PKI -->
</xs:all>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/BUILD/PATHS -->
<!-- BDISK/PROFILE/BUILD/BASEDISTRO -->
<xs:element name="basedistro"/>
<!-- END BDISK/PROFILE/BUILD/BASEDISTRO -->
</xs:all>
<xs:attribute name="its_full_of_stars" type="xs:boolean"/>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/BUILD -->
<!-- BDISK/PROFILE/ISO -->
<xs:element name="iso" maxOccurs="1" minOccurs="1"/>
<xs:element name="iso" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:attribute name="sign" type="xs:boolean"/>
<xs:attribute name="multi_arch">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="yes"/>
<xs:enumeration value="no"/>
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
<xs:enumeration value="x86_64"/>
<xs:enumeration value="x86"/>
<xs:enumeration value="64"/>
<xs:enumeration value="32"/>
<xs:enumeration value="i686"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/ISO -->
<!-- BDISK/PROFILE/IPXE -->
<xs:element name="ipxe" maxOccurs="1" minOccurs="1"/>
<xs:element name="ipxe" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:all>
<!-- BDISK/PROFILE/IPXE/URI -->
<xs:element name="uri" type="t_btag_uri" maxOccurs="1" minOccurs="1"/>
<!-- END BDISK/PROFILE/IPXE/URI -->
</xs:all>
<xs:attribute name="sign" type="xs:boolean"/>
<xs:attribute name="iso" type="xs:boolean"/>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/IPXE -->
<!-- BDISK/PROFILE/GPG -->
<xs:element name="gpg" maxOccurs="1" minOccurs="1"/>
<xs:element name="gpg" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:sequence>
<!-- BDISK/PROFILE/GPG/KEY -->
<xs:element name="key" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:all>
<!-- BDISK/PROFILE/GPG/KEY/NAME -->
<xs:element name="name" type="xs:normalizedString" maxOccurs="1"
minOccurs="1"/>
<!-- END BDISK/PROFILE/GPG/KEY/NAME -->
<!-- BDISK/PROFILE/GPG/KEY/EMAIL -->
<xs:element name="email" type="xs:normalizedString" maxOccurs="1"
minOccurs="1"/>
<!-- END BDISK/PROFILE/GPG/KEY/EMAIL -->
<!-- BDISK/PROFILE/GPG/KEY/COMMENT -->
<xs:element name="comment" type="xs:string" maxOccurs="1"
minOccurs="0"/>
<!-- END BDISK/PROFILE/GPG/KEY/COMMENT -->
</xs:all>
<xs:attribute name="algo">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="rsa"/>
<xs:enumeration value="dsa"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<!-- We COULD constrain this further, but it's conditional upon the algo type. So we'll do that in BDisk itself. -->
<!-- But it may be possible? https://stackoverflow.com/a/39045446/733214 -->
<xs:attribute name="keysize" type="xs:positiveInteger"/>
<!-- XSD doesn't have a datatype for Epoch vs. 0 (for no expire). -->
<xs:attribute name="expire">
<xs:simpleType>
<!--This is xs:integer instead of xs:positiveInteger because 0 will fail validation then. -->
<xs:restriction base="xs:integer">
<xs:pattern value="(0|[0-9]{10})"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/GPG/KEY -->
</xs:sequence>
<xs:attribute name="keyid" type="t_gpg_keyid"/>
<xs:attribute name="publish" type="xs:boolean"/>
<xs:attribute name="prompt_passphrase" type="xs:boolean"/>
<xs:attribute name="gnupghome">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="(.+)/([^/]+)"/>
<xs:pattern
value="((.+)/([^/]+))?\{variable%[A-Za-z0-9_]\}((.+)/([^/]+))?"/>
<xs:pattern
value="((.+)/([^/]+))?\{xpath%[A-Za-z0-9_\(\)\.\*\-/]+\}((.+)/([^/]+))?"/>
<xs:pattern value="(none|)"/>
<xs:pattern value="(auto|default)"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/GPG -->
<!-- BDISK/PROFILE/PKI -->
<xs:element name="pki" maxOccurs="1" minOccurs="1"/>
<xs:element name="pki" maxOccurs="1" minOccurs="0">
<xs:complexType>
<xs:sequence>
<!-- BDISK/PROFILE/PKI/CA -->
<xs:element name="ca" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:all>
<!-- BDISK/PROFILE/PKI/CA/CERT -->
<xs:element name="cert" maxOccurs="1" minOccurs="1"
type="t_pki_cert"/>
<!-- END BDISK/PROFILE/PKI/CA/CERT -->
<!-- BDISK/PROFILE/PKI/CA/CSR -->
<xs:element name="csr" maxOccurs="1" minOccurs="0" type="t_path"/>
<!-- END BDISK/PROFILE/PKI/CA/CSR -->
<!-- BDISK/PROFILE/PKI/CA/INDEX -->
<xs:element name="index" maxOccurs="1" minOccurs="0" type="t_path"/>
<!-- END BDISK/PROFILE/PKI/CA/INDEX -->
<!-- BDISK/PROFILE/PKI/CA/SERIAL -->
<xs:element name="serial" maxOccurs="1" minOccurs="0"
type="t_path"/>
<!-- END BDISK/PROFILE/PKI/CA/SERIAL -->
<!-- BDISK/PROFILE/PKI/CA/KEY -->
<xs:element name="key" minOccurs="1" maxOccurs="1"
type="t_pki_key"/>
<!-- END BDISK/PROFILE/PKI/CA/CSR -->
<!-- BDISK/PROFILE/PKI/CA/SUBJECT -->
<xs:element name="subject" maxOccurs="1" minOccurs="0"
type="t_pki_subject"/>
<!-- END BDISK/PROFILE/PKI/CA/SUBJECT -->
</xs:all>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/PKI/CA -->
<!-- BDISK/PROFILE/PKI/CLIENT -->
<xs:element name="client" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:all>
<!-- BDISK/PROFILE/PKI/CLIENT/CERT -->
<xs:element name="cert" maxOccurs="1" minOccurs="1"
type="t_pki_cert"/>
<!-- END BDISK/PROFILE/PKI/CLIENT/CERT -->
<!-- BDISK/PROFILE/PKI/CLIENT/CSR -->
<xs:element name="csr" maxOccurs="1" minOccurs="0" type="t_path"/>
<!-- END BDISK/PROFILE/PKI/CLIENT/CSR -->
<!-- BDISK/PROFILE/PKI/CLIENT/KEY -->
<xs:element name="key" minOccurs="1" maxOccurs="1"
type="t_pki_key"/>
<!-- END BDISK/PROFILE/PKI/CLIENT/CSR -->
<!-- BDISK/PROFILE/PKI/CLIENT/SUBJECT -->
<xs:element name="subject" maxOccurs="1" minOccurs="0"
type="t_pki_subject"/>
<!-- END BDISK/PROFILE/PKI/CLIENT/SUBJECT -->
</xs:all>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/PKI/CLIENT -->
</xs:sequence>
<xs:attribute name="overwrite" type="xs:boolean"/>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/PKI -->
<!-- BDISK/PROFILE/SYNC -->
<xs:element name="sync" maxOccurs="1" minOccurs="1"/>
<xs:element name="sync" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:all>
<!-- BDISK/PROFILE/SYNC/IPXE -->
<xs:element name="ipxe">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="t_path">
<xs:attribute name="enabled" type="xs:boolean"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SYNC/IPXE -->
<!-- BDISK/PROFILE/SYNC/TFTP -->
<xs:element name="tftp">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="t_path">
<xs:attribute name="enabled" type="xs:boolean"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SYNC/TFTP -->
<!-- BDISK/PROFILE/SYNC/ISO -->
<xs:element name="iso">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="t_path">
<xs:attribute name="enabled" type="xs:boolean"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SYNC/ISO -->
<!-- BDISK/PROFILE/SYNC/GPG -->
<xs:element name="gpg">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="t_path">
<xs:attribute name="enabled" type="xs:boolean"/>
<xs:attribute name="format">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="asc"/>
<xs:enumeration value="bin"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SYNC/GPG -->
<!-- BDISK/PROFILE/SYNC/RSYNC -->
<xs:element name="rsync">
<xs:complexType>
<xs:sequence>
<!-- BDISK/PROFILE/SYNC/RSYNC/USER -->
<xs:element name="user" type="t_username"/>
<!-- END BDISK/PROFILE/SYNC/RSYNC/USER -->
<!-- BDISK/PROFILE/SYNC/RSYNC/HOST -->
<xs:element name="host" type="t_net_loc"/>
<!-- END BDISK/PROFILE/SYNC/RSYNC/HOST -->
<!-- BDISK/PROFILE/SYNC/RSYNC/PORT -->
<xs:element name="port">
<xs:simpleType>
<xs:restriction base="xs:positiveInteger">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="65535"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<!-- END BDISK/PROFILE/SYNC/RSYNC/PORT -->
<xs:choice>
<!-- BDISK/PROFILE/SYNC/RSYNC/PUBKEY -->
<xs:element name="pubkey" type="t_path"/>
<!-- END BDISK/PROFILE/SYNC/RSYNC/PUBKEY -->
<!-- BDISK/PROFILE/SYNC/RSYNC/PUBKEY -->
<xs:element name="password"/>
<!-- END BDISK/PROFILE/SYNC/RSYNC/PUBKEY -->
</xs:choice>
</xs:sequence>
<xs:attribute name="enabled" type="xs:boolean"/>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SYNC/IPXE -->
</xs:all>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE/SYNC -->
</xs:all>
<xs:attribute name="id" type="xs:positiveInteger"/>
<xs:attribute name="name" type="xs:string"/>
<xs:attribute name="uuid">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern
value="[0-9a-f]{8}\-[0-9a-f]{4}\-4[0-9a-f]{3}\-[89ab][0-9a-f]{3}\-[0-9a-f]{12}"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<!-- END BDISK/PROFILE -->

View File

@ -4,7 +4,7 @@
# Go figure.

import confparse
import crypt
import datetime
import getpass
import os
import utils
@ -134,7 +134,12 @@ class ConfGenerator(object):
self.cfg = c.xml
self.append = True
else:
self.cfg = lxml.etree.Element('bdisk')
_ns = {None: 'http://bdisk.square-r00t.net/',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
_xsi = {
'{http://www.w3.org/2001/XMLSchema-instance}schemaLocation':
'http://bdisk.square-r00t.net bdisk.xsd'}
self.cfg = lxml.etree.Element('bdisk', nsmap = _ns, attrib = _xsi)
self.append = False
self.profile = lxml.etree.Element('profile')
self.cfg.append(self.profile)
@ -155,6 +160,13 @@ class ConfGenerator(object):
self.get_pki()
self.get_gpg()
self.get_sync()
# TODO: make this more specific (script? gui? web? etc.)
# and append comment to bdisk element
_comment = lxml.etree.Comment(
'Generated {0} by BDisk configuration generator'.format(
str(datetime.datetime.now())
)
)
except KeyboardInterrupt:
exit('\n\nCaught KeyboardInterrupt; quitting...')
return()
@ -472,7 +484,7 @@ class ConfGenerator(object):
print('Invalid selection. Starting over.')
continue
chksum.attrib['hash_algo'] = checksum_type
chksum.attrib['explicit'] = "no"
chksum.attrib['explicit'] = "false"
chksum.text = checksum['full_url']
else:
# Maybe it's a digest string.
@ -505,7 +517,7 @@ class ConfGenerator(object):
continue
else:
checksum_type = checksum_type[0]
chksum.attrib['explicit'] = "yes"
chksum.attrib['explicit'] = "true"
chksum.text = checksum
chksum.attrib['hash_algo'] = checksum_type
print('\n++ SOURCES || {0} || GPG ++'.format(arch.upper()))
@ -597,7 +609,7 @@ class ConfGenerator(object):
usage = (
'{0} for yes, {1} for no...\n'))
if _chk_optimizations:
build.attrib['its_full_of_stars'] = 'yes'
build.attrib['its_full_of_stars'] = 'true'
print('\n++ BUILD || PATHS ++')
# Thankfully, we can simplify a lot of this.
_dir_strings = {'base': ('the base directory (used for files that are '
@ -678,9 +690,9 @@ class ConfGenerator(object):
self.profile.append(iso)
# We have more than one arch, so we need to ask how they want to handle
# it.
_ma_strings = {'yes': ('a multi-arch ISO (both architectures on one '
_ma_strings = {'true': ('a multi-arch ISO (both architectures on one '
'ISO)'),
'no': ('separate image files for '
'false': ('separate image files for '
'{0}').format(' and '.join(_arches))}
for a in _arches:
_ma_strings[a] = 'only build an image file for {0}'.format(a)
@ -712,7 +724,7 @@ class ConfGenerator(object):
'option to configure it a bit later).\nWould you like to sign '
'the ISO/USB image files with GPG?\n'), usage = (
'{0} for yes, {1} for no...\n'))
_gpg_sign = ('yes' if _gpg_input else 'no')
_gpg_sign = ('true' if _gpg_input else 'false')
iso.attrib['sign'] = _gpg_sign
self.profile.append(iso)
return()
@ -727,21 +739,21 @@ class ConfGenerator(object):
'see the manual for more information). Would you like to '
'build iPXE support?\n'), usage = (
'{0} for yes, {1} for no...\n'))
_ipxe = ('yes' if _ipxe else 'no')
if _ipxe == 'yes':
_ipxe = ('true' if _ipxe else 'true')
if _ipxe == 'true':
print('\n++ iPXE || MINI-ISO ++')
_iso = prompt.confirm_or_no(prompt = (
'\nWould you like to build a "mini-ISO" (see the manual) for '
'bootstrapping iPXE booting from USB or optical media?\n'),
usage = ('{0} for yes, {1} for no...\n'))
ipxe.attrib['iso'] = ('yes' if _iso else 'no')
ipxe.attrib['iso'] = ('true' if _iso else 'false')
print('\n++ iPXE || SIGNING ++')
_sign = prompt.confirm_or_no(prompt = (
'\nBDisk can sign the mini-ISO and other relevant files for '
'iPXE builds using GPG. Would you like to sign the iPXE build '
'distributables? (You\'ll have the chance to configure GPG '
'later).\n'), usage = ('{0} for yes, {1} for no...\n'))
ipxe.attrib['sign'] = ('yes' if _sign else 'no')
ipxe.attrib['sign'] = ('true' if _sign else 'false')
_uri = None
while not _uri:
print('\n++ iPXE || URL ++')
@ -756,7 +768,7 @@ class ConfGenerator(object):
else:
uri = lxml.etree.SubElement(ipxe, 'uri')
uri.text = _uri
if _ipxe == 'yes':
if _ipxe == 'true':
self.profile.append(ipxe)
return()
@ -782,7 +794,7 @@ class ConfGenerator(object):
'wish to keep persistent keys and certs), you should '
'DEFINITELY answer no here.\n'),
usage = ('{0} for yes, {1} for no...\n'))
pki.attrib['overwrite'] = ('yes' if _overwrite else 'no')
pki.attrib['overwrite'] = ('true' if _overwrite else 'false')
for x in ('ca', 'client'):
print('\n++ SSL/TLS PKI || {0} ++'.format(x.upper()))
_x = None
@ -806,7 +818,7 @@ class ConfGenerator(object):
for x in _xpaths:
_x = self.profile.xpath(x)
for a in _x:
if a == 'yes':
if a == 'true':
_sigchk = True
break
if _sigchk:
@ -850,7 +862,7 @@ class ConfGenerator(object):
'\nWould you like to push the key to the SKS keyserver pool '
'(making it much easier for end-users to look it up)?\n'),
usage = ('{0} for yes, {1} for no...\n'))
gpg.attrib['publish'] = ('yes' if _gpgpublish else 'no')
gpg.attrib['publish'] = ('true' if _gpgpublish else 'false')
print('\n++ GPG || PASSWORD HANDLING ++')
_gpgpass_prompt = prompt.confirm_or_no(prompt = (
'\nWould you like BDisk to prompt you for a passphrase? If not, '
@ -858,7 +870,8 @@ class ConfGenerator(object):
'the configuration (HIGHLY unrecommended) or use a blank '
'passphrase (also HIGHLY unrecommended).\n'),
usage = ('{0} for yes, {1} for no...\n'))
gpg.attrib['prompt_passphrase'] = ('yes' if _gpgpass_prompt else 'no')
gpg.attrib['prompt_passphrase'] = ('true' if _gpgpass_prompt else
'false')
_pass = None
if not _gpgpass_prompt:
while not _pass:
@ -923,7 +936,7 @@ class ConfGenerator(object):
'\nWould you like to sync {0}?\n'.format(_syncs[s])),
usage = ('{0} for yes, {1} for no...\n'))
elem = lxml.etree.SubElement(sync, s)
elem.attrib['enabled'] = ('yes' if _item_sync_chk else 'no')
elem.attrib['enabled'] = ('true' if _item_sync_chk else 'false')
if not _item_sync_chk:
continue
if s == 'gpg':

View File

@ -168,7 +168,7 @@ class Conf(object):
## PROFILE/BUILD(/PATHS)
self.cfg['build'] = {'paths': {}}
build = self.profile.xpath('./build')[0]
_optimize = build.get('its_full_of_stars', 'no')
_optimize = build.get('its_full_of_stars', 'false')
self.cfg['build']['optimize'] = transform.xml2py(_optimize)
for path in build.xpath('./paths/*'):
self.cfg['build']['paths'][path.tag] = path.text
@ -185,7 +185,7 @@ class Conf(object):
# We enable all features by default.
elem = self.profile.xpath('./{0}'.format(x))[0]
for a in self.cfg[x]:
self.cfg[x][a] = transform.xml2py(elem.get(a, 'yes'))
self.cfg[x][a] = transform.xml2py(elem.get(a, 'true'))
if x == 'ipxe':
self.cfg[x]['uri'] = elem.xpath('./uri/text()')[0]
return()

View File

@ -15,6 +15,7 @@ import uuid
import validators
import zlib
import lxml.etree
import lxml.objectify
from bs4 import BeautifulSoup
from collections import OrderedDict
from dns import resolver
@ -446,12 +447,12 @@ class transform(object):
def py2xml(self, value, attrib = True):
if value in (False, ''):
if attrib:
return("no")
return("false")
else:
return(None)
elif isinstance(value, bool):
# We handle the False case above.
return("yes")
return("true")
elif isinstance(value, str):
return(value)
else:
@ -469,7 +470,6 @@ class transform(object):
text_out = re.sub('[^\w]', '', text_out)
return(text_out)

# noinspection PyDictCreation
def url_to_dict(self, orig_url, no_None = False):
def _getuserinfo(uinfo_str):
if len(uinfo_str) == 0:
@ -659,8 +659,8 @@ class transform(object):
return(acct)

def xml2py(self, value, attrib = True):
yes = re.compile('^\s*(y(es)?|true|1)\s*$', re.IGNORECASE)
no = re.compile('^\s*(no?|false|0)\s*$', re.IGNORECASE)
yes = re.compile('^\s*(true|1)\s*$', re.IGNORECASE)
no = re.compile('^\s*(false|0)\s*$', re.IGNORECASE)
none = re.compile('^\s*(none|)\s*$', re.IGNORECASE)
if no.search(value):
if attrib:
@ -819,12 +819,18 @@ class xml_supplicant(object):
# This is retained so we can "refresh" the profile if needed.
self.orig_profile = profile
try:
self.xml = lxml.etree.fromstring(raw)
self.orig_xml = lxml.etree.fromstring(raw)
# We need to strip the naked namespace for XPath to work.
self.xml = copy.deepcopy(self.orig_xml)
self.roottree = self.xml.getroottree()
self.tree = self.roottree.getroot()
self.strip_naked_ns()
except lxml.etree.XMLSyntaxError:
raise ValueError('The configuration provided does not seem to be '
'valid')
self.get_profile(profile = profile)
self.xml = lxml.etree.fromstring(raw)
# This is disabled; we set it above.
#self.xml = lxml.etree.fromstring(raw)
self.fmt = XPathFmt()
self.max_recurse = int(self.profile.xpath(
'//meta/max_recurse/text()')[0])
@ -969,6 +975,23 @@ class xml_supplicant(object):
).format(element.text))
return(path)

def return_naked_ns(self):
# It's so stupid I have to do this.
return(self.orig_xml.nsmap)

def strip_naked_ns(self):
# I cannot *believe* that LXML doesn't have this built-in, considering
# how common naked namespaces are.
# https://stackoverflow.com/a/18160164/733214
for elem in self.roottree.getiterator():
if not hasattr(elem.tag, 'find'):
continue
i = elem.tag.find('}')
if i >= 0:
elem.tag = elem.tag[i + 1:]
lxml.objectify.deannotate(self.roottree, cleanup_namespaces = True)
return()

def substitute(self, element, recurse_count = 0):
if recurse_count >= self.max_recurse:
return(element)

View File

@ -1,9 +1,9 @@
<?xml version='1.0' encoding='UTF-8'?>
<bdisk xmlns:bdisk="http://bdisk.square-r00t.net/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://bdisk.square-r00t.net bdisk.xsd">
<bdisk xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://bdisk.square-r00t.net/" xsi:schemaLocation="http://bdisk.square-r00t.net bdisk.xsd">
<profile name="default" id="1" uuid="8cdd6bcb-c147-4a63-9779-b5433c510dbc">
<meta>
<names>
<name>BDisk</name>
<name>BDISK</name>
<uxname>bdisk</uxname>
<!-- Just like with previous versions of BDisk, you can reference other values...
but now with the neat benefits of XPath! Everything you could do in build.ini's and more.
@ -38,18 +38,18 @@
</meta>
<accounts>
<!-- Salted/hashed password is "test" -->
<rootpass hashed="yes">$6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1</rootpass>
<user sudo="yes">
<rootpass hashed="true">$6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1</rootpass>
<user sudo="true">
<username>{xpath%//meta/names/uxname/text()}</username>
<!-- You can also use substitution from different profiles in this same configuration: -->
<!-- <username>{xpath%//profile[@name='another_profile']/meta/names/uxname"}</username> -->
<comment>{xpath%//meta/dev/author/text()}</comment>
<password hashed="no" hash_algo="sha512" salt="auto">testpassword</password>
<password hashed="false" hash_algo="sha512" salt="auto">testpassword</password>
</user>
<user sudo="no">
<user sudo="false">
<username>testuser</username>
<name>Test User</name>
<password hashed="no" hash_algo="sha512" salt="auto">anothertestpassword</password>
<comment>Test User</comment>
<password hashed="false" hash_algo="sha512" salt="auto">anothertestpassword</password>
</user>
</accounts>
<sources>
@ -57,18 +57,18 @@
<mirror>http://archlinux.mirror.domain.tld</mirror>
<rootpath>/iso/latest</rootpath>
<tarball flags="regex,latest">{regex%tarball_x86_64}</tarball>
<checksum hash_algo="sha1" explicit="no">sha1sums.txt</checksum>
<checksum hash_algo="sha1" explicit="false">sha1sums.txt</checksum>
<sig keys="7F2D434B9741E8AC" keyserver="hkp://pool.sks-keyservers.net" flags="regex,latest">{regex%sig_x86_64}</sig>
</source>
<source arch="i686">
<mirror>http://archlinux32.mirror.domain.tld</mirror>
<rootpath>/iso/latest</rootpath>
<tarball flags="regex,latest">{regex%tarball_i686}</tarball>
<checksum hash_algo="sha512" explicit="yes">cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e</checksum>
<checksum hash_algo="sha512" explicit="true">cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e</checksum>
<sig keys="248BF41F9BDD61D41D060AE774EDA3C6B06D0506" keyserver="hkp://pool.sks-keyservers.net" flags="regex,latest">{regex%sig_i686}</sig>
</source>
</sources>
<build its_full_of_stars="yes">
<build its_full_of_stars="true">
<paths>
<base>{variable%bdisk_root}/base</base>
<cache>{variable%bdisk_root}/cache</cache>
@ -85,8 +85,8 @@
</paths>
<basedistro>archlinux</basedistro>
</build>
<iso sign="yes" multi_arch="yes"/>
<ipxe sign="yes" iso="yes">
<iso sign="true" multi_arch="true"/>
<ipxe sign="true" iso="true">
<uri>{xpath%//meta/dev/website/text()}/ipxe</uri>
</ipxe>
<pki overwrite="no">
@ -147,11 +147,11 @@
</gpg>
<sync>
<!-- ipxe includes the http directory. or should, anyways. -->
<ipxe enabled="yes">/srv/http/{xpath%../../meta/names/uxname/text()}</ipxe>
<tftp enabled="yes">/tftproot/{xpath%../../meta/names/uxname/text()}</tftp>
<iso enabled="yes">/srv/http/isos/{xpath%../../meta/names/uxname/text()}</iso>
<gpg enabled="yes" format="asc">/srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc</gpg>
<rsync enabled="yes">
<ipxe enabled="true">/srv/http/{xpath%../../meta/names/uxname/text()}</ipxe>
<tftp enabled="true">/tftproot/{xpath%../../meta/names/uxname/text()}</tftp>
<iso enabled="true">/srv/http/isos/{xpath%../../meta/names/uxname/text()}</iso>
<gpg enabled="true" format="asc">/srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc</gpg>
<rsync enabled="true">
<user>root</user>
<host>mirror.domain.tld</host>
<port>22</port>
@ -159,7 +159,7 @@
</rsync>
</sync>
</profile>
<profile name="alternate" id="2" uuid="2ed07c19-2071-4d66-8569-da40475ba716">
<profile name="alternate" id="2" uuid="2ed07c19-2071-4d66-8569-da40475ba716">
<meta>
<names>
<name>AnotherCD</name>
@ -168,8 +168,7 @@
</names>
<desc>Another rescue/restore live environment.</desc>
<dev>
<author>Another Dev Eloper</author>
<!-- You can reference other profiles within the same configuration. -->
<author>Another Dev Eloper</author><!-- You can reference other profiles within the same configuration. -->
<email>{xpath%//profile[@name="default"]/meta/dev/email/text()}</email>
<website>{xpath%//profile[@name="default"]/meta/dev/website/text()}</website>
</dev>
@ -187,11 +186,11 @@
</variables>
</meta>
<accounts>
<rootpass hashed="no">atotallyinsecurepassword</rootpass>
<user sudo="no">
<rootpass hashed="false">atotallyinsecurepassword</rootpass>
<user sudo="false">
<username>testuser</username>
<comment>Test User</comment>
<password hashed="no" hash_algo="sha512" salt="auto">atestpassword</password>
<password hashed="false" hash_algo="sha512" salt="auto">atestpassword</password>
</user>
</accounts>
<sources>
@ -199,18 +198,18 @@
<mirror>http://archlinux.mirror.domain.tld</mirror>
<rootpath>/iso/latest</rootpath>
<tarball flags="regex,latest">{regex%tarball_x86_64}</tarball>
<checksum hash_algo="sha1" explicit="no">sha1sums.txt</checksum>
<checksum hash_algo="sha1" explicit="false">sha1sums.txt</checksum>
<sig keys="7F2D434B9741E8AC" keyserver="hkp://pool.sks-keyservers.net" flags="regex,latest">{regex%sig_x86_64}</sig>
</source>
<source arch="i686">
<mirror>http://archlinux32.mirror.domain.tld</mirror>
<rootpath>/iso/latest</rootpath>
<tarball flags="regex,latest">{regex%tarball_i686}</tarball>
<checksum hash_algo="sha512" explicit="yes">cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e</checksum>
<checksum hash_algo="sha512" explicit="true">cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e</checksum>
<sig keys="248BF41F9BDD61D41D060AE774EDA3C6B06D0506" keyserver="hkp://pool.sks-keyservers.net" flags="regex,latest">{regex%sig_i686}</sig>
</source>
</sources>
<build its_full_of_stars="yes">
<build its_full_of_stars="true">
<paths>
<base>{variable%bdisk_root}/base</base>
<cache>{variable%bdisk_root}/cache</cache>
@ -227,8 +226,8 @@
</paths>
<basedistro>archlinux</basedistro>
</build>
<iso sign="yes" multi_arch="yes"/>
<ipxe sign="yes" iso="yes">
<iso sign="true" multi_arch="true"/>
<ipxe sign="true" iso="true">
<uri>{xpath%//meta/dev/website/text()}/ipxe</uri>
</ipxe>
<pki overwrite="no">
@ -271,11 +270,11 @@
</key>
</gpg>
<sync>
<ipxe enabled="yes">/srv/http/{xpath%../../meta/names/uxname/text()}</ipxe>
<tftp enabled="yes">/tftproot/{xpath%../../meta/names/uxname/text()}</tftp>
<iso enabled="yes">/srv/http/isos/{xpath%../../meta/names/uxname/text()}</iso>
<gpg enabled="yes" format="asc">/srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc</gpg>
<rsync enabled="yes">
<ipxe enabled="true">/srv/http/{xpath%../../meta/names/uxname/text()}</ipxe>
<tftp enabled="true">/tftproot/{xpath%../../meta/names/uxname/text()}</tftp>
<iso enabled="true">/srv/http/isos/{xpath%../../meta/names/uxname/text()}</iso>
<gpg enabled="true" format="asc">/srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc</gpg>
<rsync enabled="true">
<user>root</user>
<host>mirror.domain.tld</host>
<port>22</port>

View File

@ -1,13 +1,34 @@
#!/usr/bin/env python3.6

import copy
from lxml import etree
from lxml import etree, objectify

parser = etree.XMLParser(remove_blank_text = True)
#parser = etree.XMLParser(remove_blank_text = True)
parser = etree.XMLParser(remove_blank_text = False)

# We need to append to a new root because you can't edit nsmap, and you can't
# xpath on an element with a naked namespace (e.g. 'xlmns="..."').
ns = {None: 'http://bdisk.square-r00t.net/',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
xsi = {'{http://www.w3.org/2001/XMLSchema-instance}schemaLocation':
'http://bdisk.square-r00t.net bdisk.xsd'}
new_cfg = etree.Element('bdisk', nsmap = ns, attrib = xsi)
new_cfg.text = '\n '

with open('single_profile.xml', 'rb') as f:
xml = etree.fromstring(f.read(), parser)


roottree = xml.getroottree()
for elem in roottree.getiterator():
if not hasattr(elem.tag, 'find'):
continue
i = elem.tag.find('}')
if i >= 0:
elem.tag = elem.tag[i + 1:]
objectify.deannotate(roottree, cleanup_namespaces = True)


single_profile = xml.xpath('/bdisk/profile[1]')[0]
alt_profile = copy.deepcopy(single_profile)
for c in alt_profile.xpath('//comment()'):
@ -42,18 +63,22 @@ for e in accounts.iter():
if e.tag in accounts_tags:
e.text = accounts_tags[e.tag]
if e.tag == 'rootpass':
e.attrib['hashed'] = 'no'
e.attrib['hashed'] = 'false'
elif e.tag == 'user':
e.attrib['sudo'] = 'no'
e.attrib['sudo'] = 'false'
# Delete the second user
accounts.remove(accounts[2])
author = alt_profile.xpath('/profile/meta/dev/author')[0]
author.addnext(etree.Comment(
' You can reference other profiles within the same configuration. '))
xml.append(alt_profile)
#xml.append(alt_profile)

for child in xml.xpath('/bdisk/profile'):
new_cfg.append(copy.deepcopy(child))
new_cfg.append(alt_profile)

with open('multi_profile.xml', 'wb') as f:
f.write(etree.tostring(xml,
f.write(etree.tostring(new_cfg,
pretty_print = True,
encoding = 'UTF-8',
xml_declaration = True))

View File

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<bdisk xmlns:bdisk="http://bdisk.square-r00t.net/"
<bdisk xmlns="http://bdisk.square-r00t.net/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://bdisk.square-r00t.net bdisk.xsd">
<profile name="default" id="1" uuid="8cdd6bcb-c147-4a63-9779-b5433c510dbc">
<meta>
<names>
<name>BDisk</name>
<name>BDISK</name>
<!--<name>{xpath%../uxname/text()}</name>-->
<uxname>bdisk</uxname>
<!-- Just like with previous versions of BDisk, you can reference other values...
but now with the neat benefits of XPath! Everything you could do in build.ini's and more.
@ -29,8 +30,7 @@
items. See the manual for more information. NO btags within the patterns is allowed. -->
<regexes>
<pattern id="tarball_x86_64">archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz$</pattern>
<pattern id="sig_x86_64">archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz\.sig$
</pattern>
<pattern id="sig_x86_64">archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz\.sig$</pattern>
<pattern id="tarball_i686">archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz$</pattern>
<pattern id="sig_i686">archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz\.sig$</pattern>
</regexes>
@ -41,56 +41,48 @@
</meta>
<accounts>
<!-- Salted/hashed password is "test" -->
<rootpass hashed="yes">
$6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1
</rootpass>
<user sudo="yes">
<rootpass hashed="true">$6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1</rootpass>
<user sudo="true">
<username>{xpath%//meta/names/uxname/text()}</username>
<!-- You can also use substitution from different profiles in this same configuration: -->
<!-- <username>{xpath%//profile[@name='another_profile']/meta/names/uxname"}</username> -->
<comment>{xpath%//meta/dev/author/text()}</comment>
<password hashed="no"
<password hashed="false"
hash_algo="sha512"
salt="auto">testpassword
</password>
salt="auto">testpassword</password>
</user>
<user sudo="no">
<user sudo="false">
<username>testuser</username>
<name>Test User</name>
<password hashed="no"
<comment>Test User</comment>
<password hashed="false"
hash_algo="sha512"
salt="auto">anothertestpassword
</password>
salt="auto">anothertestpassword</password>
</user>
</accounts>
<sources>
<source arch="x86_64">
<mirror>http://archlinux.mirror.domain.tld</mirror>
<rootpath>/iso/latest</rootpath>
<tarball flags="regex,latest">{regex%tarball_x86_64}</tarball>
<tarball flags="regex latest">{regex%tarball_x86_64}</tarball>
<checksum hash_algo="sha1"
explicit="no">sha1sums.txt
</checksum>
explicit="false"
flags="latest">sha1sums.txt</checksum>
<sig keys="7F2D434B9741E8AC"
keyserver="hkp://pool.sks-keyservers.net"
flags="regex,latest">{regex%sig_x86_64}
</sig>
flags="regex latest">{regex%sig_x86_64}</sig>
</source>
<source arch="i686">
<mirror>http://archlinux32.mirror.domain.tld</mirror>
<rootpath>/iso/latest</rootpath>
<tarball flags="regex,latest">{regex%tarball_i686}</tarball>
<tarball flags="regex latest">{regex%tarball_i686}</tarball>
<checksum hash_algo="sha512"
explicit="yes">
cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
</checksum>
explicit="true">cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e</checksum>
<sig keys="248BF41F9BDD61D41D060AE774EDA3C6B06D0506"
keyserver="hkp://pool.sks-keyservers.net"
flags="regex,latest">{regex%sig_i686}
</sig>
flags="regex latest">{regex%sig_i686}</sig>
</source>
</sources>
<build its_full_of_stars="yes">
<build its_full_of_stars="true">
<paths>
<base>{variable%bdisk_root}/base</base>
<cache>{variable%bdisk_root}/cache</cache>
@ -107,11 +99,11 @@
</paths>
<basedistro>archlinux</basedistro>
</build>
<iso sign="yes" multi_arch="yes"/>
<ipxe sign="yes" iso="yes">
<iso sign="true" multi_arch="true"/>
<ipxe sign="true" iso="true">
<uri>{xpath%//meta/dev/website/text()}/ipxe</uri>
</ipxe>
<pki overwrite="no">
<pki overwrite="false">
<!-- http://ipxe.org/crypto -->
<ca>
<cert hash_algo="sha512">{xpath%../../../build/paths/pki/text()}/ca.crt</cert>
@ -133,8 +125,7 @@
be (securely) prompted for the passphrase to unlock it/add a passphrase to it. -->
<key cipher="none"
passphrase="none"
keysize="4096">{xpath%../../../build/paths/pki/text()}/ca.key
</key>
keysize="4096">{xpath%../../../build/paths/pki/text()}/ca.key</key>
<subject>
<commonName>domain.tld</commonName>
<countryName>XX</countryName>
@ -146,16 +137,13 @@
</subject>
</ca>
<client>
<cert hash_algo="sha512">
{xpath%../../../build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.crt
</cert>
<cert hash_algo="sha512">{xpath%../../../build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.crt</cert>
<csr/>
<key cipher="none"
passphrase="none"
keysize="4096">{xpath%//build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.key
</key>
keysize="4096">{xpath%//build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.key</key>
<subject>
<commonName>some client name</commonName>
<commonName>website.tld</commonName>
<countryName>XX</countryName>
<localityName>Some City</localityName>
<stateOrProvinceName>Some State</stateOrProvinceName>
@ -169,26 +157,23 @@
blank passphrase for all operations. -->
<gpg keyid="none"
gnupghome="none"
publish="no"
prompt_passphrase="no">
publish="false"
prompt_passphrase="false">
<!-- The below is only used if we are generating a key (i.e. keyid="none"). -->
<key algo="rsa" keysize="4096" expire="0">
<name>{xpath%../../../meta/dev/author/text()}</name>
<email>{xpath%../../../meta/dev/email/text()}</email>
<comment>for {xpath%../../../meta/names/pname/text()} [autogenerated] | {xpath%../../../meta/uri/text()}
| {xpath%../../../meta/desc/text()}
</comment>
<comment>for {xpath%../../../meta/names/pname/text()} [autogenerated] | {xpath%../../../meta/uri/text()} | {xpath%../../../meta/desc/text()}</comment>
</key>
</gpg>
<sync>
<!-- ipxe includes the http directory. or should, anyways. -->
<ipxe enabled="yes">/srv/http/{xpath%../../meta/names/uxname/text()}</ipxe>
<tftp enabled="yes">/tftproot/{xpath%../../meta/names/uxname/text()}</tftp>
<iso enabled="yes">/srv/http/isos/{xpath%../../meta/names/uxname/text()}</iso>
<gpg enabled="yes"
format="asc">/srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc
</gpg>
<rsync enabled="yes">
<ipxe enabled="true">/srv/http/{xpath%../../meta/names/uxname/text()}</ipxe>
<tftp enabled="true">/tftproot/{xpath%../../meta/names/uxname/text()}</tftp>
<iso enabled="true">/srv/http/isos/{xpath%../../meta/names/uxname/text()}</iso>
<gpg enabled="true"
format="asc">/srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc</gpg>
<rsync enabled="true">
<user>root</user>
<host>mirror.domain.tld</host>
<port>22</port>