[Libguestfs] [PATCH] New API: mdadm-detail

Richard W.M. Jones
Thu, 17 Nov 2011 09:31:18 -0000

On Wed, Nov 16, 2011 at 05:54:24PM +0000, Matthew Booth
wrote:
>
> Return a hash containing metadata about a specific Linux MD device, based
on the
> output of 'mdadm -DY'.
> ---
>  daemon/md.c                    |   78
++++++++++++++++++++++++++++++++++++++++
>  generator/generator_actions.ml |   31 ++++++++++++++++
>  regressions/test-mdadm.sh      |   60 ++++++++++++++++++++++++++++++-
>  src/MAX_PROC_NR                |    2 +-
>  4 files changed, 169 insertions(+), 2 deletions(-)
>

> diff --git a/daemon/md.c b/daemon/md.c
> index 257bd0f..c1f9d2a 100644
> --- a/daemon/md.c
> +++ b/daemon/md.c
> @@ -23,6 +23,7 @@
>  #include <string.h>
>  #include <inttypes.h>
>  #include <glob.h>
> +#include <ctype.h>
>
>  #include "daemon.h"
>  #include "actions.h"
> @@ -230,3 +231,80 @@ error:
>    if (r != NULL) free_strings (r);
>    return NULL;
>  }
> +
> +char **
> +do_mdadm_detail(const char *md)
> +{
> +  int r;
> +  char *out, *err;
> +
> +  char **ret = NULL;
> +  int size = 0, alloc = 0;
> +
> +  const char *mdadm[] = { "mdadm", "-DY", md, NULL };
> +  r = commandv(&out, &err, mdadm);

Any reason not to use plain ol' "command" here?  It takes a
variable number of args.

> +  if (r == -1) {
> +    reply_with_error("%s", err);
> +    free(err);
> +    free(out);
> +    return NULL;
> +  }

Somewhere about here, free(err) needs to be called.

> +  char *p = out;
> +
> +  /* Parse the output of mdadm -DY:
> +   * MD_LEVEL=raid1
> +   * MD_DEVICES=2
> +   * MD_METADATA=1.0
> +   * MD_UUID=cfa81b59:b6cfbd53:3f02085b:58f4a2e1
> +   * MD_NAME=localhost.localdomain:0
> +   */
> +  while (*p) {
> +    /* Turn the newline (if any) into a null */
> +    char *nl = strchrnul(p, '\n');
> +    if (*nl == '\n') {
> +      *nl = '\0';
> +      nl++;
> +    }

There is a function 'split_lines' defined in daemon.h which
essentially does this (not skipping blank lines though, but
I don't think those are really possible in mdadm output).

> +    /* Skip blank lines (shouldn't happen) */
> +    if (!*p) continue;
> +
> +    /* Split the line in 2 at the equals sign */
> +    char *eq = strchr(p, '=');
> +    if (eq) {
> +      *eq = '\0'; eq++;
> +
> +      /* Remove the MD_ prefix from the key and translate the remainder to
lower
> +       * case */
> +      if (STRPREFIX(p, "MD_")) {
> +        p += 3;
> +        for (char *i = p; *i != '\0'; i++) {
> +          *i = tolower(*i);

Probably c_tolower (from "c-ctype.h") is better to use here, since
you're really dealing with 7-bit ASCII in octets, not characters.

> +        }
> +      }
> +
> +      /* Add the key/value pair to the output */
> +      if (add_string(&ret, &size, &alloc, p) == -1) {
> +        free(out);
> +        return NULL;
> +      }
> +      if (add_string(&ret, &size, &alloc, eq) == -1) {
> +        free(out);
> +        return NULL;
> +      }
> +    } else {
> +      /* Ignore lines with no equals sign (shouldn't happen). Log to
stderr so
> +       * it will show up in LIBGUESTFS_DEBUG. */
> +      fprintf(stderr, "Unexpected output from mdadm -DY: %s",
p);

It's more consistent if the error message is something like:

  "mdadm-delete: unexpected output ignored: %s"

> +    }
> +
> +    p = nl;
> +  }
> +
> +  free(out);
> +
> +  if (add_string(&ret, &size, &alloc, NULL) == -1) return
NULL;
> +
> +  return ret;
> +}
> diff --git a/generator/generator_actions.ml
b/generator/generator_actions.ml
> index a4658a0..9bf9def 100644
> --- a/generator/generator_actions.ml
> +++ b/generator/generator_actions.ml
> @@ -6496,6 +6496,37 @@ If not set, this defaults to C<raid1>.
>     "\
>  List all Linux md devices.");
>
> +  ("mdadm_detail", (RHashtable "info", [Device
"md"], []), 301,  [Optional "mdadm"],
> +   [],
> +   "obtain metadata for an MD device",
> +   "\
> +This command exposes the output of 'mdadm -DY <md>'. The
resulting hash will
> +contain at least:

I wouldn't make any specific claims about what mdadm can return.  I
would say something like:

  "The following fields are usually present in the returned hash.
  Other fields may also be present."

> +=over
> +
> +=item C<level>
> +
> +The raid level of the MD device.
> +
> +=item C<devices>
> +
> +The number of underlying devices in the MD device.
> +
> +=item C<metadata>
> +
> +The metadata version used.
> +
> +=item C<uuid>
> +
> +The UUID of the MD device.
> +
> +=item C<name>
> +
> +The name of the MD device.
> +
> +=back");
> +
>  ]
>
>  let all_functions = non_daemon_functions @ daemon_functions
> diff --git a/regressions/test-mdadm.sh b/regressions/test-mdadm.sh
> index 3ad4f22..8119561 100755
> --- a/regressions/test-mdadm.sh
> +++ b/regressions/test-mdadm.sh
> @@ -92,4 +92,62 @@ write /r5t3/baz "testing"
>
>  EOF
>
> -rm -f md-test1.img md-test2.img md-test3.img md-test4.img
> +eval `../fish/guestfish --listen`
> +../fish/guestfish --remote add-ro md-test1.img
> +../fish/guestfish --remote add-ro md-test2.img
> +../fish/guestfish --remote add-ro md-test3.img
> +../fish/guestfish --remote add-ro md-test4.img
> +../fish/guestfish --remote run
> +
> +for md in `../fish/guestfish --remote list-md-devices`; do
> +  ../fish/guestfish --remote mdadm-detail "${md}" >
mdadm-detail.out
> +
> +  sed 's/:\s*/=/' mdadm-detail.out > mdadm-detail.out.sh
> +  . mdadm-detail.out.sh
> +  rm -f mdadm-detail.out.sh
> +
> +  error=0
> +  case "$name" in
> +    *:r1t1)
> +      [ "$level" == "raid1" ] || error=1
> +      [ "$devices" == "2" ] || error=1
> +      ;;
> +
> +    *:r1t2)
> +      [ "$level" == "raid1" ] || error=1
> +      [ "$devices" == "2" ] || error=1
> +      ;;
> +
> +    *:r5t1)
> +      [ "$level" == "raid5" ] || error=1
> +      [ "$devices" == "4" ] || error=1
> +      ;;
> +
> +    *:r5t2)
> +      [ "$level" == "raid5" ] || error=1
> +      [ "$devices" == "3" ] || error=1
> +      ;;
> +
> +    *:r5t3)
> +      [ "$level" == "raid5" ] || error=1
> +      [ "$devices" == "2" ] || error=1
> +      ;;
> +
> +    *)
> +      error=1
> +  esac
> +
> +  [[ "$uuid" =~ ([0-9a-f]{8}:){3}[0-9a-f]{8} ]] || error=1
> +  [ ! -z "$metadata" ] || error=1
> +
> +  if [ "$error" == "1" ]; then
> +    echo "$0: Unexpected output from mdadm-detail for device
$md"
> +    cat mdadm-detail.out
> +    ../fish/guestfish --remote exit
> +    exit 1
> +  fi
> +done
> +
> +../fish/guestfish --remote exit
> +
> +rm -f mdadm-detail.out md-test1.img md-test2.img md-test3.img md-test4.img

Looks good.  Did the test run OK?

> diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
> index 697cb3a..d8fc48a 100644
> --- a/src/MAX_PROC_NR
> +++ b/src/MAX_PROC_NR
> @@ -1 +1 @@
> -300
> +301

Rich.

--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
libguestfs lets you edit virtual machines.  Supports shell scripting,
bindings from many languages.  http://libguestfs.org