config tool's sort of done. still need to actually generate xml from the conf dict and write to a file, then handle the other operations

This commit is contained in:
brent s 2017-05-11 18:46:36 -04:00
parent fd8b26eb48
commit 29f6761017
1 changed files with 74 additions and 27 deletions

View File

@ -32,10 +32,11 @@ class aifgen(object):
pass pass
def getOpts(self): def getOpts(self):
# This whole thing is ugly. Really, really ugly. Patches 100% welcome.
def chkPrompt(prompt, urls): def chkPrompt(prompt, urls):
txtin = None txtin = None
txtin = input(prompt) txtin = input(prompt)
if txtin in ('wikihelp', ''): if txtin == 'wikihelp':
print('\n Articles/pages that you may find helpful for this option are:') print('\n Articles/pages that you may find helpful for this option are:')
for h in urls: for h in urls:
print(' * {0}'.format(h)) print(' * {0}'.format(h))
@ -45,16 +46,16 @@ class aifgen(object):
return(txtin) return(txtin)
def sizeChk(startsize): def sizeChk(startsize):
try: try:
startn = int(re.sub('[%\-+KMGTP])', '', startsize)) startn = int(re.sub('[%\-+KMGTP]', '', startsize))
modifier = re.sub('^(\+|-)?.*$', '\g<1>', startsize) modifier = re.sub('^(\+|-)?.*$', '\g<1>', startsize)
if re.match('^(\+|-)?[0-9]+%$', n): if re.match('^(\+|-)?[0-9]+%$', startsize):
sizetype = 'percentage' sizetype = 'percentage'
elif re.match('^(\+|-)?[0-9]+[KMGTP]$', n): elif re.match('^(\+|-)?[0-9]+[KMGTP]$', n):
sizetype = 'fixed' sizetype = 'fixed'
else: else:
exit(' !! ERROR: The input you provided does not match a valid pattern.') exit(' !! ERROR: The input you provided does not match a valid pattern.')
if sizetype == 'percentage': if sizetype == 'percentage':
if int(startn) not in range(0, 100): if not (0 <= startn <= 100):
exit(' !! ERROR: You must provide a percentage or a size.') exit(' !! ERROR: You must provide a percentage or a size.')
except: except:
exit(' !! ERROR: You did not provide a valid size specifier!') exit(' !! ERROR: You did not provide a valid size specifier!')
@ -62,17 +63,19 @@ class aifgen(object):
def ifacePrompt(nethelp): def ifacePrompt(nethelp):
ifaces = {} ifaces = {}
moreIfaces = True moreIfaces = True
print('Please enter the name of the interface you would like to use.\n' + print('\nPlease enter the name of the interface you would like to use.\n' +
'Can instead be \'auto\' for automatic configuration of the first found interface\n' + 'Can instead be \'auto\' for automatic configuration of the first found interface\n' +
'with an active link. (You can only specify one auto device per system, and all subsequent\n' 'with an active link. (You can only specify one auto device per system, and all subsequent\n'
'interface entries will be ignored.)\n') 'interface entries will be ignored.)\n')
while moreIfaces: while moreIfaces:
ifacein = chkPrompt('Interface device: ', nethelp) ifacein = chkPrompt('Interface device: ', nethelp)
addrin = chkPrompt('* Address for {0} in CIDR format (can be an IPv4 or IPv6 address);\n\t' + addrin = chkPrompt(('* Address for {0} in CIDR format (can be an IPv4 or IPv6 address); ' +
'use\'auto\' for DHCP/DHCPv6): '.format(ifacein), nethelp) 'use\'auto\' for DHCP/DHCPv6): ').format(ifacein), nethelp)
if addrin == 'auto': if addrin == 'auto':
addrytpe = 'auto' addrtype = 'auto'
continue ipver = (chkPrompt('* Would you like \'ipv4\', \'ipv6\', or \'both\' to be auto-configured? ', nethelp)).lower()
if ipver not in ('ipv4', 'ipv6', 'both'):
exit(' !! ERROR: Must be one of ipv4, ipv6, or both.')
else: else:
addrtype = 'static' addrtype = 'static'
try: try:
@ -80,7 +83,25 @@ class aifgen(object):
except ValueError: except ValueError:
exit(' !! ERROR: You did not enter a valid IPv4/IPv6 address.') exit(' !! ERROR: You did not enter a valid IPv4/IPv6 address.')
if addrtype == 'static': if addrtype == 'static':
gwin = chkPrompt('* What is the gateway address for {0}? '.format(addrin), nethelp)
try:
ipaddress.ip_address(gwin)
except:
exit(' !! ERROR: You did not enter a valid IPv4/IPv6 address.')
ifaces[ifacein] = {'address': addrin, 'proto': ipver, 'gw': qwin, 'resolvers': []}
resolversin = chkPrompt('* What DNS resolvers should we use? Can accept a comma-separated list: ', nethelp)
for rslv in resolversin.split(','):
rslvaddr = rslv.strip()
ifaces[ifacein]['resolvers'].append(rslvaddr)
try:
ipaddress.ip_address(rslvaddr)
except:
exit(' !! ERROR: {0} is not a valid resolver address.'.format(rslvaddr))
else:
ifaces[ifacein] = {'address': 'auto', 'proto': ipver, 'gw': False, 'resolvers': False}
moreIfacesin = input('Would you like to add more interfaces? ((y)es/(N)O) ')
if not re.match('^y(es)?$', moreIfacesin.lower()):
moreIfaces = False
return(ifaces) return(ifaces)
conf = {} conf = {}
@ -90,7 +111,7 @@ class aifgen(object):
# https://aif.square-r00t.net/#code_disk_code # https://aif.square-r00t.net/#code_disk_code
diskhelp = ['https://wiki.archlinux.org/index.php/installation_guide#Partition_the_disks'] diskhelp = ['https://wiki.archlinux.org/index.php/installation_guide#Partition_the_disks']
diskin = chkPrompt('\nWhat disk(s) would you like to be configured on the target system?\n' + diskin = chkPrompt('\nWhat disk(s) would you like to be configured on the target system?\n' +
'If you have multiple disks, separate with a comma (e.g. \'/dev/sda,/dev/sdb\').\n', diskhelp) '\tIf you have multiple disks, separate with a comma (e.g. \'/dev/sda,/dev/sdb\'): ', diskhelp)
# NOTE: the following is a dict of fstype codes to their description. # NOTE: the following is a dict of fstype codes to their description.
fstypes = {'0700': 'Microsoft basic data', '0c01': 'Microsoft reserved', '2700': 'Windows RE', '3000': 'ONIE config', '3900': 'Plan 9', '4100': 'PowerPC PReP boot', '4200': 'Windows LDM data', '4201': 'Windows LDM metadata', '4202': 'Windows Storage Spaces', '7501': 'IBM GPFS', '7f00': 'ChromeOS kernel', '7f01': 'ChromeOS root', '7f02': 'ChromeOS reserved', '8200': 'Linux swap', '8300': 'Linux filesystem', '8301': 'Linux reserved', '8302': 'Linux /home', '8303': 'Linux x86 root (/)', '8304': 'Linux x86-64 root (/', '8305': 'Linux ARM64 root (/)', '8306': 'Linux /srv', '8307': 'Linux ARM32 root (/)', '8400': 'Intel Rapid Start', '8e00': 'Linux LVM', 'a500': 'FreeBSD disklabel', 'a501': 'FreeBSD boot', 'a502': 'FreeBSD swap', 'a503': 'FreeBSD UFS', 'a504': 'FreeBSD ZFS', 'a505': 'FreeBSD Vinum/RAID', 'a580': 'Midnight BSD data', 'a581': 'Midnight BSD boot', 'a582': 'Midnight BSD swap', 'a583': 'Midnight BSD UFS', 'a584': 'Midnight BSD ZFS', 'a585': 'Midnight BSD Vinum', 'a600': 'OpenBSD disklabel', 'a800': 'Apple UFS', 'a901': 'NetBSD swap', 'a902': 'NetBSD FFS', 'a903': 'NetBSD LFS', 'a904': 'NetBSD concatenated', 'a905': 'NetBSD encrypted', 'a906': 'NetBSD RAID', 'ab00': 'Recovery HD', 'af00': 'Apple HFS/HFS+', 'af01': 'Apple RAID', 'af02': 'Apple RAID offline', 'af03': 'Apple label', 'af04': 'AppleTV recovery', 'af05': 'Apple Core Storage', 'bc00': 'Acronis Secure Zone', 'be00': 'Solaris boot', 'bf00': 'Solaris root', 'bf01': 'Solaris /usr & Mac ZFS', 'bf02': 'Solaris swap', 'bf03': 'Solaris backup', 'bf04': 'Solaris /var', 'bf05': 'Solaris /home', 'bf06': 'Solaris alternate sector', 'bf07': 'Solaris Reserved 1', 'bf08': 'Solaris Reserved 2', 'bf09': 'Solaris Reserved 3', 'bf0a': 'Solaris Reserved 4', 'bf0b': 'Solaris Reserved 5', 'c001': 'HP-UX data', 'c002': 'HP-UX service', 'ea00': 'Freedesktop $BOOT', 'eb00': 'Haiku BFS', 'ed00': 'Sony system partition', 'ed01': 'Lenovo system partition', 'ef00': 'EFI System', 'ef01': 'MBR partition scheme', 'ef02': 'BIOS boot partition', 'f800': 'Ceph OSD', 'f801': 'Ceph dm-crypt OSD', 'f802': 'Ceph journal', 'f803': 'Ceph dm-crypt journal', 'f804': 'Ceph disk in creation', 'f805': 'Ceph dm-crypt disk in creation', 'fb00': 'VMWare VMFS', 'fb01': 'VMWare reserved', 'fc00': 'VMWare kcore crash protection', 'fd00': 'Linux RAID'} fstypes = {'0700': 'Microsoft basic data', '0c01': 'Microsoft reserved', '2700': 'Windows RE', '3000': 'ONIE config', '3900': 'Plan 9', '4100': 'PowerPC PReP boot', '4200': 'Windows LDM data', '4201': 'Windows LDM metadata', '4202': 'Windows Storage Spaces', '7501': 'IBM GPFS', '7f00': 'ChromeOS kernel', '7f01': 'ChromeOS root', '7f02': 'ChromeOS reserved', '8200': 'Linux swap', '8300': 'Linux filesystem', '8301': 'Linux reserved', '8302': 'Linux /home', '8303': 'Linux x86 root (/)', '8304': 'Linux x86-64 root (/', '8305': 'Linux ARM64 root (/)', '8306': 'Linux /srv', '8307': 'Linux ARM32 root (/)', '8400': 'Intel Rapid Start', '8e00': 'Linux LVM', 'a500': 'FreeBSD disklabel', 'a501': 'FreeBSD boot', 'a502': 'FreeBSD swap', 'a503': 'FreeBSD UFS', 'a504': 'FreeBSD ZFS', 'a505': 'FreeBSD Vinum/RAID', 'a580': 'Midnight BSD data', 'a581': 'Midnight BSD boot', 'a582': 'Midnight BSD swap', 'a583': 'Midnight BSD UFS', 'a584': 'Midnight BSD ZFS', 'a585': 'Midnight BSD Vinum', 'a600': 'OpenBSD disklabel', 'a800': 'Apple UFS', 'a901': 'NetBSD swap', 'a902': 'NetBSD FFS', 'a903': 'NetBSD LFS', 'a904': 'NetBSD concatenated', 'a905': 'NetBSD encrypted', 'a906': 'NetBSD RAID', 'ab00': 'Recovery HD', 'af00': 'Apple HFS/HFS+', 'af01': 'Apple RAID', 'af02': 'Apple RAID offline', 'af03': 'Apple label', 'af04': 'AppleTV recovery', 'af05': 'Apple Core Storage', 'bc00': 'Acronis Secure Zone', 'be00': 'Solaris boot', 'bf00': 'Solaris root', 'bf01': 'Solaris /usr & Mac ZFS', 'bf02': 'Solaris swap', 'bf03': 'Solaris backup', 'bf04': 'Solaris /var', 'bf05': 'Solaris /home', 'bf06': 'Solaris alternate sector', 'bf07': 'Solaris Reserved 1', 'bf08': 'Solaris Reserved 2', 'bf09': 'Solaris Reserved 3', 'bf0a': 'Solaris Reserved 4', 'bf0b': 'Solaris Reserved 5', 'c001': 'HP-UX data', 'c002': 'HP-UX service', 'ea00': 'Freedesktop $BOOT', 'eb00': 'Haiku BFS', 'ed00': 'Sony system partition', 'ed01': 'Lenovo system partition', 'ef00': 'EFI System', 'ef01': 'MBR partition scheme', 'ef02': 'BIOS boot partition', 'f800': 'Ceph OSD', 'f801': 'Ceph dm-crypt OSD', 'f802': 'Ceph journal', 'f803': 'Ceph dm-crypt journal', 'f804': 'Ceph disk in creation', 'f805': 'Ceph dm-crypt disk in creation', 'fb00': 'VMWare VMFS', 'fb01': 'VMWare reserved', 'fc00': 'VMWare kcore crash protection', 'fd00': 'Linux RAID'}
conf['disks'] = {} conf['disks'] = {}
@ -111,15 +132,17 @@ class aifgen(object):
else: else:
maxpart = '4' # yeah, extended volumes can do more, but that's not supported in AIF-NG. yet? maxpart = '4' # yeah, extended volumes can do more, but that's not supported in AIF-NG. yet?
partnumsin = chkPrompt('* How many partitions should this disk have? (Maximum: {0}) '.format(maxpart), diskhelp) partnumsin = chkPrompt('* How many partitions should this disk have? (Maximum: {0}) '.format(maxpart), diskhelp)
if not isinstance(partnumsin, int): try:
int(partnumsin)
except:
exit(' !! ERROR: Must be an integer.') exit(' !! ERROR: Must be an integer.')
if partnumsin < 1: if int(partnumsin) < 1:
exit(' !! ERROR: Must be a positive integer.') exit(' !! ERROR: Must be a positive integer.')
if partnumsin > int(maxpart): if int(partnumsin) > int(maxpart):
exit(' !! ERROR: Must be less than {0}'.format(maxpart)) exit(' !! ERROR: Must be less than {0}'.format(maxpart))
parthelp = diskhelp + ['https://wiki.archlinux.org/index.php/installation_guide#Format_the_partitions', parthelp = diskhelp + ['https://wiki.archlinux.org/index.php/installation_guide#Format_the_partitions',
'https://aif.square-r00t.net/#code_part_code'] 'https://aif.square-r00t.net/#code_part_code']
for partn in range(1, partnumsin + 1): for partn in range(1, int(partnumsin) + 1):
# https://aif.square-r00t.net/#code_part_code # https://aif.square-r00t.net/#code_part_code
conf['disks'][disk]['parts'][partn] = {} conf['disks'][disk]['parts'][partn] = {}
for s in ('start', 'stop'): for s in ('start', 'stop'):
@ -130,8 +153,8 @@ class aifgen(object):
newhelp = 'https://aif.square-r00t.net/#fstypes' newhelp = 'https://aif.square-r00t.net/#fstypes'
if newhelp not in parthelp: if newhelp not in parthelp:
parthelp.append(newhelp) parthelp.append(newhelp)
fstypein = chkPrompt(('** What filesystem type should {0} be? ' + fstypein = chkPrompt(('** What filesystem type should partition {0} be? ' +
'See wikihelp for valid fstypes: '.format(partn), parthelp)) 'See wikihelp for valid fstypes: ').format(partn), parthelp)
if fstypein not in fstypes.keys(): if fstypein not in fstypes.keys():
exit(' !! ERROR: {0} is not a valid filesystem type.'.format(fstypein)) exit(' !! ERROR: {0} is not a valid filesystem type.'.format(fstypein))
else: else:
@ -139,8 +162,8 @@ class aifgen(object):
mnthelp = ['https://wiki.archlinux.org/index.php/installation_guide#Mount_the_file_systems', mnthelp = ['https://wiki.archlinux.org/index.php/installation_guide#Mount_the_file_systems',
'https://aif.square-r00t.net/#code_mount_code'] 'https://aif.square-r00t.net/#code_mount_code']
mntin = chkPrompt('\nWhat mountpoint(s) would you like to be configured on the target system?\n' + mntin = chkPrompt('\nWhat mountpoint(s) would you like to be configured on the target system?\n' +
'If you have multiple mountpoints, separate with a comma (e.g. \'/mnt/aif,/mnt/aif/boot\').\n' + '\tIf you have multiple mountpoints, separate with a comma (e.g. \'/mnt/aif,/mnt/aif/boot\').\n' +
'NOTE: Can be \'swap\' for swapspace.', mnthelp) '\t(NOTE: Can be \'swap\' for swapspace.): ', mnthelp)
conf['mounts'] = {} conf['mounts'] = {}
for m in mntin.split(','): for m in mntin.split(','):
mount = m.strip() mount = m.strip()
@ -166,19 +189,19 @@ class aifgen(object):
'just leave this blank: ', mnthelp) 'just leave this blank: ', mnthelp)
if fstypein == '': if fstypein == '':
conf['mounts'][order]['fstype'] = False conf['mounts'][order]['fstype'] = False
elif not re.match('^[a-z]+([0-9]+)$', fstypein): # Not 100%, but should catch most faulty entries elif not re.match('^[a-z]+([0-9]+)?$', fstypein): # Not 100%, but should catch most faulty entries
exit(' !! ERROR: {0} does not seem to be a valid filesystem type.'.format(fstypein)) exit(' !! ERROR: {0} does not seem to be a valid filesystem type.'.format(fstypein))
else: else:
conf['mounts'][order]['fstype'] = fstypein conf['mounts'][order]['fstype'] = fstypein
mntoptsin = chkPrompt('* What, if any, mount option(s) (mount\'s -o option) do you require? (Multiple options should be separated\n' + mntoptsin = chkPrompt('* What, if any, mount option(s) (mount\'s -o option) do you require? (Multiple options should be separated\n' +
'with a comma). If none, leave this blank: ', mnthelp) '\twith a comma). If none, leave this blank: ', mnthelp)
if mntoptsin == '': if mntoptsin == '':
conf['mounts'][order]['opts'] = False conf['mounts'][order]['opts'] = False
elif not re.match('^[A-Za-z0-9_\.\-]+(,[A-Za-z0-9_\.\-]+)*', mntoptsin): elif not re.match('^[A-Za-z0-9_\.\-]+(,[A-Za-z0-9_\.\-]+)*', mntoptsin):
exit(' !! ERROR: You seem to have not specified valid mount options.') exit(' !! ERROR: You seem to have not specified valid mount options.')
else: else:
conf['mounts'][order]['opts'] = mntoptsin conf['mounts'][order]['opts'] = mntoptsin
print('Now, let\'s configure the network. Note that at this time, wireless/more exotic networking is not supported by AIF-NG.') print('\nNow, let\'s configure the network. Note that at this time, wireless/more exotic networking is not supported by AIF-NG.\n')
conf['network'] = {} conf['network'] = {}
nethelp = ['https://wiki.archlinux.org/index.php/installation_guide#Network_configuration', nethelp = ['https://wiki.archlinux.org/index.php/installation_guide#Network_configuration',
'https://aif.square-r00t.net/#code_network_code'] 'https://aif.square-r00t.net/#code_network_code']
@ -197,9 +220,33 @@ class aifgen(object):
conf['network']['hostname'] = hostname conf['network']['hostname'] = hostname
conf['network']['ifaces'] = {} conf['network']['ifaces'] = {}
nethelp.append('https://aif.square-r00t.net/#code_iface_code') nethelp.append('https://aif.square-r00t.net/#code_iface_code')
ifaces = ifacePrompt(nethelp) conf['network']['ifaces'] = ifacePrompt(nethelp)

