diff options
author | Christian Grothoff <christian@grothoff.org> | 2021-08-29 14:11:21 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2021-08-29 14:11:21 +0200 |
commit | 4234a2882f0812be37721b6b7a58156260d52379 (patch) | |
tree | 9394eba47021a7050c7764b7fed0689d5a15b6fa /default/steps/data | |
download | grid5k-4234a2882f0812be37721b6b7a58156260d52379.tar.gz grid5k-4234a2882f0812be37721b6b7a58156260d52379.tar.bz2 grid5k-4234a2882f0812be37721b6b7a58156260d52379.zip |
initial import
Diffstat (limited to 'default/steps/data')
-rw-r--r-- | default/steps/data/helpers/export_appliance.py | 242 | ||||
-rw-r--r-- | default/steps/data/helpers/netinstall_iso_finder.py | 163 | ||||
-rw-r--r-- | default/steps/data/helpers/simple_http_server.py | 129 | ||||
-rw-r--r-- | default/steps/data/preseed/debian-testing-preseed.cfg | 322 | ||||
-rw-r--r-- | default/steps/data/qemu-sendkeys.rb | 121 | ||||
-rw-r--r-- | default/steps/data/qemu-sendkeys/netinst-iso-debian | 1 |
6 files changed, 978 insertions, 0 deletions
diff --git a/default/steps/data/helpers/export_appliance.py b/default/steps/data/helpers/export_appliance.py new file mode 100644 index 0000000..634b240 --- /dev/null +++ b/default/steps/data/helpers/export_appliance.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +"""Convert a disk image to many others formats with guestfish.""" +from __future__ import division, unicode_literals + +import os +# import time +import os.path as op +import sys +import subprocess +import argparse +import logging + + +logger = logging.getLogger(__name__) + +tar_formats = ('tar', 'tar.gz', 'tgz', 'tar.bz2', 'tbz', 'tar.xz', 'txz', + 'tar.lzo', 'tzo') + +tar_options = ["--selinux", "--xattrs", "--xattrs-include=*", "--numeric-owner", "--one-file-system"] + +disk_formats = ('qcow', 'qcow2', 'qed', 'vdi', 'raw', 'vmdk') + + +def which(command): + """Locate a command. + Snippet from: http://stackoverflow.com/a/377028 + """ + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(command) + if fpath: + if is_exe(command): + return command + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, command) + if is_exe(exe_file): + return exe_file + + raise ValueError("Command '%s' not found" % command) + + +def tar_convert(disk, output, excludes, compression_level): + """Convert image to a tar rootfs archive.""" + if compression_level in ("best", "fast"): + compression_level_opt = "--%s" % compression_level + else: + compression_level_opt = "-%s" % compression_level + + compr = "" + if output.endswith(('tar.gz', 'tgz')): + try: + compr = "| %s %s" % (which("pigz"), compression_level_opt) + except: + compr = "| %s %s" % (which("gzip"), compression_level_opt) + elif output.endswith(('tar.bz2', 'tbz')): + compr = "| %s %s" % (which("bzip2"), compression_level_opt) + elif output.endswith(('tar.xz', 'txz')): + compr = "| {} {} -c --threads=0 -".format( + which("xz"), compression_level_opt) + elif output.endswith(('tar.lzo', 'tzo')): + compr = "| %s %s -c -" % (which("lzop"), compression_level_opt) + + # NB: guestfish version >= 1.32 supports the special tar options, but not available in Debian stable (jessie): do not use for now + #tar_options_list = ["selinux:true", "acls:true", "xattrs:true", + # "numericowner:true", + # "excludes:\"%s\"" % ' '.join(excludes)] + #tar_options_str = ' '.join(tar_options_list) + #cmd = which("guestfish") + \ + # " --ro -i tar-out -a %s / - %s %s > %s" + #cmd = cmd % (disk, tar_options_str, compr, output) + #proc = subprocess.Popen(cmd_mount_tar, env=os.environ.copy(), shell=True) + #proc.communicate() + #if proc.returncode: + # raise subprocess.CalledProcessError(proc.returncode, cmd) + + tar_options_str = ' '.join(tar_options + ['--exclude="%s"' % s for s in excludes]) + # Necessary to have quick access to /etc (bug 12240) and also good for reproducibility + tar_options_str += ' --sort=name' + directory = dir_path = os.path.dirname(os.path.realpath(disk)) + cmds = [ + which("mkdir") + " %s/.mnt" % directory, + which("guestmount") + " --ro -i -a %s %s/.mnt" % (disk, directory), + which("tar") + " -c %s -C %s/.mnt . %s > %s" % (tar_options_str, directory, compr, output) + ] + cmd_mount_tar = " && ".join(cmds) + proc = subprocess.Popen(cmd_mount_tar, env=os.environ.copy(), shell=True) + proc.communicate() + returncode_mount_tar = proc.returncode + + # try to umount even if the previous command failed + cmds = [ + which("guestunmount") + " %s/.mnt" % directory, + which("rmdir") + " %s/.mnt" % directory + ] + cmd_umount = " && ".join(cmds) + proc = subprocess.Popen(cmd_umount, env=os.environ.copy(), shell=True) + proc.communicate() + returncode_umount = proc.returncode + + if returncode_mount_tar: + raise subprocess.CalledProcessError(returncode_mount_tar, cmd_mount_tar) + elif returncode_umount: + raise subprocess.CalledProcessError(returncode_umount, cmd_umount) + + +def qemu_convert(disk, output_fmt, output_filename): + """Convert the disk image filename to disk image output_filename.""" + binary = which("qemu-img") + cmd = [binary, "convert", "-O", output_fmt, disk, output_filename] + if output_fmt in ("qcow", "qcow2"): + cmd.insert(2, "-c") + proc = subprocess.Popen(cmd, env=os.environ.copy(), shell=False) + proc.communicate() + if proc.returncode: + raise subprocess.CalledProcessError(proc.returncode, ' '.join(cmd)) + + +def run_guestfish_script(disk, script, mount=""): + """ + Run guestfish script. + Mount should be in ("read_only", "read_write", "ro", "rw") + """ + args = [which("guestfish"), '-a', disk] + if mount in ("read_only", "read_write", "ro", "rw"): + args.append('-i') + if mount in mount in ("read_only", "ro"): + args.append('--ro') + else: + args.append('--rw') + else: + script = "run\n%s" % script + proc = subprocess.Popen(args, + stdin=subprocess.PIPE, + env=os.environ.copy()) + proc.communicate(input=script.encode('utf-8')) + if proc.returncode: + raise subprocess.CalledProcessError(proc.returncode, ' '.join(args)) + + +def guestfish_zerofree(filename): + """Fill free space with zero""" + logger.info(guestfish_zerofree.__doc__) + cmd = "virt-filesystems -a %s" % filename + fs = subprocess.check_output(cmd.encode('utf-8'), + stderr=subprocess.STDOUT, + shell=True, + env=os.environ.copy()) + list_fs = fs.decode('utf-8').split() + logger.info('\n'.join((' `--> %s' % i for i in list_fs))) + script = '\n'.join(('zerofree %s' % i for i in list_fs)) + run_guestfish_script(filename, script, mount="read_only") + + +def convert_disk_image(args): + """Convert disk to another format.""" + filename = op.abspath(args.file.name) + output = op.abspath(args.output) + + os.environ['LIBGUESTFS_CACHEDIR'] = os.getcwd() + if args.verbose: + os.environ['LIBGUESTFS_DEBUG'] = '1' + + # sometimes guestfish fails because of other virtualization tools are + # still running use a test and retry to wait for availability + # attempts = 0 + # while attempts < 3: + # try: + # logger.info("Waiting for virtualisation to be available...") + # run_guestfish_script(filename, "cat /etc/hostname", mount='ro') + # break + # except: + # attempts += 1 + # time.sleep(1) + + if args.zerofree and (set(args.formats) & set(disk_formats)): + guestfish_zerofree(filename) + + for fmt in args.formats: + if fmt in (tar_formats + disk_formats): + output_filename = "%s.%s" % (output, fmt) + if output_filename == filename: + continue + logger.info("Creating %s" % output_filename) + try: + if fmt in tar_formats: + tar_convert(filename, output_filename, + args.tar_excludes, + args.tar_compression_level) + else: + qemu_convert(filename, fmt, output_filename) + except ValueError as exp: + logger.error("Error: %s" % exp) + + +if __name__ == '__main__': + allowed_formats = tar_formats + disk_formats + allowed_formats_help = 'Allowed values are ' + ', '.join(allowed_formats) + + allowed_levels = ["%d" % i for i in range(1, 10)] + ["best", "fast"] + allowed_levels_helps = 'Allowed values are ' + ', '.join(allowed_levels) + + parser = argparse.ArgumentParser( + description=sys.modules[__name__].__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument('file', action="store", type=argparse.FileType('r'), + help='Disk image filename') + parser.add_argument('-F', '--formats', action="store", type=str, nargs='+', + help='Output format. ' + allowed_formats_help, + choices=allowed_formats, metavar='fmt', required=True) + parser.add_argument('-o', '--output', action="store", type=str, + help='Output filename (without file extension)', + required=True, metavar='filename') + parser.add_argument('--tar-compression-level', action="store", type=str, + default="9", choices=allowed_levels, metavar='lvl', + help="Compression level. " + allowed_levels_helps) + parser.add_argument('--tar-excludes', action="store", type=str, nargs='+', + help="Files to excluded from archive", + metavar='pattern', default=[]) + parser.add_argument('--zerofree', action="store_true", default=False, + help='Zero free unallocated blocks from ext2/3 ' + 'file-systems before export to reduce image size') + parser.add_argument('--verbose', action="store_true", default=False, + help='Enable very verbose messages') + log_format = '%(levelname)s: %(message)s' + level = logging.INFO + args = parser.parse_args() + if args.verbose: + level = logging.DEBUG + + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(level) + handler.setFormatter(logging.Formatter(log_format)) + + logger.setLevel(level) + logger.addHandler(handler) + + convert_disk_image(args) diff --git a/default/steps/data/helpers/netinstall_iso_finder.py b/default/steps/data/helpers/netinstall_iso_finder.py new file mode 100644 index 0000000..b4a135b --- /dev/null +++ b/default/steps/data/helpers/netinstall_iso_finder.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +"""Find the latest netinstall iso for a Debian version and system architecture.""" + +from html.parser import HTMLParser +from urllib2 import urlopen +from urlparse import urljoin +import re +import sys +import argparse +import logging + +logger = logging.getLogger(__name__) + +class LinkParser(HTMLParser): + """Retrieve links (a hrefs) from a text/html document""" + def __init__(self, url): + HTMLParser.__init__(self) + self.url = url + self.links = set() + response = urlopen(url) + contentType = response.info().get('Content-Type') + if not contentType: + return + logger.debug("url = " + url ); + logger.debug("contentType = " + contentType ); + if ';' in contentType: + (mediaType,charset) = contentType.split(";") + charset = charset.split("=")[1] + else: + mediaType = contentType + # ISO-8859-1 is no longer the default charset, see https://tools.ietf.org/html/rfc7231#appendix-B + # Let's use UTF-8. + charset = "utf-8" + if mediaType =='text/html': + htmlBytes = response.read() + htmlString = htmlBytes.decode(charset) + self.feed(htmlString) + + def handle_starttag(self, tag, attrs): + if tag == 'a': + for (key, value) in attrs: + if key == 'href': + new_url = urljoin(self.url,value) + if re.match("^"+self.url, new_url): + self.links.add(new_url) + + def get_links(self): + """Returns all the collected links""" + return self.links + + +def url_find(to_visit_url_set,visited_url_set,found_url_set): + """Recursively look for urls given a regex, a set of urls to visit, a set of already visited urls, a set of already found urls. Returns the set of found urls""" + logger.debug("Progress: to_visit:{} visited:{} found:{}".format(len(to_visit_url_set),len(visited_url_set),len(found_url_set))) + assert(len(to_visit_url_set.intersection(visited_url_set)) == 0) + assert(len(to_visit_url_set.intersection(found_url_set)) == 0) + if (len(to_visit_url_set) == 0): + return [visited_url_set,found_url_set] + else: + url = to_visit_url_set.pop() + visited_url_set.add(url) + if target_regex.match(url): + found_url_set.add(url) + return url_find(to_visit_url_set, visited_url_set, found_url_set) + else: + new_url_set = set([url for url in LinkParser(url).get_links() if (logger.debug(url) or True) and url_regex.match(url)]) + new_url_set.difference_update(visited_url_set) + to_visit_url_set.update(new_url_set) + return url_find(to_visit_url_set, visited_url_set, found_url_set) + +def key_normalize(version_string): + """" + In order to perform a natural sorting, we normalize the version (X.Y.Z) as a unique integer with the following formula: X*100 + Y*10 + Z + For instance, it solves situations where "9.9.0" is greater than "9.9.11" + """ + splitted_string = version_string.split('.') + assert(len(splitted_string) == 3) + return int(splitted_string[0])*100+int(splitted_string[1])*10+int(splitted_string[2]) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("distrib", metavar="DISTRIB", help="distribution") + parser.add_argument("version", metavar="VERSION", help="version") + parser.add_argument("arch", metavar="ARCH", help="architecture") + parser.add_argument("mirror", metavar="MIRROR", help="mirror", nargs="?") + parser.add_argument('--info', action="store_true", default=False, help='print info messages') + parser.add_argument('--debug', action="store_true", default=False, help='print debug messages') + args = parser.parse_args() + + handler = logging.StreamHandler() + if args.debug: + logger.setLevel(logging.DEBUG) + handler.setLevel(logging.DEBUG) + elif args.info: + logger.setLevel(logging.INFO) + handler.setLevel(logging.INFO) + else: + logger.setLevel(logging.WARNING) + handler.setLevel(logging.WARNING) + handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) + logger.addHandler(handler) + + try: + visited = set([]) + found = set([]) + if (args.distrib.lower() == "debian"): + if args.mirror == None: + args.mirror = "http://cdimage.debian.org/" + if not re.match("^\d+$",args.version): + raise Exception("please give the Debian release number (e.g. 8 for Jessie)") + if args.version == '10': + url_regex = re.compile("^"+args.mirror+"cdimage/release/(?:"+args.version+"\.\d+\.\d+/(?:"+args.arch+"/(?:iso-cd/(?:debian-"+args.version+"\.\d+\.\d+-"+args.arch+"-netinst\.iso)?)?)?)?$") + else: + url_regex = re.compile("^"+args.mirror+"cdimage/archive/(?:"+args.version+"\.\d+\.\d+/(?:"+args.arch+"/(?:iso-cd/(?:debian-"+args.version+"\.\d+\.\d+-"+args.arch+"-netinst\.iso)?)?)?)?$") + target_regex = re.compile("^.*-netinst\.iso$") + [visited,found] = url_find(set([args.mirror+"cdimage/"+v+"/" for v in ["release","archive"]]), set(), set()) + elif (args.distrib.lower() == "ubuntu"): + if args.mirror == None: + args.mirror = "http://(?:archive|old-releases).ubuntu.com/" + servers = set(["http://"+s+".ubuntu.com/ubuntu/" for s in ["old-releases","archive"]]) + else: + servers = set([args.mirror]) + if not re.match("^\w+$",args.version): + raise Exception("please give the Ubuntu release name") + url_regex = re.compile("^"+args.mirror+"ubuntu/dists/(?:"+args.version+"(?:-updates)?/(?:main/(?:installer-"+args.arch+"/(?:current/(?:(?:legacy-)?images/(?:netboot/(?:mini\.iso)?)?)?)?)?)?)?$") + target_regex = re.compile("^.*/mini\.iso$") + [visited,found] = url_find(servers, set(), set()) + elif (args.distrib.lower() == "centos"): + if args.mirror == None: + args.mirror = "http://mirror.in2p3.fr/linux/CentOS/" + if not re.match("^\d+$",args.version): + raise Exception("please give the CentOS release number (e.g. 7 for CentOS-7)") + if args.version == '6': + url_regex = re.compile("^"+args.mirror+"(?:"+args.version+"/(?:isos/(?:"+args.arch+"/(?:CentOS-"+args.version+"(?:\.\d+)?-"+args.arch+"-netinstall\.iso)?)?)?)?$") + target_regex = re.compile("^.*CentOS-\d+(?:\.\d+)?-\w+-netinstall\.iso$") + elif args.version == '7': + url_regex = re.compile("^"+args.mirror+"(?:"+args.version+"/(?:isos/(?:"+args.arch+"/(?:CentOS-"+args.version+"-"+args.arch+"-NetInstall-\d+\.iso)?)?)?)?$") + target_regex = re.compile("^.*CentOS-\d+-\w+-NetInstall-\d+\.iso$") + else: + url_regex = re.compile("^"+args.mirror+"(?:"+args.version+"/(?:isos/(?:"+args.arch+"/(?:CentOS-"+args.version+"\.\d+\.\d+-"+args.arch+"-boot\.iso)?)?)?)?$") + target_regex = re.compile("^.*CentOS-\d+\.\d+\.\d+-\w+-boot\.iso$") + [visited,found] = url_find(set([args.mirror]), set(), set()) + else: + raise Exception("this distribution is not supported") + logger.info("URL regex: "+url_regex.pattern) + logger.info("Target regex: "+target_regex.pattern) + logger.debug("Visited URLs:") + for url in visited: + logger.debug(url) + logger.info("Found URLs:") + for url in found: + logger.info(url) + if len(found) > 0: + if (args.distrib.lower() == "debian"): + print(sorted(found,key=lambda x:key_normalize(re.sub(r".*/debian-(\d+).(\d+).(\d+)-"+args.arch+"-netinst\.iso$",r"\1.\2.\3",x)),reverse=True)[0]) + else: + print(sorted(found, reverse=False)[0]) + else: + raise Exception("no url found") + except Exception as exc: + sys.stderr.write(u"Error: %s\n" % exc) + sys.exit(1) diff --git a/default/steps/data/helpers/simple_http_server.py b/default/steps/data/helpers/simple_http_server.py new file mode 100644 index 0000000..881343a --- /dev/null +++ b/default/steps/data/helpers/simple_http_server.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python2 +"""Simple HTTP server""" +from __future__ import unicode_literals +import atexit +import os +import sys +import argparse + + +class HTTPServerDaemon(object): + + """A HTTP server daemon class.""" + + def __init__(self, root=os.getcwd()): + """ Initialize the object.""" + self.root = root + + def daemonize(self, pidfile): + """Deamonize class. UNIX double fork mechanism.""" + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError as err: + sys.stderr.write('fork #1 failed: {0}\n'.format(err)) + sys.exit(1) + + # decouple from parent environment + os.chdir(self.root) + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + + # exit from second parent + sys.exit(0) + except OSError as err: + sys.stderr.write('fork #2 failed: {0}\n'.format(err)) + sys.exit(1) + + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = open(os.devnull, 'r') + so = open(os.devnull, 'a+') + se = open(os.devnull, 'a+') + + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + # Make sure pid file is removed if we quit + @atexit.register + def delpid(self): + os.remove(pidfile) + + # write pidfile + pid = str(os.getpid()) + with open(pidfile, 'w+') as f: + f.write(pid + '\n') + + def start(self, pidfile, *args, **kwargs): + """Start the daemon.""" + # Check for a pidfile to see if the daemon already runs + try: + with open(pidfile, 'r') as pf: + + pid = int(pf.read().strip()) + except IOError: + pid = None + + if pid: + message = "pidfile {0} already exist. " + \ + "Daemon already running?\n" + sys.stderr.write(message.format(pidfile)) + sys.exit(1) + + # Start the daemon + self.daemonize(pidfile) + self.run(*args, **kwargs) + + def run(self, host, port): + """ Run an HTTP server.""" + if sys.version_info[0] == 3: + from http.server import HTTPServer, SimpleHTTPRequestHandler + httpd = HTTPServer((host, port), SimpleHTTPRequestHandler) + else: + import SimpleHTTPServer + import SocketServer + handler = SimpleHTTPServer.SimpleHTTPRequestHandler + httpd = SocketServer.TCPServer((host, port), handler) + + print("Running on http://%s:%s/" % (host, port)) + os.chdir(self.root) + try: + httpd.serve_forever() + except KeyboardInterrupt: + sys.stderr.write(u"\nBye\n") + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description=sys.modules[__name__].__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument('--port', action="store", default=9090, type=int, + help='Set the listening port') + parser.add_argument('--root', action="store", default=os.getcwd()) + parser.add_argument('--bind', action="store", default="0.0.0.0", + help='Set the binding address') + parser.add_argument('--daemon', action="store_true", default=False) + parser.add_argument('--pid', action="store") + + try: + args = parser.parse_args() + http_server = HTTPServerDaemon(root=args.root) + if args.daemon: + if args.pid is None: + parser.error("Need to set a pid file") + http_server.start(args.pid, args.bind, args.port) + else: + http_server.run(args.bind, args.port) + except Exception as exc: + sys.stderr.write(u"\nError: %s\n" % exc) + sys.exit(1) diff --git a/default/steps/data/preseed/debian-testing-preseed.cfg b/default/steps/data/preseed/debian-testing-preseed.cfg new file mode 100644 index 0000000..5af0d99 --- /dev/null +++ b/default/steps/data/preseed/debian-testing-preseed.cfg @@ -0,0 +1,322 @@ +#### Contents of the preconfiguration file (for wheezy) +### Localization +# Locale sets language and country. +d-i debian-installer/locale string en_US.UTF-8 + +# Keyboard selection. +#d-i keymap select us +d-i keyboard-configuration/xkb-keymap select us + +### Network configuration +# netcfg will choose an interface that has link if possible. This makes it +# skip displaying a list if there is more than one interface. +d-i netcfg/choose_interface select auto + +# To pick a particular interface instead: +#d-i netcfg/choose_interface select eth1 + +# If you have a slow dhcp server and the installer times out waiting for +# it, this might be useful. +#d-i netcfg/dhcp_timeout string 60 + +# If you prefer to configure the network manually, uncomment this line and +# the static network configuration below. +#d-i netcfg/disable_dhcp boolean true + +# If you want the preconfiguration file to work on systems both with and +# without a dhcp server, uncomment these lines and the static network +# configuration below. +#d-i netcfg/dhcp_failed note +#d-i netcfg/dhcp_options select Configure network manually + +# Static network configuration. +#d-i netcfg/get_nameservers string 192.168.1.1 +#d-i netcfg/get_ipaddress string 192.168.1.42 +#d-i netcfg/get_netmask string 255.255.255.0 +#d-i netcfg/get_gateway string 192.168.1.1 +#d-i netcfg/confirm_static boolean true + +# Any hostname and domain names assigned from dhcp take precedence over +# values set here. However, setting the values still prevents the questions +# from being shown, even if values come from dhcp. +d-i netcfg/get_hostname string kameleon +d-i netcfg/get_domain string kameleon + +# Disable that annoying WEP key dialog. +d-i netcfg/wireless_wep string +# The wacky dhcp hostname that some ISPs use as a password of sorts. +#d-i netcfg/dhcp_hostname string radish + +# If non-free firmware is needed for the network or other hardware, you can +# configure the installer to always try to load it, without prompting. Or +# change to false to disable asking. +#d-i hw-detect/load_firmware boolean true + +### Network console +# Use the following settings if you wish to make use of the network-console +# component for remote installation over SSH. This only makes sense if you +# intend to perform the remainder of the installation manually. +#d-i anna/choose_modules string network-console +#d-i network-console/password password r00tme +#d-i network-console/password-again password r00tme + +### Mirror settings +# If you select ftp, the mirror/country string does not need to be set. +#d-i mirror/protocol string ftp +d-i mirror/country string manual +d-i mirror/http/hostname string http.debian.net +d-i mirror/http/directory string /debian +d-i mirror/http/proxy string + +# Suite to install. +d-i mirror/suite string testing +# Suite to use for loading installer components (optional). +d-i mirror/udeb/suite string unstable + +### Clock and time zone setup +# Controls whether or not the hardware clock is set to UTC. +d-i clock-setup/utc boolean true + +# You may set this to any valid setting for $TZ; see the contents of +# /usr/share/zoneinfo/ for valid values. +d-i time/zone string UTC + +# Controls whether to use NTP to set the clock during the install +d-i clock-setup/ntp boolean true +# NTP server to use. The default is almost always fine here. +#d-i clock-setup/ntp-server string ntp.example.com + +### Partitioning +# If the system has free space you can choose to only partition that space. +#d-i partman-auto/init_automatically_partition select biggest_free + +# Alternatively, you can specify a disk to partition. The device name must +# be given in traditional non-devfs format. +# Note: A disk must be specified, unless the system has only one disk. +# For example, to use the first SCSI/SATA hard disk: +#d-i partman-auto/disk string /dev/sda +# In addition, you'll need to specify the method to use. +# The presently available methods are: "regular", "lvm" and "crypto" +d-i partman-auto/method string regular + +# If one of the disks that are going to be automatically partitioned +# contains an old LVM configuration, the user will normally receive a +# warning. This can be preseeded away... +d-i partman-lvm/device_remove_lvm boolean true +# The same applies to pre-existing software RAID array: +d-i partman-md/device_remove_md boolean true + +# And the same goes for the confirmation to write the lvm partitions. +d-i partman-lvm/confirm boolean true +d-i partman-lvm/confirm_nooverwrite boolean true + + +d-i partman/choose_partition select finish +d-i partman-auto-lvm/guided_size string max + +# You can choose one of the three predefined partitioning recipes: +# - atomic: all files in one partition +# - home: separate /home partition +# - multi: separate /home, /usr, /var, and /tmp partitions +d-i partman-auto/choose_recipe select atomic +d-i partman/default_filesystem string ext4 + +# Or provide a recipe of your own... +# The recipe format is documented in the file devel/partman-auto-recipe.txt. +# If you have a way to get a recipe file into the d-i environment, you can +# just point at it. +#d-i partman-auto/expert_recipe_file string /hd-media/recipe + +# If not, you can put an entire recipe into the preconfiguration file in one +# (logical) line. This example creates a small /boot partition, suitable +# swap, and uses the rest of the space for the root partition: +#d-i partman-auto/expert_recipe string \ +# boot-root :: \ +# 40 50 100 ext3 \ +# $primary{ } $bootable{ } \ +# method{ format } format{ } \ +# use_filesystem{ } filesystem{ ext3 } \ +# mountpoint{ /boot } \ +# . \ +# 500 10000 1000000000 ext3 \ +# method{ format } format{ } \ +# use_filesystem{ } filesystem{ ext3 } \ +# mountpoint{ / } \ +# . \ +# 64 512 300% linux-swap \ +# method{ swap } format{ } \ +# . + +#The preseed line that "selects finish" needs to be in a certain order in your preseed, the example-preseed does not follow this. +#http://ubuntuforums.org/archive/index.php/t-1504045.html + +# This makes partman automatically partition without confirmation, provided +# that you told it what to do using one of the methods above. +d-i partman/confirm_write_new_label boolean true +d-i partman/confirm boolean true +d-i partman/confirm_nooverwrite boolean true + + +### Base system installation +# Select the initramfs generator used to generate the initrd for 2.6 kernels. +#d-i base-installer/kernel/linux/initramfs-generators string yaird + +# The kernel image (meta) package to be installed; "none" can be used if no +# kernel is to be installed. +#d-i base-installer/kernel/image string linux-image-2.6-486 + +### Account setup +# Enable login to root account +d-i passwd/root-login boolean true +# Root password, either in clear text +d-i passwd/root-password password kameleon +d-i passwd/root-password-again password kameleon +# or encrypted using an MD5 hash. +#d-i passwd/root-password-crypted password [MD5 hash] + +# Skip creation of a normal user account. +# d-i passwd/make-user boolean false + +# To create a normal user account. +d-i passwd/user-fullname string Kameleon User +d-i passwd/username string kameleon +# Normal user's password, either in clear text +d-i passwd/user-password password kameleon +d-i passwd/user-password-again password kameleon +# or encrypted using an MD5 hash. +#d-i passwd/user-password-crypted password [MD5 hash] +# Create the first user with the specified UID instead of the default. +#d-i passwd/user-uid string 1010 +# d-i user-setup/encrypt-home boolean false +# d-i user-setup/allow-password-weak boolean true + +# The user account will be added to some standard initial groups. To +# override that, use this. +d-i passwd/user-default-groups string audio cdrom video admin + +### Apt setup +# You can choose to install non-free and contrib software. +#d-i apt-setup/non-free boolean true +#d-i apt-setup/contrib boolean true +# Uncomment this if you don't want to use a network mirror. +#d-i apt-setup/use_mirror boolean false +# Select which update services to use; define the mirrors to be used. +# Values shown below are the normal defaults. +# FIXME : temporarily remove security repo while debian fixes the installer (default value : d-i apt-setup/services-select multiselect security, volatile) +d-i apt-setup/services-select multiselect +#d-i apt-setup/security_host string security.debian.org +#d-i apt-setup/volatile_host string volatile.debian.org + +# Scan another CD or DVD? +d-i apt-setup/cdrom/set-first boolean false + +# By default the installer requires that repositories be authenticated +# using a known gpg key. This setting can be used to disable that +# authentication. Warning: Insecure, not recommended. +#d-i debian-installer/allow_unauthenticated string true + +### Package selection +tasksel tasksel/first multiselect none +# If the desktop task is selected, install the kde and xfce desktops +# instead of the default gnome desktop. +#tasksel tasksel/desktop multiselect kde, xfce + +# Individual additional packages to install +d-i pkgsel/include string openssh-server sudo rsync haveged + +# Whether to upgrade packages after debootstrap. +# Allowed values: none, safe-upgrade, full-upgrade +d-i pkgsel/upgrade select none + +# Some versions of the installer can report back on what software you have +# installed, and what software you use. The default is not to report back, +# but sending reports helps the project determine what software is most +# popular and include it on CDs. +popularity-contest popularity-contest/participate boolean false + +### Boot loader installation +# Grub is the default boot loader (for x86). If you want lilo installed +# instead, uncomment this: +#d-i grub-installer/skip boolean true +# To also skip installing lilo, and install no bootloader, uncomment this +# too: +#d-i lilo-installer/skip boolean true + +# This is fairly safe to set, it makes grub install automatically to the MBR +# if no other operating system is detected on the machine. +d-i grub-installer/only_debian boolean true + +# This one makes grub-installer install to the MBR if it also finds some other +# OS, which is less safe as it might not be able to boot that other OS. +d-i grub-installer/with_other_os boolean true + +# Alternatively, if you want to install to a location other than the mbr, +# uncomment and edit these lines: +#d-i grub-installer/only_debian boolean false +#d-i grub-installer/with_other_os boolean false +#d-i grub-installer/bootdev string (hd0,0) +# To install grub to multiple disks: +#d-i grub-installer/bootdev string (hd0,0) (hd1,0) (hd2,0) + +# Optional password for grub, either in clear text +#d-i grub-installer/password password r00tme +#d-i grub-installer/password-again password r00tme +# or encrypted using an MD5 hash, see grub-md5-crypt(8). +#d-i grub-installer/password-crypted password [MD5 hash] + +# GRUB install devices: +# Choices: /dev/sda (21474 MB; VMware_Virtual_S), /dev/sda1 (21472 MB; VMware_Virtual_S) +grub-pc grub-pc/install_devices multiselect /dev/vda +# Choices: Enter device manually, /dev/sda +grub-installer grub-installer/choose_bootdev select /dev/vda + +### Finishing up the installation +# During installations from serial console, the regular virtual consoles +# (VT1-VT6) are normally disabled in /etc/inittab. Uncomment the next +# line to prevent this. +#d-i finish-install/keep-consoles boolean true + +# Avoid that last message about the install being complete. +d-i finish-install/reboot_in_progress note + +# This will prevent the installer from ejecting the CD during the reboot, +# which is useful in some situations. +d-i cdrom-detect/eject boolean false + +# This is how to make the installer shutdown when finished, but not +# reboot into the installed system. +#d-i debian-installer/exit/halt boolean true +# This will power off the machine instead of just halting it. +d-i debian-installer/exit/poweroff boolean true + +### Preseeding other packages +# Depending on what software you choose to install, or if things go wrong +# during the installation process, it's possible that other questions may +# be asked. You can preseed those too, of course. To get a list of every +# possible question that could be asked during an install, do an +# installation, and then run these commands: +# debconf-get-selections --installer > file +# debconf-get-selections >> file + + +#### Advanced options +### Running custom commands during the installation +# d-i preseeding is inherently not secure. Nothing in the installer checks +# for attempts at buffer overflows or other exploits of the values of a +# preconfiguration file like this one. Only use preconfiguration files from +# trusted locations! To drive that home, and because it's generally useful, +# here's a way to run any shell command you'd like inside the installer, +# automatically. + +# This first command is run as early as possible, just after +# preseeding is read. +# Prevent packaged version of VirtualBox Guest Additions being installed: +#d-i preseed/early_command string sed -i \ +# '/in-target/idiscover(){/sbin/discover|grep -v VirtualBox;}' \ +# /usr/lib/pre-pkgsel.d/20install-hwpackages + +# This command is run just before the install finishes, but when there is +# still a usable /target directory. You can chroot to /target and use it +# directly, or use the apt-install and in-target commands to easily install +# packages and run commands in the target system. + diff --git a/default/steps/data/qemu-sendkeys.rb b/default/steps/data/qemu-sendkeys.rb new file mode 100644 index 0000000..d1bcb0f --- /dev/null +++ b/default/steps/data/qemu-sendkeys.rb @@ -0,0 +1,121 @@ +#!/usr/bin/env ruby +# Translate a string to "sendkey" commands for QEMU. +# Martin Vidner, MIT License + +# https://en.wikibooks.org/wiki/QEMU/Monitor#sendkey_keys +# sendkey keys +# +# You can emulate keyboard events through sendkey command. The syntax is: sendkey keys. To get a list of keys, type sendkey [tab]. Examples: +# +# sendkey a +# sendkey shift-a +# sendkey ctrl-u +# sendkey ctrl-alt-f1 +# +# As of QEMU 0.12.5 there are: +# shift shift_r alt alt_r altgr altgr_r +# ctrl ctrl_r menu esc 1 2 +# 3 4 5 6 7 8 +# 9 0 minus equal backspace tab +# q w e r t y +# u i o p ret a +# s d f g h j +# k l z x c v +# b n m comma dot slash +# asterisk spc caps_lock f1 f2 f3 +# f4 f5 f6 f7 f8 f9 +# f10 num_lock scroll_lock kp_divide kp_multiply kp_subtract +# kp_add kp_enter kp_decimal sysrq kp_0 kp_1 +# kp_2 kp_3 kp_4 kp_5 kp_6 kp_7 +# kp_8 kp_9 < f11 f12 print +# home pgup pgdn end left up +# down right insert delete + +require "optparse" + +# incomplete! only what I need now. +KEYS = { + "%" => "shift-5", + "/" => "slash", + ":" => "shift-semicolon", + "=" => "equal", + "." => "dot", + " " => "spc", + "-" => "minus", + "_" => "shift-minus", + "*" => "asterisk", + "," => "comma", + "+" => "shift-equal", + "|" => "shift-backslash", + "\\" => "backslash", +} + +class Main + attr_accessor :command + attr_accessor :delay_s + attr_accessor :keystring + + def initialize + self.command = nil + self.delay_s = 0.1 + + OptionParser.new do |opts| + opts.banner = "Usage: sendkeys [-c command_to_pipe_to] STRING\n" + + "Where STRING can be 'ls<enter>ls<gt>/dev/null<enter>'" + + opts.on("-c", "--command COMMAND", + "Pipe sendkeys to this commands, individually") do |v| + self.command = v + end + opts.on("-d", "--delay SECONDS", Float, + "Delay SECONDS after each key (default: 0.1)") do |v| + self.delay_s = v + end + end.parse! + self.keystring = ARGV[0] + end + + def sendkey(qemu_key_name) + if qemu_key_name == "wait" + sleep 1 + else + if qemu_key_name =~ /[A-Za-z]/ && qemu_key_name == qemu_key_name.upcase + key = "shift-#{qemu_key_name.downcase}" + else + key = qemu_key_name + end + qemu_cmd = "sendkey #{key}" + if command + system "echo '#{qemu_cmd}' | #{command}" + else + puts qemu_cmd + $stdout.flush # important when we are piped + end + sleep delay_s + end + end + + PATTERN = / + \G # where last match ended + < [^>]+ > + | + \G + . + /x + def run + keystring.scan(PATTERN) do |match| + if match[0] == "<" + key_name = match.slice(1..-2) + sendkey case key_name + when "lt" then "shift-comma" + when "gt" then "shift-dot" + else key_name + end + else + sendkey KEYS.fetch(match, match) + end + end + end +end + +Main.new.run diff --git a/default/steps/data/qemu-sendkeys/netinst-iso-debian b/default/steps/data/qemu-sendkeys/netinst-iso-debian new file mode 100644 index 0000000..7705a44 --- /dev/null +++ b/default/steps/data/qemu-sendkeys/netinst-iso-debian @@ -0,0 +1 @@ +<esc><wait>auto preseed/url=http://%LOCAL_IP%:%HTTP_PORT%/preseed.cfg<kp_enter> |