]> xenbits.xensource.com Git - xcp/xen-api.git/commitdiff
CP-2137: Switch to the new xapi<->v6d RPC interface
authorRob Hoes <rob.hoes@citrix.com>
Wed, 26 Jan 2011 17:39:04 +0000 (17:39 +0000)
committerRob Hoes <rob.hoes@citrix.com>
Wed, 26 Jan 2011 17:39:04 +0000 (17:39 +0000)
Signed-off-by: Rob Hoes <rob.hoes@citrix.com>
37 files changed:
ocaml/idl/api_errors.ml
ocaml/idl/datamodel.ml
ocaml/idl/ocaml_backend/OMakefile
ocaml/license/OMakefile
ocaml/license/edition.ml
ocaml/license/edition.mli
ocaml/license/fakev6.ml
ocaml/license/fakev6.mli
ocaml/license/grace_retry.ml [deleted file]
ocaml/license/license.ml
ocaml/license/license.mli
ocaml/license/license_file.ml
ocaml/license/license_file.mli
ocaml/license/license_init.ml
ocaml/license/v6alert.ml [deleted file]
ocaml/license/v6alert.mli [deleted file]
ocaml/license/v6client.ml
ocaml/license/v6client.mli
ocaml/license/v6daemon.ml
ocaml/license/v6errors.ml [new file with mode: 0644]
ocaml/license/v6rpc.ml
ocaml/license/v6rpc.mli
ocaml/xapi/OMakefile
ocaml/xapi/cli_frontend.ml
ocaml/xapi/cli_operations.ml
ocaml/xapi/create_misc.ml
ocaml/xapi/features.ml
ocaml/xapi/features.mli
ocaml/xapi/license_check.ml
ocaml/xapi/license_check.mli
ocaml/xapi/xapi.ml
ocaml/xapi/xapi_config.ml
ocaml/xapi/xapi_globs.ml
ocaml/xapi/xapi_ha.ml
ocaml/xapi/xapi_host.ml
ocaml/xapi/xapi_host.mli
ocaml/xapi/xapi_pool.ml

index 6690b79278d52c1453e282c3ca9f6f1ed2626ba8..189485777792d191e4c45a77aed3298ebe12147b 100644 (file)
@@ -294,6 +294,7 @@ let license_processing_error = "LICENSE_PROCESSING_ERROR"
 let license_cannot_downgrade_in_pool = "LICENSE_CANNOT_DOWNGRADE_WHILE_IN_POOL"
 let license_does_not_support_xha = "LICENSE_DOES_NOT_SUPPORT_XHA"
 
+let v6d_failure = "V6D_FAILURE"
 let invalid_edition = "INVALID_EDITION"
 let missing_connection_details = "MISSING_CONNECTION_DETAILS"
 let license_checkout_error = "LICENSE_CHECKOUT_ERROR"
index 81c77b7d53e07f5388b9bc5b3976722e4fcc8a74..a1d2b504cbdfbfd640e200e61908c95971fcfd6b 100644 (file)
@@ -326,8 +326,10 @@ let _ =
   error Api_errors.license_does_not_support_xha []
     ~doc:"XHA cannot be enabled because this host's license does not allow it" ();
 
+  error Api_errors.v6d_failure []
+    ~doc:"There was a problem with the license daemon (v6d). Is it running?" ();
   error Api_errors.invalid_edition ["edition"]
-    ~doc:"The edition you supplied is invalid. Valid editions are 'free', 'enterprise' and 'platinum'." ();
+    ~doc:"The edition you supplied is invalid." ();
   error Api_errors.missing_connection_details []
     ~doc:"The license-server connection details (address or port) were missing or incomplete." ();
   error Api_errors.license_checkout_error ["reason"]
