Pino Toscano
2015-Aug-12 13:12 UTC
[Libguestfs] [PATCH 0/2 v2] RFC: builder: support for Simple Streams metadata
Hi, this series adds a basic support for Simple Streams v1.0 metadata files. This makes it possible to create a repository .conf files with [cirros] uri=http://download.cirros-cloud.net format=simplestreams to read the latest version of each CirrOS image. TODO items: - check the pasted metadata: listing and creating images works, so most of the current metadata is correct - possibly wait for normalization patches: https://www.redhat.com/archives/libguestfs/2015-August/msg00058.html Thanks, Pino Toscano (2): builder: add non-int revisions builder: support Simple Streams v1.0 as index metadata builder/Makefile.am | 13 ++- builder/builder.ml | 11 +- builder/cache.ml | 2 +- builder/cache.mli | 6 +- builder/downloader.mli | 2 +- builder/index.ml | 4 +- builder/index.mli | 2 +- builder/index_parser.ml | 4 +- builder/simplestreams_parser.ml | 213 +++++++++++++++++++++++++++++++++++++++ builder/simplestreams_parser.mli | 19 ++++ builder/sources.ml | 9 ++ builder/sources.mli | 1 + builder/utils.ml | 7 ++ builder/virt-builder.pod | 7 ++ builder/yajl-c.c | 143 ++++++++++++++++++++++++++ builder/yajl.ml | 30 ++++++ builder/yajl.mli | 33 ++++++ po/POTFILES | 1 + po/POTFILES-ml | 2 + 19 files changed, 495 insertions(+), 14 deletions(-) create mode 100644 builder/simplestreams_parser.ml create mode 100644 builder/simplestreams_parser.mli create mode 100644 builder/yajl-c.c create mode 100644 builder/yajl.ml create mode 100644 builder/yajl.mli -- 2.1.0
Pino Toscano
2015-Aug-12 13:12 UTC
[Libguestfs] [PATCH 1/2] builder: add non-int revisions
Add support for non-integer revisions of entries, comparing them as integer when possible. --- builder/builder.ml | 9 ++++++++- builder/cache.ml | 2 +- builder/cache.mli | 6 +++--- builder/downloader.mli | 2 +- builder/index.ml | 4 ++-- builder/index.mli | 2 +- builder/index_parser.ml | 4 ++-- builder/utils.ml | 7 +++++++ 8 files changed, 25 insertions(+), 11 deletions(-) diff --git a/builder/builder.ml b/builder/builder.ml index adfa412..260af94 100644 --- a/builder/builder.ml +++ b/builder/builder.ml @@ -34,6 +34,13 @@ open Printf let () = Random.self_init () let remove_duplicates index + let compare_revisions rev1 rev2 + match rev1, rev2 with + | Rev_int n1, Rev_int n2 -> compare n1 n2 + | Rev_string s1, Rev_int n2 -> compare s1 (string_of_int n2) + | Rev_int n1, Rev_string s2 -> compare (string_of_int n1) s2 + | Rev_string s1, Rev_string s2 -> compare s1 s2 + in (* Fill an hash with the higher revision of the available * (name, arch) tuples, so it possible to ignore duplicates, * and versions with a lower revision. @@ -44,7 +51,7 @@ let remove_duplicates index let id = name, arch in try let rev = Hashtbl.find nseen id in - if revision > rev then + if compare_revisions rev revision > 0 then Hashtbl.replace nseen id revision with Not_found -> Hashtbl.add nseen id revision diff --git a/builder/cache.ml b/builder/cache.ml index e73bcfd..8b63a86 100644 --- a/builder/cache.ml +++ b/builder/cache.ml @@ -40,7 +40,7 @@ let create ~directory } let cache_of_name t name arch revision - t.directory // sprintf "%s.%s.%d" name arch revision + t.directory // sprintf "%s.%s.%s" name arch (string_of_revision revision) let is_cached t name arch revision let filename = cache_of_name t name arch revision in diff --git a/builder/cache.mli b/builder/cache.mli index 7edc670..1ab8ede 100644 --- a/builder/cache.mli +++ b/builder/cache.mli @@ -27,16 +27,16 @@ type t val create : directory:string -> t (** Create the abstract type. *) -val cache_of_name : t -> string -> string -> int -> string +val cache_of_name : t -> string -> string -> Utils.revision -> string (** [cache_of_name t name arch revision] return the filename of the cached file. (Note: It doesn't check if the filename exists, this is just a simple string transformation). *) -val is_cached : t -> string -> string -> int -> bool +val is_cached : t -> string -> string -> Utils.revision -> bool (** [is_cached t name arch revision] return whether the file with specified name, architecture and revision is cached. *) -val print_item_status : t -> header:bool -> (string * string * int) list -> unit +val print_item_status : t -> header:bool -> (string * string * Utils.revision) list -> unit (** [print_item_status t header items] print the status in the cache of the specified items (which are tuples of name, architecture, and revision). diff --git a/builder/downloader.mli b/builder/downloader.mli index 5e3cdaa..11ec498 100644 --- a/builder/downloader.mli +++ b/builder/downloader.mli @@ -35,7 +35,7 @@ type proxy_mode val create : curl:string -> cache:Cache.t option -> t (** Create the abstract type. *) -val download : t -> ?template:(string*string*int) -> ?progress_bar:bool -> ?proxy:proxy_mode -> uri -> (filename * bool) +val download : t -> ?template:(string*string*Utils.revision) -> ?progress_bar:bool -> ?proxy:proxy_mode -> uri -> (filename * bool) (** Download the URI, returning the downloaded filename and a temporary file flag. The temporary file flag is [true] iff the downloaded file is temporary and should be deleted by the diff --git a/builder/index.ml b/builder/index.ml index 3e8cb85..c59d6dd 100644 --- a/builder/index.ml +++ b/builder/index.ml @@ -32,7 +32,7 @@ and entry = { arch : string; signature_uri : string option; (* deprecated, will be removed in 1.26 *) checksums : Checksums.csum_t list option; - revision : int; + revision : Utils.revision; format : string option; size : int64; compressed_size : int64 option; @@ -86,7 +86,7 @@ let print_entry chan (name, { printable_name = printable_name; (Checksums.string_of_csum_t c) (Checksums.string_of_csum c) ) checksums ); - fp "revision=%d\n" revision; + fp "revision=%s\n" (string_of_revision revision); (match format with | None -> () | Some format -> fp "format=%s\n" format diff --git a/builder/index.mli b/builder/index.mli index 10ed15a..fadcad9 100644 --- a/builder/index.mli +++ b/builder/index.mli @@ -24,7 +24,7 @@ and entry = { arch : string; signature_uri : string option; (* deprecated, will be removed in 1.26 *) checksums : Checksums.csum_t list option; - revision : int; + revision : Utils.revision; format : string option; size : int64; compressed_size : int64 option; diff --git a/builder/index_parser.ml b/builder/index_parser.ml index 845d0e9..2c78fd9 100644 --- a/builder/index_parser.ml +++ b/builder/index_parser.ml @@ -112,9 +112,9 @@ let get_index ~downloader ~sigchecker try Some (List.assoc ("checksum", None) fields) with Not_found -> None in let revision - try int_of_string (List.assoc ("revision", None) fields) + try Rev_int (int_of_string (List.assoc ("revision", None) fields)) with - | Not_found -> 1 + | Not_found -> Rev_int 1 | Failure "int_of_string" -> eprintf (f_"%s: cannot parse 'revision' field for '%s'\n") prog n; corrupt_file () in diff --git a/builder/utils.ml b/builder/utils.ml index a6628eb..986bf68 100644 --- a/builder/utils.ml +++ b/builder/utils.ml @@ -26,5 +26,12 @@ type gpgkey_type | No_Key | Fingerprint of string | KeyFile of string +and revision + | Rev_int of int + | Rev_string of string let quote = Filename.quote + +let string_of_revision = function + | Rev_int n -> string_of_int n + | Rev_string s -> s -- 2.1.0
Pino Toscano
2015-Aug-12 13:12 UTC
[Libguestfs] [PATCH 2/2] builder: support Simple Streams v1.0 as index metadata
Add a simple YAJL<->OCaml bridge to expose the JSON parsing function, and use it to parse the JSON indexes of the Simple Streams v1.0 format. Read only datatype=image-downloads contents, and only the latest versions of each content available as disk image (disk.img or disk1.img). --- builder/Makefile.am | 13 ++- builder/builder.ml | 2 + builder/simplestreams_parser.ml | 213 +++++++++++++++++++++++++++++++++++++++ builder/simplestreams_parser.mli | 19 ++++ builder/sources.ml | 9 ++ builder/sources.mli | 1 + builder/virt-builder.pod | 7 ++ builder/yajl-c.c | 143 ++++++++++++++++++++++++++ builder/yajl.ml | 30 ++++++ builder/yajl.mli | 33 ++++++ po/POTFILES | 1 + po/POTFILES-ml | 2 + 12 files changed, 470 insertions(+), 3 deletions(-) create mode 100644 builder/simplestreams_parser.ml create mode 100644 builder/simplestreams_parser.mli create mode 100644 builder/yajl-c.c create mode 100644 builder/yajl.ml create mode 100644 builder/yajl.mli diff --git a/builder/Makefile.am b/builder/Makefile.am index f48efb0..3ab990e 100644 --- a/builder/Makefile.am +++ b/builder/Makefile.am @@ -48,7 +48,9 @@ SOURCES_MLI = \ pxzcat.mli \ setlocale.mli \ sigchecker.mli \ - sources.mli + simplestreams_parser.mli \ + sources.mli \ + yajl.mli SOURCES_ML = \ utils.ml \ @@ -57,6 +59,7 @@ SOURCES_ML = \ checksums.ml \ index.ml \ ini_reader.ml \ + yajl.ml \ paths.ml \ languages.ml \ cache.ml \ @@ -64,6 +67,7 @@ SOURCES_ML = \ downloader.ml \ sigchecker.ml \ index_parser.ml \ + simplestreams_parser.ml \ list_entries.ml \ cmdline.ml \ builder.ml @@ -81,7 +85,8 @@ SOURCES_C = \ index-parse.c \ index-parser-c.c \ pxzcat-c.c \ - setlocale-c.c + setlocale-c.c \ + yajl-c.c man_MANS noinst_DATA @@ -106,7 +111,8 @@ virt_builder_CFLAGS = \ -Wno-unused-macros \ $(LIBLZMA_CFLAGS) \ $(LIBTINFO_CFLAGS) \ - $(LIBXML2_CFLAGS) + $(LIBXML2_CFLAGS) \ + $(YAJL_CFLAGS) BOBJECTS = \ $(top_builddir)/mllib/libdir.cmo \ @@ -156,6 +162,7 @@ OCAMLCLIBS = \ $(LIBCRYPT_LIBS) \ $(LIBLZMA_LIBS) \ $(LIBXML2_LIBS) \ + $(YAJL_LIBS) \ $(LIBINTL) \ -lgnu diff --git a/builder/builder.ml b/builder/builder.ml index 260af94..5834259 100644 --- a/builder/builder.ml +++ b/builder/builder.ml @@ -182,6 +182,8 @@ let main () match source.Sources.format with | Sources.FormatNative -> Index_parser.get_index ~downloader ~sigchecker source + | Sources.FormatSimpleStreams -> + Simplestreams_parser.get_index ~downloader ~sigchecker source ) sources ) in let index = remove_duplicates index in diff --git a/builder/simplestreams_parser.ml b/builder/simplestreams_parser.ml new file mode 100644 index 0000000..afbf5b0 --- /dev/null +++ b/builder/simplestreams_parser.ml @@ -0,0 +1,213 @@ +(* virt-builder + * Copyright (C) 2015 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 Common_gettext.Gettext +open Common_utils + +open Yajl +open Utils + +open Printf + +let ensure_trailing_slash str + if String.length str > 0 && str.[String.length str - 1] <> '/' then str ^ "/" + else str + +let object_find_optional key = function + | Yajl_object o -> + (match List.filter (fun (k, _) -> k = key) (Array.to_list o) with + | [(k, v)] -> Some v + | [] -> None + | _ -> error (f_"more than value for the key '%s'") key) + | _ -> error (f_"the value of the key '%s' is not an object") key + +let object_find key yv + (match object_find_optional key yv with + | None -> error (f_"missing value for the key '%s'") key + | Some v -> v + ) + +let object_get_string key yv + (match object_find key yv with + | Yajl_string s -> s + | _ -> error (f_"the value for the key '%s' is not a string") key + ) + +let object_find_object key yv + (match object_find key yv with + | Yajl_object _ as o -> o + | _ -> error (f_"the value for the key '%s' is not an object") key + ) + +let object_find_objects fn = function + | Yajl_object o -> filter_map fn (Array.to_list o) + | _ -> error (f_"the value is not an object") + +let object_get_object key yv + (match object_find_object key yv with + | Yajl_object o -> o + | _ -> assert false (* object_find_object already errors out. *) + ) + +let object_get_number key yv + (match object_find key yv with + | Yajl_number n -> n + | Yajl_double d -> Int64.of_float d + | _ -> error (f_"the value for the key '%s' is not an integer") key + ) + +let objects_get_string key yvs + let rec loop = function + | [] -> None + | x :: xs -> + (match object_find_optional key x with + | Some (Yajl_string s) -> Some s + | Some _ -> error (f_"the value for key '%s' is not a string as expected") key + | None -> loop xs + ) + in + match loop yvs with + | Some s -> s + | None -> error (f_"the key '%s' was not found in a list of objects") key + +let get_index ~downloader ~sigchecker + { Sources.uri = uri; proxy = proxy } + + let uri = ensure_trailing_slash uri in + + let download_and_parse uri + let tmpfile, delete_tmpfile = Downloader.download downloader ~proxy uri in + if delete_tmpfile then + unlink_on_exit tmpfile; + let file + if Sigchecker.verifying_signatures sigchecker then ( + let tmpunsigned + Sigchecker.verify_and_remove_signature sigchecker tmpfile in + match tmpunsigned with + | None -> assert false (* only when not verifying signatures *) + | Some f -> f + ) else + tmpfile in + yajl_tree_parse (read_whole_file file) in + + let downloads + let uri_index + if Sigchecker.verifying_signatures sigchecker then + uri ^ "streams/v1/index.sjson" + else + uri ^ "streams/v1/index.json" in + let tree = download_and_parse uri_index in + + let format = object_get_string "format" tree in + if format <> "index:1.0" then + error (f_"%s is not a Simple Streams (index) v1.0 JSON file (format: %s)") + uri format; + + let index = Array.to_list (object_get_object "index" tree) in + filter_map ( + fun (_, desc) -> + let format = object_get_string "format" desc in + let datatype = object_get_string "datatype" desc in + match format, datatype with + | "products:1.0", "image-downloads" -> + Some (object_get_string "path" desc) + | _ -> None + ) index in + + let scan_product_list path + let tree = download_and_parse (uri ^ path) in + + let format = object_get_string "format" tree in + if format <> "products:1.0" then + error (f_"%s is not a Simple Streams (products) v1.0 JSON file (format: %s)") + uri format; + + let products_node = object_get_object "products" tree in + + let products = Array.to_list products_node in + filter_map ( + fun (prod, prod_desc) -> + let arch + (* XXX *) + match object_get_string "arch" prod_desc with + | "amd64" -> "x86_64" + | a -> a in + let prods = Array.to_list (object_get_object "versions" prod_desc) in + let prods = filter_map ( + fun (rel, rel_desc) -> + let pubname = objects_get_string "pubname" [rel_desc; prod_desc] in + let items = object_find_object "items" rel_desc in + let disk_items = object_find_objects ( + function + | (("disk.img"|"disk1.img"), v) -> Some v + | _ -> None + ) items in + (match disk_items with + | [] -> None + | disk_item :: _ -> + let printable_name = Some pubname in + let file_uri = uri ^ (object_get_string "path" disk_item) in + let checksums + let checksums = object_find_objects ( + function + | ("sha256", Yajl_string c) -> Some (Checksums.SHA256 c) + | ("sha512", Yajl_string c) -> Some (Checksums.SHA512 c) + | _ -> None + ) disk_item in + match checksums with + | [] -> None + | x -> Some x in + let revision = Rev_string rel in + let size = object_get_number "size" disk_item in + let aliases = Some [pubname;] in + + let entry = { Index.printable_name = printable_name; + osinfo = None; + file_uri = file_uri; + arch = arch; + signature_uri = None; + checksums = checksums; + revision = revision; + format = None; + size = size; + compressed_size = None; + expand = None; + lvexpand = None; + notes = []; + hidden = false; + aliases = aliases; + sigchecker = sigchecker; + proxy = proxy; } in + Some (rel, (prod, entry)) + ) + ) prods in + (* Select the disk image with the bigger version (i.e. usually + * the most recent one. *) + let reverse_revision_compare (rev1, _) (rev2, _) = compare rev2 rev1 in + let prods = List.sort reverse_revision_compare prods in + match prods with + | [] -> None + | (_, entry) :: _ -> Some entry + ) products in + + let entries = List.flatten (List.map scan_product_list downloads) in + if verbose () then ( + printf "simplestreams tree (%s) after parsing:\n" uri; + List.iter (Index.print_entry Pervasives.stdout) entries + ); + entries diff --git a/builder/simplestreams_parser.mli b/builder/simplestreams_parser.mli new file mode 100644 index 0000000..a4b91ba --- /dev/null +++ b/builder/simplestreams_parser.mli @@ -0,0 +1,19 @@ +(* virt-builder + * Copyright (C) 2015 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 get_index : downloader:Downloader.t -> sigchecker:Sigchecker.t -> Sources.source -> Index.index diff --git a/builder/sources.ml b/builder/sources.ml index b21e8fc..149db6f 100644 --- a/builder/sources.ml +++ b/builder/sources.ml @@ -31,6 +31,7 @@ type source = { } and source_format | FormatNative +| FormatSimpleStreams module StringSet = Set.Make (String) @@ -82,6 +83,14 @@ let parse_conf file try (match (List.assoc ("format", None) fields) with | "native" | "" -> FormatNative + | "simplestreams" as fmt -> + if not (Yajl.yajl_is_available ()) then ( + if verbose () then ( + eprintf (f_"%s: repository type '%s' not supported (missing YAJL support), skipping it\n") prog fmt; + ); + invalid_arg fmt + ) else + FormatSimpleStreams | fmt -> if verbose () then ( eprintf (f_"%s: unknown repository type '%s' in %s, skipping it\n") prog fmt file; diff --git a/builder/sources.mli b/builder/sources.mli index e861310..e621a9f 100644 --- a/builder/sources.mli +++ b/builder/sources.mli @@ -25,5 +25,6 @@ type source = { } and source_format | FormatNative +| FormatSimpleStreams val read_sources : unit -> source list diff --git a/builder/virt-builder.pod b/builder/virt-builder.pod index fc49d4d..0de643a 100644 --- a/builder/virt-builder.pod +++ b/builder/virt-builder.pod @@ -1181,6 +1181,13 @@ The possible values are: The native format of the C<virt-builder> repository. See also L</Creating and signing the index file> below. +=item B<simplestreams> + +The URI represents the root of a Simple Streams v1.0 tree of metadata. + +For more information about Simple Streams, see also +L<https://launchpad.net/simplestreams>. + =back If not present, the assumed value is C<native>. diff --git a/builder/yajl-c.c b/builder/yajl-c.c new file mode 100644 index 0000000..6a96d59 --- /dev/null +++ b/builder/yajl-c.c @@ -0,0 +1,143 @@ +/* virt-builder + * Copyright (C) 2015 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. + */ + +#include <config.h> + +#include <caml/alloc.h> +#include <caml/fail.h> +#include <caml/memory.h> +#include <caml/mlvalues.h> + +#if HAVE_YAJL +#include <yajl/yajl_tree.h> +#endif + +#include <stdio.h> +#include <string.h> + +#define Val_none (Val_int (0)) + +extern value virt_builder_yajl_is_available (value unit); +extern value virt_builder_yajl_tree_parse (value stringv); + +#if HAVE_YAJL +static value +convert_yajl_value (yajl_val val, int level) +{ + CAMLparam0 (); + CAMLlocal4 (rv, lv, v, sv); + + if (level > 20) + caml_invalid_argument ("too many levels of object/array nesting"); + + if (YAJL_IS_OBJECT (val)) { + size_t len = YAJL_GET_OBJECT(val)->len; + size_t i; + rv = caml_alloc (1, 3); + lv = caml_alloc_tuple (len); + for (i = 0; i < len; ++i) { + v = caml_alloc_tuple (2); + sv = caml_copy_string (YAJL_GET_OBJECT(val)->keys[i]); + Store_field (v, 0, sv); + sv = convert_yajl_value (YAJL_GET_OBJECT(val)->values[i], level + 1); + Store_field (v, 1, sv); + Store_field (lv, i, v); + } + Store_field (rv, 0, lv); + } else if (YAJL_IS_ARRAY (val)) { + size_t len = YAJL_GET_ARRAY(val)->len; + size_t i; + rv = caml_alloc (1, 4); + lv = caml_alloc_tuple (len); + for (i = 0; i < len; ++i) { + v = convert_yajl_value (YAJL_GET_ARRAY(val)->values[i], level + 1); + Store_field (lv, i, v); + } + Store_field (rv, 0, lv); + } else if (YAJL_IS_STRING (val)) { + rv = caml_alloc (1, 0); + v = caml_copy_string (YAJL_GET_STRING(val)); + Store_field (rv, 0, v); + } else if (YAJL_IS_DOUBLE (val)) { + rv = caml_alloc (1, 2); + lv = caml_alloc_tuple (1); + Store_double_field (lv, 0, YAJL_GET_DOUBLE(val)); + Store_field (rv, 0, lv); + } else if (YAJL_IS_INTEGER (val)) { + rv = caml_alloc (1, 1); + v = caml_copy_int64 (YAJL_GET_INTEGER(val)); + Store_field (rv, 0, v); + } else if (YAJL_IS_TRUE (val)) { + rv = caml_alloc (1, 5); + Store_field (rv, 0, Val_true); + } else if (YAJL_IS_FALSE (val)) { + rv = caml_alloc (1, 5); + Store_field (rv, 0, Val_false); + } else + rv = Val_none; + + CAMLreturn (rv); +} + +value +virt_builder_yajl_is_available (value unit) +{ + /* NB: noalloc */ + return Val_true; +} + +value +virt_builder_yajl_tree_parse (value stringv) +{ + CAMLparam1 (stringv); + CAMLlocal1 (rv); + yajl_val tree; + char error_buf[256]; + + tree = yajl_tree_parse (String_val (stringv), error_buf, sizeof error_buf); + if (tree == NULL) { + char buf[256 + sizeof error_buf]; + if (strlen (error_buf) > 0) + snprintf (buf, sizeof buf, "JSON parse error: %s", error_buf); + else + snprintf (buf, sizeof buf, "unknown JSON parse error"); + caml_invalid_argument (buf); + } + + rv = convert_yajl_value (tree, 1); + yajl_tree_free (tree); + + CAMLreturn (rv); +} + +#else + +value +virt_builder_yajl_is_available (value unit) +{ + /* NB: noalloc */ + return Val_false; +} + +value +virt_builder_yajl_tree_parse (value stringv) +{ + caml_invalid_argument ("virt-builder was compiled without yajl support"); +} + +#endif diff --git a/builder/yajl.ml b/builder/yajl.ml new file mode 100644 index 0000000..f2d5c2b --- /dev/null +++ b/builder/yajl.ml @@ -0,0 +1,30 @@ +(* virt-builder + * Copyright (C) 2015 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. + *) + +type yajl_val +| Yajl_null +| Yajl_string of string +| Yajl_number of int64 +| Yajl_double of float +| Yajl_object of (string * yajl_val) array +| Yajl_array of yajl_val array +| Yajl_bool of bool + +external yajl_is_available : unit -> bool = "virt_builder_yajl_is_available" "noalloc" + +external yajl_tree_parse : string -> yajl_val = "virt_builder_yajl_tree_parse" diff --git a/builder/yajl.mli b/builder/yajl.mli new file mode 100644 index 0000000..aaa9389 --- /dev/null +++ b/builder/yajl.mli @@ -0,0 +1,33 @@ +(* virt-builder + * Copyright (C) 2015 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. + *) + +type yajl_val +| Yajl_null +| Yajl_string of string +| Yajl_number of int64 +| Yajl_double of float +| Yajl_object of (string * yajl_val) array +| Yajl_array of yajl_val array +| Yajl_bool of bool + +val yajl_is_available : unit -> bool +(** Is YAJL built in? If not, calling any of the other yajl_* + functions will result in an error. *) + +val yajl_tree_parse : string -> yajl_val +(** Parse the JSON string. *) diff --git a/po/POTFILES b/po/POTFILES index 7f1580c..8fb68a4 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -6,6 +6,7 @@ builder/index-struct.c builder/index-validate.c builder/pxzcat-c.c builder/setlocale-c.c +builder/yajl-c.c cat/cat.c cat/filesystems.c cat/log.c diff --git a/po/POTFILES-ml b/po/POTFILES-ml index 7933c8e..87f10ee 100644 --- a/po/POTFILES-ml +++ b/po/POTFILES-ml @@ -12,8 +12,10 @@ builder/paths.ml builder/pxzcat.ml builder/setlocale.ml builder/sigchecker.ml +builder/simplestreams_parser.ml builder/sources.ml builder/utils.ml +builder/yajl.ml customize/crypt.ml customize/customize_cmdline.ml customize/customize_main.ml -- 2.1.0
Richard W.M. Jones
2015-Aug-12 16:08 UTC
Re: [Libguestfs] [PATCH 1/2] builder: add non-int revisions
On Wed, Aug 12, 2015 at 03:12:21PM +0200, Pino Toscano wrote:> Add support for non-integer revisions of entries, comparing them as > integer when possible.Have you got an example of revisions? It may be possible to use Common_utils.compare_version which does RPM revision-style comparison. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://people.redhat.com/~rjones/virt-df/
Richard W.M. Jones
2015-Aug-12 16:28 UTC
Re: [Libguestfs] [PATCH 2/2] builder: support Simple Streams v1.0 as index metadata
On Wed, Aug 12, 2015 at 03:12:22PM +0200, Pino Toscano wrote:> +let object_find key yv > + (match object_find_optional key yv with > + | None -> error (f_"missing value for the key '%s'") key > + | Some v -> v > + )You don't need the extra parentheses around the match expr. Could the yajl bindings be added in a separate patch? Also be nice to have a little unit test for the yajl bindings. I wasn't able to spot any bugs in the bindings. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://people.redhat.com/~rjones/virt-top
Possibly Parallel Threads
- [PATCH v3 1/4] mltools: Rename Yajl module as JSON_parser and move to common/mltools.
- [PATCH 1/5] builder: extract Yajl helper functions to yajl.ml
- [PATCH 4/4] builder: support Simple Streams v1.0 as index metadata
- [PATCH 4/4] mltools: JSON: unify JSON_parser type with JSON.json_t.
- [PATCH 0/2 v2] RFC: builder: support for Simple Streams metadata