diff --git a/aif-config.py b/aif-config.py index 852ca6a..0b18cec 100755 --- a/aif-config.py +++ b/aif-config.py @@ -1,11 +1,19 @@ #!/usr/bin/env python3 -try: - from lxml import etree - lxml_avail = True -except ImportError: - import xml.etree.ElementTree as etree # https://docs.python.org/3/library/xml.etree.elementtree.html +xmldebug = True + +if not xmldebug: + try: + from lxml import etree + lxml_avail = True + except ImportError: + import xml.etree.ElementTree as etree # https://docs.python.org/3/library/xml.etree.elementtree.html + lxml_avail = False +else: + # debugging + import xml.etree.ElementTree as etree lxml_avail = False + # end debugging import argparse import crypt import datetime @@ -131,10 +139,10 @@ class aifgen(object): def ifacePrompt(nethelp): ifaces = {} moreIfaces = True - print('\nPlease enter the name of the interface you would like to use.\n' + - '\tCan instead be \'auto\' for automatic configuration of the first found interface\n' + - '\twith an active link. (You can only specify one auto device per system, and all subsequent\n' - '\tinterface entries will be ignored.)\n') + print('\tNOTE: You must specify the "persistent device naming" name of the device when configuring.\n' + + '\tYou can instead specify \'auto\' for automatic configuration of the first found interface\n' + + '\twith an active link. (You can only specify one auto device per system, and all other\n' + '\tinterface entries will be ignored by AIF-NG.)\n') while moreIfaces: ifacein = chkPrompt('* Interface device: ', nethelp) addrin = chkPrompt(('** Address for {0} in CIDR format (can be an IPv4 or IPv6 address; ' + @@ -331,7 +339,7 @@ class aifgen(object): 'enabled': False}} chkdefs = chkPrompt(('* Would you like to review the default repository configuration ' + '(and possibly edit it)? ({0}y{1}/n) ').format(color.BOLD, color.END), repohelp) - fmtstr = '{0} {1:<20} {2:^10} {3:^10} {4}' # ('#', 'REPO', 'ENABLED', 'SIGLEVEL', 'URI') + fmtstr = '\t{0} {1:<20} {2:^10} {3:^10} {4}' # ('#', 'REPO', 'ENABLED', 'SIGLEVEL', 'URI') if not re.match('^no?$', chkdefs.lower()): print('{0}{1}{2}'.format(color.BOLD, fmtstr.format('#', 'REPO', 'ENABLED', 'SIGLEVEL', 'URI'), color.END)) rcnt = 1 @@ -491,12 +499,12 @@ class aifgen(object): return(scrpts) conf = {} print('[{0}] Beginning configuration...'.format(datetime.datetime.now())) - print('You may reply with \'wikihelp\' on the first prompt of a question for the relevant link(s) in the Arch wiki ' + + print('\n\tYou may reply with \'wikihelp\' on the first prompt of a question for the relevant link(s) in the Arch wiki ' + '(and other resources).') # https://aif.square-r00t.net/#code_disk_code diskhelp = ['https://wiki.archlinux.org/index.php/installation_guide#Partition_the_disks'] print('{0}= DISKS ={1}'.format(color.BOLD, color.END)) - diskin = chkPrompt('\n* What disk(s) would you like to be configured on the target system?\n' + + diskin = chkPrompt('* What disk(s) would you like to be configured on the target system?\n' + '\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. 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'} @@ -546,18 +554,19 @@ class aifgen(object): exit(' !! ERROR: {0} is not a valid filesystem type.'.format(fstypein)) else: print('\t(Selected {0})'.format(fstypes[fstypein])) + conf['disks'][disk]['parts'][partn]['fstype'] = fstypein mnthelp = ['https://wiki.archlinux.org/index.php/installation_guide#Mount_the_file_systems', 'https://aif.square-r00t.net/#code_mount_code'] - print('{0}= MOUNTS ={1}'.format(color.BOLD, color.END)) - mntin = chkPrompt('\n* What mountpoint(s) would you like to be configured on the target system?\n' + - '\tIf you have multiple mountpoints, separate with a comma (e.g. \'/mnt/aif,/mnt/aif/boot\').\n' + - '\t(NOTE: Can be \'swap\' for swapspace.): ', mnthelp) + print('\n{0}= MOUNTS ={1}'.format(color.BOLD, color.END)) + mntin = chkPrompt('* What mountpoint(s) would you like to be configured on the target system?\n' + + '\tIf you have multiple mountpoints, separate with a comma (e.g. \'/mnt/aif,/mnt/aif/boot\').\n' + + '\t(NOTE: Can be \'swap\' for swapspace.): ', mnthelp) conf['mounts'] = {} for m in mntin.split(','): mount = m.strip() if not re.match('^(/([^/\x00\s]+(/)?)+|swap)$', mount): exit('!! ERROR: Mountpoint {0} does not seem to be a valid path/specifier.'.format(mount)) - print('\n{0}==MOUNT {1}=={2}'.format(color.BOLD, mount, color.END)) + print('\n{0}== MOUNT: {1} =={2}'.format(color.BOLD, mount, color.END)) dvcin = chkPrompt('* What device/partition should be mounted here? ', mnthelp) if not re.match('^/dev/[A-Za-z0]+', dvcin): exit(' !! ERROR: Must be a full path to a device/partition.') @@ -717,10 +726,13 @@ class aifgen(object): scrptsin = chkPrompt('* Do you have any hook scripts you\'d like to add? (y/{0}n{1}) '.format(color.BOLD, color.END), scrpthlp) if re.match('^y(es)?$', scrptsin.lower()): conf['scripts'] = scrptPrompt(scrpthlp) - print('\n\n{0}ALL DONE!{1} Whew. You can find your configuration file at: {2}{3}{1}\n'.format(color.BOLD, - color.END, - color.BLUE, - self.args['cfgfile'])) + else: + conf['scripts'] = False + print('\n\n[{0}] {1}ALL DONE!{2} Whew. You can find your configuration file at: {3}{4}{2}\n'.format(datetime.datetime.now(), + color.BOLD, + color.END, + color.BLUE, + self.args['cfgfile'])) if self.args['verbose']: import pprint pprint.pprint(conf) @@ -739,7 +751,9 @@ class aifgen(object): def validateXML(self): # First we validate the XSD. if not lxml_avail: - exit('\nXML validation is only supported by LXML.\nIf you want to validate the XML, install the lxml python module (python-lxml) and try again.\n') + exit('\nXML validation is only supported by LXML.\n' + + 'If you want to validate the XML, install the lxml python module (python-lxml) ' + + 'and run:\n\t{0} validate -f {1}.\n'.format(sys.argv[0], self.args['cfgfile'])) try: xsd = etree.XMLSchema(self.getXSD()) print('\nXSD: {0}PASSED{1}'.format(color.BOLD, color.END)) @@ -753,11 +767,174 @@ class aifgen(object): print('XML: {0}FAILED{1}: {2}\n'.format(color.BOLD, color.END, e)) def genXMLFile(self, conf): + namespaces = {'aif': 'http://aif.square-r00t.net/', 'xsi': 'http://www.w3.org/2001/XMLSchema-instance'} + xsi = {'{http://www.w3.org/2001/XMLSchema-instance}schemaLocation' : 'http://aif.square-r00t.net aif.xsd'} + #for ns in namespaces.keys(): + # etree.register_namespace(ns, namespaces[ns]) if lxml_avail: - root = etree.Element('aif') + genname = 'LXML (http://lxml.de/)' + root = etree.Element('aif', nsmap = namespaces, attrib = xsi) + #xml = etree.ElementTree(root) else: - root = etree.ElementTree.Element('aif') - pass + genname = 'Python stdlib "xml" module' + for ns in namespaces.keys(): + etree.register_namespace(ns, namespaces[ns]) + root = etree.Element('aif') + if self.args['oper'] == 'convert': + fromstr = self.args['inputfile'] + else: + fromstr = 'interactive commandline' + root.append(etree.Comment('Generated by {0} on {1} from {2} via {3}'.format(sys.argv[0], datetime.datetime.now(), fromstr, genname))) + root.append(etree.Comment('THIS FILE CONTAINS SENSITIVE INFORMATION. SHARE/SCRUB WISELY.')) + # /aif/ required sections + for e in ('storage', 'network', 'system', 'pacman', 'bootloader'): + root.append(etree.Element(e)) + # /aif/ optional sections + if conf['scripts']: + root.append(etree.Element('scripts')) + # /aif/storage + strg = root.find('storage') + for d in conf['disks'].keys(): + # /aif/storage/disk + disk = etree.Element('disk', device = d, diskfmt = conf['disks'][d]['fmt']) + for p in conf['disks'][d]['parts'].keys(): + # /aif/storage/disk/part + start = conf['disks'][d]['parts'][p]['start'] + stop = conf['disks'][d]['parts'][p]['stop'] + fstype = conf['disks'][d]['parts'][p]['fstype'] + disk.append(etree.Element('part', num = p, start = start, stop = stop, fstype = fstype)) + strg.append(disk) + # /aif/storage/mount + for m in conf['mounts'].keys(): + mnt = {} + mnt['order'] = m + mnt['source'] = conf['mounts'][m]['device'] + mnt['target'] = conf['mounts'][m]['target'] + # These are optional, hence the splat and mnt dict. + for o in ('fstype', 'opts'): + if o in conf['mounts'][m].keys() and conf['mounts'][m][o]: + mnt[o] = conf['mounts'][m][o] + mount = etree.Element('mount', **mnt) + strg.append(mount) + # /aif/network + ntwk = root.find('network') + ntwk.set('hostname', conf['network']['hostname']) + for i in conf['network']['ifaces'].keys(): + # /aif/network/iface + optmap = {'gw': 'gateway', 'proto': 'netproto', 'resolvers': 'resolvers'} + iface = {} + iface['device'] = i + iface['address'] = conf['network']['ifaces'][i]['address'] + for o in optmap.keys(): + if conf['network']['ifaces'][i][o]: + if o == 'resolvers': + iface[optmap[o]] = ','.join(conf['network']['ifaces'][i][o]) + else: + iface[optmap[o]] = conf['network']['ifaces'][i][o] + interface = etree.Element('iface', **iface) + ntwk.append(interface) + # /aif/system + systm = root.find('system') + for a in ('timezone', 'locale', 'chrootpath', 'kbd', 'reboot'): + if isinstance(conf['system'][a], bool): + val = str(conf['system'][a]).lower() + else: + val = conf['system'][a] + systm.set(a, val) + # /aif/system/users + usrs = etree.Element('users', rootpass = conf['system']['rootpass']) + subs = ('home', 'xgroups') + optional = ('uid', 'group', 'gid') + if conf['system']['users']: + for u in conf['system']['users'].keys(): + # /aif/system/users/user + o = {} + o['name'] = u + for i in conf['system']['users'][u].keys(): + if isinstance(conf['system']['users'][u][i], bool): + val = str(conf['system']['users'][u][i]).lower() + else: + val = conf['system']['users'][u][i] + if i not in subs: # we handle "subs" as subelements + if i in optional: # and we only add optional attribs if they're populated + if conf['system']['users'][u][i]: + o[i] = val + else: + o[i] = val + user = etree.Element('user', **o) + # /aif/system/users/user/home + if conf['system']['users'][u]['home']: + o = {} + o['create'] = str(conf['system']['users'][u]['home']['create']).lower() + if 'path' in conf['system']['users'][u]['home'].keys(): + o['path'] = conf['system']['users'][u]['home']['path'] + home = etree.Element('home', **o) + user.append(home) + # /aig/system/users/user/xgroup + if conf['system']['users'][u]['xgroups']: + for g in conf['system']['users'][u]['xgroups'].keys(): + o = {} + o['name'] = g + o['create'] = str(conf['system']['users'][u]['xgroups'][g]['create']).lower() + if 'gid' in conf['system']['users'][u]['xgroups'][g].keys() and conf['system']['users'][u]['xgroups'][g]['gid']: + o['gid'] = conf['system']['users'][u]['xgroups'][g]['gid'] + xgrp = etree.Element('xgroup', **o) + user.append(xgrp) + usrs.append(user) + systm.append(usrs) + # /aif/system/service + if conf['system']['services']: + for s in conf['system']['services'].keys(): + o = {} + o['name'] = s + o['status'] = str(conf['system']['services'][s]).lower() + svc = etree.Element('service', **o) + systm.append(svc) + # /aif/pacman + pcmn = root.find('pacman') + if conf['software']['pkgr']: + pcmn.set('command', conf['software']['pkgr']) + # /aif/pacman/repo + repos = etree.Element('repos') + for r in conf['software']['repos'].keys(): + o = {} + o['name'] = r + o['enabled'] = str(conf['software']['repos'][r]['enabled']).lower() + o['siglevel'] = conf['software']['repos'][r]['siglevel'] + o['mirror'] = conf['software']['repos'][r]['mirror'] + repo = etree.Element('repo', **o) + repos.append(repo) + pcmn.append(repos) + # debugging + if lxml_avail: + # LXML + #print(etree.tostring(root).decode('utf-8')) + print(etree.tostring(root, xml_declaration = True, encoding = 'utf-8', pretty_print = True).decode('utf-8')) + else: + # XML + import xml.dom.minidom + xmlstr = etree.tostring(root, encoding = 'utf-8') + # holy cats, the xml module sucks. + nsstr = '' + for ns in namespaces.keys(): + nsstr += ' xmlns:{0}="{1}"'.format(ns, namespaces[ns]) + for x in xsi.keys(): + xsiname = x.split('}')[1] + nsstr += ' xsi:{0}="{1}"'.format(xsiname, xsi[x]) + outstr = xml.dom.minidom.parseString(xmlstr).toprettyxml(indent = ' ').splitlines() + outstr[0] = '' + outstr[1] = ''.format(nsstr) + print('\n'.join(outstr)) + # end debugging + # https://stackoverflow.com/questions/4886189/python-namespaces-in-xml-elementtree-or-lxml + #if lxml_avail: + #xml.write(..., xml_declaration = True, encoding='utf-8') + #else: + #import xml.dom.minidom + #xmlstr = etree.tostring(root, encoding = 'utf-8') + #with open(self.args['cfgfile'], 'w') as f: # TODO: test this. print() wrap it necessary? + #f.write(xml.dom.minidom.parseString(xmlstr).toprettyxml(indent = ' ')) + return() def main(self): if self.args['oper'] == 'create': diff --git a/docs/TODO b/docs/TODO index 6f6945f..43f4f76 100644 --- a/docs/TODO +++ b/docs/TODO @@ -5,8 +5,8 @@ - how to support mdadm, lvm? - support serverside "autoconfig"- a mechanism to let servers automatically generate xml build configs. e.g.: kernel ... aif_url="https://build.domain.tld/aif-ng.php" auto=yes - would yield the *client* sending info via URL params, e.g. - https://build.domain.tld/aif-ng.php?disk[]=sda&disk[]=sdb&disk[sda]=300GB&disk[sdb]=500GB + would yield the *client* sending info via URL params (actually, this might be better as a JSON POST, since we already have a way to generate JSON. sort of.), + e.g. https://build.domain.tld/aif-ng.php?disk[]=sda&disk[]=sdb&disk[sda]=300GB&disk[sdb]=500GB (can have it so that the autoconfig is only supported clientside if pyyaml is installed) or something like that. - parser: make sure to use https://mikeknoop.com/lxml-xxe-exploit/ fix - convert use of confobj or whatever to maybe be suitable to use webFetch instead. LOTS of duplicated code there. @@ -23,13 +23,14 @@ run on /mnt/aif/run type tmpfs (rw,nosuid,nodev,relatime,mode=755) tmp on /mnt/aif/tmp type tmpfs (rw,nosuid,nodev) -DOCUMENTATION: aif-config.py (and note sample yaml as well) -also need to add users, xgroups, etc. etc. etc. into the getOpts +DOCUMENTATION: aif-config.py (and note sample json as well) +-finish genXML() or whatever i call it +-add support- to both the config run and the XML generator also create: -create boot media with bdisk since default arch doesn't even have python 3 -- this is.. sort of? done. but iPXE/mini build is failing, need to investigate why - +-- i tihnk i fixed iPXE but i need to generate another one once 1.5 is released docs: http://lxml.de/parsing.html https://www.w3.org/2001/XMLSchema.xsd diff --git a/docs/examples/aif-sample-intermediate.json b/docs/examples/aif-sample-intermediate.json index f15cae5..1f470ba 100644 --- a/docs/examples/aif-sample-intermediate.json +++ b/docs/examples/aif-sample-intermediate.json @@ -8,10 +8,12 @@ "fmt": "gpt", "parts": { "1": { + "fstype": "8300", "start": "0%", "stop": "95%" }, "2": { + "fstype": "ef00", "start": "95%", "stop": "100%" } @@ -21,14 +23,17 @@ "fmt": "gpt", "parts": { "1": { + "fstype": "8300", "start": "0%", "stop": "47%" }, "2": { + "fstype": "8300", "start": "47%", "stop": "95%" }, "3": { + "fstype": "8200", "start": "95%", "stop": "100%" } @@ -87,6 +92,7 @@ } } }, + "scripts": false, "software": { "packages": { "openssh": "None" @@ -130,7 +136,7 @@ "kbd": "US", "locale": "en_US.UTF-8", "reboot": true, - "rootpass": "$6$0jk/xhwahQHTi5QP$VWTgGlHNdSBDbQmJXUwJPZqajfL3JqYYF7Ghxk3ZSKi12WWXb49KsjR7q0bigvgBBBk5A/mvYES3/qareytFS0", + "rootpass": "$6$OeSE5pp4BLWZUn6H$9Y.NO/2cUliOr.apu8qSmgmL4EbGei0u22cw1IANs0h6ek45t8bpHveY7rlHAlljd8PKIxvIRtY9bRCzV24h50", "services": { "sshd": true }, @@ -141,7 +147,7 @@ "gid": false, "group": false, "home": false, - "password": "$6$IlEwDkNmZRuTrT97$vKHjREGspspApBd8aQ/y1S43yRmGMjAzqOmdjNRLWaZyNKqGPrIjMHV9CJc7BzQgU12pRz3cwC6yyc8BDFARu/", + "password": "$6$RCL/E8zPTHoYjITS$MsBQ9DXibdRvjE8a0ak8F2OCzShcRg3vKXSyLAipokaIJvTwFWwlLda1MQr6zTzUxlFui.9Ep4k3B8vdRyBX6.", "sudo": true, "uid": false, "xgroups": { @@ -153,4 +159,4 @@ } } } -} +} \ No newline at end of file diff --git a/docs/examples/aif-sample-intermediate.json.txt b/docs/examples/aif-sample-intermediate.json.txt index 1aae677..9bbc381 100644 --- a/docs/examples/aif-sample-intermediate.json.txt +++ b/docs/examples/aif-sample-intermediate.json.txt @@ -1,11 +1,21 @@ {'boot': {'efi': True, 'target': '/boot'}, 'disks': {'/dev/sda': {'fmt': 'gpt', - 'parts': {1: {'start': '0%', 'stop': '95%'}, - 2: {'start': '95%', 'stop': '100%'}}}, + 'parts': {1: {'fstype': '8300', + 'start': '0%', + 'stop': '95%'}, + 2: {'fstype': 'ef00', + 'start': '95%', + 'stop': '100%'}}}, '/dev/sdb': {'fmt': 'gpt', - 'parts': {1: {'start': '0%', 'stop': '47%'}, - 2: {'start': '47%', 'stop': '95%'}, - 3: {'start': '95%', 'stop': '100%'}}}}, + 'parts': {1: {'fstype': '8300', + 'start': '0%', + 'stop': '47%'}, + 2: {'fstype': '8300', + 'start': '47%', + 'stop': '95%'}, + 3: {'fstype': '8200', + 'start': '95%', + 'stop': '100%'}}}}, 'mounts': {1: {'device': '/dev/sda1', 'fstype': 'ext4', 'opts': 'defaults', @@ -35,7 +45,8 @@ 'gw': '192.168.1.1', 'proto': 'ipv4', 'resolvers': ['4.2.2.1', '4.2.2.2']}}}, - 'software': {'packages': {'openssh': 'None'}, + 'scripts': False, + 'software': {'packages': {'openssh': None}, 'pkgr': False, 'repos': {'community': {'enabled': True, 'mirror': 'file:///etc/pacman.d/mirrorlist', @@ -59,14 +70,14 @@ 'kbd': 'US', 'locale': 'en_US.UTF-8', 'reboot': True, - 'rootpass': '$6$0jk/xhwahQHTi5QP$VWTgGlHNdSBDbQmJXUwJPZqajfL3JqYYF7Ghxk3ZSKi12WWXb49KsjR7q0bigvgBBBk5A/mvYES3/qareytFS0', + 'rootpass': '$6$OeSE5pp4BLWZUn6H$9Y.NO/2cUliOr.apu8qSmgmL4EbGei0u22cw1IANs0h6ek45t8bpHveY7rlHAlljd8PKIxvIRtY9bRCzV24h50', 'services': {'sshd': True}, 'timezone': 'UTC', 'users': {'aifusr': {'comment': 'A Test User', 'gid': False, 'group': False, 'home': False, - 'password': '$6$IlEwDkNmZRuTrT97$vKHjREGspspApBd8aQ/y1S43yRmGMjAzqOmdjNRLWaZyNKqGPrIjMHV9CJc7BzQgU12pRz3cwC6yyc8BDFARu/', + 'password': '$6$RCL/E8zPTHoYjITS$MsBQ9DXibdRvjE8a0ak8F2OCzShcRg3vKXSyLAipokaIJvTwFWwlLda1MQr6zTzUxlFui.9Ep4k3B8vdRyBX6.', 'sudo': True, 'uid': False, 'xgroups': {'users': {'create': False, diff --git a/extras/createtest.expect b/extras/createtest.expect index adc704b..bc39ba8 100755 --- a/extras/createtest.expect +++ b/extras/createtest.expect @@ -14,7 +14,8 @@ if {$force_conservative} { #set send_slow {10 .001} set timeout -1 -spawn ./aif-config.py create -v:r -f /tmp/aif.xml +#spawn ./aif-config.py create -v:r -f /tmp/aif.xml +spawn ./aif-config.py create -v -f /tmp/aif.xml ## disks send -- "/dev/sda,/dev/sdb\r" # sda