[gnome-ostree] qa: Extract guestmount into guestfish helper class
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-ostree] qa: Extract guestmount into guestfish helper class
- Date: Thu, 10 Jan 2013 23:15:03 +0000 (UTC)
commit 0fbfdaa30ad316984acbae9ced9200888d416e30
Author: Colin Walters <walters verbum org>
Date: Thu Jan 10 08:33:19 2013 -0500
qa: Extract guestmount into guestfish helper class
This is significantly cleaner, since we want other bits that also do
the mounting.
src/ostbuild/js/builtins/qa_make_disk.js | 6 +-
src/ostbuild/js/builtins/qa_pull_deploy.js | 220 ++++++++++------------------
src/ostbuild/js/builtins/qa_smoketest.js | 82 ++++++++++
src/ostbuild/js/guestfish.js | 152 +++++++++++++++++---
src/ostbuild/js/libqa.js | 38 +++++
5 files changed, 336 insertions(+), 162 deletions(-)
---
diff --git a/src/ostbuild/js/builtins/qa_make_disk.js b/src/ostbuild/js/builtins/qa_make_disk.js
index b125505..0396170 100644
--- a/src/ostbuild/js/builtins/qa_make_disk.js
+++ b/src/ostbuild/js/builtins/qa_make_disk.js
@@ -56,8 +56,8 @@ const QaMakeDisk = new Lang.Class({
part-init /dev/vda mbr\n\
blockdev-getsize64 /dev/vda\n\
blockdev-getss /dev/vda\n';
- let gf = new GuestFish.GuestFish(tmppath, true);
- let lines = gf.run(makeDiskCmd, cancellable, {partitionOpts: [], readWrite: true});
+ let gf = new GuestFish.GuestFish(tmppath, {partitionOpts: [], readWrite: true});
+ let lines = gf.run(makeDiskCmd, cancellable);
if (lines.length != 2)
throw new Error("guestfish returned unexpected output lines (" + lines.length + ", expected 2");
let diskBytesize = parseInt(lines[0]);
@@ -86,7 +86,7 @@ mkdir /boot\n\
swapOffset, rootOffset - 1,
rootOffset, endOffset - 1]);
print("partition config: ", partconfig);
- lines = gf.run(partconfig, cancellable, {partitionOpts: [], readWrite: true});
+ lines = gf.run(partconfig, cancellable);
GSystem.file_rename(tmppath, path, cancellable);
print("Created: " + path.get_path());
}
diff --git a/src/ostbuild/js/builtins/qa_pull_deploy.js b/src/ostbuild/js/builtins/qa_pull_deploy.js
index 353ae7d..e2b88ca 100644
--- a/src/ostbuild/js/builtins/qa_pull_deploy.js
+++ b/src/ostbuild/js/builtins/qa_pull_deploy.js
@@ -66,6 +66,75 @@ const QaPullDeploy = new Lang.Class({
return path;
},
+ _doPullDeloy: function(args, cancellable) {
+ let mntdir = this._mntdir;
+ let bootdir = mntdir.get_child('boot');
+ let ostreedir = mntdir.get_child('ostree');
+ let ostree_osdir = ostreedir.resolve_relative_path('deploy/' + args.osname);
+
+ let adminCmd = ['ostree', 'admin', '--ostree-dir=' + ostreedir.get_path(),
+ '--boot-dir=' + mntdir.get_child('boot').get_path()];
+ let adminEnv = GLib.get_environ();
+ adminEnv.push('LIBGSYSTEM_ENABLE_GUESTFS_FUSE_WORKAROUND=1');
+ let procdir = mntdir.get_child('proc');
+ if (!procdir.query_exists(cancellable)) {
+ ProcUtil.runSync(adminCmd.concat(['init-fs', mntdir.get_path()]), cancellable,
+ {logInitiation: true, env: adminEnv});
+ }
+
+ // *** NOTE ***
+ // Here we blow away any current deployment. This is pretty lame, but it
+ // avoids us triggering a variety of guestfs/FUSE bugs =(
+ // See: https://bugzilla.redhat.com/show_bug.cgi?id=892834
+ //
+ // But regardless, it's probably useful if every
+ // deployment starts clean, and callers can use libguestfs
+ // to crack the FS open afterwards and modify config files
+ // or the like.
+ GSystem.shutil_rm_rf(ostree_osdir, cancellable);
+
+ ProcUtil.runSync(adminCmd.concat(['os-init', args.osname]), cancellable,
+ {logInitiation: true, env: adminEnv});
+ ProcUtil.runSync(['ostree', '--repo=' + ostreedir.get_child('repo').get_path(),
+ 'pull-local', args.srcrepo, args.target], cancellable,
+ {logInitiation: true, env: adminEnv});
+
+ ProcUtil.runSync(adminCmd.concat(['deploy', '--no-kernel', args.osname, args.target]), cancellable,
+ {logInitiation: true, env: adminEnv});
+ ProcUtil.runSync(adminCmd.concat(['update-kernel', '--no-bootloader', args.osname]), cancellable,
+ {logInitiation: true, env: adminEnv});
+ ProcUtil.runSync(adminCmd.concat(['prune', args.osname]), cancellable,
+ {logInitiation: true, env: adminEnv});
+
+ let deployKernelPath = this._findCurrentKernel(mntdir, args.osname, cancellable);
+ let bootKernelPath = bootdir.resolve_relative_path('ostree/' + deployKernelPath.get_basename());
+ if (!bootKernelPath.query_exists(cancellable))
+ throw new Error("" + bootKernelPath.get_path() + " doesn't exist");
+ let kernelRelease = this._parseKernelRelease(deployKernelPath);
+ let initramfsPath = this._getInitramfsPath(mntdir, kernelRelease);
+
+ let defaultFstab = 'LABEL=gnostree-root / ext4 defaults 1 1\n\
+LABEL=gnostree-boot /boot ext4 defaults 1 2\n\
+LABEL=gnostree-swap swap swap defaults 0 0\n';
+ let fstabPath = ostreedir.resolve_relative_path('deploy/gnome-ostree/current-etc/fstab');
+ fstabPath.replace_contents(defaultFstab, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable);
+
+ let grubDir = mntdir.resolve_relative_path('boot/grub');
+ GSystem.file_ensure_directory(grubDir, false, cancellable);
+ let bootRelativeKernelPath = bootdir.get_relative_path(bootKernelPath);
+ if (bootRelativeKernelPath == null)
+ throw new Error("" + bootKernelPath.get_path() + " is not relative to " + bootdir.get_path());
+ let bootRelativeInitramfsPath = bootdir.get_relative_path(initramfsPath);
+ let grubConfPath = grubDir.get_child('grub.conf');
+ let grubConf = Format.vprintf('default=0\n\
+timeout=5\n\
+title %s\n\
+root (hd0,0)\n\
+kernel /%s root=LABEL=gnostree-root ostree=%s/current\n\
+initrd /%s\n', [args.osname, bootRelativeKernelPath, args.osname, bootRelativeInitramfsPath]);
+ grubConfPath.replace_contents(grubConf, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable);
+ },
+
execute: function(argv) {
let cancellable = null;
let parser = new ArgParse.ArgumentParser("Generate a disk image");
@@ -78,150 +147,23 @@ const QaPullDeploy = new Lang.Class({
let diskpath = Gio.File.new_for_path(args.diskpath);
- let workdir = Gio.File.new_for_path('.');
- let mntdir = workdir.get_child('mnt');
- GSystem.file_ensure_directory(mntdir, true, cancellable);
- let bootdir = mntdir.get_child('boot');
- let ostreedir = mntdir.get_child('ostree');
- let ostree_osdir = ostreedir.resolve_relative_path('deploy/' + args.osname);
- let guestmountPidFile = workdir.get_child('guestmount.pid');
-
- if (guestmountPidFile.query_exists(null)) {
- throw new Error("guestmount pid file exists (unclean shutdown?): " + guestmountPidFile.get_path());
- }
+ this._workdir = Gio.File.new_for_path('.');
+ this._mntdir = this._workdir.get_child('mnt');
+ GSystem.file_ensure_directory(this._mntdir, true, cancellable);
+ let gfmnt = new GuestFish.GuestMount(diskpath, {partitionOpts: ['-m', '/dev/sda3',
+ '-m', '/dev/sda1:/boot'],
+ readWrite: true});
+ gfmnt.mount(this._mntdir, cancellable);
try {
- let procContext = new GSystem.SubprocessContext({ argv: ['guestmount', '--rw', '-o', 'allow_root',
- '--pid-file', guestmountPidFile.get_path(),
- '-a', diskpath.get_path(),
- '-m', '/dev/sda3',
- '-m', '/dev/sda1:/boot',
- mntdir.get_path()] });
- let guestfishProc = new GSystem.Subprocess({context: procContext});
- print("starting guestfish");
- guestfishProc.init(cancellable);
- guestfishProc.wait_sync_check(cancellable);
- // guestfish should have daemonized now (unfortunately, if
- // there was a way to avoid that we would).
-
- let adminCmd = ['ostree', 'admin', '--ostree-dir=' + ostreedir.get_path(),
- '--boot-dir=' + mntdir.get_child('boot').get_path()];
- let adminEnv = GLib.get_environ();
- adminEnv.push('LIBGSYSTEM_ENABLE_GUESTFS_FUSE_WORKAROUND=1');
- let procdir = mntdir.get_child('proc');
- if (!procdir.query_exists(cancellable)) {
- ProcUtil.runSync(adminCmd.concat(['init-fs', mntdir.get_path()]), cancellable,
- {logInitiation: true, env: adminEnv});
- }
-
- // *** NOTE ***
- // Here we blow away any current deployment. This is pretty lame, but it
- // avoids us triggering a variety of guestfs/FUSE bugs =(
- // See: https://bugzilla.redhat.com/show_bug.cgi?id=892834
- //
- // But regardless, it's probably useful if every
- // deployment starts clean, and callers can use libguestfs
- // to crack the FS open afterwards and modify config files
- // or the like.
- GSystem.shutil_rm_rf(ostree_osdir, cancellable);
-
- ProcUtil.runSync(adminCmd.concat(['os-init', args.osname]), cancellable,
- {logInitiation: true, env: adminEnv});
- ProcUtil.runSync(['ostree', '--repo=' + ostreedir.get_child('repo').get_path(),
- 'pull-local', args.srcrepo, args.target], cancellable,
- {logInitiation: true, env: adminEnv});
-
- ProcUtil.runSync(adminCmd.concat(['deploy', '--no-kernel', args.osname, args.target]), cancellable,
- {logInitiation: true, env: adminEnv});
- ProcUtil.runSync(adminCmd.concat(['update-kernel', '--no-bootloader', args.osname]), cancellable,
- {logInitiation: true, env: adminEnv});
- ProcUtil.runSync(adminCmd.concat(['prune', args.osname]), cancellable,
- {logInitiation: true, env: adminEnv});
-
- let deployKernelPath = this._findCurrentKernel(mntdir, args.osname, cancellable);
- let bootKernelPath = bootdir.resolve_relative_path('ostree/' + deployKernelPath.get_basename());
- if (!bootKernelPath.query_exists(cancellable))
- throw new Error("" + bootKernelPath.get_path() + " doesn't exist");
- let kernelRelease = this._parseKernelRelease(deployKernelPath);
- let initramfsPath = this._getInitramfsPath(mntdir, kernelRelease);
-
- let defaultFstab = 'LABEL=gnostree-root / ext4 defaults 1 1\n\
-LABEL=gnostree-boot /boot ext4 defaults 1 2\n\
-LABEL=gnostree-swap swap swap defaults 0 0\n';
- let fstabPath = ostreedir.resolve_relative_path('deploy/gnome-ostree/current-etc/fstab');
- fstabPath.replace_contents(defaultFstab, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable);
-
- let grubDir = mntdir.resolve_relative_path('boot/grub');
- GSystem.file_ensure_directory(grubDir, false, cancellable);
- let bootRelativeKernelPath = bootdir.get_relative_path(bootKernelPath);
- if (bootRelativeKernelPath == null)
- throw new Error("" + bootKernelPath.get_path() + " is not relative to " + bootdir.get_path());
- let bootRelativeInitramfsPath = bootdir.get_relative_path(initramfsPath);
- let grubConfPath = grubDir.get_child('grub.conf');
- let grubConf = Format.vprintf('default=0\n\
-timeout=5\n\
-title %s\n\
- root (hd0,0)\n\
- kernel /%s root=LABEL=gnostree-root ostree=%s/current\n\
- initrd /%s\n', [args.osname, bootRelativeKernelPath, args.osname, bootRelativeInitramfsPath]);
- grubConfPath.replace_contents(grubConf, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable);
+ this._doPullDeloy(args, cancellable);
} finally {
- if (guestmountPidFile.query_exists(null)) {
- let pidStr = GSystem.file_load_contents_utf8(guestmountPidFile, cancellable);
- if (pidStr.length > 0) {
- for (let i = 0; i < 30; i++) {
- // See "man guestmount" for why retry loops here might be needed if this
- // script is running on a client machine with programs that watch for new mounts
- try {
- ProcUtil.runSync(['fusermount', '-u', mntdir.get_path()], cancellable,
- {logInitiation: true});
- break;
- } catch (e) {
- if (!(e.origError && e.origError.domain == GLib.spawn_exit_error_quark()))
- throw e;
- else {
- let proc = GSystem.Subprocess.new_simple_argv(['fuser', '-m', mntdir.get_path()],
- GSystem.SubprocessStreamDisposition.INHERIT,
- GSystem.SubprocessStreamDisposition.INHERIT,
- cancellable);
- proc.init(cancellable);
- proc.wait_sync(cancellable);
- let creds = new Gio.Credentials();
- proc = GSystem.Subprocess.new_simple_argv(['ls', '-al', '/proc/' + creds.get_unix_pid() + '/fd'],
- GSystem.SubprocessStreamDisposition.INHERIT,
- GSystem.SubprocessStreamDisposition.INHERIT,
- cancellable);
- proc.init(cancellable);
- proc.wait_sync(cancellable);
-
- GLib.usleep(GLib.USEC_PER_SEC);
- }
- }
- }
- let pid = parseInt(pidStr);
- for (let i = 0; i < 30; i++) {
- let killContext = new GSystem.SubprocessContext({argv: ['kill', '-0', ''+pid]});
- killContext.set_stderr_disposition(GSystem.SubprocessStreamDisposition.NULL);
- let killProc = new GSystem.Subprocess({context: killContext});
- killProc.init(null);
- let [waitSuccess, ecode] = killProc.wait_sync(null);
- let [killSuccess, statusStr] = ProcUtil.getExitStatusAndString(ecode);
- if (killSuccess) {
- print("Awaiting termination of guestfish, pid=" + pid + " timeout=" + (30 - i) + "s");
- GLib.usleep(GLib.USEC_PER_SEC);
- } else {
- break;
- print("guestmount exited");
- }
- }
- }
- }
+ gfmnt.umount(cancellable);
}
- let gf = new GuestFish.GuestFish(diskpath, true);
- gf.run('grub-install / /dev/vda\n', cancellable,
- {partitionOpts: ['-m', '/dev/sda3', '-m', '/dev/sda1:/boot'],
- readWrite: true});
+ let gf = new GuestFish.GuestFish(diskpath, {partitionOpts: ['-m', '/dev/sda3', '-m', '/dev/sda1:/boot'],
+ readWrite: true});
+ gf.run('grub-install / /dev/vda\n', cancellable);
print("Complete!");
}
});
diff --git a/src/ostbuild/js/builtins/qa_smoketest.js b/src/ostbuild/js/builtins/qa_smoketest.js
new file mode 100644
index 0000000..f47d4b1
--- /dev/null
+++ b/src/ostbuild/js/builtins/qa_smoketest.js
@@ -0,0 +1,82 @@
+// -*- indent-tabs-mode: nil; tab-width: 2; -*-
+// Copyright (C) 2013 Colin Walters <walters verbum org>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Format = imports.format;
+
+const GSystem = imports.gi.GSystem;
+
+const ArgParse = imports.argparse;
+const ProcUtil = imports.procutil;
+
+const loop = GLib.MainLoop.new(null, true);
+
+const QaSmokeTest = new Lang.Class({
+ Name: 'QaSmokeTest',
+
+ execute: function(argv) {
+ let cancellable = null;
+ let parser = new ArgParse.ArgumentParser("Basic smoke testing via parsing serial console");
+ parser.addArgument('diskpath');
+
+ let args = parser.parse(argv);
+
+ let diskpath = Gio.File.new_for_path(args.diskpath);
+
+ let workdir = Gio.File.new_for_path('.');
+
+ let fallbackPaths = ['/usr/libexec/qemu-kvm']
+ let qemuPathString = GLib.find_program_in_path('qemu-kvm');
+ if (qemuPathString == null) {
+ for (let i = 0; i < fallbackPaths.length; i++) {
+ let path = Gio.File.new_for_path(fallbackPaths[i]);
+ if (!path.query_exists(null))
+ continue;
+ qemuPathString = path.get_path();
+ }
+ }
+ if (qemuPathString == null) {
+ throw new Error("Unable to find qemu-kvm");
+ }
+
+ let qemuArgs = [qemuPathString, '-vga', 'std', 'm', '768M',
+ '-usb', '-usbdevice', 'tablet',
+ '-drive', 'file=' + diskpath + ',if=virtio',
+ ];
+
+ let qemuContext = new GSystem.SubprocessContext({ argv: qemuArgs });
+ let qemu = new GSystem.Subprocess({context: qemuContext});
+ print("starting qemu");
+ qemu.init(cancellable);
+
+ qemu.wait_sync_check(cancellable);
+
+ print("Complete!");
+ }
+});
+
+function main(argv) {
+ let ecode = 1;
+ var app = new QaSmokeTest();
+ GLib.idle_add(GLib.PRIORITY_DEFAULT,
+ function() { try { app.execute(argv); ecode = 0; } finally { loop.quit(); }; return false; });
+ loop.run();
+ return ecode;
+}
diff --git a/src/ostbuild/js/guestfish.js b/src/ostbuild/js/guestfish.js
index bd3a29e..9f057c8 100644
--- a/src/ostbuild/js/guestfish.js
+++ b/src/ostbuild/js/guestfish.js
@@ -23,40 +23,152 @@ const GSystem = imports.gi.GSystem;
const Params = imports.params;
const ProcUtil = imports.procutil;
-const GuestFish = new Lang.Class({
- Name: 'GuestFish',
+const LibGuestfs = new Lang.Class({
+ Name: 'LibGuestfs',
- _init: function(diskpath, useLockFile) {
+ _init: function(diskpath, params) {
+ this._params = Params.parse(params, {useLockFile: true,
+ partitionOpts: ['-i'],
+ readWrite: false});
this._diskpath = diskpath;
- if (useLockFile) {
+ this._readWrite = params.readWrite
+ this._partitionOpts = params.partitionOpts;
+ if (params.useLockFile) {
let lockfilePath = diskpath.get_parent().get_child(diskpath.get_basename() + '.guestfish-lock');
this._lockfilePath = lockfilePath;
} else {
this._lockfilePath = null;
}
},
-
- run: function(input, cancellable, params) {
- params = Params.parse(params, {partitionOpts: ['-i'],
- readWrite: false});
-
- try {
- let guestfishArgv = ['guestfish', '-a', this._diskpath.get_path()];
- if (params.readWrite)
- guestfishArgv.push('--rw');
- else
- guestfishArgv.push('--ro');
- guestfishArgv.push.apply(guestfishArgv, params.partitionOpts);
+ _lock: function() {
+ if (this._lockfilePath) {
let stream = this._lockfilePath.create(Gio.FileCreateFlags.NONE, cancellable);
stream.close(cancellable);
-
+ }
+ },
+
+ _unlock: function() {
+ if (this._lockfilePath != null) {
+ GSystem.file_unlink(this._lockfilePath, cancellable);
+ }
+ },
+
+ _appendOpts: function(argv) {
+ argv.push.apply(argv, ['-a', this._diskpath.get_path()]);
+ if (this._readWrite)
+ argv.push('--rw');
+ else
+ argv.push('--ro');
+ argv.push.apply(argv, this._partitionOpts);
+ }
+});
+
+const GuestFish = new Lang.Class({
+ Name: 'GuestFish',
+ Extends: LibGuestfs,
+
+ run: function(input, cancellable) {
+ this._lock();
+ try {
+ let guestfishArgv = ['guestfish'];
+ this._appendOpts(guestfishArgv);
return ProcUtil.runProcWithInputSyncGetLines(guestfishArgv, cancellable, input);
} finally {
- if (this._lockfilePath != null) {
- GSystem.file_unlink(this._lockfilePath, cancellable);
- }
+ this._unlock();
}
}
});
+const GuestMount = new Lang.Class({
+ Name: 'GuestMount',
+ Extends: LibGuestfs,
+
+ mount: function(mntdir, cancellable) {
+ this._lock();
+ try {
+ this._mntdir = mntdir;
+ this._mountPidFile = mntdir.get_parent().get_child(mntdir.get_basename() + '.guestmount-pid');
+ let guestmountArgv = ['guestmount', '-o', 'allow_root',
+ '--pid-file', this._mountPidFile.get_path()];
+ this._appendOpts(guestmountArgv);
+ guestmountArgv.push(mntdir.get_path());
+ let context = new GSystem.SubprocessContext({ argv: guestmountArgv });
+ let proc = new GSystem.Subprocess({ context: context });
+ proc.init(cancellable);
+
+ // guestfish daemonizes, so this process will exit, and
+ // when it has, we know the mount is ready. If there was
+ // a way to get notified instead of this indirect fashion,
+ // we'd do that.
+ proc.wait_sync_check(cancellable);
+
+ this._mounted = true;
+ } catch (e) {
+ this._unlock();
+ }
+ },
+
+ umount: function(cancellable) {
+ if (!this._mounted)
+ return;
+
+ let pidStr = GSystem.file_load_contents_utf8(this._mountPidFile, cancellable);
+ if (pidStr.length == 0) {
+ this._mounted = false;
+ return;
+ }
+
+ for (let i = 0; i < 30; i++) {
+ // See "man guestmount" for why retry loops here might be needed if this
+ // script is running on a client machine with programs that watch for new mounts
+ try {
+ ProcUtil.runSync(['fusermount', '-u', this._mntdir.get_path()], cancellable,
+ {logInitiation: true});
+ break;
+ } catch (e) {
+ if (!(e.origError && e.origError.domain == GLib.spawn_exit_error_quark()))
+ throw e;
+ else {
+ let proc = GSystem.Subprocess.new_simple_argv(['fuser', '-m', mntdir.get_path()],
+ GSystem.SubprocessStreamDisposition.INHERIT,
+ GSystem.SubprocessStreamDisposition.INHERIT,
+ cancellable);
+ proc.init(cancellable);
+ proc.wait_sync(cancellable);
+ let creds = new Gio.Credentials();
+ proc = GSystem.Subprocess.new_simple_argv(['ls', '-al', '/proc/' + creds.get_unix_pid() + '/fd'],
+ GSystem.SubprocessStreamDisposition.INHERIT,
+ GSystem.SubprocessStreamDisposition.INHERIT,
+ cancellable);
+ proc.init(cancellable);
+ proc.wait_sync(cancellable);
+
+ GLib.usleep(GLib.USEC_PER_SEC);
+ }
+ }
+ }
+
+ let pid = parseInt(pidStr);
+ let guestfishExited = false;
+ for (let i = 0; i < 30; i++) {
+ let killContext = new GSystem.SubprocessContext({argv: ['kill', '-0', ''+pid]});
+ killContext.set_stderr_disposition(GSystem.SubprocessStreamDisposition.NULL);
+ let killProc = new GSystem.Subprocess({context: killContext});
+ killProc.init(null);
+ let [waitSuccess, ecode] = killProc.wait_sync(null);
+ let [killSuccess, statusStr] = ProcUtil.getExitStatusAndString(ecode);
+ if (killSuccess) {
+ print("Awaiting termination of guestfish, pid=" + pid + " timeout=" + (30 - i) + "s");
+ GLib.usleep(GLib.USEC_PER_SEC);
+ } else {
+ guestfishExited = true;
+ break;
+ }
+ }
+ if (!guestfishExited)
+ throw new Error("guestfish failed to exit");
+ this._mounted = false;
+ this._unlock();
+ },
+});
diff --git a/src/ostbuild/js/libqa.js b/src/ostbuild/js/libqa.js
new file mode 100644
index 0000000..fd523c9
--- /dev/null
+++ b/src/ostbuild/js/libqa.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2012,2013 Colin Walters <walters verbum org>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+
+const GSystem = imports.gi.GSystem;
+const Params = imports.params;
+const ProcUtil = imports.procutil;
+const GuestFish = imports.guestfish;
+
+const ModifyBootloaderAppendKernelArg = new Lang.Class({
+ Name: 'ModifyGrub',
+ Extends: GuestFish.GuestFish,
+
+ _init: function(diskpath, params) {
+ this.parent(diskpath, params);
+ },
+
+ run: function(argsToAppend, cancellable) {
+ }
+});
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]