[Libguestfs] [PATCH 3/3] builder: add a JSON output for --list

Pino Toscano ptoscano at redhat.com
Thu Jan 16 14:04:32 UTC 2014


Simple JSON output for sources and templates, to be able to query them
with no need to parse unstructured outputs like the "--list-format long"
one.
---
 builder/cmdline.ml                |  3 +-
 builder/list_entries.ml           | 60 +++++++++++++++++++++++++++++++++++
 builder/list_entries.mli          |  2 +-
 builder/test-virt-builder-list.sh | 67 +++++++++++++++++++++++++++++++++++++++
 builder/virt-builder.pod          | 12 ++++++-
 5 files changed, 141 insertions(+), 3 deletions(-)

diff --git a/builder/cmdline.ml b/builder/cmdline.ml
index 6d6439f..e3b1484 100644
--- a/builder/cmdline.ml
+++ b/builder/cmdline.ml
@@ -136,6 +136,7 @@ let parse_cmdline () =
     list_format := match arg with
     | "short" -> `Short
     | "long" -> `Long
+    | "json" -> `Json
     | fmt ->
       eprintf (f_"%s: invalid --list-format type '%s', see the man page.\n") prog fmt;
       exit 1 in
@@ -265,7 +266,7 @@ let parse_cmdline () =
     "--list",    Arg.Unit list_mode,        ditto;
     "--long",    Arg.Unit list_set_long,    " " ^ s_"Shortcut for --list-format short";
     "--list-format", Arg.String list_set_format,
-                                            "short|long" ^ " " ^ s_"Set the format for --list (default: short)";
+                                            "short|long|json" ^ " " ^ s_"Set the format for --list (default: short)";
     "--no-logfile", Arg.Set scrub_logfile,  " " ^ s_"Scrub build log file";
     "--long-options", Arg.Unit display_long_options, " " ^ s_"List long options";
     "-m",        Arg.Int set_memsize,       "mb" ^ " " ^ s_"Set memory size";
diff --git a/builder/list_entries.ml b/builder/list_entries.ml
index 97ab201..7369e6c 100644
--- a/builder/list_entries.ml
+++ b/builder/list_entries.ml
@@ -25,6 +25,7 @@ let rec list_entries ~list_format ~sources index =
   match list_format with
   | `Short -> list_entries_short index
   | `Long -> list_entries_long ~sources index
+  | `Json -> list_entries_json ~sources index
 
 and list_entries_short index =
   List.iter (
@@ -78,3 +79,62 @@ and list_entries_long ~sources index =
         printf "\n"
       )
   ) index
+
+and list_entries_json ~sources index =
+  let trailing_comma index size =
+    if index = size - 1 then "" else "," in
+  let json_string_of_bool b =
+    if b then "true" else "false" in
+  let json_string_escape str =
+    let res = ref "" in
+    for i = 0 to String.length str - 1 do
+      res := !res ^ (match str.[i] with
+        | '"' -> "\\\""
+        | '\\' -> "\\\\"
+        | '\b' -> "\\b"
+        | '\n' -> "\\n"
+        | '\r' -> "\\r"
+        | '\t' -> "\\t"
+        | c -> String.make 1 c)
+    done;
+    !res in
+  let json_optional_printf_string key value =
+    match value with
+    | None -> ()
+    | Some str ->
+      printf "    \"%s\": \"%s\",\n" key (json_string_escape str) in
+  let json_optional_printf_int64 key value =
+    match value with
+    | None -> ()
+    | Some n ->
+      printf "    \"%s\": \"%Ld\",\n" key n in
+
+  printf "{\n";
+  printf "  \"version\": %d,\n" 1;
+  printf "  \"sources\": [\n";
+  iteri (
+    fun i (source, fingerprint) ->
+      printf "  {\n";
+      printf "    \"uri\": \"%s\",\n" source;
+      printf "    \"fingerprint\": \"%s\"\n" fingerprint;
+      printf "  }%s\n" (trailing_comma i (List.length sources))
+  ) sources;
+  printf "  ],\n";
+  printf "  \"templates\": [\n";
+  iteri (
+    fun i (name, { Index_parser.printable_name = printable_name;
+                   size = size;
+                   compressed_size = compressed_size;
+                   notes = notes;
+                   hidden = hidden }) ->
+      printf "  {\n";
+      printf "    \"os-version\": \"%s\",\n" name;
+      json_optional_printf_string "full-name" printable_name;
+      printf "    \"size\": %Ld,\n" size;
+      json_optional_printf_int64 "compressed-size" compressed_size;
+      json_optional_printf_string "notes" notes;
+      printf "    \"hidden\": %s\n" (json_string_of_bool hidden);
+      printf "  }%s\n" (trailing_comma i (List.length index))
+  ) index;
+  printf "  ]\n";
+ printf "}\n"
diff --git a/builder/list_entries.mli b/builder/list_entries.mli
index 41d0bff..e7c32f1 100644
--- a/builder/list_entries.mli
+++ b/builder/list_entries.mli
@@ -16,4 +16,4 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *)
 
