[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-API] [PATCH] A few bugfixes and a few minor improvements to current xe cmdline tool implementation
Bugfixes: * xe command line options doesn't mix well with XE_EXTRA_ARGS varialbe. E.g. setting XE_EXTRA_ARGS to "username=xxxx,password=yyyy" (or any non-nil valid configuration) and calling "xe -s <some server> vm-list" will break xe. Note that this is a common user case in a cluster-like environment where all the machines have the same user/passwd config, where one can conveniently set user/passwd in XE_EXTRA_ARGS for once and connect to different servers by only specifying different "-s" arguments in the cmdline. * Setting "compat=true" in xe's RC file won't work. E.g. xe vm-clone vm-name=<vm name> new-name=<new vm name> with "compat=true" in ~/.xe won't work (but with "compat=true" in XE_EXTRA_ARGS or in xe cmdline will work). * Setting a password with comma via XE_EXTRA_ARGS will break the logic. After the fix, it's possible to specify that by using backslash to escape the comma (e.g. password=pass\,word) * clean up the options handling logic, so that cmdline options, RC file setting and XE_EXTRA_ARGS variable can mix consistently even in some corner cases and follow the natural priority: cmdline option > XE_EXTRA_ARGS > ~/.xe RC > default settings Improvements: * change options "-debug" and "-debug-on-fail" to "--debug" and "--debug-on-fail", so that every command line option now follows the common naming convention of -shortcut v.s.--full-name (with the only standard exception of having both "-help" and "--help"). AFAICS, both debug options are (maybe deliberately) not documented in the manual, so changing the names might not be a big issue regarding compatibilities. * complete the pair relation between command line options and RC/environment variables. There were some missings from either side: e.g. "compat=xxxx" has no "--compat" correspondence and "--debug"("--debug-on-fail") has not "debug=xxxx" in par. Signed-off-by: Zheng Li <dev@xxxxxxxx> ocaml/xe-cli/newcli.ml | 577 +++++++++++++++++++++++++++------------------------- 1 files changed, 299 insertions(+), 278 deletions(-) diff -r abc48d958c40 -r ad5ea8e64ad2 ocaml/xe-cli/newcli.ml --- a/ocaml/xe-cli/newcli.ml Fri Apr 23 19:30:04 2010 +0100 +++ b/ocaml/xe-cli/newcli.ml Tue May 04 06:22:08 2010 +0100 @@ -15,33 +15,34 @@ open Stringext open Cli_protocol -(* Need to know about the host and port to know who to connect to *) -(* Strictly, we don't need to know the username and password, but I want to be able *) -(* to make a .xe file containing defaults, so we'll pull them out of Sys.argv anyway *) +(* Param config priorities: + explicit cmd option > XE_XXX env variable > ~/.xe rc file > default +*) -(* cmdline options override .xe options override these *) let xapiserver = ref "127.0.0.1" let xapiuname = ref "root" let xapipword = ref "null" let xapicompatmode = ref false let xapipasswordfile = ref "" let xapicompathost = ref "127.0.0.1" - -let usessl = ref true -let stunnel_process = ref None -let xapiport = ref None +let xapiport = ref None let get_xapiport ssl = match !xapiport with - None -> if ssl then 443 else 80 - | Some p -> p + None -> if ssl then 443 else 80 + | Some p -> p +let xeusessl = ref true +let xedebug = ref false +let xedebugonfail = ref false + +let stunnel_process = ref None let debug_channel = ref None let debug_file = ref None let error fmt = Printf.fprintf stderr fmt -let debug fmt = - let printer s = match !debug_channel with - | Some c -> output_string c s +let debug fmt = + let printer s = match !debug_channel with + | Some c -> output_string c s | None -> () in Printf.kprintf printer fmt @@ -49,12 +50,12 @@ exception Usage let usage () = - if !xapicompatmode + if !xapicompatmode then begin error "COMPATABILITY MODE\n"; error "Usage: %s <cmd> [-h server] [-p port] ([-u username] [-pw password] or [-pwf <password file>]) <other arguments>\n" Sys.argv.(0); - error "\nA full list of commands can be obtained by running \n\t%s help -s <server> -p <port>\n" Sys.argv.(0) + error "\nA full list of commands can be obtained by running \n\t%s help -h <server> -p <port>\n" Sys.argv.(0) end else begin @@ -68,36 +69,36 @@ exception Http_parse_failure let hdrs = ["content-length"; "cookie"; "connection"; "transfer-encoding"; "authorization"; "location"] - + let end_of_string s from = String.sub s from ((String.length s)-from) - + let strip_cr r = if String.length r=0 then raise Http_parse_failure; let last_char = String.sub r ((String.length r)-1) 1 in if last_char <> "\r" then raise Http_parse_failure; String.sub r 0 ((String.length r)-1) - + let rec read_rest_of_headers ic = try let r = input_line ic in let r = strip_cr r in if r="" then [] else begin - debug "read '%s'\n" r; - let hdr = List.find (fun s -> String.startswith (s^": ") (String.lowercase r)) hdrs in - let value = end_of_string r (String.length hdr + 2) in - (hdr,value)::read_rest_of_headers ic + debug "read '%s'\n" r; + let hdr = List.find (fun s -> String.startswith (s^": ") (String.lowercase r)) hdrs in + let value = end_of_string r (String.length hdr + 2) in + (hdr,value)::read_rest_of_headers ic end with - | Not_found -> read_rest_of_headers ic - | _ -> [] - + | Not_found -> read_rest_of_headers ic + | _ -> [] + let parse_url url = if String.startswith "https://" url then let stripped = end_of_string url (String.length "https://") in - let host, rest = + let host, rest = let l = String.split '/' stripped in List.hd l, List.tl l in (host,"/" ^ (String.concat "/" rest)) @@ -120,7 +121,7 @@ exit 1 -let parse_port (x: string) = +let parse_port (x: string) = try let p = int_of_string x in if p < 0 || p > 65535 then failwith "illegal"; @@ -131,104 +132,135 @@ (* Extract the arguments we're interested in. Return a list of the argumets we know *) (* nothing about. These will get passed straight into the server *) -let parse_args args = - +let parse_args = + (* Set the key to the value. Return whether the key is one we know about *) (* compat mode is special as the argument is passed in two places. Once *) - (* at the top of the message to the cli server in order to indicate that *) + (* at the top of the message to the cli server in order to indicate that *) (* we need to use 'geneva style' parsing - that is, allow key = value as *) (* opposed to key=value. Secondly, the key then gets passed along with *) (* all the others to the operations. So we need to register it's there, *) (* but not strip it *) + + let reserve_args = ref [] in + let set_keyword (k,v) = - match k with - "server" -> xapiserver := v; true - | "port" -> xapiport := Some (parse_port v); true - | "username" -> xapiuname := v; true - | "password" -> xapipword := v; true - | "passwordfile" -> xapipasswordfile := v; true - | "nossl" -> usessl := not(bool_of_string v); true - | "compat" -> xapicompatmode := (try (bool_of_string v) with _ -> false); false (* dont strip it! *) - | _ -> false - in + try + (match k with + | "server" -> xapiserver := v + | "port" -> xapiport := Some (parse_port v) + | "username" -> xapiuname := v + | "password" -> xapipword := v + | "passwordfile" -> xapipasswordfile := v + | "nossl" -> xeusessl := not(bool_of_string v) + | "compat" -> + xapicompatmode := (try (bool_of_string v) with _ -> false); + reserve_args := (k ^ "=" ^ v) :: !reserve_args + | "debug" -> xedebug := (try bool_of_string v with _ -> false) + | "debugonfail" -> xedebugonfail := (try bool_of_string v with _ -> false) + | _ -> raise Not_found); + true + with Not_found -> false in - let rec doit args = + let parse_opt args = match args with - | "--help"::_ - | "-help"::_ -> - raise Usage - | "-s"::server::xs -> - xapiserver := server; - doit xs - | "-p"::port::xs -> - xapiport := Some (parse_port port); - doit xs - | "-u"::uname::xs -> - xapiuname := uname; - doit xs - | "-pw"::pw::xs -> - xapipword := pw; - doit xs - | "--nossl"::xs -> - usessl := false; - doit xs - | "-pwf"::pwf::xs -> - xapipasswordfile := pwf; - doit xs - | "-h"::h::xs -> - xapicompathost := h; - doit xs - | x::xs -> - (* we eat cmdline params if we know about them *) - let eatit = - begin - try - let eq = String.index x '=' in - let k = String.sub x 0 eq in - let v = String.sub x (eq+1) (String.length x - (eq+1)) in - set_keyword (k,v) - with _ -> false - end - in - if eatit then doit xs else x::(doit xs) - | _ -> [] - in + | "-s" :: server :: xs -> Some ("server", server, xs) + | "-p" :: port :: xs -> Some("port", port, xs) + | "-u" :: uname :: xs -> Some("username", uname, xs) + | "-pw" :: pw :: xs -> Some("password", pw, xs) + | "-pwf" :: pwf :: xs -> Some("passwordfile", pwf, xs) + | "--nossl" :: xs -> Some("nossl", "true", xs) + | "--compat" :: xs -> Some("compat", "true", xs) + | "--debug" :: xs -> Some("debug", "true", xs) + | "--debug-on-fail" :: xs -> Some("debugonfail", "true", xs) + | "-h" :: h :: xs -> Some("server", h, xs) + | _ -> None in - let defaults = Options.read_rc () in - ignore (List.map set_keyword defaults); (* Defaults from the fil ~/.xe *) - let newargs = doit args in - (if !xapipasswordfile <> "" then read_pwf ()); - (if !xapicompatmode then xapiserver := !xapicompathost); - newargs + let parse_eql arg = + try + let eq = String.index arg '=' in + let k = String.sub arg 0 eq in + let v = String.sub arg (eq+1) (String.length arg - (eq+1)) in + Some (k,v) + with _ -> None in -let open_tcp_ssl server = + let rec process_args = function + | [] -> [] + | args -> + match parse_opt args with + | Some(k, v, rest) -> + if set_keyword(k, v) then process_args rest else process_eql args + | None -> + process_eql args + and process_eql = function + | [] -> [] + | arg :: args -> + match parse_eql arg with + | Some(k, v) when set_keyword(k,v) -> process_args args + | _ -> arg :: process_args args in + + fun args -> + let rcs = Options.read_rc() in + let rcs_rest = + List.map (fun (k,v) -> k^"="^v) + (List.filter (fun (k, v) -> not (set_keyword (k,v))) rcs) in + let extras = + let extra_args = try Sys.getenv "XE_EXTRA_ARGS" with Not_found -> "" in + let l = ref [] and pos = ref 0 and i = ref 0 in + while !pos < String.length extra_args do + if extra_args.[!pos] = ',' then (incr pos; i := !pos) + else + if !i >= String.length extra_args + || extra_args.[!i] = ',' && extra_args.[!i-1] <> '\\' then + (let seg = String.sub extra_args !pos (!i - !pos) in + l := String.filter_chars seg ((<>) '\\') :: !l; + incr i; pos := !i) + else incr i + done; + List.rev !l in + let extras_rest = process_args extras in + let help = ref false in + let args' = List.filter (fun s -> s<>"-help" && s <> "--help") args in + if List.length args' < List.length args then help := true; + let args_rest = process_args args in + if !help then raise Usage; + let () = + if !xapipasswordfile <> "" then read_pwf (); + if !xedebug then debug_channel := Some stderr; + if !xedebugonfail then begin + let tmpfile, tmpch = Filename.open_temp_file "xe_debug" "tmp" in + debug_file := Some tmpfile; + debug_channel := Some tmpch + end in + args_rest @ extras_rest @ rcs_rest @ !reserve_args + +let open_tcp_ssl server = let port = get_xapiport true in debug "Connecting via stunnel to [%s] port [%d]\n%!" server port; (* We don't bother closing fds since this requires our close_and_exec wrapper *) - let x = Stunnel.connect ~use_external_fd_wrapper:false - ~write_to_log:(fun x -> debug "stunnel: %s\n%!" x) + let x = Stunnel.connect ~use_external_fd_wrapper:false + ~write_to_log:(fun x -> debug "stunnel: %s\n%!" x) ~extended_diagnosis:(!debug_file <> None) server port in if !stunnel_process = None then stunnel_process := Some x; Unix.in_channel_of_descr x.Stunnel.fd, Unix.out_channel_of_descr x.Stunnel.fd ( ...... 455 lines left ...... ) Attachment:
xen-api.patch _______________________________________________ xen-api mailing list xen-api@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/mailman/listinfo/xen-api
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |