diff options
-rwxr-xr-x | morphlib/exts/kvm.write | 7 | ||||
-rwxr-xr-x | morphlib/exts/nfsboot.write | 22 | ||||
-rwxr-xr-x | morphlib/exts/rawdisk.write | 11 | ||||
-rwxr-xr-x | morphlib/exts/ssh-rsync.write | 28 | ||||
-rwxr-xr-x | morphlib/exts/virtualbox-ssh.write | 72 | ||||
-rw-r--r-- | morphlib/localartifactcache.py | 28 | ||||
-rw-r--r-- | morphlib/localartifactcache_tests.py | 36 | ||||
-rw-r--r-- | morphlib/plugins/gc_plugin.py | 158 | ||||
-rw-r--r-- | morphlib/util.py | 33 | ||||
-rw-r--r-- | morphlib/writeexts.py | 9 | ||||
-rw-r--r-- | without-test-modules | 1 |
11 files changed, 365 insertions, 40 deletions
diff --git a/morphlib/exts/kvm.write b/morphlib/exts/kvm.write index f2683d8e..67ac40e7 100755 --- a/morphlib/exts/kvm.write +++ b/morphlib/exts/kvm.write @@ -94,7 +94,7 @@ class KvmPlusSshWriteExtension(morphlib.writeexts.WriteExtension): self.status(msg='Transferring disk image') target = '%s:%s' % (ssh_host, vm_path) with open(raw_disk, 'rb') as f: - cliapp.runcmd(['rsync', '-zS', raw_disk, target]) + cliapp.runcmd(['rsync', '-szS', raw_disk, target]) def create_libvirt_guest(self, ssh_host, vm_name, vm_path, autostart): '''Create the libvirt virtual machine.''' @@ -106,6 +106,11 @@ class KvmPlusSshWriteExtension(morphlib.writeexts.WriteExtension): for disk in attach_disks: attach_opts.extend(['--disk', 'path=%s' % disk]) + if 'NIC_CONFIG' in os.environ: + nics = os.environ['NIC_CONFIG'].split() + for nic in nics: + attach_opts.extend(['--network', nic]) + ram_mebibytes = str(self.get_ram_size() / (1024**2)) cmdline = ['virt-install', '--connect', 'qemu:///system', '--import', diff --git a/morphlib/exts/nfsboot.write b/morphlib/exts/nfsboot.write index 61c5306a..34a72972 100755 --- a/morphlib/exts/nfsboot.write +++ b/morphlib/exts/nfsboot.write @@ -66,15 +66,15 @@ class NFSBootWriteExtension(morphlib.writeexts.WriteExtension): 'with hostname "baserock"') self.test_good_server(location) - version = os.environ['VERSION'] or 'version1' + version_label = os.getenv('VERSION_LABEL', 'factory') versioned_root = os.path.join(self._nfsboot_root, hostname, 'systems', - version) + version_label) if self.version_exists(versioned_root, location): raise cliapp.AppException('Version %s already exists on' ' this device. Deployment aborted' - % version) + % version_label) self.copy_rootfs(temp_root, location, versioned_root, hostname) - self.copy_kernel(temp_root, location, versioned_root, version, + self.copy_kernel(temp_root, location, versioned_root, version_label, hostname) self.configure_nfs(location, hostname) @@ -118,7 +118,7 @@ class NFSBootWriteExtension(morphlib.writeexts.WriteExtension): rsync_dest = 'root@%s:%s' % (location, kernel_dest) self.status(msg='Copying kernel') cliapp.runcmd( - ['rsync', kernel_src, rsync_dest]) + ['rsync', '-s', kernel_src, rsync_dest]) # Link the kernel to the right place self.status(msg='Creating links to kernel in tftp directory') @@ -153,7 +153,7 @@ class NFSBootWriteExtension(morphlib.writeexts.WriteExtension): self.status(msg='Creating \'orig\' rootfs') cliapp.runcmd( - ['rsync', '-aXSPH', '--delete', rootfs_src, + ['rsync', '-asXSPH', '--delete', rootfs_src, 'root@%s:%s' % (location, orig_path)]) self.status(msg='Creating \'run\' rootfs') @@ -171,15 +171,15 @@ class NFSBootWriteExtension(morphlib.writeexts.WriteExtension): raise cliapp.AppException('Could not create \'run\' rootfs' ' from \'orig\'') - self.status(msg='Linking \'default-run\' to latest system') + self.status(msg='Linking \'default\' to latest system') try: cliapp.ssh_runcmd('root@%s' % location, - ['ln', '-sfn', run_path, + ['ln', '-sfn', versioned_root, os.path.join(self._nfsboot_root, hostname, 'systems', - 'default-run')]) + 'default')]) except cliapp.AppException: - raise cliapp.AppException('Could not link \'default-run\' to %s' - % run_path) + raise cliapp.AppException('Could not link \'default\' to %s' + % versioned_root) def configure_nfs(self, location, hostname): exported_path = os.path.join(self._nfsboot_root, hostname) diff --git a/morphlib/exts/rawdisk.write b/morphlib/exts/rawdisk.write index a43a9cce..a74d6905 100755 --- a/morphlib/exts/rawdisk.write +++ b/morphlib/exts/rawdisk.write @@ -70,9 +70,18 @@ class RawDiskWriteExtension(morphlib.writeexts.WriteExtension): self.create_run(version_root) + default_path = os.path.join(mp, 'systems', 'default') + if os.path.exists(default_path): + os.remove(default_path) + else: + # we are upgrading and old system that does + # not have an updated extlinux config file + if self.bootloader_is_wanted(): + self.install_extlinux(mp) + os.symlink(version_label, default_path) + if self.bootloader_is_wanted(): self.install_kernel(version_root, temp_root) - self.install_extlinux(mp, version_label) self.unmount(mp) diff --git a/morphlib/exts/ssh-rsync.write b/morphlib/exts/ssh-rsync.write index 6fe1153d..fba550cd 100755 --- a/morphlib/exts/ssh-rsync.write +++ b/morphlib/exts/ssh-rsync.write @@ -72,6 +72,21 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): 'snapshot', orig_dir, run_dir]) self.install_remote_kernel(location, version_root, temp_root) + default_path = os.path.join(remote_mnt, 'systems', 'default') + if self.bootloader_is_wanted(): + output = cliapp.ssh_runcmd(location, ['sh', '-c', + 'test -e "$1" && stat -c %F "$1"' + ' || ' + 'echo missing file', + '-', default_path]) + if output != "symbolic link": + # we are upgrading and old system that does + # not have an updated extlinux config file + self.update_remote_extlinux(location, remote_mnt, + version_label) + cliapp.ssh_runcmd(location, ['ln', '-s', '-f', + version_label, + default_path]) except Exception as e: try: cliapp.ssh_runcmd(location, @@ -83,9 +98,6 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): pass raise e - if self.bootloader_is_wanted(): - self.update_remote_extlinux(location, remote_mnt, - version_label) except: raise else: @@ -103,15 +115,15 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): f.write('default linux\n') f.write('timeout 1\n') f.write('label linux\n') - f.write('kernel /systems/' + version_label + '/kernel\n') + f.write('kernel /systems/default/kernel\n') f.write('append root=/dev/sda ' - 'rootflags=subvol=systems/' + version_label + '/run ' + 'rootflags=subvol=systems/default/run ' 'init=/sbin/init rw\n') cliapp.ssh_runcmd(location, ['mv', config, config+'~']) try: - cliapp.runcmd(['rsync', '-a', temp_file, + cliapp.runcmd(['rsync', '-as', temp_file, '%s:%s' % (location, config)]) except Exception as e: try: @@ -130,7 +142,7 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): cliapp.ssh_runcmd(location, ['btrfs', 'subvolume', 'snapshot', old_orig, new_orig]) - cliapp.runcmd(['rsync', '-a', '--checksum', '--numeric-ids', + cliapp.runcmd(['rsync', '-as', '--checksum', '--numeric-ids', '--delete', temp_root, '%s:%s' % (location, new_orig)]) def get_old_orig(self, location, remote_mnt): @@ -158,7 +170,7 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): for name in image_names: try_path = os.path.join(temp_root, 'boot', name) if os.path.exists(try_path): - cliapp.runcmd(['rsync', '-a', try_path, + cliapp.runcmd(['rsync', '-as', try_path, '%s:%s' % (location, kernel_dest)]) def check_valid_target(self, location): diff --git a/morphlib/exts/virtualbox-ssh.write b/morphlib/exts/virtualbox-ssh.write index cb17b69b..3ee2eae0 100755 --- a/morphlib/exts/virtualbox-ssh.write +++ b/morphlib/exts/virtualbox-ssh.write @@ -101,24 +101,26 @@ class VirtualBoxPlusSshWriteExtension(morphlib.writeexts.WriteExtension): self.status(msg='Transfer disk and convert to VDI') with open(raw_disk, 'rb') as f: - cliapp.runcmd( - ['ssh', ssh_host, - 'VBoxManage', 'convertfromraw', 'stdin', vdi_path, + cliapp.ssh_runcmd(ssh_host, + ['VBoxManage', 'convertfromraw', 'stdin', vdi_path, str(os.path.getsize(raw_disk))], stdin=f) def create_virtualbox_guest(self, ssh_host, vm_name, vdi_path, autostart): '''Create the VirtualBox virtual machine.''' - + self.status(msg='Create VirtualBox virtual machine') ram_mebibytes = str(self.get_ram_size() / (1024**2)) + hostonly_iface = self.get_host_interface(ssh_host) + commands = [ ['createvm', '--name', vm_name, '--ostype', 'Linux26_64', '--register'], ['modifyvm', vm_name, '--ioapic', 'on', '--memory', ram_mebibytes, - '--nic1', 'nat'], + '--nic1', 'hostonly', '--hostonlyadapter1', hostonly_iface, + '--nic2', 'nat', '--natnet2', 'default'], ['storagectl', vm_name, '--name', '"SATA Controller"', '--add', 'sata', '--bootable', 'on', '--sataportcount', '2'], ['storageattach', vm_name, '--storagectl', '"SATA Controller"', @@ -140,9 +142,65 @@ class VirtualBoxPlusSshWriteExtension(morphlib.writeexts.WriteExtension): commands.append(['startvm', vm_name]) for command in commands: - argv = ['ssh', ssh_host, 'VBoxManage'] + command - cliapp.runcmd(argv) + argv = ['VBoxManage'] + command + cliapp.ssh_runcmd(ssh_host, argv) + + def get_host_interface(self, ssh_host): + host_ipaddr = os.environ.get('HOST_IPADDR') + netmask = os.environ.get('NETMASK') + network_config = os.environ.get("NETWORK_CONFIG") + if network_config is None: + raise cliapp.AppException('NETWORK_CONFIG was not given') + + if "eth0:" not in network_config: + raise cliapp.AppException( + 'NETWORK_CONFIG does not contain ' + 'the eth0 configuration') + + if "eth1:" not in network_config: + raise cliapp.AppException( + 'NETWORK_CONFIG does not contain ' + 'the eth1 configuration') + + if host_ipaddr is None: + raise cliapp.AppException('HOST_IPADDR was not given') + + if netmask is None: + raise cliapp.AppException('NETMASK was not given') + + # 'VBoxManage list hostonlyifs' retrieves a list with the hostonly + # interfaces on the host. For each interface, the following lines + # are shown on top: + # + # Name: vboxnet0 + # GUID: 786f6276-656e-4074-8000-0a0027000000 + # Dhcp: Disabled + # IPAddress: 192.168.100.1 + # + # The following command tries to retrieve the hostonly interface + # name (e.g. vboxnet0) associated with the given ip address. + iface = None + lines = cliapp.ssh_runcmd(ssh_host, + ['VBoxManage', 'list', 'hostonlyifs']).splitlines() + for i, v in enumerate(lines): + if host_ipaddr in v: + iface = lines[i-3].split()[1] + break + + if iface is None: + iface = cliapp.ssh_runcmd(ssh_host, + ['VBoxManage', 'hostonlyif', 'create']) + # 'VBoxManage hostonlyif create' shows the name of the + # created hostonly interface inside single quotes + iface = iface[iface.find("'") + 1 : iface.rfind("'")] + cliapp.ssh_runcmd(ssh_host, + ['VBoxManage', 'hostonlyif', + 'ipconfig', iface, + '--ip', host_ipaddr, + '--netmask', netmask]) + + return iface VirtualBoxPlusSshWriteExtension().run() diff --git a/morphlib/localartifactcache.py b/morphlib/localartifactcache.py index b845cebf..76d085d1 100644 --- a/morphlib/localartifactcache.py +++ b/morphlib/localartifactcache.py @@ -14,6 +14,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +import collections import os import morphlib @@ -115,3 +116,30 @@ class LocalArtifactCache(object): for basename in basenames: os.remove(os.path.join(dirname, basename)) + def list_contents(self): + '''Return the set of sources cached and related information. + + returns a [(cache_key, set(artifacts), last_used)] + + ''' + CacheInfo = collections.namedtuple('CacheInfo', ('artifacts', 'mtime')) + contents = collections.defaultdict(lambda: CacheInfo(set(), 0)) + for dirpath, dirnames, filenames in os.walk(self.cachedir): + for filename in filenames: + cachekey = filename[:63] + artifact = filename[65:] + artifacts, max_mtime = contents[cachekey] + artifacts.add(artifact) + this_mtime = os.stat(os.path.join(dirpath, filename)).st_mtime + contents[cachekey] = CacheInfo(artifacts, + max(max_mtime, this_mtime)) + return ((cache_key, info.artifacts, info.mtime) + for cache_key, info in contents.iteritems()) + + + def remove(self, cachekey): + '''Remove all artifacts associated with the given cachekey.''' + for dirpath, dirnames, filenames in os.walk(self.cachedir): + for filename in filenames: + if filename.startswith(cachekey): + os.remove(os.path.join(dirpath, filename)) diff --git a/morphlib/localartifactcache_tests.py b/morphlib/localartifactcache_tests.py index 36b5e891..082b926a 100644 --- a/morphlib/localartifactcache_tests.py +++ b/morphlib/localartifactcache_tests.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012 Codethink Limited +# Copyright (C) 2012,2013 Codethink Limited # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -47,8 +47,10 @@ class LocalArtifactCacheTests(unittest.TestCase): 'repo', 'ref', 'sha1', 'tree', morph, 'chunk.morph') self.runtime_artifact = morphlib.artifact.Artifact( self.source, 'chunk-runtime') + self.runtime_artifact.cache_key = '0'*64 self.devel_artifact = morphlib.artifact.Artifact( self.source, 'chunk-devel') + self.devel_artifact.cache_key = '0'*64 def tearDown(self): self.tempdir.remove() @@ -155,3 +157,35 @@ class LocalArtifactCacheTests(unittest.TestCase): cache.clear() self.assertFalse(cache.has(self.runtime_artifact)) + def test_put_artifacts_and_list_them_afterwards(self): + cache = morphlib.localartifactcache.LocalArtifactCache( + self.tempdir.dirname) + + handle = cache.put(self.runtime_artifact) + handle.write('runtime') + handle.close() + + self.assertTrue(len(list(cache.list_contents())) == 1) + + handle = cache.put(self.devel_artifact) + handle.write('devel') + handle.close() + + self.assertTrue(len(list(cache.list_contents())) == 1) + + def test_put_artifacts_and_remove_them_afterwards(self): + cache = morphlib.localartifactcache.LocalArtifactCache( + self.tempdir.dirname) + + handle = cache.put(self.runtime_artifact) + handle.write('runtime') + handle.close() + + handle = cache.put(self.devel_artifact) + handle.write('devel') + handle.close() + + key = list(cache.list_contents())[0][0] + cache.remove(key) + + self.assertTrue(len(list(cache.list_contents())) == 0) diff --git a/morphlib/plugins/gc_plugin.py b/morphlib/plugins/gc_plugin.py new file mode 100644 index 00000000..ded4cf02 --- /dev/null +++ b/morphlib/plugins/gc_plugin.py @@ -0,0 +1,158 @@ +# Copyright (C) 2013 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import logging +import os +import shutil +import time + +import cliapp + +import morphlib + + +class GCPlugin(cliapp.Plugin): + + def enable(self): + self.app.add_subcommand('gc', self.gc, + arg_synopsis='') + self.app.settings.integer(['cachedir-artifact-delete-older-than'], + 'always delete artifacts older than this ' + 'period in seconds, (default: 1 week)', + metavar='PERIOD', + group="Storage Options", + default=(60*60*24*7)) + self.app.settings.integer(['cachedir-artifact-keep-younger-than'], + 'allow deletion of artifacts older than ' + 'this period in seconds, (default: 1 day)', + metavar='PERIOD', + group="Storage Options", + default=(60*60*24)) + + def disable(self): + pass + + def gc(self, args): + '''Make space by removing unused files. + + This removes all artifacts older than + --cachedir-artifact-delete-older-than, and may delete artifacts + older than --cachedir-artifact-keep-younger-than if it still + needs to make space. + + This removes extracted chunks and staging areas for failed builds + from the directory specified by --tempdir. + + ''' + + tempdir = self.app.settings['tempdir'] + cachedir = self.app.settings['cachedir'] + tempdir_min_space, cachedir_min_space = \ + morphlib.util.unify_space_requirements( + tempdir, self.app.settings['tempdir-min-space'], + cachedir, self.app.settings['cachedir-min-space']) + + self.cleanup_tempdir(tempdir, tempdir_min_space) + self.cleanup_cachedir(cachedir, cachedir_min_space) + + def cleanup_tempdir(self, temp_path, min_space): + self.app.status(msg='Cleaning up temp dir %(temp_path)s', + temp_path=temp_path, chatty=True) + for subdir in ('failed', 'chunks'): + if morphlib.util.get_bytes_free_in_path(temp_path) >= min_space: + self.app.status(msg='Not Removing subdirectory ' + '%(subdir)s, enough space already cleared', + subdir=os.path.join(temp_path, subdir), + chatty=True) + break + self.app.status(msg='Removing temp subdirectory: %(subdir)s', + subdir=subdir) + path = os.path.join(temp_path, subdir) + if os.path.exists(path): + shutil.rmtree(path) + + def calculate_delete_range(self): + now = time.time() + always_delete_age = \ + now - self.app.settings['cachedir-artifact-delete-older-than'] + may_delete_age = \ + now - self.app.settings['cachedir-artifact-keep-younger-than'] + return always_delete_age, may_delete_age + + def find_deletable_artifacts(self, lac, max_age, min_age): + '''Get a list of cache keys in order of how old they are.''' + contents = list(lac.list_contents()) + always = set(cachekey + for cachekey, artifacts, mtime in contents + if mtime < max_age) + maybe = ((cachekey, mtime) + for cachekey, artifacts, mtime in contents + if max_age <= mtime < min_age) + return always, [cachekey for cachekey, mtime + in sorted(maybe, key=lambda x: x[1])] + + def cleanup_cachedir(self, cache_path, min_space): + def sufficient_free(): + free = morphlib.util.get_bytes_free_in_path(cache_path) + return (free >= min_space) + if sufficient_free(): + self.app.status(msg='Not cleaning up cachedir, ' + 'sufficient space already cleared', + chatty=True) + return + lac = morphlib.localartifactcache.LocalArtifactCache(cache_path) + max_age, min_age = self.calculate_delete_range() + logging.debug('Must remove artifacts older than timestamp %d' + % max_age) + always_delete, may_delete = \ + self.find_deletable_artifacts(lac, max_age, min_age) + removed = 0 + source_count = len(always_delete) + len(may_delete) + logging.debug('Must remove artifacts %s' % repr(always_delete)) + logging.debug('Can remove artifacts %s' % repr(may_delete)) + + # Remove all old artifacts + for cachekey in always_delete: + self.app.status(msg='Removing source %(cachekey)s', + cachekey=cachekey, chatty=True) + lac.remove(cachekey) + removed += 1 + + # Maybe remove remaining middle-aged artifacts + for cachekey in may_delete: + if sufficient_free(): + self.app.status(msg='Finished cleaning up cachedir with ' + '%(remaining)d old sources remaining', + remaining=(source_count - removed), + chatty=True) + break + self.app.status(msg='Removing source %(cachekey)s', + cachekey=cachekey, chatty=True) + lac.remove(cachekey) + removed += 1 + + if sufficient_free(): + self.app.status(msg='Made sufficient space in %(cache_path)s ' + 'after removing %(removed)d sources', + removed=removed, cache_path=cache_path) + return + self.app.status(msg='Unable to clear enough space in %(cache_path)s ' + 'after removing %(removed)d sources. Please ' + 'reduce cachedir-artifact-keep-younger-than, ' + 'clear space from elsewhere, enlarge the disk ' + 'or reduce cachedir-min-space.', + cache_path=cache_path, removed=removed, + error=True) diff --git a/morphlib/util.py b/morphlib/util.py index 40420bf1..a9c22217 100644 --- a/morphlib/util.py +++ b/morphlib/util.py @@ -241,16 +241,29 @@ def on_same_filesystem(path_a, path_b): # pragma: no cover # TODO: return true if one path is a subvolume of the other on btrfs? return os.stat(path_a).st_dev == os.stat(path_b).st_dev +def unify_space_requirements(tmp_path, tmp_min_size, + cache_path, cache_min_size): # pragma: no cover + """Adjust minimum sizes when paths share a disk. + + Given pairs of path and minimum size, return the minimum sizes such + that when the paths are on the same disk, the sizes are added together. + + """ + # TODO: make this work for variable number of (path, size) pairs as needed + # hint: try list.sort and itertools.groupby + if not on_same_filesystem(tmp_path, cache_path): + return tmp_min_size, cache_min_size + unified_size = tmp_min_size + cache_min_size + return unified_size, unified_size + def check_disk_available(tmp_path, tmp_min_size, cache_path, cache_min_size): # pragma: no cover # if both are on the same filesystem, assume they share a storage pool, # so the sum of the two sizes needs to be available - # TODO: if we need to do this on any more than 2 filesystems - # extend it to take a [(path, min)] and do some arcane mathematics - # to split it into groups that share a filesystem - # hint: try list.sort and itertools.groupby - if on_same_filesystem(tmp_path, cache_path): - tmp_min_size = cache_min_size = tmp_min_size + cache_min_size + # TODO: if we need to do this on any more than 2 paths + # extend it to take a [(path, min)] + tmp_min_size, cache_min_size = unify_space_requirements( + tmp_path, tmp_min_size, cache_path, cache_min_size) tmp_size, cache_size = map(get_bytes_free_in_path, (tmp_path, cache_path)) errors = [] for path, min in [(tmp_path, tmp_min_size), (cache_path, cache_min_size)]: @@ -260,4 +273,10 @@ def check_disk_available(tmp_path, tmp_min_size, 'has %(free)d' % locals()) if not errors: return - raise morphlib.Error('Insufficient space on disk:\n' + '\n'.join(errors)) + raise morphlib.Error('Insufficient space on disk:\n' + + '\n'.join(errors) + '\n' + 'Please run `morph gc`. If the problem persists ' + 'increase the disk size, manually clean up some ' + 'space or reduce the disk space required by the ' + 'tempdir-min-space and cachedir-min-space ' + 'configuration options.') diff --git a/morphlib/writeexts.py b/morphlib/writeexts.py index df4cec33..de4189f8 100644 --- a/morphlib/writeexts.py +++ b/morphlib/writeexts.py @@ -71,9 +71,10 @@ class WriteExtension(cliapp.Application): self.create_orig(version_root, temp_root) self.create_fstab(version_root) self.create_run(version_root) + os.symlink(version_label, os.path.join(mp, 'systems', 'default')) if self.bootloader_is_wanted(): self.install_kernel(version_root, temp_root) - self.install_extlinux(mp, version_label) + self.install_extlinux(mp) except BaseException, e: sys.stderr.write('Error creating disk image') self.unmount(mp) @@ -226,7 +227,7 @@ class WriteExtension(cliapp.Application): cliapp.runcmd(['cp', '-a', try_path, kernel_dest]) break - def install_extlinux(self, real_root, version_label): + def install_extlinux(self, real_root): '''Install extlinux on the newly created disk image.''' self.status(msg='Creating extlinux.conf') @@ -235,9 +236,9 @@ class WriteExtension(cliapp.Application): f.write('default linux\n') f.write('timeout 1\n') f.write('label linux\n') - f.write('kernel /systems/' + version_label + '/kernel\n') + f.write('kernel /systems/default/kernel\n') f.write('append root=/dev/sda ' - 'rootflags=subvol=systems/' + version_label + '/run ' + 'rootflags=subvol=systems/default/run ' 'init=/sbin/init rw\n') self.status(msg='Installing extlinux') diff --git a/without-test-modules b/without-test-modules index 89b0bd8c..f143eb46 100644 --- a/without-test-modules +++ b/without-test-modules @@ -26,3 +26,4 @@ morphlib/plugins/__init__.py morphlib/writeexts.py morphlib/plugins/copy-artifacts_plugin.py morphlib/plugins/trovectl_plugin.py +morphlib/plugins/gc_plugin.py |