daemon: Reimplement ‘lvs’ API in OCaml.

This commit is contained in:
Richard W.M. Jones
2017-06-03 14:56:57 +01:00
parent 02adcd1cd9
commit 5866271518
5 changed files with 120 additions and 151 deletions

View File

@@ -251,6 +251,7 @@ SOURCES_MLI = \
is.mli \
ldm.mli \
link.mli \
lvm.mli \
mount.mli \
mountable.mli \
parted.mli \
@@ -272,6 +273,7 @@ SOURCES_ML = \
is.ml \
ldm.ml \
link.ml \
lvm.ml \
mount.ml \
parted.ml \
realpath.ml \

View File

@@ -101,89 +101,6 @@ convert_lvm_output (char *out, const char *prefix)
return take_stringsbuf (&ret);
}
/* Filter a colon-separated output of
* lvs -o lv_attr,vg_name,lv_name
* removing thin layouts, and building the device path as we expect it.
*
* This is used only when lvm has no -S.
*/
static char **
filter_convert_old_lvs_output (char *out)
{
char *p, *pend;
CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
p = out;
while (p) {
size_t len;
char *saveptr;
char *lv_attr, *vg_name, *lv_name;
pend = strchr (p, '\n'); /* Get the next line of output. */
if (pend) {
*pend = '\0';
pend++;
}
while (*p && c_isspace (*p)) /* Skip any leading whitespace. */
p++;
/* Sigh, skip trailing whitespace too. "pvs", I'm looking at you. */
len = strlen (p)-1;
while (*p && c_isspace (p[len]))
p[len--] = '\0';
if (!*p) { /* Empty line? Skip it. */
skip_line:
p = pend;
continue;
}
lv_attr = strtok_r (p, ":", &saveptr);
if (!lv_attr)
goto skip_line;
vg_name = strtok_r (NULL, ":", &saveptr);
if (!vg_name)
goto skip_line;
lv_name = strtok_r (NULL, ":", &saveptr);
if (!lv_name)
goto skip_line;
/* Ignore thin layouts (RHBZ#1278878). */
if (lv_attr[0] == 't')
goto skip_line;
/* Ignore activationskip (RHBZ#1306666). */
if (strlen (lv_attr) >= 10 && lv_attr[9] == 'k')
goto skip_line;
/* Ignore "unknown device" message (RHBZ#1054761). */
if (STRNEQ (p, "unknown device")) {
char buf[256];
snprintf (buf, sizeof buf, "/dev/%s/%s", vg_name, lv_name);
if (add_string (&ret, buf) == -1) {
free (out);
return NULL;
}
}
p = pend;
}
free (out);
if (ret.size > 0)
sort_strings (ret.argv, ret.size);
if (end_stringsbuf (&ret) == -1)
return NULL;
return take_stringsbuf (&ret);
}
char **
do_pvs (void)
{
@@ -220,74 +137,6 @@ do_vgs (void)
return convert_lvm_output (out, NULL);
}
/* Check whether lvs has -S to filter its output.
* It is available only in lvm2 >= 2.02.107.
*/
static int
test_lvs_has_S_opt (void)
{
static int result = -1;
if (result != -1)
return result;
CLEANUP_FREE char *out = NULL;
CLEANUP_FREE char *err = NULL;
int r = command (&out, &err, "lvm", "lvs", "--help", NULL);
if (r == -1) {
reply_with_error ("lvm lvs --help: %s", err);
return -1;
}
if (strstr (out, "-S") == NULL)
result = 0;
else
result = 1;
return result;
}
char **
do_lvs (void)
{
char *out;
CLEANUP_FREE char *err = NULL;
int r;
const int has_S = test_lvs_has_S_opt ();
if (has_S < 0)
return NULL;
if (has_S > 0) {
r = command (&out, &err,
"lvm", "lvs",
"-o", "vg_name,lv_name",
"-S", "lv_role=public && lv_skip_activation!=yes",
"--noheadings",
"--separator", "/", NULL);
if (r == -1) {
reply_with_error ("%s", err);
free (out);
return NULL;
}
return convert_lvm_output (out, "/dev/");
} else {
r = command (&out, &err,
"lvm", "lvs",
"-o", "lv_attr,vg_name,lv_name",
"--noheadings",
"--separator", ":", NULL);
if (r == -1) {
reply_with_error ("%s", err);
free (out);
return NULL;
}
return filter_convert_old_lvs_output (out);
}
}
/* These were so complex to implement that I ended up auto-generating
* the code. That code is in stubs.c, and it is generated as usual
* by generator.ml.

98
daemon/lvm.ml Normal file
View File

@@ -0,0 +1,98 @@
(* guestfs-inspection
* Copyright (C) 2009-2017 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
open Printf
open Std_utils
open Utils
(* Check whether lvs has -S to filter its output.
* It is available only in lvm2 >= 2.02.107.
*)
let lvs_has_S_opt = lazy (
let out = command "lvm" ["lvs"; "--help"] in
String.find out "-S" >= 0
)
let rec lvs () =
let has_S = Lazy.force lvs_has_S_opt in
if has_S then (
let out = command "lvm" ["lvs";
"-o"; "vg_name,lv_name";
"-S"; "lv_role=public && lv_skip_activation!=yes";
"--noheadings";
"--separator"; "/"] in
convert_lvm_output ~prefix:"/dev/" out
)
else (
let out = command "lvm" ["lvs";
"-o"; "lv_attr,vg_name,lv_name";
"--noheadings";
"--separator"; ":"] in
filter_convert_old_lvs_output out
)
and convert_lvm_output ?prefix out =
let lines = String.nsplit "\n" out in
(* Skip leading and trailing ("pvs", I'm looking at you) whitespace. *)
let lines = List.map String.trim lines in
(* Skip empty lines. *)
let lines = List.filter ((<>) "") lines in
(* Ignore "unknown device" message (RHBZ#1054761). *)
let lines = List.filter ((<>) "unknown device") lines in
(* Add a prefix? *)
let lines =
match prefix with
| None -> lines
| Some prefix -> List.map ((^) prefix) lines in
(* Sort and return. *)
List.sort compare lines
(* Filter a colon-separated output of
* lvs -o lv_attr,vg_name,lv_name
* removing thin layouts, and building the device path as we expect it.
*
* This is used only when lvm has no -S.
*)
and filter_convert_old_lvs_output out =
let lines = String.nsplit "\n" out in
let lines = List.map String.trim lines in
let lines = List.filter ((<>) "") lines in
let lines = List.filter ((<>) "unknown device") lines in
let lines = filter_map (
fun line ->
match String.nsplit ":" line with
| [ lv_attr; vg_name; lv_name ] ->
(* Ignore thin layouts (RHBZ#1278878). *)
if String.length lv_attr > 0 && lv_attr.[0] = 't' then None
(* Ignore activationskip (RHBZ#1306666). *)
else if String.length lv_attr > 9 && lv_attr.[9] = 'k' then None
else
Some (sprintf "/dev/%s/%s" vg_name lv_name)
| _ ->
None
) lines in
List.sort compare lines

19
daemon/lvm.mli Normal file
View File

@@ -0,0 +1,19 @@
(* guestfs-inspection
* Copyright (C) 2009-2017 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
val lvs : unit -> string list

View File

@@ -1732,6 +1732,7 @@ See also C<guestfs_vgs_full>." };
{ defaults with
name = "lvs"; added = (0, 0, 4);
style = RStringList (RDevice, "logvols"), [], [];
impl = OCaml "Lvm.lvs";
optional = Some "lvm2";
tests = [
InitBasicFSonLVM, Always, TestResult (