Move the simple OCaml JSON writer to mllib, so that can be enhanced and used also outside v2v. --- mllib/JSON.ml | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ mllib/JSON.mli | 26 ++++++++++++++++++++++++++ mllib/Makefile.am | 5 ++++- po/POTFILES-ml | 2 +- v2v/JSON.ml | 53 ----------------------------------------------------- v2v/JSON.mli | 26 -------------------------- v2v/Makefile.am | 3 +-- 7 files changed, 85 insertions(+), 83 deletions(-) create mode 100644 mllib/JSON.ml create mode 100644 mllib/JSON.mli delete mode 100644 v2v/JSON.ml delete mode 100644 v2v/JSON.mli diff --git a/mllib/JSON.ml b/mllib/JSON.ml new file mode 100644 index 0000000..5e3a879 --- /dev/null +++ b/mllib/JSON.ml @@ -0,0 +1,53 @@ +(* virt-v2v + * Copyright (C) 2009-2014 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. + *) + +(* Poor man's JSON generator. *) + +open Printf + +open Common_utils + +type field = string * json_t +and json_t = String of string | Int of int +and doc = field list + +(* JSON quoting. *) +let json_quote str + let str = replace_str str "\\" "\\\\" in + let str = replace_str str "\"" "\\\"" in + let str = replace_str str "'" "\\'" in + let str = replace_str str "\008" "\\b" in + let str = replace_str str "\012" "\\f" in + let str = replace_str str "\n" "\\n" in + let str = replace_str str "\r" "\\r" in + let str = replace_str str "\t" "\\t" in + let str = replace_str str "\011" "\\v" in + str + +let string_of_doc fields + "{ " ^ + String.concat ", " ( + List.map ( + function + | (n, String v) -> + sprintf "\"%s\" : \"%s\"" n (json_quote v) + | (n, Int v) -> + sprintf "\"%s\" : %d" n v + ) fields + ) + ^ " }" diff --git a/mllib/JSON.mli b/mllib/JSON.mli new file mode 100644 index 0000000..1e3a1b3 --- /dev/null +++ b/mllib/JSON.mli @@ -0,0 +1,26 @@ +(* virt-v2v + * Copyright (C) 2009-2014 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. + *) + +(** Poor man's JSON generator. *) + +type field = string * json_t +and json_t = String of string | Int of int +and doc = field list + +val string_of_doc : doc -> string + (** Serialize {!doc} object as a string. *) diff --git a/mllib/Makefile.am b/mllib/Makefile.am index ac953ac..653b8aa 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -32,6 +32,8 @@ SOURCES = \ fsync-c.c \ fsync.mli \ fsync.ml \ + JSON.mli \ + JSON.ml \ mkdtemp.mli \ mkdtemp.ml \ mkdtemp-c.c \ @@ -65,7 +67,8 @@ ocaml_modules = config \ uRI \ mkdtemp \ planner \ - regedit + regedit \ + JSON OBJECTS = \ $(top_builddir)/fish/guestfish-progress.o \ diff --git a/po/POTFILES-ml b/po/POTFILES-ml index df39e7b..d919b6f 100644 --- a/po/POTFILES-ml +++ b/po/POTFILES-ml @@ -25,6 +25,7 @@ customize/perl_edit.ml customize/random_seed.ml customize/timezone.ml customize/urandom.ml +mllib/JSON.ml mllib/common_gettext.ml mllib/common_utils.ml mllib/common_utils_tests.ml @@ -83,7 +84,6 @@ sysprep/sysprep_operation_user_account.ml sysprep/sysprep_operation_utmp.ml sysprep/sysprep_operation_yum_uuid.ml v2v/DOM.ml -v2v/JSON.ml v2v/OVF.ml v2v/cmdline.ml v2v/convert_linux.ml diff --git a/v2v/JSON.ml b/v2v/JSON.ml deleted file mode 100644 index 5e3a879..0000000 --- a/v2v/JSON.ml +++ /dev/null @@ -1,53 +0,0 @@ -(* virt-v2v - * Copyright (C) 2009-2014 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. - *) - -(* Poor man's JSON generator. *) - -open Printf - -open Common_utils - -type field = string * json_t -and json_t = String of string | Int of int -and doc = field list - -(* JSON quoting. *) -let json_quote str - let str = replace_str str "\\" "\\\\" in - let str = replace_str str "\"" "\\\"" in - let str = replace_str str "'" "\\'" in - let str = replace_str str "\008" "\\b" in - let str = replace_str str "\012" "\\f" in - let str = replace_str str "\n" "\\n" in - let str = replace_str str "\r" "\\r" in - let str = replace_str str "\t" "\\t" in - let str = replace_str str "\011" "\\v" in - str - -let string_of_doc fields - "{ " ^ - String.concat ", " ( - List.map ( - function - | (n, String v) -> - sprintf "\"%s\" : \"%s\"" n (json_quote v) - | (n, Int v) -> - sprintf "\"%s\" : %d" n v - ) fields - ) - ^ " }" diff --git a/v2v/JSON.mli b/v2v/JSON.mli deleted file mode 100644 index 1e3a1b3..0000000 --- a/v2v/JSON.mli +++ /dev/null @@ -1,26 +0,0 @@ -(* virt-v2v - * Copyright (C) 2009-2014 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. - *) - -(** Poor man's JSON generator. *) - -type field = string * json_t -and json_t = String of string | Int of int -and doc = field list - -val string_of_doc : doc -> string - (** Serialize {!doc} object as a string. *) diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 8bf8f07..dafe27e 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -37,7 +37,6 @@ SOURCES_MLI = \ input_libvirt.mli \ input_libvirtxml.mli \ input_ova.mli \ - JSON.mli \ kvmuid.mli \ linux.mli \ modules_list.mli \ @@ -62,7 +61,6 @@ SOURCES_ML = \ xml.ml \ domainxml.ml \ DOM.ml \ - JSON.ml \ kvmuid.ml \ vCenter.ml \ xen.ml \ @@ -122,6 +120,7 @@ BOBJECTS = \ $(top_builddir)/mllib/progress.cmo \ $(top_builddir)/mllib/config.cmo \ $(top_builddir)/mllib/mkdtemp.cmo \ + $(top_builddir)/mllib/JSON.cmo \ $(top_builddir)/customize/urandom.cmo \ $(top_builddir)/customize/random_seed.cmo \ $(top_builddir)/customize/hostname.cmo \ -- 1.9.3
Pino Toscano
2014-Oct-10 15:10 UTC
[Libguestfs] [PATCH 2/3] mllib: improve JSON: more types, indented output style
Improve the homebrew JSON writer: - add more types (including also nested dictionaries and lists) - format in a compact way (single line), or indented (multilines) --- mllib/JSON.ml | 118 ++++++++++++++++++++++++++++++++++++++++++++------------- mllib/JSON.mli | 16 ++++++-- 2 files changed, 104 insertions(+), 30 deletions(-) diff --git a/mllib/JSON.ml b/mllib/JSON.ml index 5e3a879..316b3f2 100644 --- a/mllib/JSON.ml +++ b/mllib/JSON.ml @@ -16,38 +16,102 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(* Poor man's JSON generator. *) - -open Printf - -open Common_utils +(* Simple JSON generator. *) type field = string * json_t -and json_t = String of string | Int of int +and json_t + | String of string + | Int of int + | Int64 of int64 + | Bool of bool + | List of json_t list + | Dict of field list and doc = field list +type output_format + | Compact + | Indented + + +let spaces_for_indent level + let len = level * 2 in + let s = String.create len in + String.fill s 0 len ' '; + s + +let print_dict_after_start ~fmt ~indent ~size + match size, fmt with + | 0, Compact -> "" + | _, Compact -> " " + | _, Indented -> "\n" + +let print_dict_before_end ~fmt ~indent ~size + match size, fmt with + | 0, _ -> "" + | _, Compact -> " " + | _, Indented -> "\n" + +let print_indent ~fmt ~indent + match fmt with + | Compact -> "" + | Indented -> spaces_for_indent indent + (* JSON quoting. *) -let json_quote str - let str = replace_str str "\\" "\\\\" in - let str = replace_str str "\"" "\\\"" in - let str = replace_str str "'" "\\'" in - let str = replace_str str "\008" "\\b" in - let str = replace_str str "\012" "\\f" in - let str = replace_str str "\n" "\\n" in - let str = replace_str str "\r" "\\r" in - let str = replace_str str "\t" "\\t" in - let str = replace_str str "\011" "\\v" in - str - -let string_of_doc fields - "{ " ^ - String.concat ", " ( +let json_escape_string 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 + +let json_quote_string str + "\"" ^ (json_escape_string str) ^ "\"" + +let rec output_dict fields ~fmt ~indent + let size = List.length fields in + let newlinesep + match fmt with + | Compact -> ", " + | Indented -> ",\n" in + "{" ^ (print_dict_after_start ~fmt ~indent ~size) ^ + String.concat newlinesep ( + List.map ( + fun (n, f) -> + (print_indent ~fmt ~indent:(indent + 1)) ^ (json_quote_string n) + ^ ": " ^ (output_field ~fmt ~indent f) + ) fields + ) + ^ (print_dict_before_end ~fmt ~indent ~size) ^ (print_indent ~fmt ~indent) ^ "}" + +and output_list fields ~fmt ~indent + let size = List.length fields in + let newlinesep + match fmt with + | Compact -> ", " + | Indented -> ",\n" in + "[" ^ (print_dict_after_start ~fmt ~indent ~size) ^ + String.concat newlinesep ( List.map ( - function - | (n, String v) -> - sprintf "\"%s\" : \"%s\"" n (json_quote v) - | (n, Int v) -> - sprintf "\"%s\" : %d" n v + fun f -> + (print_indent ~fmt ~indent:(indent + 1)) ^ (output_field ~fmt ~indent f) ) fields ) - ^ " }" + ^ (print_dict_before_end ~fmt ~indent ~size) ^ (print_indent ~fmt ~indent) ^ "]" + +and output_field ~indent ~fmt = function + | String s -> json_quote_string s + | Int i -> string_of_int i + | Bool b -> if b then "true" else "false" + | Int64 i -> Int64.to_string i + | List l -> output_list ~indent:(indent + 1) ~fmt l + | Dict d -> output_dict ~indent:(indent + 1) ~fmt d + +let string_of_doc ?(fmt = Compact) fields + output_dict fields ~fmt ~indent:0 diff --git a/mllib/JSON.mli b/mllib/JSON.mli index 1e3a1b3..fbaffab 100644 --- a/mllib/JSON.mli +++ b/mllib/JSON.mli @@ -16,11 +16,21 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(** Poor man's JSON generator. *) +(** Simple JSON generator. *) type field = string * json_t -and json_t = String of string | Int of int +and json_t + | String of string + | Int of int + | Int64 of int64 + | Bool of bool + | List of json_t list + | Dict of field list and doc = field list -val string_of_doc : doc -> string +type output_format + | Compact + | Indented + +val string_of_doc : ?fmt:output_format -> doc -> string (** Serialize {!doc} object as a string. *) -- 1.9.3
Switch to the JSON OCaml module for JSON output. The resulting output is the same, except from an indentation level more within lists. --- builder/Makefile.am | 1 + builder/list_entries.ml | 138 +++++++++++++++----------------------- builder/test-virt-builder-list.sh | 134 ++++++++++++++++++------------------ 3 files changed, 123 insertions(+), 150 deletions(-) diff --git a/builder/Makefile.am b/builder/Makefile.am index 4b1e1b8..dd96533 100644 --- a/builder/Makefile.am +++ b/builder/Makefile.am @@ -94,6 +94,7 @@ deps = \ $(top_builddir)/mllib/fsync.cmx \ $(top_builddir)/mllib/planner.cmx \ $(top_builddir)/mllib/regedit.cmx \ + $(top_builddir)/mllib/JSON.cmx \ $(top_builddir)/mllib/uri-c.o \ $(top_builddir)/mllib/uRI.cmx \ $(top_builddir)/mllib/mkdtemp-c.o \ diff --git a/builder/list_entries.ml b/builder/list_entries.ml index 1891679..2727c9f 100644 --- a/builder/list_entries.ml +++ b/builder/list_entries.ml @@ -97,92 +97,64 @@ and list_entries_long ~sources index ) 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 = function - | None -> () - | Some str -> - printf " \"%s\": \"%s\",\n" key (json_string_escape str) in - let json_optional_printf_int64 key = function - | None -> () - | Some n -> - printf " \"%s\": \"%Ld\",\n" key n in - let json_optional_printf_stringlist key = function - | None -> () - | Some l -> - printf " \"%s\": [" key; - iteri ( - fun i alias -> - printf " \"%s\"%s" alias (trailing_comma i (List.length l)) - ) l; - printf " ],\n" in - let print_notes = function - | [] -> () - | notes -> - printf " \"notes\": {\n"; - iteri ( - fun i (lang, langnotes) -> - let lang - match lang with - | "" -> "C" - | x -> x in - printf " \"%s\": \"%s\"%s\n" - (json_string_escape lang) (json_string_escape langnotes) - (trailing_comma i (List.length notes)) - ) notes; - printf " },\n" in - - printf "{\n"; - printf " \"version\": %d,\n" 1; - printf " \"sources\": [\n"; - iteri ( - fun i (source, key, proxy) -> - printf " {\n"; - (match key with - | Sigchecker.No_Key -> () - | Sigchecker.Fingerprint fp -> - printf " \"fingerprint\": \"%s\",\n" fp; - | Sigchecker.KeyFile kf -> - printf " \"key\": \"%s\",\n" kf; - ); - printf " \"uri\": \"%s\"\n" source; - 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; + let json_sources + List.map ( + fun (source, key, proxy) -> + let item = [ "uri", JSON.String source ] in + let item + match key with + | Sigchecker.No_Key -> item + | Sigchecker.Fingerprint fp -> + ("fingerprint", JSON.String fp) :: item + | Sigchecker.KeyFile kf -> + ("key", JSON.String kf) :: item in + JSON.Dict item + ) sources in + let json_templates + List.map ( + fun (name, { Index_parser.printable_name = printable_name; arch = arch; size = size; compressed_size = compressed_size; notes = notes; aliases = aliases; hidden = hidden }) -> - printf " {\n"; - printf " \"os-version\": \"%s\",\n" name; - json_optional_printf_string "full-name" printable_name; - printf " \"arch\": \"%s\",\n" arch; - printf " \"size\": %Ld,\n" size; - json_optional_printf_int64 "compressed-size" compressed_size; - print_notes notes; - json_optional_printf_stringlist "aliases" aliases; - printf " \"hidden\": %s\n" (json_string_of_bool hidden); - printf " }%s\n" (trailing_comma i (List.length index)) - ) index; - printf " ]\n"; - printf "}\n" + let item = [ "os-version", JSON.String name ] in + let item + match printable_name with + | None -> item + | Some str -> ("full-name", JSON.String str) :: item in + let item = ("arch", JSON.String arch) :: item in + let item = ("size", JSON.Int64 size) :: item in + let item + match compressed_size with + | None -> item + | Some n -> ("compressed-size", JSON.String (Int64.to_string n)) :: item in + let item + let json_notes + List.fold_right ( + fun (lang, langnotes) acc -> + let lang + match lang with + | "" -> "C" + | x -> x in + (lang, JSON.String langnotes) :: acc + ) notes [] in + if List.length json_notes = 0 then item + else ("notes", JSON.Dict json_notes) :: item in + let item + match aliases with + | None -> item + | Some l -> + let l = List.map (fun x -> JSON.String x) l in + ("aliases", JSON.List l) :: item in + let item = ("hidden", JSON.Bool hidden) :: item in + JSON.Dict (List.rev item) + ) index in + let doc = [ + "version", JSON.Int 1; + "sources", JSON.List json_sources; + "templates", JSON.List json_templates; + ] in + print_string (JSON.string_of_doc ~fmt:JSON.Indented doc); + print_newline () diff --git a/builder/test-virt-builder-list.sh b/builder/test-virt-builder-list.sh index 49daae7..2b6dfad 100755 --- a/builder/test-virt-builder-list.sh +++ b/builder/test-virt-builder-list.sh @@ -116,81 +116,81 @@ json_list=$(virt-builder --no-check-signature --no-cache --list --list-format js if [ "$json_list" != "{ \"version\": 1, \"sources\": [ - { - \"uri\": \"file://$abs_builddir/test-index\" - } + { + \"uri\": \"file://$abs_builddir/test-index\" + } ], \"templates\": [ - { - \"os-version\": \"phony-debian\", - \"full-name\": \"Phony Debian\", - \"arch\": \"x86_64\", - \"size\": 536870912, - \"notes\": { - \"C\": \"Phony Debian look-alike used for testing.\" + { + \"os-version\": \"phony-debian\", + \"full-name\": \"Phony Debian\", + \"arch\": \"x86_64\", + \"size\": 536870912, + \"notes\": { + \"C\": \"Phony Debian look-alike used for testing.\" + }, + \"hidden\": false }, - \"hidden\": false - }, - { - \"os-version\": \"phony-fedora\", - \"full-name\": \"Phony Fedora\", - \"arch\": \"x86_64\", - \"size\": 1073741824, - \"notes\": { - \"C\": \"Phony Fedora look-alike used for testing.\" + { + \"os-version\": \"phony-fedora\", + \"full-name\": \"Phony Fedora\", + \"arch\": \"x86_64\", + \"size\": 1073741824, + \"notes\": { + \"C\": \"Phony Fedora look-alike used for testing.\" + }, + \"hidden\": false }, - \"hidden\": false - }, - { - \"os-version\": \"phony-fedora-qcow2\", - \"full-name\": \"Phony Fedora qcow2\", - \"arch\": \"x86_64\", - \"size\": 1073741824, - \"notes\": { - \"C\": \"Phony Fedora look-alike used for testing.\" + { + \"os-version\": \"phony-fedora-qcow2\", + \"full-name\": \"Phony Fedora qcow2\", + \"arch\": \"x86_64\", + \"size\": 1073741824, + \"notes\": { + \"C\": \"Phony Fedora look-alike used for testing.\" + }, + \"hidden\": false }, - \"hidden\": false - }, - { - \"os-version\": \"phony-fedora-qcow2-uncompressed\", - \"full-name\": \"Phony Fedora qcow2 uncompressed\", - \"arch\": \"x86_64\", - \"size\": 1073741824, - \"notes\": { - \"C\": \"Phony Fedora look-alike used for testing.\" + { + \"os-version\": \"phony-fedora-qcow2-uncompressed\", + \"full-name\": \"Phony Fedora qcow2 uncompressed\", + \"arch\": \"x86_64\", + \"size\": 1073741824, + \"notes\": { + \"C\": \"Phony Fedora look-alike used for testing.\" + }, + \"hidden\": false }, - \"hidden\": false - }, - { - \"os-version\": \"phony-fedora-no-format\", - \"full-name\": \"Phony Fedora\", - \"arch\": \"x86_64\", - \"size\": 1073741824, - \"notes\": { - \"C\": \"Phony Fedora look-alike used for testing.\" + { + \"os-version\": \"phony-fedora-no-format\", + \"full-name\": \"Phony Fedora\", + \"arch\": \"x86_64\", + \"size\": 1073741824, + \"notes\": { + \"C\": \"Phony Fedora look-alike used for testing.\" + }, + \"hidden\": false }, - \"hidden\": false - }, - { - \"os-version\": \"phony-ubuntu\", - \"full-name\": \"Phony Ubuntu\", - \"arch\": \"x86_64\", - \"size\": 536870912, - \"notes\": { - \"C\": \"Phony Ubuntu look-alike used for testing.\" + { + \"os-version\": \"phony-ubuntu\", + \"full-name\": \"Phony Ubuntu\", + \"arch\": \"x86_64\", + \"size\": 536870912, + \"notes\": { + \"C\": \"Phony Ubuntu look-alike used for testing.\" + }, + \"hidden\": false }, - \"hidden\": false - }, - { - \"os-version\": \"phony-windows\", - \"full-name\": \"Phony Windows\", - \"arch\": \"x86_64\", - \"size\": 536870912, - \"notes\": { - \"C\": \"Phony Windows look-alike used for testing.\" - }, - \"hidden\": false - } + { + \"os-version\": \"phony-windows\", + \"full-name\": \"Phony Windows\", + \"arch\": \"x86_64\", + \"size\": 536870912, + \"notes\": { + \"C\": \"Phony Windows look-alike used for testing.\" + }, + \"hidden\": false + } ] }" ]; then echo "$0: unexpected --list --format json output:" -- 1.9.3
Simple code move, so: ACK. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into KVM guests. http://libguestfs.org/virt-v2v
Richard W.M. Jones
2014-Oct-10 15:17 UTC
Re: [Libguestfs] [PATCH 3/3] builder: use the JSON module
ACK series. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-builder quickly builds VMs from scratch http://libguestfs.org/virt-builder.1.html