From 85ed8cef99c19b4143844991d14e0b848fecc5da Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 18 May 2009 17:16:24 +0100 Subject: 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 323dc0b4..71c9d59e 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 3e6589e2..dcffd2a2 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 d1a6cd60..63a3e7e5 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 9d7a220f..2f5206ed 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 e80ba9be..2fd2da93 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 5680bfa3..8d50021e 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)"); @@ -523,6 +525,12 @@ void display_command (const char *cmd) else 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) @@ -2932,6 +2970,12 @@ int run_action (const char *cmd, int argc, char *argv[]) else 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); diff --git a/fish/completion.c b/fish/completion.c index 03760ad6..a072ce36 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 213fa66e..7e33cbbe 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 abeed8ff..9ed1cce4 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 7eafce0f..a6d6f6db 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 be7ea9f2..3bf5c7e5 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 d50b2d8c..fa60a7bb 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 358a6d30..5ce91220 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 f2d13b03..f0aa7e23 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 ff7ca9e1..374a40d1 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 0249a412..0e4dde4d 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 140594d2..e967bd94 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 8ac4037c..4a2804b4 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 00000000..1a0ca3bb --- /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 00000000..17a7e770 --- /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 49e8961d..41d873f9 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 b07e3dd5..3c760f23 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 e6e865e6..0a3fddc0 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -1762,6 +1762,29 @@ xdr_guestfs_sfdisk_disk_geometry_ret (XDR *xdrs, guestfs_sfdisk_disk_geometry_re return TRUE; } +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) { diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index 62355774..25ed3529 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 0df5cb07..38bb8ea9 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 31fa3bf2..b3443208 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) -- cgit