@@ -3613,7 +3615,7 @@ let host_set_license_params = call
 let host_apply_edition = call ~flags:[`Session]
   ~name:"apply_edition"
   ~in_product_since:rel_midnight_ride
-  ~doc:"Change to another edition, or reactivate it after a license has expired. Possible editions are 'free', 'enterprise', and 'platinum'. Upgrading from free to enterprise or platinum is subject to the successful checkout of an appropriate license from the license server."
+  ~doc:"Change to another edition, or reactivate the current edition after a license has expired. This may be subject to the successful checkout of an appropriate license."
   ~params:[ 
     Ref _host, "host", "The host";
     String, "edition", "The requested edition"
index 0b116d890888adc14c329e0f1c94f0e2f476700b..29a0771fe557f0ca3bbb25486d523aaf81444c38 100644 (file)
@@ -112,9 +112,11 @@ XAPI_CLIENT_OBJS = \
        ../../util/util_inventory \
        ../../util/version \
        ../../xapi/xapi_inventory \
+       ../../xapi/xapi_fist \
        ../../xapi/features \
        ../../license/v6rpc \
        ../../license/v6daemon \
+       ../../license/v6errors \
        $(COMMON_OBJS) \
        $(CLIENT_OBJS)
 
@@ -156,7 +158,6 @@ lib-install: META
        mkdir -p $(INSTALL_PATH)
        ocamlfind install -destdir $(INSTALL_PATH) -ldconf ignore xapi-client META $(addsuffixes .cmi, $(XAPI_CLIENT_OBJS)) $(if $(BYTE_ENABLED), xapi_client.cma) $(if $(NATIVE_ENABLED), xapi_client.cmxa xapi_client.a $(addsuffixes .cmx, $(XAPI_CLIENT_OBJS)))
 
-
 .PHONY: lib-uninstall
 lib-uninstall:
        ocamlfind remove -destdir $(INSTALL_PATH) xapi-client
index 11bcbe452b1c54ca297f8f454b263a80977e2e79..5e03691262076641e2d6dc3b8e13f1c6b6790a7d 100644 (file)
@@ -2,12 +2,14 @@ OCAML_LIBS    = ../util/version ../idl/ocaml_backend/xapi_client
 OCAMLINCLUDES = ../idl/ocaml_backend ../idl ../autogen ../xapi ../gpg ../util
 OCAMLPACKS    = xml-light2 stdext stunnel http-svr log rpc-light
 
-UseCamlp4(rpc-light.syntax, v6rpc)
+UseCamlp4(rpc-light.syntax, v6rpc v6errors)
 
 V6FILES = \
        fakev6 \
        v6rpc \
-       v6daemon
+       v6errors \
+       v6daemon \
+       edition
 
 # Name of daemon to install in dom0:
 V6D = v6testd
index f048706e2e6e780fde2cd33b722d41d4f5929e52..d65d6177e8792bd4c8c20a09e5b1d4e66f5ce79e 100644 (file)
@@ -36,11 +36,11 @@ let to_marketing_name = function
 let to_features = function
        | Free -> all_features
 
-let edition_to_int = function
+let to_int = function
        | Free | _ -> 0
 
 let equal e0 e1 =
-       edition_to_int e0 = edition_to_int e1
+       to_int e0 = to_int e1
        
 let min l =
        Free
index c2106732883e1864f8afa7cce73c5d551e252b7c..68c2baefb52dd657587de444e316d028819211c1 100644 (file)
@@ -37,6 +37,9 @@ val to_marketing_name : edition -> string
 (** Get the list of {!feature}s enabled for a given {!edition}. *)
 val to_features : edition -> Features.feature list
 
+(** Provides a total order. *)
+val to_int : edition -> int
+
 (** Compare two editions for equality (used before pool join). *)
 val equal : edition -> edition -> bool
 
index ea102fd35da807b5c66fbe048fccf1d74ac9eb14..17c8dd79a41f0444be689847b95197e5f2b820d8 100644 (file)
 module D=Debug.Debugger(struct let name="v6api" end)
 open D
 
-let initialise address port edition =
-       ("real", Int32.of_int (-1))
+let supported_editions = [Edition.Free]
+
+let apply_edition edition additional_params =
+       let edition' = Edition.of_string edition in
+       if List.mem edition' supported_editions then
+               edition, Edition.to_features edition', []
+       else
+               failwith "unknown edition"
                
-let shutdown () =
-       debug "shutdown";
-       true
+let get_editions () =
+       List.map (fun e -> Edition.to_string e, Edition.to_marketing_name e,
+               Edition.to_short_string e, Edition.to_int e) supported_editions
+
+let get_version () =
+       ""
 
 let reopen_logs () =
        try
@@ -29,4 +38,4 @@ let reopen_logs () =
                debug "Logfiles reopened";
                true
        with _ -> false
-         
+
index 179b483c40956ca0403a19c4b5aed462cf331a00..b4137e48cb7e5427155718f46d3e3d73cc1e7d92 100644 (file)
 (** An example implementation of a licensing service which always returns "real" 
     licenses that never expire. *)
 
-val initialise : string -> int32 -> string -> string * int32
 (** Obtain a license *)
-val shutdown : unit -> bool
+val apply_edition : string -> (string * string) list ->
+       string * Features.feature list * (string * string) list
+
 (** Release the license *)
-val reopen_logs : unit -> bool
+val get_editions : unit -> (string * string * string * int) list
+
+val get_version : unit -> string
+
 (** Close and re-open the log file *)
+val reopen_logs : unit -> bool
 
diff --git a/ocaml/license/grace_retry.ml b/ocaml/license/grace_retry.ml
deleted file mode 100644 (file)
index 7882843..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-(*
- * Copyright (C) 2006-2009 Citrix Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * 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 Lesser General Public License for more details.
- *)
-(** Helper to keep trying to get a "real" license after a "grace" license was checked out.
- *  @group Licensing *)
-open Client
-
-let event_name = "retry after obtaining grace license"
-let schedule = Xapi_periodic_scheduler.OneShot
-
-(** Schedule a timer to call [Host.apply_edition] again after an hour. Call this
- *  after getting a "grace" license in order to check whether the license server
- *  happened to come back. If so, a "real" license will be checked out.
- *  Note: the LPE already does a similar thing, but does not notify the product (xapi)
- *  if it succeeds to check out a "real" license! *)
-let retry_periodically host edition =
-       let period =
-               if Xapi_fist.reduce_grace_retry_period () then
-                       300.    (* 5min *)
-               else
-                       3600.   (* 1h *)
-       in
-       let retry_fn () = 
-               let now = (Unix.gettimeofday ()) in
-               Server_helpers.exec_with_new_task "grace_retry"
-                       (fun __context ->
-                               Helpers.call_api_functions ~__context (fun rpc session_id ->
-                                       (* Retry checkout *)
-                                       Client.Host.apply_edition rpc session_id host edition;
-                                       (* Remove any newly generated grace alerts *)
-                                       let alerts = Client.Message.get_since rpc session_id (Date.of_float now) in
-                                       let check_and_maybe_remove (ref, msg) =
-                                               if msg.API.message_name = "GRACE_LICENSE" then
-                                                       Helpers.call_api_functions ~__context
-                                                               (fun rpc session_id -> Client.Message.destroy rpc session_id ref)
-                                       in
-                                       List.iter check_and_maybe_remove alerts
-                               )
-                       )
-       in
-       Xapi_periodic_scheduler.add_to_queue event_name schedule period retry_fn
-       
-let cancel () =
-       Xapi_periodic_scheduler.remove_from_queue event_name
index 078ac055f58b6fb2d91992e213398ecd3f789e74..c5dadbc110b9d980ead150dba48cc7c4b811ccc5 100644 (file)
@@ -150,8 +150,6 @@ let default () =
       sku_marketing_name = Edition.to_marketing_name edition;
     }
 
-let license : license ref = ref (default ())
-
 (* Calls to obtain info about license *)
 
 let check_expiry l = 
index 35848b07513129ca92ff02d1d3a8e8730b84cfc6..33971c19d4a372ceb89b5a4f7b9a3e310af119ba 100644 (file)
@@ -39,9 +39,6 @@ type license =
                sku_marketing_name : string;    (** Official marketing name of the license *)
        }
 
-(** The current license on this host. *)
-val license : license ref
-
 (** Converts a license into a association list to place in DB. *)
 val to_assoc_list : license -> (string * string) list
 
index 66d0da295371ef047bdec89a511022042037889d..8c8410eb79510281dd1d46f6a443a6cc0fab62ce 100644 (file)
@@ -41,7 +41,8 @@ exception License_expired of license
 exception License_file_deprecated
 
 (* Set from config file: *)
-let filename = ref ""
+(* ...but only in xapi, not in the v6 daemon!!! *)
+let filename = ref "/etc/xensource/license"
 
 (* License setting functions *)
        
@@ -128,7 +129,7 @@ let do_parse_and_validate fname =
     (if not (License.check_expiry newlicense) then raise (License_expired newlicense));
 
     (* At this point, license is valid and hasn't expired *)
-    license := newlicense
+    newlicense
   with e ->
     (match e with
       | License_expired l -> warn "License has expired"
index 46abbde2ac4e401b95f23b81acaa1567bc180915..f02973eb3f5514afb03df3f3bd5ff814163f4b16 100644 (file)
@@ -24,7 +24,7 @@ val read_license_file : string -> License.license option
 
 (** As read_license_file, but also set license state variable if not expired.
  *  Called from host.license_apply. *)
-val do_parse_and_validate : string -> unit
+val do_parse_and_validate : string -> License.license
 
 
 (** Thrown if the license data is malformed. *)
index 2f034dacad2abdb023e5d3444d86c7951c6174bb..73482f2091e2274668d8d1613d3c3d392f150d6c 100644 (file)
  * GNU Lesser General Public License for more details.
  *)
 
-open License
-
 module D = Debug.Debugger(struct let name="license" end)
 open D
 
-let write_grace_to_file grace_expiry =
-       let grace_expiry_str = string_of_float grace_expiry in
-       Unixext.write_string_to_file Xapi_globs.upgrade_grace_file grace_expiry_str
-
-let read_grace_from_file () =
-       try
-               let grace_expiry_str = Unixext.string_of_file Xapi_globs.upgrade_grace_file in
-               float_of_string grace_expiry_str
-       with _ -> 0.
-
 (* xapi calls this function upon startup *)
 let initialise ~__context ~host =
-       let existing_license_params = Db.Host.get_license_params ~__context ~self:host in
-       let existing_edition = Db.Host.get_edition ~__context ~self:host in
-       let default = default () in
-       let free = Edition.to_string Edition.Free in
-       let new_license =
-               try 
-                       let existing_license = of_assoc_list existing_license_params in
-                       try match Edition.of_string existing_edition with
-                       | Edition.Free ->
-                               (* old Floodgate-free behaviour *)
-                               begin try
-                                       License_file.do_parse_and_validate !License_file.filename;
-                                       info "Existing free license with expiry date %s still in effect." (Date.to_string (Date.of_float !license.expiry));
-                                       !license (* do_parse_and_validate already sets !license *)
-                               with
-                               | License_file.License_expired l -> l (* keep expired license *)
-                               | _ ->
-                                       (* activation file does not exist or is invalid *)
-                                       if existing_license.expiry < default.expiry then begin
-                                               info "Existing free license with expiry date %s still in effect." (Date.to_string (Date.of_float existing_license.expiry));
-                                               {default with expiry = existing_license.expiry}
-                                       end else begin
-                                               info "Generating new free license, which needs to be activated in 30 days.";
-                                               default
-                                       end
-                               end
-                       | edition ->
-                               (* existing license is a v6 Essentials license -> try to check one out again *)
-                               begin try
-                                       V6client.get_v6_license ~__context ~host ~edition;
-                               with _ -> error "The license-server connection details (address or port) were missing or incomplete." end;
-                               begin match !V6client.licensed with 
-                               | None ->
-                                       let upgrade_grace = read_grace_from_file () > Unix.time () in
-                                       if upgrade_grace then begin
-                                               info "No %s license is available, but we are still in the upgrade grace period." existing_edition;
-                                               {existing_license with grace = "upgrade grace"}
-                                       end else begin
-                                               info "No %s license is available. Essentials features have been disabled." existing_edition;
-                                               {existing_license with expiry = 0.} (* expiry date 0 means 01-01-1970, so always expired *)
-                                       end
-                               | Some license ->
-                                       info "Successfully checked out %s license." existing_edition;
-                                       (* delete upgrade-grace file, if it exists *)
-                                       Unixext.unlink_safe Xapi_globs.upgrade_grace_file;
-                                       if !V6client.grace then begin
-                                               Grace_retry.retry_periodically host existing_edition;
-                                               {existing_license with grace = "regular grace"; expiry = !V6client.expires}
-                                       end else
-                                               {existing_license with grace = "no"; expiry = !V6client.expires}
-                               end
-                       with Edition.Undefined_edition _ ->
-                               warn "Edition field corrupted; generating a new free license, which needs to be activated in 30 days.";
-                               default
-               with License.Missing_license_param _ ->
-                       (* no license_params -> first boot *)
-                       Db.Host.set_edition ~__context ~self:host ~value:free;
-                       begin try
-                               License_file.do_parse_and_validate !License_file.filename;
-                               info "Found a free-license activation key with expiry date %s." (Date.to_string (Date.of_float !license.expiry));
-                               !license (* do_parse_and_validate already sets !license *)
-                       with
-                       | License_file.License_expired l -> l (* keep expired license *)
-                       | _ ->
-                               (* activation file does not exist or is invalid *)
-                               info "Generating new free license, which needs to be activated in 30 days.";
-                               default
-                       end
-       in
-       license := new_license
-       
+       try
+               let edition = Db.Host.get_edition ~__context ~self:host in
+               let edition', features, additional =
+                       V6client.apply_edition ~__context edition ["startup", "true"] in
+               Db.Host.set_edition ~__context ~self:host ~value:edition';
+               (* Copy resulting license to the database *)
+               Xapi_host.copy_license_to_db ~__context ~host ~features ~additional
+       with _ -> ()
+
diff --git a/ocaml/license/v6alert.ml b/ocaml/license/v6alert.ml
deleted file mode 100644 (file)
index 9cbe1bd..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-(*
- * Copyright (C) 2006-2009 Citrix Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * 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 Lesser General Public License for more details.
- *)
-
-open Client
-
-let xapirpc xml = Xmlrpcclient.do_xml_rpc_unix ~version:"1.0" ~filename:"/var/xapi/xapi" ~path:"/" xml
-
-let send_alert msg body = 
-       let host_uuid = Xapi_inventory.lookup Xapi_inventory._installation_uuid in
-       let session = Client.Session.login_with_password ~rpc:xapirpc ~uname:"" ~pwd:"" ~version:Xapi_globs.api_version_string in
-       Pervasiveext.finally
-               (fun () -> Client.Message.create xapirpc session msg 1L `Host host_uuid body)
-               (fun () -> Client.Session.logout xapirpc session)
-
-let send_v6_grace_license () =
-       ignore (send_alert Api_messages.v6_grace_license "The license server is unreachable. However, a grace license is given, as a similar license was successfully checked out recently.")
-
-let send_v6_upgrade_grace_license () =
-       ignore (send_alert Api_messages.v6_grace_license "An upgrade grace license is given, which allows 30 days to connect to a license server holding a valid license.")
-       
-let send_v6_rejected () =
-       ignore (send_alert Api_messages.v6_rejected "The requested license is not available at the license server.")
-
-let send_v6_comm_error () =
-       ignore (send_alert Api_messages.v6_comm_error "The license could not be checked out, because the license server could not be reached at the given address/port. Please check the connection details, and verify that the license server is running.")
diff --git a/ocaml/license/v6alert.mli b/ocaml/license/v6alert.mli
deleted file mode 100644 (file)
index 27f8fcc..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-(*
- * Copyright (C) 2006-2009 Citrix Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * 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 Lesser General Public License for more details.
- *)
-(** Helper module to let the licensing daemon send alerts to xapi *)
-
-(** Send a XenAPI message *)
-val send_alert : string -> string -> API.ref_message
-
-val send_v6_grace_license : unit -> unit
-
-val send_v6_upgrade_grace_license : unit -> unit
-
-val send_v6_rejected : unit -> unit
-
-val send_v6_comm_error : unit -> unit
index d843a065303da76de5445d733397acf46c1ac1e5..411189dae7cd6af4407f1f624649b8d1743c315d 100644 (file)
  * GNU Lesser General Public License for more details.
  *)
 
