Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

CLEARWATER: CP-4102: xapi licensing changes #1129

Merged
merged 12 commits into from

1 participant

@johnelse
Owner
  • Create field pool.license_state
  • Create API call pool.apply_edition
  • Add socket count to Host.cpu_info
  • Add database upgrade rule
  • Add a high-level testing framework and some tests
johnelse added some commits
@johnelse johnelse CP-4325: Split License_check.check_expiry up into two functions.
Signed-off-by: John Else <john.else@citrix.com>
e8fd8b2
@johnelse johnelse CP-4325: Implement pool.get_license_state
Signed-off-by: John Else <john.else@citrix.com>
eb9765a
@johnelse johnelse CP-4325: Add pool.license_state getter to CLI
Signed-off-by: John Else <john.else@citrix.com>
986649c
@johnelse johnelse Add Test_common.make_pool
Signed-off-by: John Else <john.else@citrix.com>
01718fa
@johnelse johnelse Add high-level unit testing framework
Signed-off-by: John Else <john.else@citrix.com>
c265aee
@johnelse johnelse CP-4325: Add tests for pool-wide license logic
Signed-off-by: John Else <john.else@citrix.com>
7e5566d
@johnelse johnelse Whitespace: create_misc.ml
$ camlp4o -printer o -no_comments ocaml/xapi/create_misc.ml | md5sum
016846314b732c13ecadc24ae47ec3f6  -
$ git checkout HEAD^
$ camlp4o -printer o -no_comments ocaml/xapi/create_misc.ml | md5sum
016846314b732c13ecadc24ae47ec3f6  -

Signed-off-by: John Else <john.else@citrix.com>
84aa08b
@johnelse johnelse CP-4319: Add field host.cpu_info:socket_count
Signed-off-by: John Else <john.else@citrix.com>
f800ae2
@johnelse johnelse CP-4319: Pass number of CPU sockets to v6d
Signed-off-by: John Else <john.else@citrix.com>
9985e83
@johnelse johnelse CP-4324: Implement pool.apply_edition
Signed-off-by: John Else <john.else@citrix.com>
8bef8fd
@johnelse johnelse CP-4324: Add pool.apply_edition to the CLI
Signed-off-by: John Else <john.else@citrix.com>
bbb01c4
@johnelse johnelse CP-4321: Host edition upgrade rules
Free and XenDesktop licenses are left as is.
Advanced, Enterprise and Platinum licenses are converted to per-socket
license.
Anything else is made into a free license.

Signed-off-by: John Else <john.else@citrix.com>
d47ca50
@johnelse
Owner

Duplicate of #1080 for Clearwater

@johnelse johnelse merged commit 4a16616 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 3, 2013
  1. @johnelse

    CP-4325: Split License_check.check_expiry up into two functions.

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  2. @johnelse

    CP-4325: Implement pool.get_license_state

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  3. @johnelse

    CP-4325: Add pool.license_state getter to CLI

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  4. @johnelse

    Add Test_common.make_pool

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  5. @johnelse

    Add high-level unit testing framework

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  6. @johnelse

    CP-4325: Add tests for pool-wide license logic

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  7. @johnelse

    Whitespace: create_misc.ml

    johnelse authored
    $ camlp4o -printer o -no_comments ocaml/xapi/create_misc.ml | md5sum
    016846314b732c13ecadc24ae47ec3f6  -
    $ git checkout HEAD^
    $ camlp4o -printer o -no_comments ocaml/xapi/create_misc.ml | md5sum
    016846314b732c13ecadc24ae47ec3f6  -
    
    Signed-off-by: John Else <john.else@citrix.com>
  8. @johnelse

    CP-4319: Add field host.cpu_info:socket_count

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  9. @johnelse

    CP-4319: Pass number of CPU sockets to v6d

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  10. @johnelse

    CP-4324: Implement pool.apply_edition

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  11. @johnelse

    CP-4324: Add pool.apply_edition to the CLI

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
  12. @johnelse

    CP-4321: Host edition upgrade rules

    johnelse authored
    Free and XenDesktop licenses are left as is.
    Advanced, Enterprise and Platinum licenses are converted to per-socket
    license.
    Anything else is made into a free license.
    
    Signed-off-by: John Else <john.else@citrix.com>