-val list_entries : list_format:([ `Short | `Long ]) -> sources:(string * string) list -> Index_parser.index -> unit
+val list_entries : list_format:([ `Short | `Long | `Json ]) -> sources:(string * string) list -> Index_parser.index -> unit
diff --git a/builder/test-virt-builder-list.sh b/builder/test-virt-builder-list.sh
index 083c035..c3b791f 100755
--- a/builder/test-virt-builder-list.sh
+++ b/builder/test-virt-builder-list.sh
@@ -103,3 +103,70 @@ Phony Windows look-alike used for testing." ]; then
     echo "$long_list"
     exit 1
 fi
+
+json_list=$(./virt-builder --no-check-signature --no-cache --list --list-format json)
+
+if [ "$json_list" != "{
+  \"version\": 1,
+  \"sources\": [
+  {
+    \"uri\": \"$VIRT_BUILDER_SOURCE\",
+    \"fingerprint\": \"F777 4FB1 AD07 4A7E 8C87 67EA 9173 8F73 E1B7 68A0\"
+  }
+  ],
+  \"templates\": [
+  {
+    \"os-version\": \"phony-debian\",
+    \"full-name\": \"Phony Debian\",
+    \"size\": 536870912,
+    \"notes\": \"Phony Debian look-alike used for testing.\",
+    \"hidden\": false
+  },
+  {
+    \"os-version\": \"phony-fedora\",
+    \"full-name\": \"Phony Fedora\",
+    \"size\": 1073741824,
+    \"notes\": \"Phony Fedora look-alike used for testing.\",
+    \"hidden\": false
+  },
+  {
+    \"os-version\": \"phony-fedora-qcow2\",
+    \"full-name\": \"Phony Fedora qcow2\",
+    \"size\": 1073741824,
+    \"notes\": \"Phony Fedora look-alike used for testing.\",
+    \"hidden\": false
+  },
+  {
+    \"os-version\": \"phony-fedora-qcow2-uncompressed\",
+    \"full-name\": \"Phony Fedora qcow2 uncompressed\",
+    \"size\": 1073741824,
+    \"notes\": \"Phony Fedora look-alike used for testing.\",
+    \"hidden\": false
+  },
+  {
+    \"os-version\": \"phony-fedora-no-format\",
+    \"full-name\": \"Phony Fedora\",
+    \"size\": 1073741824,
+    \"notes\": \"Phony Fedora look-alike used for testing.\",
+    \"hidden\": false
+  },
+  {
+    \"os-version\": \"phony-ubuntu\",
+    \"full-name\": \"Phony Ubuntu\",
+    \"size\": 536870912,
+    \"notes\": \"Phony Ubuntu look-alike used for testing.\",
+    \"hidden\": false
+  },
+  {
+    \"os-version\": \"phony-windows\",
+    \"full-name\": \"Phony Windows\",
+    \"size\": 536870912,
+    \"notes\": \"Phony Windows look-alike used for testing.\",
+    \"hidden\": false
+  }
+  ]
+}" ]; then
+    echo "$0: unexpected --list --format json output:"
+    echo "$json_list"
+    exit 1
+fi
diff --git a/builder/virt-builder.pod b/builder/virt-builder.pod
index 05abcc6..ded045b 100644
--- a/builder/virt-builder.pod
+++ b/builder/virt-builder.pod
@@ -31,7 +31,7 @@ virt-builder - Build virtual machine images quickly
     [--firstboot SCRIPT] [--firstboot-command 'CMD ARGS ...']
     [--firstboot-install PKG,[PKG...]]
 
- virt-builder -l|--list [--long] [--list-format short|long]
+ virt-builder -l|--list [--long] [--list-format short|long|json]
 
  virt-builder --notes os-version
 
@@ -395,6 +395,16 @@ its short description.
 Prints a textual list with the details of the available sources, followed
 by the details of the available templates.
 
+=item B<json>
+
+Prints a JSON object with the details of the available sources and
+the details of the available templates.
+
+The C<version> key in the main object represents the "compatibility version",
+and it is bumped every time the resulting JSON output is incompatible with
+the previous versions (for example the structure has changed, or non-optional
+keys are no more present).
+
 =back
 
 I<--long> is a shorthand for the C<long> format.
-- 
1.8.3.1




More information about the Libguestfs mailing list