print('\nNow let\'s configure some basic system settings.')
if args['verbose']: syshelp = ['https://aif.square-r00t.net/#code_system_code']
syshelp.append('https://wiki.archlinux.org/index.php/installation_guide#Time_zone')
tzin = chkPrompt('* What timezone should the newly installed system use? (Default is UTC): ', syshelp)
if tzin == '':
tzin = 'UTC'
syshelp[1] = 'https://wiki.archlinux.org/index.php/installation_guide#Locale'
localein = chkPrompt('* What locale should the new system use? (Default is en_US.UTF-8): ', syshelp)
if localein == '':
localein = 'en_US.UTF-8'
syshelp[1] = 'https://aif.square-r00t.net/#code_mount_code'
chrootpathin = chkPrompt('* What chroot path should the host use? This should be one of the mounts you specified above: ', syshelp)
if not re.match('^/([^/\x00\s]+(/)?)+$', chrootpathin):
exit('!! ERROR: Your chroot path does not seem to be a valid path/specifier.')
syshelp[1] = 'https://wiki.archlinux.org/index.php/installation_guide#Set_the_keyboard_layout'
kbdin = chkPrompt('* What keyboard layout should the newly installed system use? (Default is US): ', syshelp)
if kbdin == '':
kbdin = 'US'
del(syshelp[1])
rbtin = chkPrompt('* Would you like to reboot the host system after installation completes? ((Y)ES/(n)o): ', syshelp)
if not re.match('^no?$', rbtin.lower()):
rebootme = True
else:
rebootme = False
conf['system'] = {'timezone': tzin, 'locale': localein, 'chrootpath': chrootpathin, 'kbd': kbdin, 'reboot': rbtin}
if self.args['verbose']:
import pprint import pprint
pprint.pprint(conf) pprint.pprint(conf)
return(conf) return(conf)
@ -209,7 +256,7 @@ class aifgen(object):
def main(self): def main(self):
if self.args['oper'] == 'create': if self.args['oper'] == 'create':
self.getOpts() conf = self.getOpts()
if self.args['oper'] in ('create', 'view'): if self.args['oper'] in ('create', 'view'):
self.validateXML() self.validateXML()


@ -293,4 +340,4 @@ def main():
aif.getOpts() aif.getOpts()


if __name__ == '__main__': if __name__ == '__main__':
main() main()