-open V6rpc
 module D=Debug.Debugger(struct let name="v6client" end)
 open D
 
 exception V6DaemonFailure
 
-(* define "never" as 01-01-2030 *)
-let start_of_epoch = Unix.gmtime 0.
-let never, _ = Unix.mktime {start_of_epoch with Unix.tm_year = 130}
-
-(* state *)
-let connected = ref false
-let licensed = ref None
-let expires = ref never
-let grace = ref false
 let retry = ref true
 
-let socket = "/var/xapi/v6"
-
 (* RPC function for communication with the v6 daemon *)
+let socket = "/var/xapi/v6"
 let v6rpc call = Rpc_client.do_rpc_unix ~version:"1.0" ~filename:socket ~path:"/" call
 
-(* conversion to v6 edition codes *)
-let editions = [Edition.Free, "FREE"]
-       
-(* reset to not-licensed state *)
-let reset_state () =
-       connected := false;
-       licensed := None;
-       expires := never;
-       grace := false
-       
-let disconnect () = 
-       if !connected then begin
-               debug "release license";
-               try
-                       let success =
-                               let call = Rpc.call "shutdown" [] in
-                               let response = v6rpc call in
-                               try Rpc.bool_of_rpc response.Rpc.contents
-                               with e ->
-                                       error "Got error %s" (Printexc.to_string e);
-                                       raise V6DaemonFailure in
-                       debug "success: %b" success;
-                       if success then begin
-                               match !licensed with
-                                       | None -> ()
-                                       | Some edition ->
-                                               info "Checked %s license back in to license server." (Edition.to_string edition);
-                                               reset_state ()
-                               end
-                       else
-                               raise V6DaemonFailure
-               with
-               | Unix.Unix_error(a, b, c) ->
-                       warn "Problem while disconnecting (%s): %s." b (Unix.error_message a);
-                       raise V6DaemonFailure
-               | V6DaemonFailure ->
-                       warn "Did not get a proper response from the v6 licensing daemon!";
-                       raise V6DaemonFailure
-       end     else
-               debug "v6 engine not connected"
-       
-let connect_and_get_license edition address port =
-       if !connected then begin
-               debug "already connected to v6 engine; disconnecting and reconnecting with new parameters";
-               try
-                       disconnect ()
-               with _ -> reset_state ()
-       end;
-       (* state is now default *)
-       debug "get license";
-       if not (List.mem_assoc edition editions) then
-               debug "invalid edition!"
-       else begin
-               try
-                       let edition' = List.assoc edition editions in
-                       let params = rpc_of_initialise_in { address = address; port = port; edition = edition' } in
-                       let call = Rpc.call "initialise" [ params ] in
-                       let response = v6rpc call in
-                       debug "response: %s" (Rpc.to_string response.Rpc.contents);
-                       let license, days_to_expire =
-                               if response.Rpc.success then
-                                       let r = initialise_out_of_rpc response.Rpc.contents in r.license, r.days_to_expire
-                               else
-                                       raise V6DaemonFailure in
-                       debug "license: %s; days-to-expire: %ld" license days_to_expire;
-                       connected := true;
-                       (* set expiry date *)
-                       let now = Unix.time () in
-                       if days_to_expire > -1l then
-                               expires := now +. (Int32.to_float days_to_expire *. 24. *. 3600.)
-                       else
-                               expires := never;
-                       (* check fist point *)
-                       (* CA-33155: FIST point may only set an expiry date earlier than the actual one *)
-                       begin match Xapi_fist.set_expiry_date () with
-                               | None -> ()
-                               | Some d ->
-                                       let fist_date = Date.to_float (Date.of_string d) in
-                                       if fist_date < !expires then expires := fist_date
-                       end;
-                       (* check return status *)
-                       if license = "real" then begin
-                               info "Checked out %s license from license server." (Edition.to_string edition);
-                               licensed := Some edition;
-                               grace := false
-                       end else if license = "grace" then begin
-                               info "Obtained %s grace license." (Edition.to_string edition);
-                               licensed := Some edition;
-                               grace := true;
-                               if Xapi_fist.reduce_grace_period () then
-                                       expires := now +. (15. *. 60.)
-                       end else begin
-                               info "License check out failed.";
-                               licensed := None;
-                               grace := false
-                       end
-               with
-               | Unix.Unix_error(a, b, c) ->
-                       error "Problem while initialising (%s): %s" b (Unix.error_message a);
-                       raise V6DaemonFailure
-               | V6DaemonFailure | _ ->
-                       warn "Did not get a proper response from the v6 licensing daemon!";
-                       raise V6DaemonFailure
-       end
-       
-let rec get_v6_license ~__context ~host ~edition =
+let rec apply_edition ~__context edition additional =
+       let host = Helpers.get_localhost ~__context in
+       let license_server = Db.Host.get_license_server ~__context ~self:host in
+       let current_edition = Db.Host.get_edition ~__context ~self:host in
+       let current_license_params = Db.Host.get_license_params ~__context ~self:host in
+       let additional = ("current_edition", current_edition) ::
+               license_server @ current_license_params @ additional in
+       let params = V6rpc.rpc_of_apply_edition_in
+               {V6rpc.edition_in = edition; V6rpc.additional_in = additional} in
        try
-               let ls = Db.Host.get_license_server ~__context ~self:host in
-               let address = List.assoc "address" ls in
-               let port = Int32.of_string (List.assoc "port" ls) in
-               debug "obtaining %s v6 license; license server address: %s; port: %ld" (Edition.to_string edition) address port;
-               (* obtain v6 license *)
-               connect_and_get_license edition address port
-       with
-       | Not_found -> failwith "Missing connection details"
-       | V6DaemonFailure ->
-               reset_state ();
+               let call = Rpc.call "apply_edition" [ params ] in
+               let response = try v6rpc call with _ -> raise V6DaemonFailure in
+               debug "response: %s" (Rpc.to_string response.Rpc.contents);
+               if response.Rpc.success then
+                       let r = V6rpc.apply_edition_out_of_rpc response.Rpc.contents in
+                       r.V6rpc.edition_out, r.V6rpc.features_out, r.V6rpc.additional_out
+               else
+                       let e = V6errors.error_of_rpc response.Rpc.contents in
+                       match e with
+                       | s, _ when s = V6errors.v6d_failure ->
+                               raise V6DaemonFailure
+                       | name, args ->
+                               raise (Api_errors.Server_error (name, args))
+       with V6DaemonFailure ->
                if !retry then begin
-                       error "Checkout failed. Retrying once...";
+                       error "Apply_edition failed. Retrying once...";
                        retry := false;
                        Thread.delay 2.;
-                       get_v6_license ~__context ~host ~edition
-               end else
-                       error "Checkout failed.";
-                       retry := true
-       
-let release_v6_license () =
+                       apply_edition ~__context edition additional
+               end else begin
+                       error "Apply_edition failed.";
+                       retry := true;
+                       raise (Api_errors.Server_error (Api_errors.v6d_failure, []))
+               end
+
+let get_editions () =
+       try
+               let call = Rpc.call "get_editions" [Rpc.rpc_of_unit ()] in
+               let response = v6rpc call in
+               debug "response: %s" (Rpc.to_string response.Rpc.contents);
+               if response.Rpc.success then
+                       let r = V6rpc.get_editions_out_of_rpc response.Rpc.contents in
+                       r.V6rpc.editions
+               else
+                       raise V6DaemonFailure
+       with _ ->
+               raise (Api_errors.Server_error (Api_errors.v6d_failure, []))
+
+let get_version () =
        try
-               disconnect ()
-       with _ -> reset_state ()
-       
+               let call = Rpc.call "get_version" [Rpc.rpc_of_unit ()] in
+               let response = v6rpc call in
+               debug "response: %s" (Rpc.to_string response.Rpc.contents);
+               if response.Rpc.success then
+                       Rpc.string_of_rpc response.Rpc.contents
+               else
+                       raise V6DaemonFailure
+       with _ ->
+               raise (Api_errors.Server_error (Api_errors.v6d_failure, []))
+
index 9da32cff1c44f31019eeb5409129e72daa31b2cf..f6720b60289c4049d28516bd60eb8cfdad4cee06 100644 (file)
  * @group Licensing
  *)
 
