[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


  • To: xen-api@xxxxxxxxxxxxxxxxxxx
  • From: Zheng Li <dev@xxxxxxxx>
  • Date: Tue, 04 May 2010 06:23:30 -0000
  • Delivery-date: Mon, 03 May 2010 19:06:51 -0700
  • List-id: Discussion of API issues surrounding Xen <xen-api.lists.xensource.com>

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
Description: Text Data

_______________________________________________
xen-api mailing list
xen-api@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/mailman/listinfo/xen-api

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.