From 85ed8cef99c19b4143844991d14e0b848fecc5da Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 18 May 2009 17:16:24 +0100 Subject: [PATCH] Add vg-activate{,-all} commands, and resize recipe. --- TODO | 22 ---- daemon/actions.h | 2 + daemon/lvm.c | 39 +++++++ daemon/sfdisk.c | 5 +- daemon/stubs.c | 63 ++++++++++ fish/cmds.c | 44 +++++++ fish/completion.c | 2 + guestfish-actions.pod | 27 +++++ guestfs-actions.pod | 34 ++++++ java/com/redhat/et/libguestfs/GuestFS.java | 50 ++++++++ java/com_redhat_et_libguestfs_GuestFS.c | 47 ++++++++ ocaml/guestfs.ml | 2 + ocaml/guestfs.mli | 6 + ocaml/guestfs_c_actions.c | 48 ++++++++ perl/Guestfs.xs | 24 ++++ perl/lib/Sys/Guestfs.pm | 23 ++++ python/guestfs-py.c | 57 ++++++++++ python/guestfs.py | 26 +++++ recipes/resize.html | 19 ++++ recipes/resize.sh | 35 ++++++ recipes/resize.title | 1 + ruby/ext/guestfs/_guestfs.c | 51 +++++++++ src/generator.ml | 27 +++++ src/guestfs-actions.c | 177 +++++++++++++++++++++++++++++ src/guestfs-actions.h | 2 + src/guestfs_protocol.c | 23 ++++ src/guestfs_protocol.h | 22 +++- src/guestfs_protocol.x | 11 ++ tests.c | 2 + 29 files changed, 867 insertions(+), 24 deletions(-) create mode 100644 recipes/resize.html create mode 100755 recipes/resize.sh create mode 100644 recipes/resize.title diff --git a/TODO b/TODO index 323dc0b..71c9d59 100644 --- a/TODO +++ b/TODO @@ -27,25 +27,3 @@ Implement febootstrap command. ---------------------------------------------------------------------- Complete the Haskell bindings (see discussion on haskell-cafe). - ----------------------------------------------------------------------- - -Practically, resizing the partitions when a block device is resized -isn't possible. So for example it's not possible to resize a Fedora -block device. If you try to use sfdisk-N to change the boundaries of -the existing partition to fill up the new space, you get an error that -the partition is in use. - -The reason, I now think, is because LVM is using the partition as a -PV, and this locks it as far as the kernel is concerned. - -Removing the PV [which is what we do in the test suite] isn't -desirable if the PV contains data you care about. Rebooting the qemu -subprocess after the partition table change works, but isn't very -cool. I believe what we need to do is to temporarily reconfigure LVM -(using /etc/lvm/lvm.conf) to ignore the PV, vgscan (which will then -ignore the PV), make the changes to the partition table, then set the -LVM configuration back and do a final vgscan. - -Need to test the above, and find a nice way to present it through -the API. diff --git a/daemon/actions.h b/daemon/actions.h index 3e6589e..dcffd2a 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -123,3 +123,5 @@ extern int do_sfdisk_N (const char *device, int n, int cyls, int heads, int sect extern char *do_sfdisk_l (const char *device); extern char *do_sfdisk_kernel_geometry (const char *device); extern char *do_sfdisk_disk_geometry (const char *device); +extern int do_vg_activate_all (int activate); +extern int do_vg_activate (int activate, char * const* const volgroups); diff --git a/daemon/lvm.c b/daemon/lvm.c index d1a6cd6..63a3e7e 100644 --- a/daemon/lvm.c +++ b/daemon/lvm.c @@ -376,3 +376,42 @@ do_pvresize (const char *device) free (err); return 0; } + +int +do_vg_activate (int activate, char * const* const volgroups) +{ + char *err; + int r, i, argc; + const char **argv; + + argc = count_strings (volgroups) + 4; + argv = malloc (sizeof (char *) * (argc+1)); + if (argv == NULL) { + reply_with_perror ("malloc"); + return -1; + } + + argv[0] = "/sbin/lvm"; + argv[1] = "vgchange"; + argv[2] = "-a"; + argv[3] = activate ? "y" : "n"; + for (i = 4; i <= argc; ++i) + argv[i] = volgroups[i-4]; + + r = commandv (NULL, &err, argv); + if (r == -1) { + reply_with_error ("vgchange: %s", err); + free (err); + return -1; + } + + free (err); + return 0; +} + +int +do_vg_activate_all (int activate) +{ + char *empty[] = { NULL }; + return do_vg_activate (activate, empty); +} diff --git a/daemon/sfdisk.c b/daemon/sfdisk.c index 9d7a220..2f5206e 100644 --- a/daemon/sfdisk.c +++ b/daemon/sfdisk.c @@ -39,7 +39,7 @@ sfdisk (const char *device, int n, int cyls, int heads, int sectors, IS_DEVICE (device, -1); - strcpy (buf, "/sbin/sfdisk --no-reread"); + strcpy (buf, "/sbin/sfdisk"); if (n > 0) sprintf (buf + strlen (buf), " -N %d", n); if (cyls) @@ -51,6 +51,9 @@ sfdisk (const char *device, int n, int cyls, int heads, int sectors, /* Safe because of IS_DEVICE above: */ sprintf (buf + strlen (buf), " %s", device); + if (verbose) + printf ("%s\n", buf); + fp = popen (buf, "w"); if (fp == NULL) { reply_with_perror (buf); diff --git a/daemon/stubs.c b/daemon/stubs.c index e80ba9b..2fd2da9 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -2560,6 +2560,63 @@ done: xdr_free ((xdrproc_t) xdr_guestfs_sfdisk_disk_geometry_args, (char *) &args); } +static void vg_activate_all_stub (XDR *xdr_in) +{ + int r; + struct guestfs_vg_activate_all_args args; + int activate; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_vg_activate_all_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "vg_activate_all"); + return; + } + activate = args.activate; + + r = do_vg_activate_all (activate); + if (r == -1) + /* do_vg_activate_all has already called reply_with_error */ + goto done; + + reply (NULL, NULL); +done: + xdr_free ((xdrproc_t) xdr_guestfs_vg_activate_all_args, (char *) &args); +} + +static void vg_activate_stub (XDR *xdr_in) +{ + int r; + struct guestfs_vg_activate_args args; + int activate; + char **volgroups; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_vg_activate_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "vg_activate"); + return; + } + activate = args.activate; + volgroups = realloc (args.volgroups.volgroups_val, + sizeof (char *) * (args.volgroups.volgroups_len+1)); + if (volgroups == NULL) { + reply_with_perror ("realloc"); + goto done; + } + volgroups[args.volgroups.volgroups_len] = NULL; + args.volgroups.volgroups_val = volgroups; + + r = do_vg_activate (activate, volgroups); + if (r == -1) + /* do_vg_activate has already called reply_with_error */ + goto done; + + reply (NULL, NULL); +done: + xdr_free ((xdrproc_t) xdr_guestfs_vg_activate_args, (char *) &args); +} + void dispatch_incoming_message (XDR *xdr_in) { switch (proc_nr) { @@ -2869,6 +2926,12 @@ void dispatch_incoming_message (XDR *xdr_in) case GUESTFS_PROC_SFDISK_DISK_GEOMETRY: sfdisk_disk_geometry_stub (xdr_in); break; + case GUESTFS_PROC_VG_ACTIVATE_ALL: + vg_activate_all_stub (xdr_in); + break; + case GUESTFS_PROC_VG_ACTIVATE: + vg_activate_stub (xdr_in); + break; default: reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr); } diff --git a/fish/cmds.c b/fish/cmds.c index 5680bfa..8d50021 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -146,6 +146,8 @@ void list_commands (void) printf ("%-20s %s\n", "umount", "unmount a filesystem"); printf ("%-20s %s\n", "umount-all", "unmount all filesystems"); printf ("%-20s %s\n", "upload", "upload a file from the local machine"); + printf ("%-20s %s\n", "vg-activate", "activate or deactivate some volume groups"); + printf ("%-20s %s\n", "vg-activate-all", "activate or deactivate all volume groups"); printf ("%-20s %s\n", "vgcreate", "create an LVM volume group"); printf ("%-20s %s\n", "vgremove", "remove an LVM volume group"); printf ("%-20s %s\n", "vgs", "list the LVM volume groups (VGs)"); @@ -524,6 +526,12 @@ void display_command (const char *cmd) if (strcasecmp (cmd, "sfdisk_disk_geometry") == 0 || strcasecmp (cmd, "sfdisk-disk-geometry") == 0) pod2text ("sfdisk-disk-geometry - display the disk geometry from the partition table", " sfdisk-disk-geometry \n\nThis displays the disk geometry of C read from the\npartition table. Especially in the case where the underlying\nblock device has been resized, this can be different from the\nkernel's idea of the geometry (see C).\n\nThe result is in human-readable format, and not designed to\nbe parsed."); else + if (strcasecmp (cmd, "vg_activate_all") == 0 || strcasecmp (cmd, "vg-activate-all") == 0) + pod2text ("vg-activate-all - activate or deactivate all volume groups", " vg-activate-all \n\nThis command activates or (if C is false) deactivates\nall logical volumes in all volume groups.\nIf activated, then they are made known to the\nkernel, ie. they appear as C devices. If deactivated,\nthen those devices disappear.\n\nThis command is the same as running C"); + else + if (strcasecmp (cmd, "vg_activate") == 0 || strcasecmp (cmd, "vg-activate") == 0) + pod2text ("vg-activate - activate or deactivate some volume groups", " vg-activate \n\nThis command activates or (if C is false) deactivates\nall logical volumes in the listed volume groups C.\nIf activated, then they are made known to the\nkernel, ie. they appear as C devices. If deactivated,\nthen those devices disappear.\n\nThis command is the same as running C\n\nNote that if C is an empty list then B volume groups\nare activated or deactivated."); + else display_builtin_command (cmd); } @@ -2565,6 +2573,36 @@ static int run_sfdisk_disk_geometry (const char *cmd, int argc, char *argv[]) return 0; } +static int run_vg_activate_all (const char *cmd, int argc, char *argv[]) +{ + int r; + int activate; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + activate = is_true (argv[0]) ? 1 : 0; + r = guestfs_vg_activate_all (g, activate); + return r; +} + +static int run_vg_activate (const char *cmd, int argc, char *argv[]) +{ + int r; + int activate; + char **volgroups; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + activate = is_true (argv[0]) ? 1 : 0; + volgroups = parse_string_list (argv[1]); + r = guestfs_vg_activate (g, activate, volgroups); + return r; +} + int run_action (const char *cmd, int argc, char *argv[]) { if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0) @@ -2933,6 +2971,12 @@ int run_action (const char *cmd, int argc, char *argv[]) if (strcasecmp (cmd, "sfdisk_disk_geometry") == 0 || strcasecmp (cmd, "sfdisk-disk-geometry") == 0) return run_sfdisk_disk_geometry (cmd, argc, argv); else + if (strcasecmp (cmd, "vg_activate_all") == 0 || strcasecmp (cmd, "vg-activate-all") == 0) + return run_vg_activate_all (cmd, argc, argv); + else + if (strcasecmp (cmd, "vg_activate") == 0 || strcasecmp (cmd, "vg-activate") == 0) + return run_vg_activate (cmd, argc, argv); + else { fprintf (stderr, "%s: unknown command\n", cmd); return -1; diff --git a/fish/completion.c b/fish/completion.c index 03760ad..a072ce3 100644 --- a/fish/completion.c +++ b/fish/completion.c @@ -159,6 +159,8 @@ static const char *const commands[] = { "unmount-all", "upload", "verbose", + "vg-activate", + "vg-activate-all", "vgcreate", "vgremove", "vgs", diff --git a/guestfish-actions.pod b/guestfish-actions.pod index 213fa66..7e33cbb 100644 --- a/guestfish-actions.pod +++ b/guestfish-actions.pod @@ -1268,6 +1268,33 @@ See also C. Use C<-> instead of a filename to read/write from stdin/stdout. +=head2 vg-activate + + vg-activate true|false 'volgroups ...' + +This command activates or (if C is false) deactivates +all logical volumes in the listed volume groups C. +If activated, then they are made known to the +kernel, ie. they appear as C devices. If deactivated, +then those devices disappear. + +This command is the same as running C + +Note that if C is an empty list then B volume groups +are activated or deactivated. + +=head2 vg-activate-all + + vg-activate-all true|false + +This command activates or (if C is false) deactivates +all logical volumes in all volume groups. +If activated, then they are made known to the +kernel, ie. they appear as C devices. If deactivated, +then those devices disappear. + +This command is the same as running C + =head2 vgcreate vgcreate volgroup 'physvols ...' diff --git a/guestfs-actions.pod b/guestfs-actions.pod index abeed8f..9ed1cce 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -1719,6 +1719,40 @@ See also C. This function returns 0 on success or -1 on error. +=head2 guestfs_vg_activate + + int guestfs_vg_activate (guestfs_h *handle, + int activate, + char * const* const volgroups); + +This command activates or (if C is false) deactivates +all logical volumes in the listed volume groups C. +If activated, then they are made known to the +kernel, ie. they appear as C devices. If deactivated, +then those devices disappear. + +This command is the same as running C + +Note that if C is an empty list then B volume groups +are activated or deactivated. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_vg_activate_all + + int guestfs_vg_activate_all (guestfs_h *handle, + int activate); + +This command activates or (if C is false) deactivates +all logical volumes in all volume groups. +If activated, then they are made known to the +kernel, ie. they appear as C devices. If deactivated, +then those devices disappear. + +This command is the same as running C + +This function returns 0 on success or -1 on error. + =head2 guestfs_vgcreate int guestfs_vgcreate (guestfs_h *handle, diff --git a/java/com/redhat/et/libguestfs/GuestFS.java b/java/com/redhat/et/libguestfs/GuestFS.java index 7eafce0..a6d6f6d 100644 --- a/java/com/redhat/et/libguestfs/GuestFS.java +++ b/java/com/redhat/et/libguestfs/GuestFS.java @@ -2864,4 +2864,54 @@ public class GuestFS { private native String _sfdisk_disk_geometry (long g, String device) throws LibGuestFSException; + /** + * activate or deactivate all volume groups + * + * This command activates or (if "activate" is false) + * deactivates all logical volumes in all volume groups. If + * activated, then they are made known to the kernel, ie. + * they appear as "/dev/mapper" devices. If deactivated, + * then those devices disappear. + * + * This command is the same as running "vgchange -a y|n" + * + * @throws LibGuestFSException + */ + public void vg_activate_all (boolean activate) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("vg_activate_all: handle is closed"); + _vg_activate_all (g, activate); + } + private native void _vg_activate_all (long g, boolean activate) + throws LibGuestFSException; + + /** + * activate or deactivate some volume groups + * + * This command activates or (if "activate" is false) + * deactivates all logical volumes in the listed volume + * groups "volgroups". If activated, then they are made + * known to the kernel, ie. they appear as "/dev/mapper" + * devices. If deactivated, then those devices disappear. + * + * This command is the same as running "vgchange -a y|n + * volgroups..." + * + * Note that if "volgroups" is an empty list then all + * volume groups are activated or deactivated. + * + * @throws LibGuestFSException + */ + public void vg_activate (boolean activate, String[] volgroups) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("vg_activate: handle is closed"); + _vg_activate (g, activate, volgroups); + } + private native void _vg_activate (long g, boolean activate, String[] volgroups) + throws LibGuestFSException; + } diff --git a/java/com_redhat_et_libguestfs_GuestFS.c b/java/com_redhat_et_libguestfs_GuestFS.c index be7ea9f..3bf5c7e 100644 --- a/java/com_redhat_et_libguestfs_GuestFS.c +++ b/java/com_redhat_et_libguestfs_GuestFS.c @@ -2847,3 +2847,50 @@ Java_com_redhat_et_libguestfs_GuestFS__1sfdisk_1disk_1geometry return jr; } +JNIEXPORT void JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1vg_1activate_1all + (JNIEnv *env, jobject obj, jlong jg, jboolean jactivate) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + int r; + int activate; + + activate = jactivate; + r = guestfs_vg_activate_all (g, activate); + if (r == -1) { + throw_exception (env, guestfs_last_error (g)); + return ; + } +} + +JNIEXPORT void JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1vg_1activate + (JNIEnv *env, jobject obj, jlong jg, jboolean jactivate, jobjectArray jvolgroups) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + int r; + int activate; + int volgroups_len; + const char **volgroups; + int i; + + activate = jactivate; + volgroups_len = (*env)->GetArrayLength (env, jvolgroups); + volgroups = guestfs_safe_malloc (g, sizeof (char *) * (volgroups_len+1)); + for (i = 0; i < volgroups_len; ++i) { + jobject o = (*env)->GetObjectArrayElement (env, jvolgroups, i); + volgroups[i] = (*env)->GetStringUTFChars (env, o, NULL); + } + volgroups[volgroups_len] = NULL; + r = guestfs_vg_activate (g, activate, volgroups); + for (i = 0; i < volgroups_len; ++i) { + jobject o = (*env)->GetObjectArrayElement (env, jvolgroups, i); + (*env)->ReleaseStringUTFChars (env, o, volgroups[i]); + } + free (volgroups); + if (r == -1) { + throw_exception (env, guestfs_last_error (g)); + return ; + } +} + diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index d50b2d8..fa60a7b 100644 --- a/ocaml/guestfs.ml +++ b/ocaml/guestfs.ml @@ -241,3 +241,5 @@ external sfdisk_N : t -> string -> int -> int -> int -> int -> string -> unit = external sfdisk_l : t -> string -> string = "ocaml_guestfs_sfdisk_l" external sfdisk_kernel_geometry : t -> string -> string = "ocaml_guestfs_sfdisk_kernel_geometry" external sfdisk_disk_geometry : t -> string -> string = "ocaml_guestfs_sfdisk_disk_geometry" +external vg_activate_all : t -> bool -> unit = "ocaml_guestfs_vg_activate_all" +external vg_activate : t -> bool -> string array -> unit = "ocaml_guestfs_vg_activate" diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli index 358a6d3..5ce9122 100644 --- a/ocaml/guestfs.mli +++ b/ocaml/guestfs.mli @@ -502,3 +502,9 @@ val sfdisk_kernel_geometry : t -> string -> string val sfdisk_disk_geometry : t -> string -> string (** display the disk geometry from the partition table *) +val vg_activate_all : t -> bool -> unit +(** activate or deactivate all volume groups *) + +val vg_activate : t -> bool -> string array -> unit +(** activate or deactivate some volume groups *) + diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c index f2d13b0..f0aa7e2 100644 --- a/ocaml/guestfs_c_actions.c +++ b/ocaml/guestfs_c_actions.c @@ -3331,3 +3331,51 @@ ocaml_guestfs_sfdisk_disk_geometry (value gv, value devicev) CAMLreturn (rv); } +CAMLprim value +ocaml_guestfs_vg_activate_all (value gv, value activatev) +{ + CAMLparam2 (gv, activatev); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("vg_activate_all: used handle after closing it"); + + int activate = Bool_val (activatev); + int r; + + caml_enter_blocking_section (); + r = guestfs_vg_activate_all (g, activate); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "vg_activate_all"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_vg_activate (value gv, value activatev, value volgroupsv) +{ + CAMLparam3 (gv, activatev, volgroupsv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("vg_activate: used handle after closing it"); + + int activate = Bool_val (activatev); + char **volgroups = ocaml_guestfs_strings_val (g, volgroupsv); + int r; + + caml_enter_blocking_section (); + r = guestfs_vg_activate (g, activate, volgroups); + caml_leave_blocking_section (); + ocaml_guestfs_free_strings (volgroups); + if (r == -1) + ocaml_guestfs_raise_error (g, "vg_activate"); + + rv = Val_unit; + CAMLreturn (rv); +} + diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs index ff7ca9e..374a40d 100644 --- a/perl/Guestfs.xs +++ b/perl/Guestfs.xs @@ -1870,3 +1870,27 @@ PREINIT: OUTPUT: RETVAL +void +vg_activate_all (g, activate) + guestfs_h *g; + int activate; +PREINIT: + int r; + PPCODE: + r = guestfs_vg_activate_all (g, activate); + if (r == -1) + croak ("vg_activate_all: %s", guestfs_last_error (g)); + +void +vg_activate (g, activate, volgroups) + guestfs_h *g; + int activate; + char **volgroups; +PREINIT: + int r; + PPCODE: + r = guestfs_vg_activate (g, activate, volgroups); + free (volgroups); + if (r == -1) + croak ("vg_activate: %s", guestfs_last_error (g)); + diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm index 0249a41..0e4dde4 100644 --- a/perl/lib/Sys/Guestfs.pm +++ b/perl/lib/Sys/Guestfs.pm @@ -1141,6 +1141,29 @@ C can also be a named pipe. See also C<$h-Edownload>. +=item $h->vg_activate ($activate, \@volgroups); + +This command activates or (if C is false) deactivates +all logical volumes in the listed volume groups C. +If activated, then they are made known to the +kernel, ie. they appear as C devices. If deactivated, +then those devices disappear. + +This command is the same as running C + +Note that if C is an empty list then B volume groups +are activated or deactivated. + +=item $h->vg_activate_all ($activate); + +This command activates or (if C is false) deactivates +all logical volumes in all volume groups. +If activated, then they are made known to the +kernel, ie. they appear as C devices. If deactivated, +then those devices disappear. + +This command is the same as running C + =item $h->vgcreate ($volgroup, \@physvols); This creates an LVM volume group called C diff --git a/python/guestfs-py.c b/python/guestfs-py.c index 140594d..e967bd9 100644 --- a/python/guestfs-py.c +++ b/python/guestfs-py.c @@ -3557,6 +3557,61 @@ py_guestfs_sfdisk_disk_geometry (PyObject *self, PyObject *args) return py_r; } +static PyObject * +py_guestfs_vg_activate_all (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + int r; + int activate; + + if (!PyArg_ParseTuple (args, (char *) "Oi:guestfs_vg_activate_all", + &py_g, &activate)) + return NULL; + g = get_handle (py_g); + + r = guestfs_vg_activate_all (g, activate); + if (r == -1) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + Py_INCREF (Py_None); + py_r = Py_None; + return py_r; +} + +static PyObject * +py_guestfs_vg_activate (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + int r; + int activate; + PyObject *py_volgroups; + const char **volgroups; + + if (!PyArg_ParseTuple (args, (char *) "OiO:guestfs_vg_activate", + &py_g, &activate, &py_volgroups)) + return NULL; + g = get_handle (py_g); + volgroups = get_string_list (py_volgroups); + if (!volgroups) return NULL; + + r = guestfs_vg_activate (g, activate, volgroups); + free (volgroups); + if (r == -1) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + Py_INCREF (Py_None); + py_r = Py_None; + return py_r; +} + static PyMethodDef methods[] = { { (char *) "create", py_guestfs_create, METH_VARARGS, NULL }, { (char *) "close", py_guestfs_close, METH_VARARGS, NULL }, @@ -3686,6 +3741,8 @@ static PyMethodDef methods[] = { { (char *) "sfdisk_l", py_guestfs_sfdisk_l, METH_VARARGS, NULL }, { (char *) "sfdisk_kernel_geometry", py_guestfs_sfdisk_kernel_geometry, METH_VARARGS, NULL }, { (char *) "sfdisk_disk_geometry", py_guestfs_sfdisk_disk_geometry, METH_VARARGS, NULL }, + { (char *) "vg_activate_all", py_guestfs_vg_activate_all, METH_VARARGS, NULL }, + { (char *) "vg_activate", py_guestfs_vg_activate, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; diff --git a/python/guestfs.py b/python/guestfs.py index 8ac4037..4a2804b 100644 --- a/python/guestfs.py +++ b/python/guestfs.py @@ -1397,3 +1397,29 @@ class GuestFS: """ return libguestfsmod.sfdisk_disk_geometry (self._o, device) + def vg_activate_all (self, activate): + u"""This command activates or (if "activate" is false) + deactivates all logical volumes in all volume groups. If + activated, then they are made known to the kernel, ie. + they appear as "/dev/mapper" devices. If deactivated, + then those devices disappear. + + This command is the same as running "vgchange -a y|n" + """ + return libguestfsmod.vg_activate_all (self._o, activate) + + def vg_activate (self, activate, volgroups): + u"""This command activates or (if "activate" is false) + deactivates all logical volumes in the listed volume + groups "volgroups". If activated, then they are made + known to the kernel, ie. they appear as "/dev/mapper" + devices. If deactivated, then those devices disappear. + + This command is the same as running "vgchange -a y|n + volgroups..." + + Note that if "volgroups" is an empty list then all + volume groups are activated or deactivated. + """ + return libguestfsmod.vg_activate (self._o, activate, volgroups) + diff --git a/recipes/resize.html b/recipes/resize.html new file mode 100644 index 0000000..1a0ca3b --- /dev/null +++ b/recipes/resize.html @@ -0,0 +1,19 @@ +

+This example shows how a block device containing a partition +and a physical volume can be resized. +

+ +

+If you try this out, you +may find that attempts to repartition the disk fail because the +disk is locked by the LVM devices which exist on it. You have +to deactivate (temporarily) the volume groups, perform the +fdisk, and then activate them again. +

+ +

+This example script is self-contained. It first creates a +block device (a temporary file) containing some LVs, then it extends +the temporary file, and shows how to deactivate volgroups, repartition, +and activate them again. +

diff --git a/recipes/resize.sh b/recipes/resize.sh new file mode 100755 index 0000000..17a7e77 --- /dev/null +++ b/recipes/resize.sh @@ -0,0 +1,35 @@ +#!/bin/sh - + +guestfish <). The result is in human-readable format, and not designed to be parsed."); + ("vg_activate_all", (RErr, [Bool "activate"]), 103, [], + [], + "activate or deactivate all volume groups", + "\ +This command activates or (if C is false) deactivates +all logical volumes in all volume groups. +If activated, then they are made known to the +kernel, ie. they appear as C devices. If deactivated, +then those devices disappear. + +This command is the same as running C"); + + ("vg_activate", (RErr, [Bool "activate"; StringList "volgroups"]), 104, [], + [], + "activate or deactivate some volume groups", + "\ +This command activates or (if C is false) deactivates +all logical volumes in the listed volume groups C. +If activated, then they are made known to the +kernel, ie. they appear as C devices. If deactivated, +then those devices disappear. + +This command is the same as running C + +Note that if C is an empty list then B volume groups +are activated or deactivated."); + ] let all_functions = non_daemon_functions @ daemon_functions diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index 49e8961..41d873f 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -9356,3 +9356,180 @@ char *guestfs_sfdisk_disk_geometry (guestfs_h *g, return ctx.ret.partitions; /* caller will free */ } +struct vg_activate_all_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void vg_activate_all_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct vg_activate_all_ctx *ctx = (struct vg_activate_all_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_vg_activate_all"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_vg_activate_all"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_vg_activate_all"); + return; + } + goto done; + } + done: + ctx->cb_sequence = 1; +} + +int guestfs_vg_activate_all (guestfs_h *g, + int activate) +{ + struct guestfs_vg_activate_all_args args; + struct vg_activate_all_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_vg_activate_all") == -1) return -1; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.activate = activate; + serial = guestfs__send_sync (g, GUESTFS_PROC_VG_ACTIVATE_ALL, + (xdrproc_t) xdr_guestfs_vg_activate_all_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return -1; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, vg_activate_all_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_vg_activate_all"); + guestfs_end_busy (g); + return -1; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_VG_ACTIVATE_ALL, serial) == -1) { + guestfs_end_busy (g); + return -1; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return -1; + } + + guestfs_end_busy (g); + return 0; +} + +struct vg_activate_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void vg_activate_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct vg_activate_ctx *ctx = (struct vg_activate_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_vg_activate"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_vg_activate"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_vg_activate"); + return; + } + goto done; + } + done: + ctx->cb_sequence = 1; +} + +int guestfs_vg_activate (guestfs_h *g, + int activate, + char * const* const volgroups) +{ + struct guestfs_vg_activate_args args; + struct vg_activate_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_vg_activate") == -1) return -1; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.activate = activate; + args.volgroups.volgroups_val = (char **) volgroups; + for (args.volgroups.volgroups_len = 0; volgroups[args.volgroups.volgroups_len]; args.volgroups.volgroups_len++) ; + serial = guestfs__send_sync (g, GUESTFS_PROC_VG_ACTIVATE, + (xdrproc_t) xdr_guestfs_vg_activate_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return -1; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, vg_activate_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_vg_activate"); + guestfs_end_busy (g); + return -1; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_VG_ACTIVATE, serial) == -1) { + guestfs_end_busy (g); + return -1; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return -1; + } + + guestfs_end_busy (g); + return 0; +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index b07e3dd..3c760f2 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -145,3 +145,5 @@ extern int guestfs_sfdisk_N (guestfs_h *handle, const char *device, int n, int c extern char *guestfs_sfdisk_l (guestfs_h *handle, const char *device); extern char *guestfs_sfdisk_kernel_geometry (guestfs_h *handle, const char *device); extern char *guestfs_sfdisk_disk_geometry (guestfs_h *handle, const char *device); +extern int guestfs_vg_activate_all (guestfs_h *handle, int activate); +extern int guestfs_vg_activate (guestfs_h *handle, int activate, char * const* const volgroups); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index e6e865e..0a3fddc 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -1763,6 +1763,29 @@ xdr_guestfs_sfdisk_disk_geometry_ret (XDR *xdrs, guestfs_sfdisk_disk_geometry_re } bool_t +xdr_guestfs_vg_activate_all_args (XDR *xdrs, guestfs_vg_activate_all_args *objp) +{ + register int32_t *buf; + + if (!xdr_bool (xdrs, &objp->activate)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_vg_activate_args (XDR *xdrs, guestfs_vg_activate_args *objp) +{ + register int32_t *buf; + + if (!xdr_bool (xdrs, &objp->activate)) + return FALSE; + if (!xdr_array (xdrs, (char **)&objp->volgroups.volgroups_val, (u_int *) &objp->volgroups.volgroups_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp) { register int32_t *buf; diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index 6235577..25ed352 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -887,6 +887,20 @@ struct guestfs_sfdisk_disk_geometry_ret { }; typedef struct guestfs_sfdisk_disk_geometry_ret guestfs_sfdisk_disk_geometry_ret; +struct guestfs_vg_activate_all_args { + bool_t activate; +}; +typedef struct guestfs_vg_activate_all_args guestfs_vg_activate_all_args; + +struct guestfs_vg_activate_args { + bool_t activate; + struct { + u_int volgroups_len; + str *volgroups_val; + } volgroups; +}; +typedef struct guestfs_vg_activate_args guestfs_vg_activate_args; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -990,7 +1004,9 @@ enum guestfs_procedure { GUESTFS_PROC_SFDISK_L = 100, GUESTFS_PROC_SFDISK_KERNEL_GEOMETRY = 101, GUESTFS_PROC_SFDISK_DISK_GEOMETRY = 102, - GUESTFS_PROC_NR_PROCS = 102 + 1, + GUESTFS_PROC_VG_ACTIVATE_ALL = 103, + GUESTFS_PROC_VG_ACTIVATE = 104, + GUESTFS_PROC_NR_PROCS = 104 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -1181,6 +1197,8 @@ extern bool_t xdr_guestfs_sfdisk_kernel_geometry_args (XDR *, guestfs_sfdisk_ke extern bool_t xdr_guestfs_sfdisk_kernel_geometry_ret (XDR *, guestfs_sfdisk_kernel_geometry_ret*); extern bool_t xdr_guestfs_sfdisk_disk_geometry_args (XDR *, guestfs_sfdisk_disk_geometry_args*); extern bool_t xdr_guestfs_sfdisk_disk_geometry_ret (XDR *, guestfs_sfdisk_disk_geometry_ret*); +extern bool_t xdr_guestfs_vg_activate_all_args (XDR *, guestfs_vg_activate_all_args*); +extern bool_t xdr_guestfs_vg_activate_args (XDR *, guestfs_vg_activate_args*); extern bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*); extern bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*); extern bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*); @@ -1330,6 +1348,8 @@ extern bool_t xdr_guestfs_sfdisk_kernel_geometry_args (); extern bool_t xdr_guestfs_sfdisk_kernel_geometry_ret (); extern bool_t xdr_guestfs_sfdisk_disk_geometry_args (); extern bool_t xdr_guestfs_sfdisk_disk_geometry_ret (); +extern bool_t xdr_guestfs_vg_activate_all_args (); +extern bool_t xdr_guestfs_vg_activate_args (); extern bool_t xdr_guestfs_procedure (); extern bool_t xdr_guestfs_message_direction (); extern bool_t xdr_guestfs_message_status (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index 0df5cb0..38bb8ea 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -689,6 +689,15 @@ struct guestfs_sfdisk_disk_geometry_ret { string partitions<>; }; +struct guestfs_vg_activate_all_args { + bool activate; +}; + +struct guestfs_vg_activate_args { + bool activate; + str volgroups<>; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -792,6 +801,8 @@ enum guestfs_procedure { GUESTFS_PROC_SFDISK_L = 100, GUESTFS_PROC_SFDISK_KERNEL_GEOMETRY = 101, GUESTFS_PROC_SFDISK_DISK_GEOMETRY = 102, + GUESTFS_PROC_VG_ACTIVATE_ALL = 103, + GUESTFS_PROC_VG_ACTIVATE = 104, GUESTFS_PROC_NR_PROCS }; diff --git a/tests.c b/tests.c index 31fa3bf..b344320 100644 --- a/tests.c +++ b/tests.c @@ -121,6 +121,8 @@ static void no_test_warnings (void) fprintf (stderr, "warning: \"guestfs_sfdisk_l\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_sfdisk_kernel_geometry\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_sfdisk_disk_geometry\" has no tests\n"); + fprintf (stderr, "warning: \"guestfs_vg_activate_all\" has no tests\n"); + fprintf (stderr, "warning: \"guestfs_vg_activate\" has no tests\n"); } static int test_zerofree_0 (void) -- 1.8.3.1