-(** {2 State variables} *)
-
-val licensed : Edition.edition option ref
-(** Equal to the edition string, if a license has been checked out,
- *  or None otherwise *)
-val expires : float ref
-(** Equal to the expiry date, if a license has been checked out *)
-val grace : bool ref
-(** Reflects whether the current license is a grace license, 
- *  if a license has been checked out *)
-
-(** {2 Obtaining and Releasing a License} *)
-
-val get_v6_license : __context:Context.t -> host:[`host] Ref.t -> edition:Edition.edition -> unit
-(** Obtain a v6 license via the licensing daemon. The edition parameter is
- *  either "enterprise" or "platinum". Uses the contact details in host.license_server. *)
-val release_v6_license : unit -> unit
-(** Release the current v6 license (if any) via the licensing daemon *)
+val apply_edition : __context:Context.t -> string -> (string * string) list ->
+       string * Features.feature list * (string * string) list
+val get_editions : unit -> (string * string * string * int) list
+val get_version : unit -> string
 
index bb6ea6def8b0161a06a68aa00428390703bb5838..90abcefb9aadf8b18ed11899e99c3b2875681bb0 100644 (file)
@@ -28,12 +28,12 @@ let xmlrpc_handler process req bio =
        in
        debug "path=%s" path;
        let body = Http_svr.read_body req bio in
-       debug "Request: %s" body;
        let s = Buf_io.fd_of bio in
        let rpc = Xmlrpc.call_of_string body in
+       debug "Request: %s %s" rpc.Rpc.name (Rpc.to_string (List.hd rpc.Rpc.params));
        let result = process rpc in
+       debug "Response: %s" (Rpc.to_string result.Rpc.contents);
        let str = Xmlrpc.string_of_response result in
-       debug "Response: %s" str;
        Http_svr.response_str req s str
        
 
diff --git a/ocaml/license/v6errors.ml b/ocaml/license/v6errors.ml
new file mode 100644 (file)
index 0000000..7557511
--- /dev/null
@@ -0,0 +1,19 @@
+type error = string * string list with rpc
+exception Error of error
+
+let to_string = function
+  | Error (name, args) ->
+      Printf.sprintf "V6error(%s, [ %a ])" name (fun () -> String.concat "; ") args
+  | e -> Printexc.to_string e
+
+
+let invalid_edition = "INVALID_EDITION"
+let v6d_failure = "V6D_FAILURE"
+
+let license_expired = "LICENSE_EXPIRED"
+let license_processing_error = "LICENSE_PROCESSING_ERROR"
+let missing_connection_details = "MISSING_CONNECTION_DETAILS"
+let license_checkout_error = "LICENSE_CHECKOUT_ERROR"
+let license_file_deprecated = "LICENSE_FILE_DEPRECATED"
+let activation_while_not_free = "ACTIVATION_WHILE_NOT_FREE"
+
index d1326a299bda912048438e57a5bcd7463d825851..e66882e48f51aaac891ffd473fca48b7ef6f4464 100644 (file)
  * GNU Lesser General Public License for more details.
  *)
 
-module D = Debug.Debugger(struct let name="v6xmlrpc" end)
+module D = Debug.Debugger(struct let name="v6rpc" end)
 open D
 
-exception Unmarshalling_error of string
-
-type initialise_in = {
-       address: string;
-       port: int32;
-       edition: string;
+type apply_edition_in = {
+       edition_in: string;
+       additional_in: (string * string) list;
 } with rpc
 
-type initialise_out = {
-       license: string;
-       days_to_expire: int32;
+type apply_edition_out = {
+       edition_out: string;
+       features_out: Features.feature list;
+       additional_out: (string * string) list;
 } with rpc
 
-type failure = string * (string list) with rpc
-let response_of_failure code params =
-       Rpc.failure (rpc_of_failure (code, params))
-let response_of_fault code =
-       Rpc.failure (rpc_of_failure ("Fault", [code]))
+type names = string * string * string * int with rpc
+type get_editions_out = {
+       editions: names list;
+} with rpc
 
 module type V6api = sig
-       val initialise : string -> int32 -> string -> string * int32
-       val shutdown : unit -> bool
+       (* edition -> additional_params -> enabled_features, additional_params *)
+       val apply_edition : string -> (string * string) list ->
+               string * Features.feature list * (string * string) list
+       (* () -> list of editions (name, marketing name, short name) *)
+       val get_editions : unit -> (string * string * string * int) list
+       (* () -> result *)
+       val get_version : unit -> string
+       (* () -> version *)
        val reopen_logs : unit -> bool
 end
 
@@ -44,22 +47,34 @@ module V6process = functor(V: V6api) -> struct
        let process call =
                let response =
                        try match call.Rpc.name with
-                       | "initialise" -> 
-                               let arg_rpc = match call.Rpc.params with [a] -> a | _ -> raise (Unmarshalling_error "initialise") in
-                               let arg = initialise_in_of_rpc arg_rpc in
-                               let l,d = V.initialise arg.address arg.port arg.edition in
-                               let response = rpc_of_initialise_out { license = l; days_to_expire = d } in 
+                       | "apply_edition" -> 
+                               let arg_rpc = match call.Rpc.params with [a] -> a | _ -> raise (V6errors.Error ("unmarchalling_error", [])) in
+                               let arg = apply_edition_in_of_rpc arg_rpc in
+                               let edition, features, additional_params = V.apply_edition arg.edition_in arg.additional_in in
+                               let response = rpc_of_apply_edition_out
+                                       {edition_out = edition; features_out = features; additional_out = additional_params} in 
                                Rpc.success response
-                       | "shutdown" ->
-                               let response = Rpc.rpc_of_bool (V.shutdown ()) in
+                       | "get_editions" ->
+                               let response = rpc_of_get_editions_out {editions = V.get_editions ()} in
+                               Rpc.success response
+                       | "get_version" ->
+                               let response = Rpc.rpc_of_string (V.get_version ()) in
                                Rpc.success response
                        | "reopen-logs" ->
                                let response = Rpc.rpc_of_bool (V.reopen_logs ()) in
                                Rpc.success response
-                       | x -> response_of_fault ("unknown RPC: " ^ x)
-                       with e ->
+                       | x -> failwith ("unknown RPC: " ^ x)
+                       with 
+                       | V6errors.Error e as exn ->
+                               error "%s" (V6errors.to_string exn);
+                               log_backtrace ();
+                               Rpc.failure (V6errors.rpc_of_error e)
+                       | e ->
+                               let e = Printexc.to_string e in
+                               error "Error: %s" e;
                                log_backtrace ();
-                               response_of_failure "INTERNAL_ERROR" [Printexc.to_string e] in
+                               Rpc.failure (V6errors.rpc_of_error (V6errors.v6d_failure, [e]))
+               in
                response
 end
 
index f508d47e23b6bfa8ba2fcf915efc0529d976f0f4..5530dcabebba8848d235414444a7ea12471ea3e4 100644 (file)
 (** The XML/RPC interface of the licensing daemon *)
 module type V6api =
        sig
-               val initialise : string -> int32 -> string -> string * int32
-               val shutdown : unit -> bool
+               (* edition -> additional_params -> enabled_features, additional_params *)
+               val apply_edition : string -> (string * string) list ->
+                       string * Features.feature list * (string * string) list
+               (* () -> list of editions *)
+               val get_editions : unit -> (string * string * string * int) list
+               (* () -> result *)
+               val get_version : unit -> string
+               (* () -> version *)
                val reopen_logs : unit -> bool
-       end
-  
+       end  
 (** XML/RPC handler *)
+
 module V6process : functor (V : V6api) ->
        sig
                (** Process an XML/RPC call *)
@@ -31,17 +37,28 @@ module V6process : functor (V : V6api) ->
 
 (** {2 Marshaling functions} *)
 
-type initialise_in = {
-       address: string;
-       port: int32;
-       edition: string;
+type apply_edition_in = {
+       edition_in: string;
+       additional_in: (string * string) list;
+}
+
+val apply_edition_in_of_rpc : Rpc.t -> apply_edition_in
+val rpc_of_apply_edition_in : apply_edition_in -> Rpc.t
+
+type apply_edition_out = {
+       edition_out: string;
+       features_out: Features.feature list;
+       additional_out: (string * string) list;
 }
 
-val rpc_of_initialise_in : initialise_in -> Rpc.t
+val apply_edition_out_of_rpc : Rpc.t -> apply_edition_out
+val rpc_of_apply_edition_out : apply_edition_out -> Rpc.t
 
-type initialise_out = {
-       license: string;
-       days_to_expire: int32;
+type names = string * string * string * int
+type get_editions_out = {
+       editions: names list;
 }
 
-val initialise_out_of_rpc : Rpc.t -> initialise_out
+val get_editions_out_of_rpc : Rpc.t -> get_editions_out
+val rpc_of_get_editions_out : get_editions_out -> Rpc.t
+
index 42be8ce78bf9879d84b75d66de090466f2da4f5e..78ebeda2b7531067e40679615ab3dc254031dfc1 100644 (file)
@@ -6,7 +6,7 @@ OCAMLINCLUDES = ../idl ../idl/ocaml_backend \
        ../xenops ../xva ../util \
        ../auth ../license ../client_records ../rfb ../gpg
 
-
+UseCamlp4(rpc-light.syntax, features)
 
 CFLAGS += -std=gnu99 -Wall -Werror -I$(shell ocamlc -where)
 
@@ -42,7 +42,6 @@ COMMON = \
        xapi_mgmt_iface \
        smint \
        ../gpg/gpg \
-       ../license/license \
        helpers \
        at_least_once_more \
        create_templates \
@@ -235,14 +234,11 @@ XAPI_MODULES = $(COMMON) \
        certificates \
        ../license/v6client \
        ../license/v6rpc \
+       ../license/v6errors \
        bios_strings \
        xapi_config \
        features \
        pool_features \
-       ../license/grace_retry \
-       ../license/v6alert \
-       ../license/edition \
-       ../license/license_file \
        ../license/license_init
 
 OCamlProgram(xapi, $(XAPI_MODULES))
index 2624732ac0b258848eb2ae4412d5976a76ebdd64..2bf44024a194abe70483e3856a601b22df54a4e9 100644 (file)
@@ -1911,7 +1911,7 @@ add a mapping of 'path' -> '/tmp', the command line should contain the argument
     {
       reqd=["edition"];
       optn=["host-uuid"; "license-server-address"; "license-server-port"];
-      help="Change to another edition, or reactivate it after a license has expired. Possible editions are 'free', 'enterprise', and 'platinum'. Upgrading from free to enterprise or platinum is subject to the successful checkout of an appropriate license from the license server.";
+      help="Change to another edition, or reactivate the current edition after a license has expired. This may be subject to the successful checkout of an appropriate license";
       implementation=No_fd Cli_operations.host_apply_edition;
       flags=[];
     };
index c8acaeba714d3d1ef6b1e7e2c3b61abddb46abf3..cd159eef96a08be33a3ecec86d45b5e361e87823 100644 (file)
@@ -144,28 +144,43 @@ type host_license = {
        hostname: string;
        uuid: string;
        rstr: Features.feature list;
-       license: License.license
+       edition: string;
+       edition_short: string;
+       expiry: float;
 }
-let host_license_of_r host_r =
+let host_license_of_r host_r editions =
        let params = host_r.API.host_license_params in
        let rstr = Features.of_assoc_list params in
-       let license = License.of_assoc_list params in
-       { hostname = host_r.API.host_hostname;
-       uuid = host_r.API.host_uuid;
-       rstr = rstr;
-       license = license }
+       let expiry =
+               if List.mem_assoc "expiry" params then
+                       Date.to_float (Date.of_string (List.assoc "expiry" params))
+               else
+                       0.
+       in
+       let edition = host_r.API.host_edition in
+       let edition_short = List.hd
+               (List.filter_map (fun (a, _, b, _) -> if a = edition then Some b else None) editions) in
+       {
+               hostname = host_r.API.host_hostname;
+               uuid = host_r.API.host_uuid;
+               rstr = rstr;
+               edition = edition;
+               edition_short = edition_short;
+               expiry = expiry;
+       }
 
 let diagnostic_license_status printer rpc session_id params =
        let hosts = Client.Host.get_all_records rpc session_id in
        let heading = [ "Hostname"; "UUID"; "Features"; "Code"; "Free"; "Expiry"; "Days left" ] in
+       let editions = V6client.get_editions () in
 
-       let valid, invalid = List.partition (fun (_, host_r) -> try ignore(host_license_of_r host_r); true with _ -> false) hosts in
-       let host_licenses = List.map (fun (_, host_r) -> host_license_of_r host_r) valid in
+       let valid, invalid = List.partition (fun (_, host_r) -> try ignore(host_license_of_r host_r editions); true with _ -> false) hosts in
+       let host_licenses = List.map (fun (_, host_r) -> host_license_of_r host_r editions) valid in
        (* Sort licenses into nearest-expiry first then free *)
        let host_licenses = List.sort (fun a b ->
-               let a_expiry = a.license.License.expiry and b_expiry = b.license.License.expiry in
-               let a_free = (Edition.of_string a.license.License.sku) = Edition.Free
-               and b_free = (Edition.of_string b.license.License.sku) = Edition.Free in
+               let a_expiry = a.expiry and b_expiry = b.expiry in
+               let a_free = a.edition = "free"
+               and b_free = b.edition = "free" in
                if a_expiry < b_expiry then -1
                else
                        if a_expiry > b_expiry then 1
@@ -176,12 +191,12 @@ let diagnostic_license_status printer rpc session_id params =
                                        else 0) host_licenses in
        let now = Unix.gettimeofday () in
        let hosts = List.map (fun h -> [ h.hostname;
-       String.sub h.uuid 0 8;
-       Features.to_compact_string h.rstr;
-       Edition.to_short_string (Edition.of_string h.license.License.sku);
-       string_of_bool ((Edition.of_string h.license.License.sku) = Edition.Free);
-       Date.to_string (Date.of_float h.license.License.expiry);
-       Printf.sprintf "%.1f" ((h.license.License.expiry -. now) /. (24. *. 60. *. 60.));
+               String.sub h.uuid 0 8;
+               Features.to_compact_string h.rstr;
+               h.edition_short; 
+               string_of_bool (h.edition = "free");                            
+               Date.to_string (Date.of_float h.expiry);
+               Printf.sprintf "%.1f" ((h.expiry -. now) /. (24. *. 60. *. 60.));
        ]) host_licenses in
        let invalid_hosts = List.map (fun (_, host_r) -> [ host_r.API.host_hostname;
        String.sub host_r.API.host_uuid 0 8;
@@ -189,7 +204,7 @@ let diagnostic_license_status printer rpc session_id params =
        let __context = Context.make "diagnostic_license_status" in
        let pool = List.hd (Db.Pool.get_all ~__context) in
        let pool_features = Features.of_assoc_list (Db.Pool.get_restrictions ~__context ~self:pool) in
-       let pool_free = List.fold_left (||) false (List.map (fun h -> (Edition.of_string h.license.License.sku) = Edition.Free) host_licenses) in
+       let pool_free = List.fold_left (||) false (List.map (fun h -> h.edition = "free") host_licenses) in
        let divider = [ "-"; "-"; "-"; "-"; "-"; "-"; "-" ] in
        let pool = [ "-"; "-"; Features.to_compact_string pool_features; "-"; string_of_bool pool_free; "-"; "-" ] in
        let table = heading :: divider :: hosts @ invalid_hosts @ [ divider; pool ] in
@@ -2592,7 +2607,7 @@ let host_apply_edition printer rpc session_id params =
        try
                Client.Host.apply_edition rpc session_id host edition
        with
-               | Api_errors.Server_error (name, args) when name = Api_errors.license_checkout_error ->
+               | Api_errors.Server_error (name, args) as e when name = Api_errors.license_checkout_error ->
                        (* Put back original license server details *)
                        Client.Host.set_license_server rpc session_id host current_license_server;
                        let alerts = Client.Message.get_since rpc session_id (Date.of_float now) in
@@ -2602,10 +2617,16 @@ let host_apply_edition printer rpc session_id params =
                                        printer (Cli_printer.PStderr msg.API.message_body)
                        in
                        if alerts = [] then
-                               printer (Cli_printer.PStderr "Internal error: the licensing daemon was not found.")
-                       else
+                               raise e
+                       else begin
                                List.iter print_if_checkout_error alerts;
-                       raise (ExitWithError 1)
+                               raise (ExitWithError 1)
+                       end
+               | Api_errors.Server_error (name, args) as e when name = Api_errors.invalid_edition ->
+                       let editions = List.map (fun (x, _, _, _) -> x) (V6client.get_editions ()) in
+                       let editions = String.concat ", " editions in
+                       printer (Cli_printer.PStderr ("Valid editions are: " ^ editions));
+                       raise e
                | e -> raise e
 
 let host_evacuate printer rpc session_id params =
index 25d5ee73bbd6422178b8ae3900979b8c2d15da85..22628da58eafa1663c119c1b5d7a9a0f58c72fca 100644 (file)
@@ -369,8 +369,11 @@ let make_packs_info () =
 let make_software_version () =
        let option_to_list k o = match o with None -> [] | Some x -> [ k, x ] in
        let info = read_localhost_info () in
+       let v6_version = V6client.get_version () in
        Xapi_globs.software_version @
-       ["xapi", get_xapi_verstring ();
+       (if v6_version = "" then [] else ["dbv", v6_version]) @
+       [
+       "xapi", get_xapi_verstring ();
        "xen", info.xen_verstring;
        "linux", info.linux_verstring;
        "xencenter_min", Xapi_globs.xencenter_min_verstring;
index b9ac87ac5b03138a026b64534304b3f525805a09..d22c560c97d344df4d21a4f2f019023d37174d7f 100644 (file)
@@ -37,6 +37,7 @@ type feature =
        | No_platform_filter
        | No_nag_dialog
        | VMPR
+       with rpc
 
 type orientation = Positive | Negative
 
index 2fa67150b07147347ef3e82191cab77909da0288..5de1c3a316bfc19e1183321f3f9460d2fa50249b 100644 (file)
@@ -38,6 +38,9 @@ type feature =
        | No_nag_dialog                (** Used by XenCenter *)
        | VMPR                         (** Enable use of VM Protection and Recovery *)
 
+val feature_of_rpc : Rpc.t -> feature
+val rpc_of_feature : feature -> Rpc.t
+
 (** The list of all known features. *)
 val all_features : feature list
 
index 1389c7dbea2304fc5f424e6d9f9afd8fb07f852a..ba2b50513d2d7e1a09b56256b61b8807ace2af13 100644 (file)
@@ -15,9 +15,7 @@ module L = Debug.Debugger(struct let name="license" end)
 open Vmopshelpers
 open Stringext
 
-let vm ~__context vm = ()
-(*     (* Here we check that the license is still valid - this should be the only place where this happens *)
-       let host = Helpers.get_localhost ~__context in
+let check_expiry ~__context ~host = 
        let license = Db.Host.get_license_params ~__context ~self:host in
        let expired =
                if List.mem_assoc "expiry" license = false then
@@ -28,7 +26,12 @@ let vm ~__context vm = ()
                        Unix.time () > expiry
                end
        in
-       if expired then raise (Api_errors.Server_error (Api_errors.license_expired, [])) *)
+       if expired then raise (Api_errors.Server_error (Api_errors.license_expired, []))
+
+let vm ~__context vm =
+       (* Here we check that the license is still valid - this should be the only place where this happens *)
+       let host = Helpers.get_localhost ~__context in
+       check_expiry ~__context ~host
 
 (* XXX: why use a "with_" style function here? *)
 let with_vm_license_check ~__context v f =
index ce0555349235030f9dd2e822e8f79ce0307bda18..1468207f4cc0cf0e15d89baa0748c96f8c4e0345 100644 (file)
@@ -16,6 +16,9 @@
  * @group Licensing
  *)
 
+(** Raises {!Api_errors.license_expired} if the current license has expired. *)
+val check_expiry : __context:Context.t -> host:API.ref_host -> unit
+
 (** Raises {!Api_errors.license_expired} if the current license has expired.
  *  The consequence would be that the VM is not allowed to start. *)
 val vm : __context:Context.t -> API.ref_VM -> unit
index d5add7bafc4eb5d0e1fca62267689ed81d79d8cb..ff1ced84afc7ed9e28142b222a9cf9dc68240637 100644 (file)
@@ -585,9 +585,7 @@ let handle_licensing () =
        Server_helpers.exec_with_new_task "Licensing host"
                (fun __context ->
                        let host = Helpers.get_localhost ~__context in
-                       License_init.initialise ~__context ~host;
-                       (* Copy resulting license to the database *)
-                       Xapi_host.copy_license_to_db ~__context ~host
+                       License_init.initialise ~__context ~host
                )
 
 (** Writes the memory policy to xenstore and triggers the ballooning daemon. *)
index 44ebd22c5ca02a9451d702197a49265d621adc29..8b67fff9c8fc471d70f678fd9518302924aafdb6 100644 (file)
@@ -98,7 +98,7 @@ let read_config filename =
         in
 
     let configargs = [
-        "license_filename", Config.Set_string License_file.filename;
+(*      "license_filename", Config.Set_string License_file.filename; *)
         "http-port", Config.Set_int http_port;
         "stunnelng", Config.Set_bool Stunnel.use_new_stunnel;
         "log", Config.String set_log;
@@ -118,5 +118,5 @@ let dump_config () =
     debug "build_number: %s" Version.build_number;
     debug "hg changeset: %s" Version.hg_id;
     debug "version: %d.%d" version_major version_minor;
-    debug "License filename: %s" !License_file.filename
+(*  debug "License filename: %s" !License_file.filename *)
     
index b4b3a159a7fda5dbc2f18c9b216b469a3a5e04e8..bd113e05d90760ef361272207cc957404f04986b 100644 (file)
@@ -46,9 +46,6 @@ let tools_version = ref (-1, -1, -1, -1)
 let xencenter_min_verstring = "1.8"
 let xencenter_max_verstring = "1.8"
 
-(** Date-Based Version: burn-in date of current XenServer release (RTM date) *)
-let dbv = "2009.0201"
-
 (* linux pack vsn key in host.software_version (used for a pool join restriction *)
 let linux_pack_vsn_key = "xs:linux"
 let packs_dir = "/etc/xensource/installed-repos"
@@ -141,8 +138,7 @@ let software_version = [ _product_version, Version.product_version;
                         _build_number,    Version.build_number;
                         _hg_id,           Version.hg_id;
                         _hostname,        Version.hostname;
-                        _date,            Version.date;
-                        _dbv,                     dbv ]
+                        _date,            Version.date]
 
 let pygrub_path = "/usr/bin/pygrub"
 let eliloader_path = "/usr/bin/eliloader"
index d685d4e24dc6fb0a86c4b8c3b9d56eff64a8a665..a0320ee94e992b443c85d74058600599451f760c 100644 (file)
@@ -439,17 +439,16 @@ module Monitor = struct
                                ) livemap;
 
                                (* Find hosts whose license expiry is in the past and forcibly disable them if necessary. *)
-                               let now = Unix.gettimeofday () in
                                let license_has_expired host =
-                                       let params = Db.Host.get_license_params ~__context ~self:host in
-                                       try
-                                               let license = License.of_assoc_list params in
-                                               license.License.expiry < now
-                                       with _ -> false (* fail safe *) in
+                               let params = Db.Host.get_license_params ~__context ~self:host in
+                               try
+                                       License_check.check_expiry ~__context ~host;
+                                       true
+                               with _ -> false (* fail safe *) in
                                let expired_hosts = List.filter license_has_expired all_hosts in
                                (* Find the expired ones which are still enabled *)
                                let enabled_but_expired = List.filter (fun self -> Db.Host.get_enabled ~__context ~self) expired_hosts in
-                               List.iter
+                               List.iter 
                                        (fun host ->
                                                warn "Host uuid %s: license expired in the past; forcibly disabling" (Db.Host.get_uuid ~__context ~self:host);
                                                Db.Host.set_enabled ~__context ~self:host ~value:false
@@ -514,6 +513,7 @@ module Monitor = struct
 
                                end;
 
+                               let now = Unix.gettimeofday () in
                                let plan_too_old = now -. !last_plan_time > Xapi_globs.ha_monitor_plan_timer in
                                if plan_too_old || !plan_out_of_date then begin
                                        let changed = Xapi_ha_vm_failover.update_pool_status ~__context in
index 428823e3bc158eb4ea8a85f42756114ce8663695..69650bbc078bdf65bf1a948f4e31de5f7d909ede 100644 (file)
@@ -32,10 +32,6 @@ let local_assert_healthy ~__context = match Pool_role.get_role () with
   | Pool_role.Master -> ()
   | Pool_role.Broken -> raise !Xapi_globs.emergency_mode_error 
   | Pool_role.Slave _ -> if !Xapi_globs.slave_emergency_mode then raise !Xapi_globs.emergency_mode_error
-
-let set_license_params ~__context ~self ~value = 
-  Db.Host.set_license_params ~__context ~self ~value;
-  Pool_features.update_pool_features ~__context
   
 let set_power_on_mode ~__context ~self ~power_on_mode ~power_on_config =
   Db.Host.set_power_on_mode ~__context ~self ~value:power_on_mode;
@@ -63,9 +59,8 @@ let assert_safe_to_reenable ~__context ~self =
       let unplugged_pifs = List.filter (fun pif -> not(Db.PIF.get_currently_attached ~__context ~self:pif)) pifs in
       (* Make sure it is 'ok' to have these PIFs remain unplugged *)
       List.iter (fun self -> Xapi_pif.abort_if_network_attached_to_protected_vms ~__context ~self) unplugged_pifs;
-      (* Make sure our license hasn't expired *)
-      if !License.license.License.expiry < Unix.gettimeofday ()
-      then raise (Api_errors.Server_error(Api_errors.license_expired, []))
+      (* Make sure our license hasn't expired (an exception is raised is it is) *)
+      License_check.check_expiry ~__context ~host:self
     end
 
 let xen_bugtool = "/usr/sbin/xen-bugtool"
@@ -529,18 +524,6 @@ let send_debug_keys ~__context ~host ~keys =
 let list_methods ~__context =
   raise (Api_errors.Server_error (Api_errors.not_implemented, [ "list_method" ]))
 
-exception Pool_record_expected_singleton
-let copy_license_to_db ~__context ~host =
-  let license_kvpairs = License.to_assoc_list !License.license in
-  let edition = Edition.of_string (Db.Host.get_edition ~__context ~self:host) in
-  let features = Edition.to_features edition in
-  let restrict_kvpairs = Features.to_assoc_list features in
-  let license_params = license_kvpairs @ restrict_kvpairs in
-  Helpers.call_api_functions ~__context
-    (fun rpc session_id ->
-       (* This will trigger a pool sku/restrictions recomputation *)
-       Client.Client.Host.set_license_params rpc session_id !Xapi_globs.localhost_ref license_params)
-
 let is_slave ~__context ~host = not (Pool_role.is_master ())
 
 let ask_host_if_it_is_a_slave ~__context ~host = 
@@ -570,53 +553,6 @@ let is_host_alive ~__context ~host =
     false
   end
 
-let license_apply ~__context ~host ~contents =
-  let edition = Db.Host.get_edition ~__context ~self:host in
-  if edition <> "free" then raise (Api_errors.Server_error(Api_errors.activation_while_not_free, []));
-  let license = Base64.decode contents in
-  let tmp = Filenameext.temp_file_in_dir !License_file.filename in
-  let fd = Unix.openfile tmp [Unix.O_WRONLY; Unix.O_CREAT] 0o644 in
-  let close =
-    let already_done = ref false in
-    fun () -> if not(!already_done) then begin
-       Unix.close fd;
-       already_done := true
-      end in
-  try
-    let length = String.length license in
-    if Unix.write fd license 0 length <> length then (failwith "Short write!");
-    close ();
-
-    (* if downgrade and in a pool [i.e. a slave, or a master with >1 host record] then fail *)
-    let new_license = match License_file.read_license_file tmp with
-      | Some l -> l
-      | None -> failwith "Failed to read license" (* Gets translated into generic failure below *)
-    in
-    
-    (* CA-27011: if HA is enabled block the application of a license whose SKU does not support HA *)
-    let new_features = Edition.to_features (Edition.of_string new_license.License.sku) in
-    let pool = List.hd (Db.Pool.get_all ~__context) in
-    if Db.Pool.get_ha_enabled ~__context ~self:pool && (not (List.mem Features.HA new_features))
-    then raise (Api_errors.Server_error(Api_errors.ha_is_enabled, []));
-
-      License_file.do_parse_and_validate tmp; (* throws exception if not valid which should really be translated into API error here.. *)
-      Unix.rename tmp !License_file.filename; (* atomically overwrite host license file *)
-      copy_license_to_db ~__context ~host; (* update pool.license_params for clients that want to see license info through API *)
-      Unixext.unlink_safe tmp;
-  with
-      _ as e ->
-       close ();
-       Unixext.unlink_safe tmp;
-       match e with
-         | License_file.License_expired l -> raise (Api_errors.Server_error(Api_errors.license_expired, []))
-         | License_file.License_file_deprecated -> raise (Api_errors.Server_error(Api_errors.license_file_deprecated, []))
-         | (Api_errors.Server_error (x,y)) -> raise (Api_errors.Server_error (x,y)) (* pass through api exceptions *)
-         | e ->
-             begin
-               debug "Exception processing license: %s" (Printexc.to_string e);
-               raise (Api_errors.Server_error(Api_errors.license_processing_error, []))
-             end
-
 let create ~__context ~uuid ~name_label ~name_description ~hostname ~address ~external_auth_type ~external_auth_service_name ~external_auth_configuration ~license_params ~edition ~license_server =
 
   let existing_host = try Some (Db.Host.get_by_uuid __context uuid) with _ -> None in
@@ -1231,72 +1167,63 @@ let set_localdb_key ~__context ~host ~key ~value =
        debug "Local-db key '%s' has been set to '%s'" key value
 
 (* Licensing *)
-               
+
+exception Pool_record_expected_singleton
+let copy_license_to_db ~__context ~host ~features ~additional =
+       let restrict_kvpairs = Features.to_assoc_list features in
+       let license_params = additional @ restrict_kvpairs in
+       Helpers.call_api_functions ~__context
+               (fun rpc session_id ->
+                       (* This will trigger a pool sku/restrictions recomputation *)
+                       Client.Client.Host.set_license_params rpc session_id !Xapi_globs.localhost_ref license_params)
+
+let set_license_params ~__context ~self ~value = 
+       Db.Host.set_license_params ~__context ~self ~value;
+       Pool_features.update_pool_features ~__context
+       
 let apply_edition ~__context ~host ~edition =
-       debug "apply_edition to %s" edition;
-       Grace_retry.cancel (); (* cancel any existing grace-retry timer *)
-       let current_edition = Db.Host.get_edition ~__context ~self:host in
-       let current_license = !License.license in
-       let default = License.default () in
-       let new_license = 
-               try match Edition.of_string edition with
-               | Edition.Free ->       
-                       if Edition.of_string current_edition = Edition.Free then begin
-                               info "The host's edition is already 'free'. No change.";
-                               current_license
-                       end else begin
-                               info "Downgrading from %s to free edition." current_edition;                    
-                               (* CA-27011: if HA is enabled block the application from downgrading to free *)
-                               let pool = List.hd (Db.Pool.get_all ~__context) in
-                               if Db.Pool.get_ha_enabled ~__context ~self:pool then
-                                       raise (Api_errors.Server_error (Api_errors.ha_is_enabled, []))
-                               else begin
-                                       V6client.release_v6_license ();
-                                       Unixext.unlink_safe !License_file.filename; (* delete activation key, if it exists *)
-                                       default (* default is free edition with 30 day grace validity *)
-                               end
-                       end
-               | e ->
-                       (* Try to get the a v6 license; if one has already been checked out,
-                        * it will be automatically checked back in. *)
-                       if Edition.of_string current_edition = Edition.Free then
-                               info "Upgrading from free to %s edition..." edition
-                       else
-                               info "(Re)applying %s license..." edition;
-               
-                       begin try
-                               V6client.get_v6_license ~__context ~host ~edition:e
-                       with _ -> raise (Api_errors.Server_error (Api_errors.missing_connection_details, [])) end;
+       (* if HA is enabled do not allow the edition to be changed *)
+       let pool = List.hd (Db.Pool.get_all ~__context) in
+       if Db.Pool.get_ha_enabled ~__context ~self:pool then
+               raise (Api_errors.Server_error (Api_errors.ha_is_enabled, []))
+       else begin
+               let edition', features, additional = V6client.apply_edition ~__context edition [] in
+               Db.Host.set_edition ~__context ~self:host ~value:edition';
+               copy_license_to_db ~__context ~host ~features ~additional
+       end
+
+let license_apply ~__context ~host ~contents =
+       let license = Base64.decode contents in
+       let tmp = "/tmp/new_license" in
+       let fd = Unix.openfile tmp [Unix.O_WRONLY; Unix.O_CREAT] 0o644 in
+       let length = String.length license in
+       let written = Unix.write fd license 0 length in
+       Unix.close fd;
+       finally
+               (fun () ->
+                       if written <> length then begin
+                               debug "Short write!";
+                               raise (Api_errors.Server_error(Api_errors.license_processing_error, []))
+                       end;
+                       let edition', features, additional = V6client.apply_edition ~__context "" ["license_file", tmp] in
+                       Db.Host.set_edition ~__context ~self:host ~value:edition';
+                       copy_license_to_db ~__context ~host ~features ~additional
+               )
+               (fun () ->
+                       (* The language will have been moved to a standard location if it was valid, and
+                        * should be removed otherwise -> always remove the file at the tmp path, if any. *)
+                       Unixext.unlink_safe tmp
+               )
                
-                       begin match !V6client.licensed with 
-                       | None -> 
-                               error "License could not be checked out. Edition is not changed.";
-                               V6client.release_v6_license ();
-                               raise (Api_errors.Server_error (Api_errors.license_checkout_error, [edition]))
-                       | Some license ->
-                               let name = Edition.to_marketing_name (Edition.of_string edition) in
-                               let basic = {default with License.sku = edition; License.sku_marketing_name = name;
-                                       License.expiry = !V6client.expires} in
-                               if !V6client.grace then begin
-                                       Grace_retry.retry_periodically host edition;
-                                       {basic with License.grace = "regular grace"}
-                               end else
-                                       basic
-                       end
-               with Edition.Undefined_edition e ->
-                       error "Invalid edition ('%s')!" e;
-                       raise (Api_errors.Server_error (Api_errors.invalid_edition, [e]))
-       in
-       License.license := new_license;
-       Db.Host.set_edition ~__context ~self:host ~value:edition;
-       copy_license_to_db ~__context ~host; (* update host.license_params, pool sku and restrictions *)
-       Unixext.unlink_safe Xapi_globs.upgrade_grace_file (* delete upgrade-grace file, if it exists *)
+(* Supplemental packs *)
 
 let refresh_pack_info ~__context ~host =
        debug "Refreshing software_version";
        let software_version = Create_misc.make_software_version () in
        Db.Host.set_software_version ~__context ~self:host ~value:software_version
-       
+
+(* Network reset *)
+
 let reset_networking ~__context ~host =
        debug "Resetting networking";
        let local_pifs = List.filter (fun pif -> Db.PIF.get_host ~__context ~self:pif = host) (Db.PIF.get_all ~__context) in
@@ -1320,7 +1247,9 @@ let reset_networking ~__context ~host =
                Db.Tunnel.destroy ~__context ~self:tunnel) tunnels;
        List.iter (fun pif -> debug "destroying PIF %s" (Db.PIF.get_uuid ~__context ~self:pif);
                Db.PIF.destroy ~__context ~self:pif) local_pifs
-               
+
+(* CPU feature masking *)
+
 let set_cpu_features ~__context ~host ~features =
        debug "Set CPU features";
        (* check restrictions *)
@@ -1365,6 +1294,8 @@ let reset_cpu_features ~__context ~host =
        let cpu_info = List.replace_assoc "features_after_reboot" physical_features cpu_info in
        Db.Host.set_cpu_info ~__context ~self:host ~value:cpu_info
 
+(* Local storage caching *)
+
 let enable_local_storage_caching ~__context ~host ~sr =
        assert_bacon_mode ~__context ~host;
        let ty = Db.SR.get_type ~__context ~self:sr in
@@ -1393,4 +1324,3 @@ let disable_local_storage_caching ~__context ~host =
        try Db.SR.set_local_cache_enabled ~__context ~self:sr ~value:false with _ -> () 
 
 
-               
index 06c7f7a6c56fb695fbdf932aa44d8a33dadc3f82..87cfbe784f6bb7bdae027bc91bc1a56244a98794 100644 (file)
@@ -25,11 +25,6 @@ val set_emergency_mode_error : string -> string list -> unit
 
 val local_assert_healthy : __context:'a -> unit 
 
-val set_license_params :
-  __context:Context.t ->
-  self:[ `host ] Ref.t -> value:(string * string) list -> unit
-(** Called by post-floodgate slaves to update the database AND recompute the pool_sku on the master *)
-  
 val set_power_on_mode :
   __context:Context.t ->
   self:[ `host ] Ref.t -> power_on_mode: string -> power_on_config:(string * string) list -> unit
@@ -65,7 +60,6 @@ val dmesg_clear : __context:'a -> host:'b -> 'c
 val get_log : __context:'a -> host:'b -> 'c
 val send_debug_keys : __context:'a -> host:'b -> keys:string -> unit
 val list_methods : __context:'a -> 'b
-val copy_license_to_db : __context:Context.t -> host:[ `host ] Ref.t -> unit
 val is_slave : __context:'a -> host:'b -> bool
 
 (** Contact the host and return whether it is a slave or not. 
@@ -78,7 +72,6 @@ val ask_host_if_it_is_a_slave :
     to make sure. *)
 val is_host_alive : __context:Context.t -> host:API.ref_host -> bool
 
