Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

CP-4102: xapi licensing changes #1080

Merged
merged 12 commits into from

3 participants

@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>
bdf5b7c
@johnelse johnelse Add Test_common.make_pool
Signed-off-by: John Else <john.else@citrix.com>
3c9f82a
@johnelse johnelse CP-4325: Implement pool.get_license_state
Signed-off-by: John Else <john.else@citrix.com>
f859c04
@johnelse johnelse CP-4325: Add pool.license_state getter to CLI
Signed-off-by: John Else <john.else@citrix.com>
fa08e43
@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>
b7a22d9
@johnelse johnelse CP-4325: Add tests for pool-wide license logic
Signed-off-by: John Else <john.else@citrix.com>
557edeb
@johnelse johnelse Add high-level unit testing framework
Signed-off-by: John Else <john.else@citrix.com>
e88d45e
@johnelse johnelse Whitespace: create_misc.ml
$ camlp4o -printer o -no_comments ocaml/xapi/create_misc.ml | md5sum
363fca0fe17fb3e89753b6aa62add69f  -
$ git checkout HEAD^
$ camlp4o -printer o -no_comments ocaml/xapi/create_misc.ml | md5sum
363fca0fe17fb3e89753b6aa62add69f  -

Signed-off-by: John Else <john.else@citrix.com>
dd87b95
@johnelse johnelse CP-4319: Add field host.cpu_info:socket_count
Signed-off-by: John Else <john.else@citrix.com>
021d81e
@johnelse johnelse CP-4324: Implement pool.apply_edition
Signed-off-by: John Else <john.else@citrix.com>
9d84a07
@johnelse johnelse CP-4319: Pass number of CPU sockets to v6d
Signed-off-by: John Else <john.else@citrix.com>
dc5260c
@johnelse johnelse CP-4324: Add pool.apply_edition to the CLI
Signed-off-by: John Else <john.else@citrix.com>
0d74f50
@mcclurmc mcclurmc was assigned
@jonludlam
Owner

Are there any dependencies for this? Does QA need to fix anything before we merge this?

@mcclurmc mcclurmc merged commit 6ce7f80 into xapi-project:master

1 check passed

Details default Merged build finished.
@mcclurmc mcclurmc was unassigned by simonjbeaumont
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 13, 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

    Add Test_common.make_pool

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

    CP-4325: Implement pool.get_license_state

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

    CP-4325: Add pool.license_state getter to CLI

    johnelse authored
    Signed-off-by: John Else <john.else@citrix.com>
Commits on Mar 15, 2013
  1. @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>
  2. @johnelse

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

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

    Add high-level unit testing framework

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

    Whitespace: create_misc.ml

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

    CP-4319: Add field host.cpu_info:socket_count

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

    CP-4324: Implement pool.apply_edition

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

    CP-4319: Pass number of CPU sockets to v6d

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

    CP-4324: Add pool.apply_edition to the CLI

    johnelse authored
    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
@@ -6164,6 +6164,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
@@ -6228,6 +6250,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
@@ -25,11 +25,13 @@ OCAML_LIBS = \
OCAML_OBJS = \
../idl/api_lowlevel \
mock \
+ test_highlevel \
test_common \
test_basic \
test_pool_db_backup \
test_xapi_db_upgrade \
test_ca91480 \
test_pr1510 \
+ test_pool_license \
OCamlProgram(suite, suite $(OCAML_OBJS) )
View
1  ocaml/test/suite.ml
@@ -22,6 +22,7 @@ let base_suite =
Test_xapi_db_upgrade.test;
Test_ca91480.test;
Test_pr1510.suite;
+ 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
@@ -119,6 +119,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
@@ -3689,6 +3689,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
@@ -76,20 +76,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
@@ -127,10 +128,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*)
@@ -191,12 +192,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
@@ -254,16 +254,16 @@ and create_domain_zero_default_memory_constraints host_info : Vm_memory_constrai
dynamic_max = state.Vm.memory_target;
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. *)
@@ -322,8 +322,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.
@@ -387,12 +387,12 @@ let make_software_version ~__context =
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", Network_interface.string_of_kind (Net.Bridge.get_kind dbg ());
+ "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", Network_interface.string_of_kind (Net.Bridge.get_kind dbg ());
] @
(option_to_list "oem_manufacturer" info.oem_manufacturer) @
(option_to_list "oem_model" info.oem_model) @
@@ -402,14 +402,18 @@ let make_software_version ~__context =
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
@@ -422,9 +426,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 () =
@@ -434,10 +438,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 ()
)
@@ -448,12 +453,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 *)
@@ -467,7 +472,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;
@@ -481,8 +487,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
@@ -491,10 +497,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
@@ -1235,7 +1235,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
@@ -1697,3 +1697,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.