This page is out of date. Refresh to see the latest.
View
2  ocaml/client_records/records.ml
@@ -485,6 +485,8 @@ let pool_record rpc session_id pool =
~get_set:(fun () -> (x ()).API.pool_tags)
~add_to_set:(fun tag -> Client.Pool.add_tags rpc session_id pool tag)
~remove_from_set:(fun tag -> Client.Pool.remove_tags rpc session_id pool tag) ();
+ make_field ~name:"license-state"
+ ~get:(fun () -> Record_util.s2sm_to_string "; " (Client.Pool.get_license_state rpc session_id pool)) ();
]}
let subject_record rpc session_id subject =
View
24 ocaml/idl/datamodel.ml
@@ -6156,6 +6156,28 @@ let pool_disable_local_storage_caching = call
~allowed_roles:_R_POOL_OP
()
+let pool_get_license_state = call
+ ~name:"get_license_state"
+ ~in_oss_since:None
+ ~in_product_since:rel_clearwater
+ ~params:[Ref _pool, "self", "Reference to the pool"]
+ ~doc:"This call returns the license state for the pool"
+ ~allowed_roles:_R_READ_ONLY
+ ~result:(Map(String,String), "The pool's license state")
+ ()
+
+let pool_apply_edition = call
+ ~name:"apply_edition"
+ ~in_oss_since:None
+ ~in_product_since:rel_clearwater
+ ~params:[
+ Ref _pool, "self", "Reference to the pool";
+ String, "edition", "The requested edition";
+ ]
+ ~doc:"Apply an edition to all hosts in the pool"
+ ~allowed_roles:_R_POOL_OP
+ ()
+
(** A pool class *)
let pool =
create_obj
@@ -6220,6 +6242,8 @@ let pool =
; pool_test_archive_target
; pool_enable_local_storage_caching
; pool_disable_local_storage_caching
+ ; pool_get_license_state
+ ; pool_apply_edition
]
~contents:
[uid ~in_oss_since:None _pool
View
2  ocaml/test/OMakefile
@@ -23,9 +23,11 @@ OCAML_LIBS = \
OCAML_OBJS = \
../idl/api_lowlevel \
mock \
+ test_highlevel \
test_common \
test_basic \
test_pool_db_backup \
test_xapi_db_upgrade \
+ test_pool_license \
OCamlProgram(suite, suite $(OCAML_OBJS) )
View
1  ocaml/test/suite.ml
@@ -20,6 +20,7 @@ let base_suite =
Test_basic.test;
Test_pool_db_backup.test;
Test_xapi_db_upgrade.test;
+ Test_pool_license.test;
]
let _ = run_test_tt_main base_suite
View
20 ocaml/test/test_common.ml
@@ -83,3 +83,23 @@ let make_pif ~__context ~network ~host ?(device="eth0") ?(mAC="C0:FF:EE:C0:FF:EE
let make_network ~__context ?(name_label="net") ?(name_description="description") ?(mTU=1500L)
?(other_config=[]) ?(bridge="xenbr0") () =
Xapi_network.pool_introduce ~__context ~name_label ~name_description ~mTU ~other_config ~bridge
+
+let make_pool ~__context ~master ?(name_label="") ?(name_description="")
+ ?(default_SR=Ref.null) ?(suspend_image_SR=Ref.null) ?(crash_dump_SR=Ref.null)
+ ?(ha_enabled=false) ?(ha_configuration=[]) ?(ha_statefiles=[])
+ ?(ha_host_failures_to_tolerate=0L) ?(ha_plan_exists_for=0L)
+ ?(ha_allow_overcommit=false) ?(ha_overcommitted=false) ?(blobs=[]) ?(tags=[])
+ ?(gui_config=[]) ?(wlb_url="") ?(wlb_username="") ?(wlb_password=Ref.null)
+ ?(wlb_enabled=false) ?(wlb_verify_cert=false) ?(redo_log_enabled=false)
+ ?(redo_log_vdi=Ref.null) ?(vswitch_controller="") ?(restrictions=[])
+ ?(other_config=[Xapi_globs.memory_ratio_hvm; Xapi_globs.memory_ratio_pv]) () =
+ let pool_ref = Ref.make () in
+ Db.Pool.create ~__context ~ref:pool_ref
+ ~uuid:(Uuid.to_string (Uuid.make_uuid ())) ~name_label ~name_description
+ ~master ~default_SR ~suspend_image_SR ~crash_dump_SR ~ha_enabled
+ ~ha_configuration ~ha_statefiles ~ha_host_failures_to_tolerate
+ ~ha_plan_exists_for ~ha_allow_overcommit ~ha_overcommitted ~blobs ~tags
+ ~gui_config ~wlb_url ~wlb_username ~wlb_password ~wlb_enabled
+ ~wlb_verify_cert ~redo_log_enabled ~redo_log_vdi ~vswitch_controller
+ ~restrictions ~other_config;
+ pool_ref
View
101 ocaml/test/test_highlevel.ml
@@ -0,0 +1,101 @@
+(*
+ * Copyright (C) 2006-2013 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.
+ *)
+
+(* This framework should be useful if you want to apply lots of different
+ * inputs to a function or system, and check that the output is correct in
+ * each case.
+ *
+ * The basic idea is to either:
+ *
+ * 1. Create a module of type STATELESS_TEST or
+ * 2. Create a module of type STATEFUL_TEST and turn it into a STATELESS_TEST
+ * with the EncapsulateState functor
+ *
+ * and then pass the STATELESS_TEST into the Make functor.
+ *
+ * This gives you a module with the functions test_equal and
+ * test_equal_multiple, which take an input-output pair or a list of
+ * input-output pairs and check that the output of the system is as expected
+ * for each input.
+ *)
+
+open OUnit
+
+module Generic = struct
+ module type IO = sig
+ (* The type of inputs to a system being tested. *)
+ type input_t
+ (* The type of outputs from a system being tested. *)
+ type output_t
+
+ (* Helper functions for printing error messages on test failure. *)
+ val string_of_input_t : input_t -> string
+ val string_of_output_t : output_t -> string
+ end
+
+ module type STATE = sig
+ (* The type of system state, which will be modified by inputs to the system. *)
+ type state_t
+ (* Create a base system state. *)
+ val create_default_state : unit -> state_t
+ end
+
+ module type STATELESS_TEST = sig
+ module Io : IO
+ (* A function to transform an input into an output. *)
+ val transform : Io.input_t -> Io.output_t
+ end
+
+ module type STATEFUL_TEST = sig
+ module Io : IO
+ module State : STATE
+ (* A function to apply an input to the system state. *)
+ val load_input : State.state_t -> Io.input_t -> unit
+ (* A function to extract an output from the system state. *)
+ val extract_output : State.state_t -> Io.output_t
+ end
+
+ (* Turn a stateful test module into a stateless test module. *)
+ module EncapsulateState(T: STATEFUL_TEST) = struct
+ module Io = T.Io
+
+ let transform input =
+ let state = T.State.create_default_state () in
+ T.load_input state input;
+ T.extract_output state
+ end
+
+ (* Create functions which will actually run a test or tests. *)
+ module Make(T: STATELESS_TEST) = struct
+ let test_equal ~input ~expected_output =
+ let actual_output = T.transform input in
+ assert_equal
+ ~msg:(Printf.sprintf
+ "Failure: input = %s, output = %s, expected output = %s"
+ (T.Io.string_of_input_t input)
+ (T.Io.string_of_output_t actual_output)
+ (T.Io.string_of_output_t expected_output))
+ actual_output expected_output
+
+ let test_equal_multiple ~input_output_pairs =
+ List.iter
+ (fun (input, expected_output) -> test_equal ~input ~expected_output)
+ input_output_pairs
+ end
+end
+
+module XapiDb : Generic.STATE with type state_t = Context.t = struct
+ type state_t = Context.t
+ let create_default_state () = Mock.make_context_with_new_db "test context"
+end
View
234 ocaml/test/test_pool_license.ml
@@ -0,0 +1,234 @@
+(*
+ * Copyright (C) 2006-2013 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 Fun
+open OUnit
+open Test_highlevel
+
+type host_license_state = {
+ license_params: (string * string) list;
+ edition: string;
+}
+
+let string_of_string_map map =
+ Printf.sprintf "[%s]"
+ (List.map (fun (k, v) -> k ^ ": " ^ v) map |> String.concat "; ")
+
+let string_of_host_license_state state =
+ Printf.sprintf "{license_params = %s; edition = %s"
+ (string_of_string_map state.license_params)
+ state.edition
+
+let string_of_date_opt = function
+ | None -> "None"
+ | Some date -> Printf.sprintf "Some %s" (Date.to_string date)
+
+let f2d = Date.of_float
+let f2d2s = Date.to_string ++ Date.of_float
+
+module CompareDates = struct
+ module Tests = Generic.Make(struct
+ module Io = struct
+ type input_t = (Date.iso8601 option * Date.iso8601 option)
+ type output_t = int
+
+ let string_of_input_t input =
+ Printf.sprintf "(%s, %s)"
+ (string_of_date_opt (fst input))
+ (string_of_date_opt (snd input))
+
+ let string_of_output_t = string_of_int
+ end
+
+ let transform (date1, date2) = Xapi_pool_license.compare_dates date1 date2
+ end)
+
+ (* Tuples of ((value 1, value 2), expected result from comparing values) *)
+ let compare_date_tests = [
+ ((None, None), 0);
+ ((None, Some (f2d 5.0)), 1);
+ ((Some (f2d 10.0), Some (f2d 5.0)), 1);
+ ((Some (f2d 15.0), None), -1);
+ ((Some (f2d 20.0), Some (f2d 30.0)), -1);
+ ((Some (f2d 150.0), Some (f2d 150.0)), 0);
+ ]
+
+ (* Compare each pair of date options, and check that the result is what we expect. *)
+ let test () =
+ Tests.test_equal_multiple ~input_output_pairs:compare_date_tests
+end
+
+module PoolExpiryDate = struct
+ module Tests = Generic.Make(Generic.EncapsulateState(struct
+ module Io = struct
+ type input_t = Date.iso8601 option list
+ type output_t = Date.iso8601 option
+
+ let string_of_input_t input =
+ Printf.sprintf "[%s]"
+ (input |> List.map string_of_date_opt |> String.concat "; ")
+
+ let string_of_output_t = string_of_date_opt
+ end
+ module State = XapiDb
+
+ (* Create a host in the database for each expiry date in the list. *)
+ let load_input __context expiry_dates =
+ List.iter
+ (fun expiry_date ->
+ let license_params = match expiry_date with
+ | None -> []
+ | Some date -> ["expiry", (Date.to_string date)]
+ in
+ let (_: API.ref_host) = Test_common.make_host ~__context ~license_params () in ())
+ expiry_dates
+
+ let extract_output __context =
+ let hosts = Db.Host.get_all ~__context in
+ Xapi_pool_license.get_earliest_expiry_date ~__context ~hosts
+ end))
+
+ (* Tuples of ((host expiry date) list, expected pool expiry date) *)
+ let expiry_date_tests = [
+ ([None; None; Some (f2d 500.0); None], Some (f2d 500.0));
+ ([None; None; None; None], None);
+ ([Some (f2d 100.0)], Some (f2d 100.0));
+ ([Some (f2d 300.0); Some (f2d 150.0); Some (f2d 450.0)], Some (f2d 150.0));
+ ([None; Some (f2d 650.0); None; Some (f2d 350.0)], Some (f2d 350.0));
+ ]
+
+ let test () =
+ Tests.test_equal_multiple ~input_output_pairs:expiry_date_tests
+end
+
+module PoolEdition = struct
+ module Tests = Generic.Make(Generic.EncapsulateState(struct
+ module Io = struct
+ type input_t = string list
+ type output_t = string
+
+ let string_of_input_t input =
+ Printf.sprintf "[%s]" (String.concat "; " input)
+ let string_of_output_t = (fun x -> x)
+ end
+ module State = XapiDb
+
+ (* Create a host for each edition in the list. *)
+ let load_input __context editions =
+ List.iter
+ (fun edition ->
+ let (_: API.ref_host) = Test_common.make_host ~__context ~edition () in ())
+ editions
+
+ let extract_output __context =
+ let hosts = Db.Host.get_all ~__context in
+ Xapi_pool_license.get_lowest_edition ~__context ~hosts
+ end))
+
+ (* Tuples of ((host edition) list, expected pool edition) *)
+ let pool_edition_tests = [
+ (["free"], "free");
+ (["free"; "per-socket"; "free"; "per-socket"], "free");
+ (["xendesktop"; "xendesktop"; "xendesktop"; "xendesktop"], "xendesktop");
+ (["per-socket"; "per-socket"; "per-socket"], "per-socket");
+ (["xendesktop"; "xendesktop"; "free"; "free"], "free");
+ ]
+
+ let test () =
+ Tests.test_equal_multiple ~input_output_pairs:pool_edition_tests
+end
+
+module PoolLicenseState = struct
+ module Tests = Generic.Make(Generic.EncapsulateState(struct
+ module Io = struct
+ type input_t = host_license_state list
+ type output_t = (string * string) list
+
+ let string_of_input_t input =
+ Printf.sprintf "[%s]"
+ (List.map string_of_host_license_state input |> String.concat "; ")
+ let string_of_output_t = string_of_string_map
+ end
+ module State = XapiDb
+
+ (* For each (license_params, edition) pair, create a host.
+ * Also create a pool object. *)
+ let load_input __context hosts =
+ List.iter
+ (fun host ->
+ let (_: API.ref_host) =
+ Test_common.make_host ~__context
+ ~edition:host.edition
+ ~license_params:host.license_params () in ())
+ hosts;
+ let (_: API.ref_pool) =
+ Test_common.make_pool ~__context
+ ~master:(List.hd (Db.Host.get_all ~__context)) () in ()
+
+ let extract_output __context =
+ let pool = Helpers.get_pool ~__context in
+ Xapi_pool.get_license_state ~__context ~self:pool
+ end))
+
+ (* Tuples of (host_license_state list, expected pool license state) *)
+ let pool_license_tests = [
+ (* A pool of free edition hosts, none of which has an expiry date. *)
+ ([
+ {license_params = []; edition = "free"};
+ {license_params = []; edition = "free"};
+ {license_params = []; edition = "free"};
+ ],
+ ["edition", "free"; "expiry", "never"]);
+ (* A pool of per-socket edition hosts, of which two have expiry dates. *)
+ ([
+ {license_params = []; edition = "per-socket"};
+ {license_params = ["expiry", f2d2s 500.0]; edition = "per-socket"};
+ {license_params = ["expiry", f2d2s 350.0]; edition = "per-socket"};
+ ],
+ ["edition", "per-socket"; "expiry", f2d2s 350.0]);
+ (* A pool of per-socket edition hosts, of which none have expiry dates. *)
+ ([
+ {license_params = []; edition = "per-socket"};
+ {license_params = []; edition = "per-socket"};
+ {license_params = []; edition = "per-socket"};
+ ],
+ ["edition", "per-socket"; "expiry", "never"]);
+ (* A pool of xendesktop edition hosts, of which none have expiry dates. *)
+ ([
+ {license_params = []; edition = "xendesktop"};
+ {license_params = []; edition = "xendesktop"};
+ {license_params = []; edition = "xendesktop"};
+ ],
+ ["edition", "xendesktop"; "expiry", "never"]);
+ (* A pool of hosts, some per-socket (with different expiry dates) and some free. *)
+ ([
+ {license_params = ["expiry", f2d2s 5000.0]; edition = "per-socket"};
+ {license_params = []; edition = "free"};
+ {license_params = ["expiry", f2d2s 6000.0]; edition = "per-socket"};
+ ],
+ ["edition", "free"; "expiry", "never"]);
+ ]
+
+ let test () =
+ Tests.test_equal_multiple ~input_output_pairs:pool_license_tests
+end
+
+let test =
+ "pool_license" >:::
+ [
+ "test_compare_dates" >:: CompareDates.test;
+ "test_pool_expiry_date" >:: PoolExpiryDate.test;
+ "test_pool_edition" >:: PoolEdition.test;
+ "test_pool_license_state" >:: PoolLicenseState.test;
+ ]
View
1  ocaml/xapi/OMakefile
@@ -118,6 +118,7 @@ XAPI_MODULES = $(COMMON) \
xapi_http \
xapi_host_patch \
xapi_pool_patch \
+ xapi_pool_license \
xapi_support \
xapi_session \
xapi_auth \
View
9 ocaml/xapi/cli_frontend.ml
@@ -486,6 +486,15 @@ let rec cmdtable_data : (string*cmd_spec) list =
flags=[];
};
+ "pool-apply-edition",
+ {
+ reqd=["edition"];
+ optn=["uuid"];
+ help="Apply an edition across the pool";
+ implementation=No_fd Cli_operations.pool_apply_edition;
+ flags=[];
+ };
+
"host-shutdown",
{
reqd=[];
View
11 ocaml/xapi/cli_operations.ml
@@ -3686,6 +3686,17 @@ let pool_disable_local_storage_caching printer rpc session_id params =
let pool = List.hd (Client.Pool.get_all rpc session_id) in
Client.Pool.disable_local_storage_caching rpc session_id pool
+let pool_apply_edition printer rpc session_id params =
+ let pool =
+ if (List.mem_assoc "uuid" params) then (*user provided a pool uuid*)
+ let pool_uuid = List.assoc "uuid" params in
+ Client.Pool.get_by_uuid rpc session_id pool_uuid
+ else (*user didn't provide a pool uuid: let's fetch the default pool*)
+ List.hd (Client.Pool.get_all rpc session_id)
+ in
+ let edition = List.assoc "edition" params in
+ Client.Pool.apply_edition rpc session_id pool edition
+
let host_set_power_on_mode printer rpc session_id params =
let power_on_mode = List.assoc "power-on-mode" params in
let power_on_config = read_map_params "power-on-config" params in
View
126 ocaml/xapi/create_misc.ml
@@ -75,20 +75,21 @@ let read_localhost_info () =
let result = List.fold_left Int64.add 0L values in
Int64.mul 1024L result in
- {name_label=this_host_name;
- xen_verstring=xen_verstring;
- linux_verstring=linux_verstring;
- hostname=this_host_name;
- uuid=me;
- dom0_uuid = Xapi_inventory.lookup Xapi_inventory._control_domain_uuid;
- oem_manufacturer = lookup_inventory_nofail Xapi_inventory._oem_manufacturer;
- oem_model = lookup_inventory_nofail Xapi_inventory._oem_model;
- oem_build_number = lookup_inventory_nofail Xapi_inventory._oem_build_number;
- machine_serial_number = lookup_inventory_nofail Xapi_inventory._machine_serial_number;
- machine_serial_name = lookup_inventory_nofail Xapi_inventory._machine_serial_name;
- total_memory_mib = total_memory_mib;
- dom0_static_max = dom0_static_max;
- }
+ {
+ name_label=this_host_name;
+ xen_verstring=xen_verstring;
+ linux_verstring=linux_verstring;
+ hostname=this_host_name;
+ uuid=me;
+ dom0_uuid = Xapi_inventory.lookup Xapi_inventory._control_domain_uuid;
+ oem_manufacturer = lookup_inventory_nofail Xapi_inventory._oem_manufacturer;
+ oem_model = lookup_inventory_nofail Xapi_inventory._oem_model;
+ oem_build_number = lookup_inventory_nofail Xapi_inventory._oem_build_number;
+ machine_serial_number = lookup_inventory_nofail Xapi_inventory._machine_serial_number;
+ machine_serial_name = lookup_inventory_nofail Xapi_inventory._machine_serial_name;
+ total_memory_mib = total_memory_mib;
+ dom0_static_max = dom0_static_max;
+ }
(** Returns the maximum of two values. *)
let maximum x y = if x > y then x else y
@@ -126,10 +127,10 @@ and ensure_domain_zero_record ~__context (host_info: host_info): [`VM] Ref.t =
and ensure_domain_zero_console_record ~__context ~domain_zero_ref : unit =
let dom0_consoles = Db.VM.get_consoles ~__context ~self: domain_zero_ref in
- let console_records_rfb = List.filter (fun x -> Db.Console.get_protocol ~__context ~self:x = `rfb) dom0_consoles in
+ let console_records_rfb = List.filter (fun x -> Db.Console.get_protocol ~__context ~self:x = `rfb) dom0_consoles in
let console_records_vt100 = List.filter (fun x -> Db.Console.get_protocol ~__context ~self:x = `vt100) dom0_consoles in
- match List.length console_records_rfb, List.length console_records_vt100 with
+ match List.length console_records_rfb, List.length console_records_vt100 with
| 1, 1 -> debug "1 RFB, 1 VT100 console found";
| _ ->
(* if there's not more than one console of each type then something strange is happening*)
@@ -190,12 +191,11 @@ and create_domain_zero_record ~__context ~domain_zero_ref (host_info: host_info)
~start_delay:0L
~shutdown_delay:0L
~order:0L
- ~suspend_SR:Ref.null
- ~version:0L
- ;
+ ~suspend_SR:Ref.null
+ ~version:0L;
Xapi_vm_helpers.update_memory_overhead ~__context ~vm:domain_zero_ref
-and create_domain_zero_console_record_with_protocol ~__context ~domain_zero_ref ~dom0_console_protocol =
+and create_domain_zero_console_record_with_protocol ~__context ~domain_zero_ref ~dom0_console_protocol =
let console_ref = Ref.make () in
let address = Db.Host.get_address ~__context ~self: (Helpers.get_localhost ~__context) in
let location = Printf.sprintf "https://%s%s?ref=%s" address Constants.console_uri (Ref.string_of domain_zero_ref) in
@@ -253,16 +253,16 @@ and create_domain_zero_default_memory_constraints host_info : Vm_memory_constrai
dynamic_max = vm.Vm.memory_dynamic_max;
target = state.Vm.memory_target;
}
- with _ ->
- let target = static_min +++ (Int64.(mul 100L (mul 1024L 1024L))) in
- let target = if target > static_max then static_max else target in
- {
- static_min = static_min;
- dynamic_min = target;
- target = target;
- dynamic_max = target;
- static_max = static_max;
- }
+ with _ ->
+ let target = static_min +++ (Int64.(mul 100L (mul 1024L 1024L))) in
+ let target = if target > static_max then static_max else target in
+ {
+ static_min = static_min;
+ dynamic_min = target;
+ target = target;
+ dynamic_max = target;
+ static_max = static_max;
+ }
and update_domain_zero_record ~__context ~domain_zero_ref (host_info: host_info) : unit =
(* Fetch existing memory constraints for domain 0. *)
@@ -321,8 +321,8 @@ let create_root_user ~__context =
if all = [] then Db.User.create ~__context ~ref ~fullname ~short_name ~uuid ~other_config:[]
let get_xapi_verstring () =
- Printf.sprintf "%d.%d" Xapi_globs.version_major Xapi_globs.version_minor
-
+ Printf.sprintf "%d.%d" Xapi_globs.version_major Xapi_globs.version_minor
+
(** Create assoc list of Supplemental-Pack information.
* The package information is taking from the [XS-REPOSITORY] XML file in the package
* directory.
@@ -385,12 +385,12 @@ let make_software_version () =
Xapi_globs.software_version @
(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;
- "xencenter_max", Xapi_globs.xencenter_max_verstring;
- "network_backend", Netdev.string_of_kind Netdev.network.Netdev.kind;
+ "xapi", get_xapi_verstring ();
+ "xen", info.xen_verstring;
+ "linux", info.linux_verstring;
+ "xencenter_min", Xapi_globs.xencenter_min_verstring;
+ "xencenter_max", Xapi_globs.xencenter_max_verstring;
+ "network_backend", Netdev.string_of_kind Netdev.network.Netdev.kind;
] @
(option_to_list "oem_manufacturer" info.oem_manufacturer) @
(option_to_list "oem_model" info.oem_model) @
@@ -400,14 +400,18 @@ let make_software_version () =
make_packs_info ()
let create_host_cpu ~__context =
- let get_nb_cpus () =
- let xc = Xenctrl.interface_open () in
- let p = Xenctrl.physinfo xc in
- Xenctrl.interface_close xc;
- p.Xenctrl.Phys_info.nr_cpus
+ let get_cpu_layout () =
+ let open Xenctrl in
+ let p = Vmopshelpers.with_xc (fun xc -> physinfo xc) in
+ let cpu_count = p.Phys_info.nr_cpus in
+ let socket_count =
+ p.Phys_info.nr_cpus /
+ (p.Phys_info.threads_per_core * p.Phys_info.cores_per_socket)
in
+ cpu_count, socket_count
+ in
let trim_end s =
- let i = ref (String.length s - 1) in
+ let i = ref (String.length s - 1) in
while !i > 0 && (List.mem s.[!i] [ ' '; '\t'; '\n'; '\r' ])
do
decr i
@@ -420,9 +424,9 @@ let create_host_cpu ~__context =
modified dom0's VCPUs we don't change out host config... [Important to get this right, otherwise
pool homogeneity checks fail] *)
let get_cpuinfo () =
- let cpu_info_file =
- try Unix.access Xapi_globs.cpu_info_file [ Unix.F_OK ]; Xapi_globs.cpu_info_file
- with _ -> "/proc/cpuinfo" in
+ let cpu_info_file =
+ try Unix.access Xapi_globs.cpu_info_file [ Unix.F_OK ]; Xapi_globs.cpu_info_file
+ with _ -> "/proc/cpuinfo" in
let in_chan = open_in cpu_info_file in
let tbl = Hashtbl.create 32 in
let rec get_lines () =
@@ -432,10 +436,11 @@ let create_host_cpu ~__context =
else (
let i = String.index s ':' in
let k = trim_end (String.sub s 0 i) in
- let v = if String.length s < i + 2 then
- ""
- else
- String.sub s (i + 2) (String.length s - i - 2) in
+ let v =
+ if String.length s < i + 2
+ then ""
+ else String.sub s (i + 2) (String.length s - i - 2)
+ in
Hashtbl.add tbl k v;
get_lines ()
)
@@ -446,12 +451,12 @@ let create_host_cpu ~__context =
Hashtbl.find tbl "model name",
Hashtbl.find tbl "cpu MHz",
Hashtbl.find tbl "flags",
- Hashtbl.find tbl "stepping",
- Hashtbl.find tbl "model",
- Hashtbl.find tbl "cpu family"
+ Hashtbl.find tbl "stepping",
+ Hashtbl.find tbl "model",
+ Hashtbl.find tbl "cpu family"
in
let vendor, modelname, cpu_mhz, flags, stepping, model, family = get_cpuinfo () in
- let number = get_nb_cpus () in
+ let cpu_count, socket_count = get_cpu_layout () in
let host = Helpers.get_localhost ~__context in
(* Fill in Host.cpu_info *)
@@ -465,7 +470,8 @@ let create_host_cpu ~__context =
| Cpuid.Full -> "full"
in
let cpu = [
- "cpu_count", string_of_int number;
+ "cpu_count", string_of_int cpu_count;
+ "socket_count", string_of_int socket_count;
"vendor", vendor;
"speed", cpu_mhz;
"modelname", modelname;
@@ -479,8 +485,8 @@ let create_host_cpu ~__context =
"maskable", maskable;
] in
Db.Host.set_cpu_info ~__context ~self:host ~value:cpu;
-
- (* Recreate all Host_cpu objects *)
+
+ (* Recreate all Host_cpu objects *)
let speed = Int64.of_float (float_of_string cpu_mhz) in
let model = Int64.of_string model in
@@ -489,10 +495,10 @@ let create_host_cpu ~__context =
(* Recreate all Host_cpu objects *)
let host_cpus = List.filter (fun (_, s) -> s.API.host_cpu_host = host) (Db.Host_cpu.get_all_records ~__context) in
List.iter (fun (r, _) -> Db.Host_cpu.destroy ~__context ~self:r) host_cpus;
- for i = 0 to number - 1
+ for i = 0 to cpu_count - 1
do
let uuid = Uuid.to_string (Uuid.make_uuid ())
- and ref = Ref.make () in
+ and ref = Ref.make () in
debug "Creating CPU %d: %s" i uuid;
ignore (Db.Host_cpu.create ~__context ~ref ~uuid ~host ~number:(Int64.of_int i)
~vendor ~speed ~modelname
View
17 ocaml/xapi/license_check.ml
@@ -14,16 +14,17 @@
module L = Debug.Debugger(struct let name="license" end)
open Stringext
-let check_expiry ~__context ~host =
+let get_expiry_date ~__context ~host =
let license = Db.Host.get_license_params ~__context ~self:host in
+ if List.mem_assoc "expiry" license
+ then Some (Date.of_string (List.assoc "expiry" license))
+ else None
+
+let check_expiry ~__context ~host =
let expired =
- if List.mem_assoc "expiry" license = false then
- (* No expiry date means no expiry :) *)
- false
- else begin
- let expiry = (Date.to_float (Date.of_string (List.assoc "expiry" license))) in
- Unix.time () > expiry
- end
+ match get_expiry_date ~__context ~host with
+ | None -> false (* No expiry date means no expiry :) *)
+ | Some date -> Unix.time () > (Date.to_float date)
in
if expired then raise (Api_errors.Server_error (Api_errors.license_expired, []))
View
4 ocaml/xapi/license_check.mli
@@ -16,6 +16,10 @@
* @group Licensing
*)
+(** Returns (Some date) if the host's license has an expiry date,
+ * otherwise returns None. *)
+val get_expiry_date : __context:Context.t -> host:API.ref_host -> Date.iso8601 option
+
(** Raises {!Api_errors.license_expired} if the current license has expired. *)
val check_expiry : __context:Context.t -> host:API.ref_host -> unit
View
8 ocaml/xapi/message_forwarding.ml
@@ -710,6 +710,14 @@ module Forward = functor(Local: Custom_actions.CUSTOM_ACTIONS) -> struct
let set_vswitch_controller ~__context ~address =
info "Pool.set_vswitch_controller: pool = '%s'; address = '%s'" (current_pool_uuid ~__context) address;
Local.Pool.set_vswitch_controller ~__context ~address
+
+ let get_license_state ~__context ~self =
+ info "Pool.get_license_state: pool = '%s'" (pool_uuid ~__context self);
+ Local.Pool.get_license_state ~__context ~self
+
+ let apply_edition ~__context ~self ~edition =
+ info "Pool.apply_edition: pool = '%s'; edition = '%s'" (pool_uuid ~__context self) edition;
+ Local.Pool.apply_edition ~__context ~self ~edition
end
module VM = struct
View
20 ocaml/xapi/xapi_db_upgrade.ml
@@ -46,6 +46,7 @@ let apply_upgrade_rules ~__context rules previous_version =
let george = Datamodel.george_release_schema_major_vsn, Datamodel.george_release_schema_minor_vsn
let cowley = Datamodel.cowley_release_schema_major_vsn, Datamodel.cowley_release_schema_minor_vsn
let boston = Datamodel.boston_release_schema_major_vsn, Datamodel.boston_release_schema_minor_vsn
+let tampa = Datamodel.tampa_release_schema_major_vsn, Datamodel.tampa_release_schema_minor_vsn
let upgrade_vm_memory_overheads = {
description = "Upgrade VM.memory_overhead fields";
@@ -311,6 +312,24 @@ let upgrade_pif_metrics = {
) phy_and_bond_pifs
}
+let upgrade_host_editions = {
+ description = "Upgrading host editions";
+ version = (fun x -> x <= tampa);
+ fn = fun ~__context ->
+ let hosts = Db.Host.get_all ~__context in
+ List.iter
+ (fun host ->
+ match Db.Host.get_edition ~__context ~self:host with
+ | "free" -> ()
+ | "enterprise-xd" ->
+ Db.Host.set_edition ~__context ~self:host ~value:"xendesktop"
+ | "advanced" | "enterprise" | "platinum" ->
+ Db.Host.set_edition ~__context ~self:host ~value:"per-socket"
+ | _ ->
+ Db.Host.set_edition ~__context ~self:host ~value:"free")
+ hosts
+}
+
let rules = [
upgrade_vm_memory_overheads;
upgrade_wlb_configuration;
@@ -323,6 +342,7 @@ let rules = [
upgrade_cpu_flags;
upgrade_auto_poweron;
upgrade_pif_metrics;
+ upgrade_host_editions;
]
(* Maybe upgrade most recent db *)
View
7 ocaml/xapi/xapi_host.ml
@@ -1234,7 +1234,12 @@ let apply_edition ~__context ~host ~edition =
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
+ let cpu_info = Db.Host.get_cpu_info ~__context ~self:host in
+ let socket_count = List.assoc "socket_count" cpu_info in
+ let edition', features, additional =
+ V6client.apply_edition ~__context edition
+ ["sockets", socket_count]
+ in
Db.Host.set_edition ~__context ~self:host ~value:edition';
copy_license_to_db ~__context ~host ~features ~additional
end
View
29 ocaml/xapi/xapi_pool.ml
@@ -1695,3 +1695,32 @@ let disable_local_storage_caching ~__context ~self =
if List.length failed_hosts > 0 then
raise (Api_errors.Server_error (Api_errors.hosts_failed_to_disable_caching, List.map Ref.string_of failed_hosts))
else ()
+
+let get_license_state ~__context ~self =
+ let hosts = Db.Host.get_all ~__context in
+ let pool_edition = Xapi_pool_license.get_lowest_edition ~__context ~hosts in
+ (* If any hosts are free edition then the pool is free edition with no expiry
+ * date. If all hosts are licensed then the pool has that license, and the
+ * earliest expiry date applies to the pool. *)
+ let pool_expiry_date =
+ match pool_edition with
+ | "free" -> "never"
+ | _ -> begin
+ match Xapi_pool_license.get_earliest_expiry_date ~__context ~hosts with
+ | None -> "never"
+ | Some date -> Date.to_string date
+ end
+ in
+ [
+ "edition", pool_edition;
+ "expiry", pool_expiry_date;
+ ]
+
+let apply_edition ~__context ~self ~edition =
+ let hosts = Db.Host.get_all ~__context in
+ let apply_fn =
+ (fun ~__context ~host ~edition -> Helpers.call_api_functions ~__context
+ (fun rpc session_id ->
+ Client.Host.apply_edition ~rpc ~session_id ~host ~edition))
+ in
+ Xapi_pool_license.apply_edition_with_rollback ~__context ~hosts ~edition ~apply_fn
View
3  ocaml/xapi/xapi_pool.mli
@@ -192,3 +192,6 @@ val audit_log_append : __context:Context.t -> line:string -> unit
val test_archive_target : __context:Context.t -> self:API.ref_pool -> config:API.string_to_string_map -> string
val enable_local_storage_caching : __context:Context.t -> self:API.ref_pool -> unit
val disable_local_storage_caching : __context:Context.t -> self:API.ref_pool -> unit
+
+val get_license_state : __context:Context.t -> self:API.ref_pool -> (string * string) list
+val apply_edition : __context:Context.t -> self:API.ref_pool -> edition:string -> unit
View
79 ocaml/xapi/xapi_pool_license.ml
@@ -0,0 +1,79 @@
+(*
+ * Copyright (C) 2006-2013 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 Fun
+
+module D=Debug.Debugger(struct let name="xapi" end)
+open D
+
+(* Compare two date options, where None is always greater than (Some _) *)
+let compare_dates (a: Date.iso8601 option) (b: Date.iso8601 option) =
+ match a, b with
+ | None, None -> 0
+ | None, Some _ -> 1
+ | Some _, None -> -1
+ | Some a', Some b' -> compare a' b'
+
+(* Get the earliest expiry date of a list of hosts. *)
+let get_earliest_expiry_date ~__context ~hosts =
+ List.map (fun host -> License_check.get_expiry_date ~__context ~host) hosts
+ |> List.sort compare_dates
+ |> List.hd
+
+(* If any hosts are free edition, then the pool is free edition.
+ * Otherwise, the pool has the same edition as the first host.
+ * We assume that the pool won't contain a mixture of xendesktop and
+ * per-socket licenses. *)
+let get_lowest_edition ~__context ~hosts =
+ let all_editions =
+ List.map
+ (fun host -> Db.Host.get_edition ~__context ~self:host)
+ hosts
+ in
+ if List.mem "free" all_editions
+ then "free"
+ else List.hd all_editions
+
+(* Separate this logic out from Xapi_pool.apply_edition for testing purposes. *)
+let apply_edition_with_rollback ~__context ~hosts ~edition ~apply_fn =
+ (* Filter out the hosts which already have the correct edition;
+ * list the other hosts against the edition we're upgrading *from*. *)
+ let to_apply =
+ List.fold_left
+ (fun acc host ->
+ let old_edition = Db.Host.get_edition ~__context ~self:host in
+ if old_edition <> edition
+ then (host, old_edition) :: acc
+ else acc)
+ [] hosts
+ in
+ (* This list will be added to as hosts have the new edition applied. *)
+ let to_rollback = ref [] in
+ try
+ List.iter
+ (fun (host, old_edition) ->
+ apply_fn ~__context ~host ~edition;
+ to_rollback := (host, old_edition) :: !to_rollback)
+ to_apply
+ with e ->
+ debug
+ "Caught %s while trying to upgrade pool to edition %s - attempting rollback"
+ (Printexc.to_string e) edition;
+ (* Best-effort attempt to roll everything back. *)
+ List.iter
+ (fun (host, old_edition) ->
+ try apply_fn ~__context ~host ~edition:old_edition with _ -> ())
+ !to_rollback;
+ (* Raise the original exception. *)
+ raise e
Something went wrong with that request. Please try again.