-val license_apply : __context:Context.t -> host:API.ref_host -> contents:string -> unit
 val create :
   __context:Context.t ->
   uuid:string ->
@@ -235,14 +228,26 @@ val refresh_pack_info : __context:Context.t -> host:API.ref_host -> unit
 
 (** {2 Licensing} *)
 
-(** Attempt to activate the given edition (one of "free", "enterprise" or "platinum".
+(** Called by post-floodgate slaves to update the database AND recompute the pool_sku on the master *)
+val set_license_params :
+       __context:Context.t ->
+       self:[ `host ] Ref.t -> value:(string * string) list -> unit
+
+val copy_license_to_db :
+       __context:Context.t ->
+       host:[ `host ] Ref.t ->
+       features:Features.feature list -> additional:(string * string) list -> unit
+
+val license_apply : __context:Context.t -> host:API.ref_host -> contents:string -> unit
+
+(** Attempt to activate the given edition.
  *  In needed, the function automatically checks v6 licenses in and out
  *  from the license server (via the v6 daemon). If the requested edition is not
  *  available, the call will fail with an exception, leaving the edition as it is.
  *  Also call this function to change to a different license server, after the
  *  connection details in host.license_server have been amended. *)
 val apply_edition : __context:Context.t -> host:API.ref_host -> edition:string -> unit 
+
 
 (** {2 CPU Feature Masking} *)
  
index 21adc2bc70e2e3d0bcb28186fddd31dbdb876daf..b36e8dd533ad34730c7ddf58e16c5b4776ea19f4 100644 (file)
@@ -60,15 +60,23 @@ let pre_join_checks ~__context ~rpc ~session_id ~force =
 
        (* CA-26975: Pool edition MUST match *)
        let assert_restrictions_match () =
+               let editions = V6client.get_editions () in
+               let edition_to_int e =
+                       match List.find (fun (name, _, _, _) -> name = e) editions with _, _, _, a -> a
+               in
+               let min_edition l =
+                       List.fold_left (fun m e -> if edition_to_int e < edition_to_int m then e else m) (List.hd l) l
+               in
+               (* get pool edition: the "minimum" edition among all hosts *)
                let host_records = List.map snd (Client.Host.get_all_records ~rpc ~session_id) in
-               (* check pool edition *)
-               let pool_editions = List.map (fun host_r -> Edition.of_string host_r.API.host_edition) host_records in
-               let pool_edition = Edition.min pool_editions in
-               let my_edition = Edition.of_string (Db.Host.get_edition ~__context ~self:(Helpers.get_localhost ~__context)) in
-               if not (Edition.equal pool_edition my_edition) then begin
+               let pool_editions = List.map (fun host_r -> host_r.API.host_edition) host_records in
+               let pool_edition = min_edition pool_editions in
+               (* compare my edition to pool edition *)
+               let my_edition = Db.Host.get_edition ~__context ~self:(Helpers.get_localhost ~__context) in
+               if (edition_to_int pool_edition) <> (edition_to_int my_edition) then begin
                        error "Pool.join failed because of editions mismatch";
-                       error "Remote has %s" (Edition.to_string pool_edition);
-                       error "Local has  %s" (Edition.to_string my_edition);
+                       error "Remote has %s" pool_edition;
+                       error "Local has  %s" my_edition;
                        raise (Api_errors.Server_error(Api_errors.license_restriction, []))
                end
        in