commiting sample JSON input file fir aif-config.py

This commit is contained in:
brent s 2017-05-11 23:27:22 -04:00
parent cdc77545ea
commit 094623f710
6 changed files with 144 additions and 22 deletions

View File

@ -69,7 +69,7 @@ class aifgen(object):
'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); ' + 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':
addrtype = 'auto' addrtype = 'auto'
@ -79,7 +79,12 @@ class aifgen(object):
else: else:
addrtype = 'static' addrtype = 'static'
try: try:
ipver = ipaddress.ip_network(ipaddr, strict = False) ipaddress.ip_network(addrin, strict = False)
try:
ipaddress.IPv4Address(addrin.split('/')[0])
ipver = 'ipv4'
except ipaddress.AddressValueError:
ipver = 'ipv6'
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':
@ -88,7 +93,7 @@ class aifgen(object):
ipaddress.ip_address(gwin) ipaddress.ip_address(gwin)
except: except:
exit(' !! ERROR: You did not enter a valid IPv4/IPv6 address.') exit(' !! ERROR: You did not enter a valid IPv4/IPv6 address.')
ifaces[ifacein] = {'address': addrin, 'proto': ipver, 'gw': qwin, 'resolvers': []} ifaces[ifacein] = {'address': addrin, 'proto': ipver, 'gw': gwin, 'resolvers': []}
resolversin = chkPrompt('* What DNS resolvers should we use? Can accept a comma-separated list: ', nethelp) resolversin = chkPrompt('* What DNS resolvers should we use? Can accept a comma-separated list: ', nethelp)
for rslv in resolversin.split(','): for rslv in resolversin.split(','):
rslvaddr = rslv.strip() rslvaddr = rslv.strip()
@ -197,10 +202,11 @@ class aifgen(object):
'\twith 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_\.\-=]+)*', re.sub('\s', '', mntoptsin)): # TODO: shlex split this instead?
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 # TODO: slex this instead? is it possible for mount opts to contain whitespace?
conf['mounts'][order]['opts'] = re.sub('\s', '', mntoptsin)
print('\nNow, let\'s configure the network. Note that at this time, wireless/more exotic networking is not supported by AIF-NG.\n') 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',
@ -245,19 +251,29 @@ class aifgen(object):
rebootme = True rebootme = True
else: else:
rebootme = False rebootme = False
conf['system'] = {'timezone': tzin, 'locale': localein, 'chrootpath': chrootpathin, 'kbd': kbdin, 'reboot': rbtin} conf['system'] = {'timezone': tzin, 'locale': localein, 'chrootpath': chrootpathin, 'kbd': kbdin, 'reboot': rebootme}
if self.args['verbose']: if self.args['verbose']:
import pprint import pprint
pprint.pprint(conf) pprint.pprint(conf)
return(conf) return(conf)
def convertJSON(self):
with open(args['inputfile'], 'r') as f:
try:
conf = json.loads(f.read())
except:
exit(' !! ERROR: {0} does not seem to be a strict JSON file.'.format(args['inputfile']))
return(conf)

def validateXML(self): def validateXML(self):
pass pass
def main(self): def main(self):
if self.args['oper'] == 'create': if self.args['oper'] == 'create':
conf = self.getOpts() conf = self.getOpts()
if self.args['oper'] in ('create', 'view'): elif self.args['oper'] == 'convert':
conf = self.convertJSON()
if self.args['oper'] in ('create', 'view', 'convert'):
self.validateXML() self.validateXML()


