[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-API] [PATCH 11 of 17] CP-2137: Switch to the new xapi<->v6d RPC interface
Signed-off-by: Rob Hoes <rob.hoes@xxxxxxxxxx> # HG changeset patch # User Rob Hoes <rob.hoes@xxxxxxxxxx> # Date 1294419412 0 # Node ID 4313f93b13635bdae6a8cea43bd52d10fb5d3a6c # Parent c1100dd68e16dae897e796633d90688063cd62fd CP-2137: Switch to the new xapi<->v6d RPC interface Signed-off-by: Rob Hoes <rob.hoes@xxxxxxxxxx> diff -r c1100dd68e16 -r 4313f93b1363 ocaml/idl/api_errors.ml --- a/ocaml/idl/api_errors.ml +++ b/ocaml/idl/api_errors.ml @@ -294,6 +294,7 @@ 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" diff -r c1100dd68e16 -r 4313f93b1363 ocaml/idl/datamodel.ml --- a/ocaml/idl/datamodel.ml +++ b/ocaml/idl/datamodel.ml @@ -326,8 +326,10 @@ 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_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" diff -r c1100dd68e16 -r 4313f93b1363 ocaml/idl/ocaml_backend/OMakefile --- a/ocaml/idl/ocaml_backend/OMakefile +++ b/ocaml/idl/ocaml_backend/OMakefile @@ -112,9 +112,11 @@ ../../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 @@ 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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/OMakefile --- a/ocaml/license/OMakefile +++ b/ocaml/license/OMakefile @@ -2,12 +2,14 @@ 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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/edition.ml --- a/ocaml/license/edition.ml +++ b/ocaml/license/edition.ml @@ -36,11 +36,11 @@ 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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/edition.mli --- a/ocaml/license/edition.mli +++ b/ocaml/license/edition.mli @@ -37,6 +37,9 @@ (** 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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/fakev6.ml --- a/ocaml/license/fakev6.ml +++ b/ocaml/license/fakev6.ml @@ -15,12 +15,21 @@ 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 @@ debug "Logfiles reopened"; true with _ -> false - + diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/fakev6.mli --- a/ocaml/license/fakev6.mli +++ b/ocaml/license/fakev6.mli @@ -15,10 +15,15 @@ (** 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 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 -(** Close and re-open the log file *) diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/grace_retry.ml --- a/ocaml/license/grace_retry.ml +++ /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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/license.ml --- a/ocaml/license/license.ml +++ b/ocaml/license/license.ml @@ -150,8 +150,6 @@ sku_marketing_name = Edition.to_marketing_name edition; } -let license : license ref = ref (default ()) - (* Calls to obtain info about license *) let check_expiry l = diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/license.mli --- a/ocaml/license/license.mli +++ b/ocaml/license/license.mli @@ -39,9 +39,6 @@ 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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/license_file.ml --- a/ocaml/license/license_file.ml +++ b/ocaml/license/license_file.ml @@ -41,7 +41,8 @@ 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 @@ (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" diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/license_file.mli --- a/ocaml/license/license_file.mli +++ b/ocaml/license/license_file.mli @@ -24,7 +24,7 @@ (** 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. *) diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/license_init.ml --- a/ocaml/license/license_init.ml +++ b/ocaml/license/license_init.ml @@ -12,91 +12,17 @@ * 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 -r c1100dd68e16 -r 4313f93b1363 ocaml/license/v6alert.ml --- a/ocaml/license/v6alert.ml +++ /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 -r c1100dd68e16 -r 4313f93b1363 ocaml/license/v6alert.mli --- a/ocaml/license/v6alert.mli +++ /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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/v6client.ml --- a/ocaml/license/v6client.ml +++ b/ocaml/license/v6client.ml @@ -12,156 +12,74 @@ * 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 +(* RPC function for communication with the v6 daemon *) let socket = "/var/xapi/v6" - -(* RPC function for communication with the v6 daemon *) 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 +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 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 - 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 = - 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 (); + | 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 - disconnect () - with _ -> reset_state () - + 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 + 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, [])) + diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/v6client.mli --- a/ocaml/license/v6client.mli +++ b/ocaml/license/v6client.mli @@ -16,22 +16,8 @@ * @group Licensing *) -(** {2 State variables} *) +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 -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 *) - diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/v6daemon.ml --- a/ocaml/license/v6daemon.ml +++ b/ocaml/license/v6daemon.ml @@ -28,12 +28,12 @@ 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 -r c1100dd68e16 -r 4313f93b1363 ocaml/license/v6errors.ml --- /dev/null +++ b/ocaml/license/v6errors.ml @@ -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" + diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/v6rpc.ml --- a/ocaml/license/v6rpc.ml +++ b/ocaml/license/v6rpc.ml @@ -12,31 +12,34 @@ * 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 @@ 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 (); - response_of_failure "INTERNAL_ERROR" [Printexc.to_string e] in + Rpc.failure (V6errors.rpc_of_error e) + | e -> + let e = Printexc.to_string e in + error "Error: %s" e; + log_backtrace (); + Rpc.failure (V6errors.rpc_of_error (V6errors.v6d_failure, [e])) + in response end diff -r c1100dd68e16 -r 4313f93b1363 ocaml/license/v6rpc.mli --- a/ocaml/license/v6rpc.mli +++ b/ocaml/license/v6rpc.mli @@ -17,12 +17,18 @@ (** 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 @@ (** {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 rpc_of_initialise_in : initialise_in -> Rpc.t +val apply_edition_in_of_rpc : Rpc.t -> apply_edition_in +val rpc_of_apply_edition_in : apply_edition_in -> Rpc.t -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; } -val initialise_out_of_rpc : Rpc.t -> initialise_out +val apply_edition_out_of_rpc : Rpc.t -> apply_edition_out +val rpc_of_apply_edition_out : apply_edition_out -> Rpc.t + +type names = string * string * string * int +type get_editions_out = { + editions: names list; +} + +val get_editions_out_of_rpc : Rpc.t -> get_editions_out +val rpc_of_get_editions_out : get_editions_out -> Rpc.t + diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/OMakefile --- a/ocaml/xapi/OMakefile +++ b/ocaml/xapi/OMakefile @@ -6,7 +6,7 @@ ../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 @@ xapi_mgmt_iface \ smint \ ../gpg/gpg \ - ../license/license \ helpers \ at_least_once_more \ create_templates \ @@ -235,14 +234,11 @@ 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)) diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/cli_frontend.ml --- a/ocaml/xapi/cli_frontend.ml +++ b/ocaml/xapi/cli_frontend.ml @@ -1911,7 +1911,7 @@ { 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=[]; }; diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/cli_operations.ml --- a/ocaml/xapi/cli_operations.ml +++ b/ocaml/xapi/cli_operations.ml @@ -144,28 +144,43 @@ 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 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 editions = V6client.get_editions () 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 @@ 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 __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 @@ 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 @@ 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 = diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/create_misc.ml --- a/ocaml/xapi/create_misc.ml +++ b/ocaml/xapi/create_misc.ml @@ -369,8 +369,11 @@ 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; diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/features.ml --- a/ocaml/xapi/features.ml +++ b/ocaml/xapi/features.ml @@ -37,6 +37,7 @@ | No_platform_filter | No_nag_dialog | VMPR + with rpc type orientation = Positive | Negative diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/features.mli --- a/ocaml/xapi/features.mli +++ b/ocaml/xapi/features.mli @@ -38,6 +38,9 @@ | 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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/license_check.ml --- a/ocaml/xapi/license_check.ml +++ b/ocaml/xapi/license_check.ml @@ -15,9 +15,7 @@ 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 @@ 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 = diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/license_check.mli --- a/ocaml/xapi/license_check.mli +++ b/ocaml/xapi/license_check.mli @@ -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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/xapi.ml --- a/ocaml/xapi/xapi.ml +++ b/ocaml/xapi/xapi.ml @@ -585,9 +585,7 @@ 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. *) diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/xapi_config.ml --- a/ocaml/xapi/xapi_config.ml +++ b/ocaml/xapi/xapi_config.ml @@ -98,7 +98,7 @@ 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 @@ 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 *) diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/xapi_globs.ml --- a/ocaml/xapi/xapi_globs.ml +++ b/ocaml/xapi/xapi_globs.ml @@ -46,9 +46,6 @@ 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 @@ _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" diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/xapi_ha.ml --- a/ocaml/xapi/xapi_ha.ml +++ b/ocaml/xapi/xapi_ha.ml @@ -439,17 +439,16 @@ ) 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 @@ 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 diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/xapi_host.ml --- a/ocaml/xapi/xapi_host.ml +++ b/ocaml/xapi/xapi_host.ml @@ -32,10 +32,6 @@ | 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 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 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 @@ 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 @@ 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 = + (* 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 + ) -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; - - 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 @@ 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 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 @@ try Db.SR.set_local_cache_enabled ~__context ~self:sr ~value:false with _ -> () - diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/xapi_host.mli --- a/ocaml/xapi/xapi_host.mli +++ b/ocaml/xapi/xapi_host.mli @@ -25,11 +25,6 @@ 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 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 @@ 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 @@ (** {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} *) diff -r c1100dd68e16 -r 4313f93b1363 ocaml/xapi/xapi_pool.ml --- a/ocaml/xapi/xapi_pool.ml +++ b/ocaml/xapi/xapi_pool.ml @@ -60,15 +60,23 @@ (* 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 Attachment:
xen-api.hg-11.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 |