2016-11-28 02:56:15 -05:00
|
|
|
import os
|
|
|
|
import shutil
|
2016-12-06 01:52:59 -05:00
|
|
|
import re
|
|
|
|
import subprocess
|
2016-11-28 02:56:15 -05:00
|
|
|
import jinja2
|
2016-11-29 03:16:44 -05:00
|
|
|
import git
|
|
|
|
import patch
|
2016-12-03 06:07:41 -05:00
|
|
|
import datetime
|
2016-12-09 12:41:17 -05:00
|
|
|
import humanize
|
|
|
|
import hashlib
|
2016-11-28 02:56:15 -05:00
|
|
|
|
|
|
|
|
|
|
|
def buildIPXE(conf):
|
|
|
|
build = conf['build']
|
|
|
|
bdisk = conf['bdisk']
|
|
|
|
ipxe = conf['ipxe']
|
2016-12-09 12:41:17 -05:00
|
|
|
mini = ipxe['iso']
|
|
|
|
usb = ipxe['usb']
|
2016-12-06 01:52:59 -05:00
|
|
|
tempdir = conf['build']['tempdir']
|
2016-11-28 02:56:15 -05:00
|
|
|
templates_dir = build['basedir'] + '/extra/templates'
|
2016-11-28 13:35:24 -05:00
|
|
|
ipxe_tpl = templates_dir + '/iPXE'
|
2016-12-06 01:52:59 -05:00
|
|
|
patches_dir = tempdir + '/patches'
|
2016-11-28 13:35:24 -05:00
|
|
|
srcdir = build['srcdir']
|
2016-12-09 12:41:17 -05:00
|
|
|
embedscript = build['dlpath'] + '/EMBED'
|
2016-11-28 13:35:24 -05:00
|
|
|
ipxe_src = srcdir + '/ipxe'
|
2016-12-06 01:52:59 -05:00
|
|
|
img_path = build['isodir'] + '/'
|
2016-12-13 23:43:53 -05:00
|
|
|
ipxe_usb = '{0}-{1}-{2}.usb.img'.format(bdisk['uxname'], bdisk['ver'], build['buildnum'])
|
|
|
|
ipxe_mini = '{0}-{1}-{2}.mini.iso'.format(bdisk['uxname'], bdisk['ver'], build['buildnum'])
|
|
|
|
ipxe_emini = '{0}-{1}-{2}.mini.eiso'.format(bdisk['uxname'], bdisk['ver'], build['buildnum'])
|
2016-12-14 00:46:16 -05:00
|
|
|
usb_file = '{0}{1}'.format(img_path, ipxe_usb)
|
2016-12-13 23:43:53 -05:00
|
|
|
emini_file = '{0}{1}'.format(img_path, ipxe_emini)
|
2016-12-09 12:41:17 -05:00
|
|
|
mini_file = '{0}{1}'.format(img_path, ipxe_mini)
|
2016-11-29 03:16:44 -05:00
|
|
|
ipxe_git_uri = 'git://git.ipxe.org/ipxe.git'
|
|
|
|
patches_git_uri = 'https://github.com/eworm-de/ipxe.git'
|
2016-12-11 17:06:01 -05:00
|
|
|
print('{0}: [IPXE] Prep/fetch sources...'.format(
|
2016-12-09 12:41:17 -05:00
|
|
|
datetime.datetime.now()))
|
2016-11-29 03:16:44 -05:00
|
|
|
# Get the source and apply some cherrypicks
|
|
|
|
if os.path.isdir(ipxe_src):
|
|
|
|
shutil.rmtree(ipxe_src)
|
|
|
|
ipxe_repo = git.Repo.clone_from(ipxe_git_uri, ipxe_src)
|
2016-12-06 01:52:59 -05:00
|
|
|
# Generate patches
|
|
|
|
os.makedirs(patches_dir, exist_ok = True)
|
|
|
|
os.makedirs(img_path, exist_ok = True)
|
|
|
|
tpl_loader = jinja2.FileSystemLoader(ipxe_tpl)
|
|
|
|
env = jinja2.Environment(loader = tpl_loader)
|
2016-11-29 03:16:44 -05:00
|
|
|
patches = ipxe_repo.create_remote('eworm', patches_git_uri)
|
|
|
|
patches.fetch()
|
2016-12-13 23:43:53 -05:00
|
|
|
# TODO: per http://ipxe.org/download#uefi, it builds efi *binaries* now.
|
|
|
|
# we can probably skip the commit patching from eworm and the iso/eiso
|
|
|
|
# (and even usb) generation, and instead use the same method we use in genISO
|
2016-11-29 03:16:44 -05:00
|
|
|
eiso_commit = '189652b03032305a2db860e76fb58e81e3420c4d'
|
|
|
|
nopie_commit = '58557055e51b2587ad3843af58075de916e5399b'
|
|
|
|
# patch files
|
2016-12-06 01:52:59 -05:00
|
|
|
for p in ('01.git-version.patch', '02.banner.patch'):
|
2016-11-29 03:16:44 -05:00
|
|
|
try:
|
2016-12-06 01:52:59 -05:00
|
|
|
tpl = env.get_template('patches/{0}.j2'.format(p))
|
|
|
|
tpl_out = tpl.render(bdisk = bdisk)
|
|
|
|
with open('{0}/{1}'.format(patches_dir, p), 'w+') as f:
|
|
|
|
f.write(tpl_out)
|
2016-12-01 07:13:24 -05:00
|
|
|
patchfile = patch.fromfile(patches_dir + '/' + p)
|
|
|
|
patchfile.apply(strip = 2, root = ipxe_src + '/src')
|
2016-11-29 03:16:44 -05:00
|
|
|
except:
|
|
|
|
pass
|
2016-12-06 01:52:59 -05:00
|
|
|
tpl = env.get_template('EMBED.j2')
|
|
|
|
tpl_out = tpl.render(ipxe = ipxe)
|
|
|
|
with open(embedscript, 'w+') as f:
|
|
|
|
f.write(tpl_out)
|
2016-11-29 03:16:44 -05:00
|
|
|
# Patch using the files before applying the cherrypicks
|
|
|
|
ipxe_repo.git.cherry_pick('-n', eiso_commit)
|
|
|
|
ipxe_repo.git.cherry_pick('-n', nopie_commit)
|
2016-12-06 01:52:59 -05:00
|
|
|
# Feature enabling
|
|
|
|
# In config/general.h
|
|
|
|
with open('{0}/src/config/general.h'.format(ipxe_src), 'r') as f:
|
2016-12-09 12:41:17 -05:00
|
|
|
generalconf = f.read()
|
2016-12-06 01:52:59 -05:00
|
|
|
# And in config/console.h
|
|
|
|
with open('{0}/src/config/console.h'.format(ipxe_src), 'r') as f:
|
2016-12-09 12:41:17 -05:00
|
|
|
consoleconf = f.read()
|
|
|
|
patterns = (('^#undef(\s*NET_PROTO_IPV6.*)$','#define\g<1>'), # enable IPv6
|
|
|
|
('^#undef(\s*DOWNLOAD_PROTO_HTTPS)','#define\g<1>'), # enable HTTPS
|
|
|
|
('^//(#define\s*IMAGE_TRUST_CMD)','\g<1>'), # moar HTTPS
|
|
|
|
('^#undef(\s*DOWNLOAD_PROTO_FTP)','#define\g<1>')) # enable FTP
|
|
|
|
#('^//(#define\s*CONSOLE_CMD)','\g<1>'), # BROKEN in EFI? TODO. if enable, replace } with , above etc.
|
|
|
|
#('^//(#define\s*IMAGE_PNG','\g<1>'), # SAME, broken in EFI? TODO.
|
|
|
|
#console = ('^//(#define\s*CONSOLE_VESAFB)','\g<1>') # BROKEN in EFI? TODO.
|
2016-12-06 01:52:59 -05:00
|
|
|
# https://stackoverflow.com/a/4427835
|
|
|
|
# https://emilics.com/notebook/enblog/p869.html
|
2016-12-09 12:41:17 -05:00
|
|
|
# The above methods don't seem to work. it craps out on the pattern matchings
|
|
|
|
# so we use tuples instead.
|
|
|
|
for x in patterns:
|
|
|
|
generalconf = re.sub(x[0], x[1], generalconf, flags=re.MULTILINE)
|
2016-12-06 01:52:59 -05:00
|
|
|
with open('{0}/src/config/general.h'.format(ipxe_src), 'w') as f:
|
2016-12-09 12:41:17 -05:00
|
|
|
f.write(generalconf)
|
2016-12-06 01:52:59 -05:00
|
|
|
# Uncomment when we want to test the above consdict etc.
|
2016-12-09 12:41:17 -05:00
|
|
|
#for x in patterns:
|
|
|
|
# generalconf = re.sub(x[0], x[1], generalconf, flags=re.MULTILINE)
|
2016-12-06 01:52:59 -05:00
|
|
|
#with open('{0}/src/config/console.h'.format(ipxe_src), 'w') as f:
|
2016-12-09 12:41:17 -05:00
|
|
|
# f.write(console)
|
2016-12-04 14:27:23 -05:00
|
|
|
# Now we make!
|
|
|
|
cwd = os.getcwd()
|
|
|
|
os.chdir(ipxe_src + '/src')
|
2016-12-06 01:52:59 -05:00
|
|
|
# TODO: split this into logic to only create the selected images.
|
|
|
|
# Command to build the .efi file
|
2016-12-09 12:41:17 -05:00
|
|
|
modenv = os.environ.copy()
|
|
|
|
modenv['EMBED'] = embedscript
|
|
|
|
#modenv['TRUST'] = ipxe_ssl_ca # TODO: test these
|
|
|
|
#modenv['CERT'] = '{0},{1}'.format(ipxe_ssl_ca, ipxe_ssl_crt) # TODO: test these
|
|
|
|
#modenv['PRIVKEY'] = ipxe_ssl_ckey # TODO: test these
|
|
|
|
build_cmd = {}
|
2016-12-13 23:43:53 -05:00
|
|
|
build_cmd['base'] = ['/usr/bin/make',
|
|
|
|
'all',
|
|
|
|
'EMBED="{0}"'.format(embedscript)]
|
|
|
|
# TODO: copy the UNDI stuff/chainloader to tftpboot, if enabled
|
|
|
|
build_cmd['undi'] = ['/usr/bin/make',
|
|
|
|
'bin/ipxe.pxe',
|
|
|
|
'EMBED="{0}"'.format(embedscript)]
|
2016-12-09 12:41:17 -05:00
|
|
|
build_cmd['efi'] = ['/usr/bin/make',
|
2016-12-13 23:43:53 -05:00
|
|
|
'bin-i386-efi/ipxe.efi',
|
|
|
|
'bin-x86_64-efi/ipxe.efi',
|
|
|
|
'EMBED="{0}"'.format(embedscript)]
|
2016-12-06 01:52:59 -05:00
|
|
|
# Command to build the actual USB and Mini images
|
2016-12-13 23:43:53 -05:00
|
|
|
build_cmd['iso'] = ['/usr/bin/make',
|
|
|
|
'bin/ipxe.liso',
|
|
|
|
'bin/ipxe.eiso',
|
|
|
|
'EMBED="{0}"'.format(embedscript)]
|
2016-12-06 01:52:59 -05:00
|
|
|
# Now we call the commands.
|
2016-12-09 12:41:17 -05:00
|
|
|
DEVNULL = open(os.devnull, 'w')
|
|
|
|
if os.path.isfile(build['dlpath'] + '/ipxe.log'):
|
|
|
|
os.remove(build['dlpath'] + '/ipxe.log')
|
2016-12-14 00:46:16 -05:00
|
|
|
print(('{0}: [IPXE] Building iPXE ({1}). PROGRESS: tail -f {2}/ipxe.log ...').format(
|
2016-12-09 12:41:17 -05:00
|
|
|
datetime.datetime.now(),
|
|
|
|
ipxe_src,
|
|
|
|
build['dlpath']))
|
|
|
|
with open('{0}/ipxe.log'.format(build['dlpath']), 'a') as f:
|
2016-12-13 23:43:53 -05:00
|
|
|
subprocess.call(build_cmd['base'], stdout = f, stderr = subprocess.STDOUT, env=modenv)
|
|
|
|
subprocess.call(build_cmd['undi'], stdout = f, stderr = subprocess.STDOUT, env=modenv)
|
2016-12-09 12:41:17 -05:00
|
|
|
subprocess.call(build_cmd['efi'], stdout = f, stderr = subprocess.STDOUT, env=modenv)
|
2016-12-13 23:43:53 -05:00
|
|
|
if mini:
|
|
|
|
subprocess.call(build_cmd['iso'], stdout = f, stderr = subprocess.STDOUT, env=modenv)
|
2016-12-11 17:06:01 -05:00
|
|
|
print('{0}: [IPXE] Built iPXE image(s) successfully.'.format(datetime.datetime.now()))
|
2016-12-04 14:27:23 -05:00
|
|
|
os.chdir(cwd)
|
2016-12-06 01:52:59 -05:00
|
|
|
# move the files to the results dir
|
2016-12-13 23:43:53 -05:00
|
|
|
# TODO: grab ipxe.pxe here too.
|
|
|
|
if usb:
|
|
|
|
os.rename('{0}/src/bin/ipxe.usb'.format(ipxe_src), usb_file)
|
|
|
|
if mini:
|
|
|
|
os.rename('{0}/src/bin/ipxe.eiso'.format(ipxe_src), emini_file)
|
|
|
|
os.rename('{0}/src/bin/ipxe.iso'.format(ipxe_src), mini_file)
|
2016-12-06 01:52:59 -05:00
|
|
|
# Get size etc. of build results
|
|
|
|
iso = {}
|
2016-12-09 12:41:17 -05:00
|
|
|
stream = {}
|
2016-12-06 01:52:59 -05:00
|
|
|
iso['name'] = []
|
2016-12-13 23:43:53 -05:00
|
|
|
for t in ('usb', 'mini'): # TODO: do this programmatically based on config
|
|
|
|
if t == 'usb':
|
|
|
|
imgname = 'USB'
|
|
|
|
elif t == 'mini':
|
|
|
|
imgname = 'Mini'
|
2016-12-06 01:52:59 -05:00
|
|
|
iso['name'].append(t)
|
2016-12-09 12:41:17 -05:00
|
|
|
iso[t] = {}
|
|
|
|
shasum = False
|
|
|
|
shasum = hashlib.sha256()
|
2016-12-13 23:43:53 -05:00
|
|
|
if t == 'usb':
|
2016-12-06 01:52:59 -05:00
|
|
|
isopath = usb_file
|
2016-12-13 23:43:53 -05:00
|
|
|
elif t == 'mini':
|
2016-12-06 01:52:59 -05:00
|
|
|
isopath = mini_file
|
2016-12-09 12:41:17 -05:00
|
|
|
stream = False
|
2016-12-13 23:43:53 -05:00
|
|
|
if os.path.isfile(isopath):
|
|
|
|
with open(isopath, 'rb') as f:
|
|
|
|
while True:
|
|
|
|
stream = f.read(65536) # 64kb chunks
|
|
|
|
if not stream:
|
|
|
|
break
|
|
|
|
shasum.update(stream)
|
|
|
|
iso[t]['sha'] = shasum.hexdigest()
|
|
|
|
iso[t]['file'] = isopath
|
|
|
|
iso[t]['size'] = humanize.naturalsize(os.path.getsize(isopath))
|
|
|
|
iso[t]['type'] = 'iPXE {0}'.format(imgname)
|
|
|
|
if t == 'usb':
|
|
|
|
iso[t]['fmt'] = 'Image'
|
|
|
|
elif t == 'mini':
|
|
|
|
iso[t]['fmt'] = 'ISO'
|
2016-12-06 01:52:59 -05:00
|
|
|
return(iso)
|