def parseArgs(): def parseArgs():
@ -281,12 +297,19 @@ def parseArgs():
viewargs = subparsers.add_parser('view', viewargs = subparsers.add_parser('view',
help = 'View an AIF-NG XML configuration file.', help = 'View an AIF-NG XML configuration file.',
parents = [commonargs]) parents = [commonargs])
convertargs = subparsers.add_parser('convert',
help = 'Convert a "more" human-readable JSON configuration file to AIF-NG-compatible XML.',
parents = [commonargs])
createargs.add_argument('-v', createargs.add_argument('-v',
'--verbose', '--verbose',
dest = 'verbose', dest = 'verbose',
action = 'store_true', action = 'store_true',
help = 'Print the dict of raw values used to create the XML. Mostly/only useful for debugging.') help = 'Print the dict of raw values used to create the XML. Mostly/only useful for debugging.')
convertargs.add_argument('-i',
'--input',
dest = 'inputfile',
required = True,
help = 'The JSON file to import and convert into XML.')
return(args) return(args)
def verifyArgs(args): def verifyArgs(args):
@ -294,27 +317,17 @@ def verifyArgs(args):
args['cfgfile'] = re.sub('^/+', '/', args['cfgfile']) args['cfgfile'] = re.sub('^/+', '/', args['cfgfile'])
# Path/file handling - make sure we can create the parent dir if it doesn't exist, # Path/file handling - make sure we can create the parent dir if it doesn't exist,
# check that we can write to the file, etc. # check that we can write to the file, etc.
if args['oper'] == 'create': if args['oper'] in ('create', 'convert'):
args['cfgbak'] = '{0}.bak.{1}'.format(args['cfgfile'], int(datetime.datetime.utcnow().timestamp())) args['cfgbak'] = '{0}.bak.{1}'.format(args['cfgfile'], int(datetime.datetime.utcnow().timestamp()))
try: try:
temp = True temp = True
#mtime = None
#atime = None
if os.path.lexists(args['cfgfile']): if os.path.lexists(args['cfgfile']):
temp = False temp = False
#mtime = os.stat(args['cfgfile']).st_mtime
#atime = os.stat(args['cfgfile']).st_atime
os.makedirs(os.path.dirname(args['cfgfile']), exist_ok = True) os.makedirs(os.path.dirname(args['cfgfile']), exist_ok = True)
with open(args['cfgfile'], 'a') as f: with open(args['cfgfile'], 'a') as f:
f.write('') f.write('')
if temp: if temp:
os.remove(args['cfgfile']) os.remove(args['cfgfile'])
#else:
# WE WERE NEVER HERE.
# I lied; ctime will still be modified, but I think this is playing it safely enough.
# Turns out, though, f.write('') does no modifications but WILL throw the perm error we want.
# Good.
#os.utime(args['cfgfile'], times = (atime, mtime))
except OSError as e: except OSError as e:
print('\nERROR: {0}: {1}'.format(e.strerror, e.filename)) print('\nERROR: {0}: {1}'.format(e.strerror, e.filename))
exit(('\nWe encountered an error when trying to use path {0}.\n' + exit(('\nWe encountered an error when trying to use path {0}.\n' +
@ -327,6 +340,15 @@ def verifyArgs(args):
print('\nERROR: {0}: {1}'.format(e.strerror, e.filename)) print('\nERROR: {0}: {1}'.format(e.strerror, e.filename))
exit(('\nWe encountered an error when trying to use path {0}.\n' + exit(('\nWe encountered an error when trying to use path {0}.\n' +
'Please review the output and address any issues present.').format(args['cfgfile'])) 'Please review the output and address any issues present.').format(args['cfgfile']))
if args['oper'] == 'convert':
# And we need to make sure we have read perms to the JSON input file.
try:
with open(args['inputfile'], 'r') as f:
f.read()
except OSError as e:
print('\nERROR: {0}: {1}'.format(e.strerror, e.filename))
exit(('\nWe encountered an error when trying to read path {0}.\n' +
'Please review the output and address any issues present.').format(args['inputfile']))
return(args) return(args)


def main(): def main():
@ -334,10 +356,13 @@ def main():
if not args['oper']: if not args['oper']:
parseArgs().print_help() parseArgs().print_help()
else: else:
# verifyArgs(args) # Once aifgen.main() is complete, we only need to call that.
# That should handle all the below logic.
aif = aifgen(verifyArgs(args)) aif = aifgen(verifyArgs(args))
if args['oper'] == 'create': if args['oper'] == 'create':
aif.getOpts() aif.getOpts()
elif args['oper'] == 'convert':
aif.convertJSON()


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

View File

@ -23,7 +23,7 @@
run on /mnt/aif/run type tmpfs (rw,nosuid,nodev,relatime,mode=755) run on /mnt/aif/run type tmpfs (rw,nosuid,nodev,relatime,mode=755)
tmp on /mnt/aif/tmp type tmpfs (rw,nosuid,nodev) tmp on /mnt/aif/tmp type tmpfs (rw,nosuid,nodev)


DOCUMENTATION: BUG REPORTS/FEATURE REQUESTS!!!! DOCUMENTATION: aif-config.py (and note sample yaml as well)


also create: also create:
-create boot media with bdisk since default arch doesn't even have python 3 -create boot media with bdisk since default arch doesn't even have python 3

View File

@ -0,0 +1,97 @@
{
"disks":
{
"/dev/sda":
{"fmt": "gpt",
"parts":
{
"1":
{
"start": "0%",
"stop": "90%"
},
"2":
{
"start": "90%",
"stop": "100%"
}
}
},
"/dev/sdb":
{
"fmt": "gpt",
"parts":
{
"1":
{
"start": "0%",
"stop": "10%"
},
"2":
{
"start": "10%",
"stop": "100%"
}
}
}
},
"mounts":
{
"1":
{
"device": "/dev/sda1",
"fstype": "ext4",
"opts": "rw,noatime,errors=remount-ro",
"target": "/mnt/aif"
},
"2":
{
"device": "/dev/sda2",
"fstype": "vfat",
"opts": "rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro,auto",
"target": "/mnt/aif/boot"
},
"3":
{
"device": "/dev/sdb1",
"fstype": "swap",
"opts": false,
"target": "swap"},
"4":
{
"device": "/dev/sdb2",
"fstype": false,
"opts": false,
"target": "/mnt/aif/mnt/data"
}
},
"network":
{
"hostname": "aif.loc.lan",
"ifaces":
{
"ens3":
{
"address": "auto",
"gw": false,
"proto": "ipv4",
"resolvers": false
},
"ens4":
{
"address": "192.168.1.2/24",
"gw": "192.168.1.1",
"proto": "ipv4",
"resolvers": ["4.2.2.1", "4.2.2.2", "8.8.8.8"]
}
}
},
"system":
{
"chrootpath": "/mnt/aif",
"kbd": "US",
"locale": "en_US.UTF-8",
"reboot": true,
"timezone": "UTC"
}
}