]> xenbits.xensource.com Git - xcp/xen-api.git/commitdiff
Rewrite feature restrictions code
authorRob Hoes <rob.hoes@citrix.com>
Wed, 14 Jul 2010 15:43:32 +0000 (16:43 +0100)
committerRob Hoes <rob.hoes@citrix.com>
Wed, 14 Jul 2010 15:43:32 +0000 (16:43 +0100)
This patch replaces restrictions.ml by features.ml and editions.ml, and splits up license.ml in three separate files (license.ml, license_init.ml and license_file.ml).

The Features module controls which XCP features are enabled. The Editions module defines "editions", which are sets of features that are enabled. An XCP host is always running as (exactly) one particular edition. Currently, there is just one edition defined, called "Free" (for backwards compatibility), in which all currently implemented features are enabled.

One use of this is that new or experimental features can be enabled only in special editions to keep them separate from the stable version of XCP.

Signed-off-by: Rob Hoes <rob.hoes@citrix.com>
24 files changed:
ocaml/license/edition.ml
ocaml/license/edition.mli
ocaml/license/features.ml
ocaml/license/features.mli
ocaml/license/license.ml
ocaml/license/license.mli
ocaml/license/license_init.ml
ocaml/license/v6client.ml
ocaml/xapi/OMakefile
ocaml/xapi/cli_operations.ml
ocaml/xapi/vmops.ml
ocaml/xapi/workload_balancing.ml
ocaml/xapi/xapi.ml
ocaml/xapi/xapi_config.ml
ocaml/xapi/xapi_globs.ml
ocaml/xapi/xapi_ha.ml
ocaml/xapi/xapi_host.ml
ocaml/xapi/xapi_host.mli
ocaml/xapi/xapi_message.ml
ocaml/xapi/xapi_pool.ml
ocaml/xapi/xapi_subject.ml
ocaml/xapi/xapi_vm.ml
ocaml/xapi/xapi_vm_memory_constraints.ml
ocaml/xe-cli/bash-completion

index f048706e2e6e780fde2cd33b722d41d4f5929e52..54c9c91c4f92487f94fff2b1c0d9937f596e26d8 100644 (file)
@@ -36,12 +36,3 @@ let to_marketing_name = function
 let to_features = function
        | Free -> all_features
 
-let edition_to_int = function
-       | Free | _ -> 0
-
-let equal e0 e1 =
-       edition_to_int e0 = edition_to_int e1
-       
-let min l =
-       Free
-
index c2106732883e1864f8afa7cce73c5d551e252b7c..5baea405eb76b0907b93f08ae93d8d40070994be 100644 (file)
@@ -19,7 +19,7 @@
 type edition =
        | Free (** Default Edition *)
 
-(** Raised by {!of_string} if the given string does not map to an edition. *)
+(** Raised by {!edition_of_string} if the given string does not map to an edition. *)
 exception Undefined_edition of string
 
 (** Convert a string to an {!edition}. *)
@@ -37,9 +37,3 @@ val to_marketing_name : edition -> string
 (** Get the list of {!feature}s enabled for a given {!edition}. *)
 val to_features : edition -> Features.feature list
 
-(** Compare two editions for equality (used before pool join). *)
-val equal : edition -> edition -> bool
-
-(** Return the "least capable" edition (used to determine the pool edition). *)
-val min : edition list -> edition
-
index f7988b533e3408eca68843f9a811b509e7b78c97..a29c022cf831d13d5c4da553472e1d3cb6de5aa9 100644 (file)
@@ -19,7 +19,7 @@ open D
 
 type feature = VLAN | QoS | Shared_storage | Netapp | Equalogic | Pooling
        | HA | Marathon | Email | Performance | WLB | RBAC | DMC | Checkpoint
-       | Vswitch_controller | CPU_masking | Connection | No_platform_filter | No_nag_dialog | VMPR
+       | Vswitch_controller | CPU_masking | Connection | No_platform_filter | No_nag_dialog
 
 type orientation = Positive | Negative
 
@@ -44,7 +44,6 @@ let keys_of_features =
                Connection, ("restrict_connection", Negative, "Cnx");
                No_platform_filter, ("platform_filter", Negative, "Plat");
                No_nag_dialog, ("regular_nag_dialog", Negative, "nonag");
-               VMPR, ("restrict_vmpr", Negative, "VMPR");
        ]
 
 let string_of_feature f =
index 48cdb2c074e242bc0d0913bb7e928d01fb05afb3..fbb2ab8422c538729a155701351c1cdca004d20e 100644 (file)
@@ -36,7 +36,6 @@ type feature =
 | Connection                   (** Used by XenCenter *)
 | No_platform_filter   (** Filter platform data *)
 | No_nag_dialog                        (** Used by XenCenter *)
-| VMPR                                 (** Enable use of VM Protection and Recovery *)
        
 (** The list of all known features. *)
 val all_features : feature list
index 10c7ff54034a57369aedeea75ffd7a725f831a13..114a73869b3673f8eee83e44802a10347d3f0029 100644 (file)
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Lesser General Public License for more details.
  *)
-(* OLD:
- * There are 2 ways that a license can be registered by xapi. The
- * first is on startup, when the file /etc/xensource/license is read,
- * and the second is the api call 'host.license_apply'. There are also
- * 2 possible problems with a license - that it might have expired, and
- * that it might be invalid. The API call will refuse to do anything if
- * the license has either problem, but initial startup will always
- * apply a parsable license, even if it has expired.
- * 
- * The license is checked to see if it has expired in exactly one place
- * - Vmops.create_check, and so an expired license will mean it is
- * impossible to start new domains.  *)
 
 open Stringext
 open Pervasiveext
@@ -30,11 +18,8 @@ open Pervasiveext
 module D = Debug.Debugger(struct let name="license" end)
 open D
 
-(* Set from config file: *)
-let filename = ref ""
-
 (* Defaults *)
-let sku = "XE Enterprise"
+let edition = Edition.Free
 
 (* Round a date, given by Unix.time, to days *)
 let round_to_days d =
@@ -59,27 +44,6 @@ let default_version = Version.product_version
 let default_sockets = 1
 let default_productcode = ""
 
-let sku_and_name_of_edition = function
-| _ -> "undefined", "Undefined"
-
-(* Convert the sku_type encoded and signed in the license into a sku_marketing_name by looking up the
-   key in an XML table stored in the dom0. *)
-let marketing_string_of_sku sku = 
-  try
-    let xml = Unixext.read_whole_file_to_string Xapi_globs.sku_marketing_name_db in
-    let db = Hashtbl_xml.of_xml (Xmlm.make_input (`String (0,xml))) in
-    if Hashtbl.mem db sku
-    then Hashtbl.find db sku
-    else begin
-      warn "marketing_string_of_sku %s: no corresponding entry in the sku_marketing_name db; defaulting to \"\"" sku;
-      ""
-    end
-  with 
-  | Unix.Unix_error(Unix.ENOENT, _, _) -> warn "marketing_string_of_sku %s: sku_marketing_name db missing; defaulting to \"\"" sku; ""
-  | Hashtbl_xml.Unmarshall_error x -> warn "marketing_string_of_sku %s: Caught error unmarshalling sku_marketing_name db: %s; defaulting to \"\""  sku x; ""
-  | e -> warn "marketing_string_of_sku %s: Caught unknown exception unmarshalling sku_marketing_name db: %s; defaulting to \"\"" sku (Printexc.to_string e); ""
-
-
 (* Only read out the fields we care about. The signature covers the other
    fields so there's no verification required here. *)
 type license =
@@ -168,7 +132,7 @@ let of_assoc_list (x: (string * string) list) =
     
 let default () =
     { 
-      sku = sku;
+      sku = Edition.to_string edition;
       version = default_version;
       serialnumber = "";
       sockets = default_sockets;
@@ -183,217 +147,15 @@ let default () =
       state = "";
       postalcode = "";
       country = "";
-      sku_marketing_name = marketing_string_of_sku sku;
+      sku_marketing_name = Edition.to_marketing_name edition;
     }
 
 let license : license ref = ref (default ())
 
-exception LicenseParseError
-exception LicenseCannotReadFile
-exception LicenseFieldMissing of string
-exception License_expired of license
-exception License_file_deprecated
-
 (* Calls to obtain info about license *)
 
 let check_expiry l =
   Unix.time () < l.expiry
 
-let license_valid () = true
-
-(* License setting functions *)
-       
-let validate_signature fname = 
-  Gpg.with_signed_cleartext fname
-    (fun fingerprint fd -> 
-      (match fingerprint with 
-       | Some f -> 
-           (* base64-encoded fingerprint of our licensing public key *)
-           if (Base64.encode f)<>"QzA5Qzk4REIwNjM4RjNFQjZEQUFERkU4QTJCRjA0QkM3QThDNzhBNw==" 
-           then
-             (
-               debug "Got fingerprint: %s" f;
-               (* debug "Encoded: %s" (Base64.encode f); -- don't advertise the fact that we've got an encoded string in here! *)
-               raise Gpg.InvalidSignature
-             )
-       | None -> 
-           debug "No fingerprint!";
-           raise Gpg.InvalidSignature);
-      Unixext.read_whole_file 500 500 fd)
-      
-(* only activation keys are accepted as license files since XS 6.0 *)
-let parse_license license_data =
-  let lic_xml = Xml.parse_string license_data in
-
-  let readfld fname attrs =
-    try
-      List.assoc fname attrs
-    with Not_found -> raise (LicenseFieldMissing fname) in
-
-  let maybe_readfld fname attrs =
-    try
-      List.assoc fname attrs
-    with Not_found -> "" in
+let license_valid () = check_expiry !license
 
-  match lic_xml with
-  | Xml.Element("xe_license", attrs, _) ->
-       let sku = readfld "sku_type" attrs in
-       (* we now only accept activation keys for the free edition fo XS *)
-       if sku <> "XE Express" then
-               raise License_file_deprecated
-       else
-               {sku = sku;
-                version = readfld "version" attrs;
-                serialnumber = readfld "serialnumber" attrs;
-                sockets = int_of_string (readfld "sockets" attrs); 
-                productcode = (readfld "productcode" attrs);
-                expiry = float_of_string (readfld "expiry" attrs);
-                grace = "no";
-                name = maybe_readfld "name" attrs;
-                company = maybe_readfld "company" attrs;
-                address1 = maybe_readfld "address1" attrs;
-                address2 = maybe_readfld "address2" attrs;
-                city = maybe_readfld "city" attrs;
-                state = maybe_readfld "state" attrs;
-                postalcode = maybe_readfld "postalcode" attrs;
-                country = maybe_readfld "country" attrs;
-                sku_marketing_name = marketing_string_of_sku sku;
-               }
-  | _ -> raise LicenseParseError
-
-(* only activation keys are accepted as license files since XS 6.0 *)
-let read_license_file fname =
-  try
-    Unix.access fname [Unix.F_OK];
-    let license_data = validate_signature fname in
-    let newlicense = parse_license license_data in
-      Some newlicense
-  with 
-  | License_file_deprecated -> raise License_file_deprecated
-  | e ->
-    begin
-      debug "Failed to read license file: %s" (Printexc.to_string e);
-      None
-    end
-
-(* only activation keys are accepted as license files since XS 6.0 *)
-let do_parse_and_validate fname =
-  try
-    let _ = try Unix.access fname [Unix.F_OK] with _ -> raise LicenseCannotReadFile in
-    let license_data = validate_signature fname in
-    let newlicense = parse_license license_data in
-
-    (if not (check_expiry newlicense) then raise (License_expired newlicense));
-
-    (* At this point, license is valid and hasn't expired *)
-    license := newlicense
-  with e ->
-    (match e with
-      | License_expired l -> warn "License has expired"
-      | LicenseCannotReadFile -> warn "License application failed: cannot read license file."
-      | Gpg.InvalidSignature -> warn "License application failed: invalid signature on license file."
-      | LicenseFieldMissing fname -> warn "License application failed: essential field '%s' missing from license." fname
-      | LicenseParseError -> warn "License application failed: reverting to previous license"
-      | License_file_deprecated -> warn "License application failed: deprecated license file"
-      | e -> warn "License application failed: exception '%s' in license parsing." (Printexc.to_string e);
-         log_backtrace ());
-    raise e
-
-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.read_whole_file_to_string 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 new_license = try 
-               let existing_license = of_assoc_list existing_license_params in
-               match existing_edition with
-               | "free" ->
-                       (* old Floodgate-free behaviour *)
-                       begin try
-                               do_parse_and_validate !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_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
-               | "enterprise" | "platinum" ->
-                       (* existing license is a v6 Essentials license -> try to check one out again *)
-                       begin try
-                               V6client.get_v6_license ~__context ~host ~edition:existing_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
-               | "" -> 
-                       (* upgrade from pre-MNR *)
-                       if existing_license.sku = "XE Express" then begin
-                               info "Upgrade from free: set to free edition.";
-                               (* all existing license_params are kept; only fill in edition field *)
-                               Db.Host.set_edition ~__context ~self:host ~value:"free";
-                               {default with expiry = existing_license.expiry}
-                       end else begin
-                               info "Upgrade from Essentials: transition to enterprise edition (30-day grace license).";
-                               Db.Host.set_edition ~__context ~self:host ~value:"enterprise";
-                               let expiry = upgrade_grace_expiry () in
-                               write_grace_to_file expiry;
-                               Unixext.unlink_safe !filename;
-                               V6alert.send_v6_upgrade_grace_license ();
-                               let sku, name = sku_and_name_of_edition "enterprise" in
-                               {default with sku = sku; expiry = expiry; grace = "upgrade grace"; sku_marketing_name = name}
-                       end
-               | _ ->
-                       warn "Edition field corrupted; generating a new free license, which needs to be activated in 30 days.";
-                       default
-               with _ ->
-                       (* no license_params -> first boot *)
-                       Db.Host.set_edition ~__context ~self:host ~value:"free";
-                       begin try
-                               do_parse_and_validate !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_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
-       
index 0fd67c4dc71efdf4a164ca830d6e5f29d1a75729..58a1f30f22b2c08c49965bfcc52e505110ff4e71 100644 (file)
 
 (** Type for holding all details about a license *)
 type license =
-    {
-      sku       : string;
-      (** License type: currently either [XE Express] (free) or [XE Enterprise] (paid for) *)
-      version   : string;
-      (** No longer used *)
-      serialnumber : string;
-      (** No longer used *)
-      sockets   : int;
-      (** No longer used *)
-      productcode : string;
-      (** No longer used *)
-      expiry : float;
-      (** Expiry date (result of Unix.time) *)
-      grace : string;
-      (** Indicates whether the current license is a grace license.
-       *  Possible values: "no", "upgrade grace", "regular grace" *)
+       {
+               sku       : string;             (** License type *)
+               version   : string;             (** No longer used *)
+               serialnumber : string;  (** No longer used *)
+               sockets   : int;                (** No longer used *)
+               productcode : string;   (** No longer used *)
+               expiry : float;                 (** Expiry date (result of Unix.time) *)
+               grace : string;                 (** Indicates whether the current license is a grace license.
+                                                                *  Possible values: "no", "upgrade grace", "regular grace" *)
 
-      name : string;
-      (** No longer used *)
-      company : string;
-      (** No longer used *)
-      address1 : string;
-      (** No longer used *)
-      address2 : string;
-      (** No longer used *)
-      city : string;
-      (** No longer used *)
-      state : string;
-      (** No longer used *)
-      postalcode : string;
-      (** No longer used *)
-      country : string;
-      (** No longer used *)
+               name : string;                  (** No longer used *)
+               company : string;               (** No longer used *)
+               address1 : string;              (** No longer used *)
+               address2 : string;              (** No longer used *)
+               city : string;                  (** No longer used *)
+               state : string;                 (** No longer used *)
+               postalcode : string;    (** No longer used *)
+               country : string;               (** No longer used *)
 
-      sku_marketing_name : string;
-      (** Official marketing name of the license *)
-    }
+               sku_marketing_name : string;    (** Official marketing name of the license *)
+       }
 
+(** The current license on this host. *)
 val license : license ref
-(** The current license *)
 
-val filename : string ref
-(** Path to the license file in use. Only free-license activation keys are
- *  used since XS 6.0 *)
-
-exception Missing_license_param of string
-(** Thrown if we fail to find a license param *)
-exception LicenseParseError
-(** Thrown if the license data is malformed *)
-exception LicenseCannotReadFile
-(** Thrown if the given license file cannot be opened *)
-exception LicenseFieldMissing of string
-(** Thrown if a particular field is missing from the license data *)
-exception License_expired of license
-(** Thrown if the license in a given file is found to be expired *)
-exception License_file_deprecated
-(** Thrown if an old-style, deprecated license file is used *)
-
-val sku_and_name_of_edition : string -> string * string
-(** map an edition string to an sku name and a marketing name *)
-
-val to_assoc_list : license -> (string * string) list
 (** Converts a license into a association list to place in DB. *)
-val of_assoc_list : (string * string) list -> license
+val to_assoc_list : license -> (string * string) list
+
 (** Converts a license association list from DB into a license value. *)
+val of_assoc_list : (string * string) list -> license
+
+(** Returns a default, free license with 30-day grace. *)
 val default : unit -> license
-(** Returns a default, free license with 30-day grace *)
 
-val license_valid : unit -> bool
+(** Check whether a given license is valid or expired. *)
+val check_expiry : license -> bool
+
 (** Check whether the current license is valid or expired.
  *  Called from xapi/license_check.ml. *)
-val read_license_file : string -> license option
-(** Parse a license file and return the result. Only works for free-license
- *  activation keys since XS 6.0.
- *  Called from host.license_apply. *)
-val do_parse_and_validate : string -> unit
-(** As read_license_file, but also set license state variable if not expired.
- *  Only works for free-license activation keys since XS 6.0.
- *  Called from host.license_apply. *)
+val license_valid : unit -> bool
+
+(** Thrown if we fail to find a license param. *)
+exception Missing_license_param of string
+
+(** Obtain a date that lies 30 days in the future to set as grace expiry date. *)
+val grace_expiry : unit -> float
 
-val initialise : __context:Context.t -> host:[ `host ] Ref.t -> unit
-(** Initialises licensing on xapi start up *)
+(** Obtain a date that lies 30 days in the future to set as upgrade grace expiry date. *)
+val upgrade_grace_expiry : unit -> float
 
index 361fbd89423a33ba6920679b9de0308225bb9482..9d77b379f320efec5e0d1ed4b15b9653fee15479 100644 (file)
@@ -32,60 +32,75 @@ 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
+       let new_license = try 
+               let existing_license = of_assoc_list existing_license_params in
+               match existing_edition with
+               | "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
-                       | 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
+               | "enterprise" | "platinum" | "enterprise-xd" ->
+                       (* existing license is a v6 Essentials license -> try to check one out again *)
+                       begin try
+                               V6client.get_v6_license ~__context ~host ~edition:existing_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
-                       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 _ ->
+                       | 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
+               | "" -> 
+                       (* upgrade from pre-MNR *)
+                       if existing_license.sku = "XE Express" then begin
+                               info "Upgrade from free: set to free edition.";
+                               (* all existing license_params are kept; only fill in edition field *)
+                               Db.Host.set_edition ~__context ~self:host ~value:"free";
+                               {default with sku = "free"; expiry = existing_license.expiry}
+                       end else begin
+                               info "Upgrade from Essentials: transition to enterprise edition (30-day grace license).";
+                               Db.Host.set_edition ~__context ~self:host ~value:"enterprise";
+                               let expiry = upgrade_grace_expiry () in
+                               write_grace_to_file expiry;
+                               Unixext.unlink_safe !License_file.filename;
+                               V6alert.send_v6_upgrade_grace_license ();
+                               let name = Edition.to_marketing_name Edition.Enterprise in
+                               {default with sku = "enterprise"; expiry = expiry; grace = "upgrade grace"; sku_marketing_name = name}
+                       end
+               | _ ->
+                       warn "Edition field corrupted; generating a new free license, which needs to be activated in 30 days.";
+                       default
+               with _ ->
                        (* no license_params -> first boot *)
-                       Db.Host.set_edition ~__context ~self:host ~value:free;
+                       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));
index 5918cd8045b64a74fb63d66e5b705340519e2477..22b490fd3237d263fcbfd58e65e85ace4e7f51ea 100644 (file)
@@ -34,7 +34,7 @@ let socket = "/var/xapi/v6"
 let v6rpc xml = Xmlrpcclient.do_xml_rpc_unix ~version:"1.0" ~filename:socket ~path:"/" xml
 
 (* conversion to v6 edition codes *)
-let editions = ["enterprise", "ENT"; "platinum", "PLT"]
+let editions = ["enterprise", "ENT"; "platinum", "PLT"; "enterprise-xd", "XD"]
        
 (* reset to not-licensed state *)
 let reset_state () =
index fa45089be0dc60c4a0938ece6d1da57f6b37eefa..5242e7305420968e450b446cbc250455e2a48c1e 100644 (file)
@@ -131,7 +131,6 @@ XAPI_MODULES = $(COMMON) \
        config_file_sync \
        config_file_io \
        slave_backup \
-       ../license/restrictions \
        sm_fs_ops \
        vmopshelpers \
        vm_config \
@@ -234,7 +233,11 @@ XAPI_MODULES = $(COMMON) \
        bios_strings \
        xapi_config \
        ../license/grace_retry \
-       ../license/v6alert
+       ../license/v6alert \
+       ../license/edition \
+       ../license/features \
+       ../license/license_file \
+       ../license/license_init
 
 OCamlProgram(xapi, $(XAPI_MODULES))
 OCamlDocProgram(xapi, $(XAPI_MODULES))
index 476ba208514c89bc0f2e8444386dd6862d168503..67575c3659b256492d904584eb26bb3d9eccfd3a 100644 (file)
@@ -141,12 +141,12 @@ let diagnostic_db_log printer rpc session_id params =
 type host_license = {
   hostname: string;
   uuid: string;
-  rstr: Restrictions.restrictions;
+  rstr: Features.feature list;
   license: License.license
 }
 let host_license_of_r host_r =
   let params = host_r.API.host_license_params in
-  let rstr = Restrictions.of_assoc_list 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;
@@ -162,8 +162,8 @@ let diagnostic_license_status printer rpc session_id params =
   (* 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 = Restrictions.is_floodgate_free (Restrictions.sku_of_string a.license.License.sku)
-                                  and b_free = Restrictions.is_floodgate_free (Restrictions.sku_of_string b.license.License.sku) 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
                                   if a_expiry < b_expiry then -1
                                   else 
                                     if a_expiry > b_expiry then 1
@@ -175,9 +175,9 @@ let diagnostic_license_status printer rpc session_id params =
   let now = Unix.gettimeofday () in
   let hosts = List.map (fun h -> [ h.hostname; 
                                   String.sub h.uuid 0 8; 
-                                  Restrictions.to_compact_string h.rstr; 
-                                  Restrictions.obfuscated_string_of_sku (Restrictions.sku_of_string h.license.License.sku); 
-                                  string_of_bool (Restrictions.is_floodgate_free (Restrictions.sku_of_string h.license.License.sku)); 
+                                  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.));
                                 ]) host_licenses in
@@ -185,10 +185,11 @@ let diagnostic_license_status printer rpc session_id params =
                                                     String.sub host_r.API.host_uuid 0 8;
                                                     "-"; "-"; "-"; "-"; "-" ]) invalid in
   let __context = Context.make "diagnostic_license_status" in
-  let pool_restrictions = Restrictions.get_pool ~__context in
-  let pool_free = List.fold_left (||) false (List.map (fun h -> Restrictions.is_floodgate_free (Restrictions.sku_of_string h.license.License.sku)) host_licenses) 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 divider = [ "-"; "-"; "-"; "-"; "-"; "-"; "-" ] in
-  let pool = [ "-"; "-"; Restrictions.to_compact_string pool_restrictions; "-"; string_of_bool pool_free; "-"; "-" ] 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
 
   (* Compute the required column widths *)
index 801a59e849dfa2250a0041ca4d54b5b0f249927d..bc066d0c98d19b8a5f021710720b7a4988de14c2 100644 (file)
@@ -346,10 +346,11 @@ let create ~__context ~xc ~xs ~self (snapshot: API.vM_t) ~reservation_id () =
        let allowed_xsdata (x, _) = List.fold_left (||) false (List.map (fun p -> String.startswith p x) [ "vm-data/"; "FIST/" ]) in
        let xsdata = List.filter allowed_xsdata xsdata in
 
-       let rstr = Restrictions.get () in
        let platformdata =
                let p = Db.VM.get_platform ~__context ~self in
-               if rstr.Restrictions.platform_filter then List.filter (fun (k, v) -> List.mem k filtered_platform_flags) p else p
+               if not (Features.is_enabled ~__context Features.No_platform_filter) then
+                       List.filter (fun (k, v) -> List.mem k filtered_platform_flags) p
+               else p
        in
        (* XXX: add extra configuration info to the platform/ map for now.
           Eventually we'll put this somewhere where the guest can't see it. *)
index e8e87bbbd961dbbed647434eb6445ec9a2b3449a..8418ac99a5ce7ec847ccfa282976282dad7c871e 100755 (executable)
@@ -87,7 +87,7 @@ let wlb_host_port ~__context =
     split_host_port url
 
 let assert_wlb_licensed ~__context =
-  if not (Restrictions.license_ok_for_wlb ~__context)
+  if not (Features.is_enabled ~__context Features.WLB)
   then
     raise_license_restriction()
 
index 6025fac2f1b2eaf6c2525a3f318f0430ca9ad0e4..5059b14f3ac72a8df9f562a9df3abaedc30828fb 100644 (file)
@@ -535,9 +535,9 @@ let handle_licensing () =
   Server_helpers.exec_with_new_task "Licensing host"
     (fun __context ->
        let host = Helpers.get_localhost ~__context in
-       License.initialise ~__context ~host;
+       License_init.initialise ~__context ~host;
        (* Copy resulting license to the database *)
-       Xapi_host.copy_license_to_db ~__context
+       Xapi_host.copy_license_to_db ~__context ~host
     )
 
 (** Writes the memory policy to xenstore and triggers the ballooning daemon. *)
@@ -763,7 +763,6 @@ let server_init() =
     "Starting periodic scheduler", [Startup.OnThread], Xapi_periodic_scheduler.loop;
     "Remote requests", [Startup.OnThread], Remote_requests.handle_requests;
   ];
-    let (_: Restrictions.restrictions) = Restrictions.get () in
     begin match Pool_role.get_role () with
     | Pool_role.Master ->
        ()
index bf83215de0a649d4abc35ddfe66bbca8a73089d7..44ebd22c5ca02a9451d702197a49265d621adc29 100644 (file)
@@ -98,7 +98,7 @@ let read_config filename =
         in
 
     let configargs = [
-        "license_filename", Config.Set_string License.filename;
+        "license_filename", Config.Set_string License_file.filename;
         "http-port", Config.Set_int http_port;
         "stunnelng", Config.Set_bool Stunnel.use_new_stunnel;
         "log", Config.String set_log;
@@ -118,5 +118,5 @@ let dump_config () =
     debug "build_number: %s" Version.build_number;
     debug "hg changeset: %s" Version.hg_id;
     debug "version: %d.%d" version_major version_minor;
-    debug "License filename: %s" !License.filename
+    debug "License filename: %s" !License_file.filename
     
index 5f7b6d64f646b619c55dd02434760e38f38e19d0..e7231d03a5c6aacdeea6a239edf2a72b9dba904c 100644 (file)
@@ -543,9 +543,6 @@ let serialize_pool_enable_disable_extauth = Mutex.create()
 
 let event_hook_auth_on_xapi_initialize_succeeded = ref false
 
-(** Contains an XML key/value pair database containing the mapping from sku_type to sku_marketing_name *)
-let sku_marketing_name_db = "/etc/xensource/sku.db"
-
 (** Directory used by the v6 license policy engine for caching *)
 let upgrade_grace_file = "/var/xapi/ugp"
 
index f81333252f16638231382ab9d20c80cfc54f283f..66b5aaea5bd18fddcfe1ab80daa24fe7ea1c1963 100644 (file)
@@ -1366,7 +1366,7 @@ let enable __context heartbeat_srs configuration =
   if Db.Pool.get_ha_enabled ~__context ~self:pool
   then raise (Api_errors.Server_error(Api_errors.ha_is_enabled, []));
 
-  if not ((Restrictions.get_pool ~__context).Restrictions.enable_xha)
+  if not (Features.is_enabled ~__context Features.HA)
   then raise (Api_errors.Server_error(Api_errors.license_restriction, []));
 
   (* Check that all of our 'disallow_unplug' PIFs are currently attached *)
index 405e96c22895e473b561c4645669833db46657ea..86cf045ca390319f9507c791a3e873ece73cc9f0 100644 (file)
@@ -35,7 +35,7 @@ let local_assert_healthy ~__context = match Pool_role.get_role () with
 
 let set_license_params ~__context ~self ~value = 
   Db.Host.set_license_params ~__context ~self ~value;
-  Restrictions.update_pool_restrictions ~__context
+  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;
@@ -521,9 +521,11 @@ 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 =
+let copy_license_to_db ~__context ~host =
   let license_kvpairs = License.to_assoc_list !License.license in
-  let restrict_kvpairs = Restrictions.to_assoc_list (Restrictions.get ()) 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 ->
@@ -563,7 +565,7 @@ 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.filename 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
@@ -577,28 +579,28 @@ let license_apply ~__context ~host ~contents =
     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.read_license_file tmp with
+    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_restrictions = Restrictions.restrictions_of_sku (Restrictions.sku_of_string new_license.License.sku) in
+    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 new_restrictions.Restrictions.enable_xha)
+    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.do_parse_and_validate tmp; (* throws exception if not valid which should really be translated into API error here.. *)
-      Unix.rename tmp !License.filename; (* atomically overwrite host license file *)
-      copy_license_to_db ~__context;     (* update pool.license_params for clients that want to see license info through API *)
+      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.License_expired l -> raise (Api_errors.Server_error(Api_errors.license_expired, []))
-         | License.License_file_deprecated -> raise (Api_errors.Server_error(Api_errors.license_file_deprecated, []))
+         | 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
@@ -1221,11 +1223,11 @@ let apply_edition ~__context ~host ~edition =
                                raise (Api_errors.Server_error (Api_errors.ha_is_enabled, []))
                        else begin
                                V6client.release_v6_license ();
-                               Unixext.unlink_safe !License.filename; (* delete activation key, if it exists *)
+                               Unixext.unlink_safe !License_file.filename; (* delete activation key, if it exists *)
                                default (* default is free edition with 30 day grace validity *)
                        end
                end
-       | "enterprise" | "platinum" ->
+       | "enterprise" | "platinum" | "enterprise-xd" ->
                (* Try to get the a v6 license; if one has already been checked out,
                 * it will be automatically checked back in. *)
                if current_edition = "free" then
@@ -1243,8 +1245,8 @@ let apply_edition ~__context ~host ~edition =
                        V6client.release_v6_license ();
                        raise (Api_errors.Server_error (Api_errors.license_checkout_error, [edition]))
                | Some license ->
-                       let sku, name = License.sku_and_name_of_edition edition in
-                       let basic = {default with License.sku = sku; License.sku_marketing_name = name;
+                       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;
@@ -1258,7 +1260,7 @@ let apply_edition ~__context ~host ~edition =
        in
        License.license := new_license;
        Db.Host.set_edition ~__context ~self:host ~value:edition;
-       copy_license_to_db ~__context; (* update host.license_params, pool sku and restrictions *)
+       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 *)
 
 let refresh_pack_info ~__context ~host =
@@ -1269,7 +1271,7 @@ let refresh_pack_info ~__context ~host =
 let set_cpu_features ~__context ~host ~features =
        debug "Set CPU features";
        (* check restrictions *)
-       if not (Restrictions.ok_for_cpu_masking ~__context) then
+       if not (Features.is_enabled ~__context Features.CPU_masking) then
                raise (Api_errors.Server_error (Api_errors.feature_restricted, []));
        
        let cpuid = Cpuid.read_cpu_info () in
index 68a9423e72248b6799cfd91b9855ecf7f0d7e996..9747d7b45830c7e07460f8789146ef67c02c1cdf 100644 (file)
@@ -65,7 +65,7 @@ val dmesg_clear : __context:'a -> host:'b -> 'c
 val get_log : __context:'a -> host:'b -> 'c
 val send_debug_keys : __context:'a -> host:'b -> keys:string -> unit
 val list_methods : __context:'a -> 'b
-val copy_license_to_db : __context:Context.t -> unit
+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. 
index d30ef4f83f406a8ce0630041aeeea04f93cffcb5..6b1c4672da85f53efc47727474239225a1e2c879 100644 (file)
@@ -165,7 +165,7 @@ let message_to_string (_ref,message) =
 
 let handle_message ~__context message = 
   try
-    if not (Restrictions.get_pool ~__context).Restrictions.enable_email
+    if not (Features.is_enabled ~__context Features.Email)
     then info "Email alerting is restricted by current license: not generating email"
     else begin
       let output, log = Forkhelpers.execute_command_get_output (Xapi_globs.xapi_message_script) [message] in
index d3459d925fbbdc74afb6f366b5e46fd47d923f30..f28cf45c0679ccc480ceba77dd442ba7a7a89d69 100644 (file)
@@ -57,14 +57,14 @@ let pre_join_checks ~__context ~rpc ~session_id ~force =
                        raise (Api_errors.Server_error(Api_errors.ha_is_enabled, []));
                end in
 
-       (* CA-26975: Pool restrictions (implied by pool_sku) MUST match *)
+       (* CA-26975: Pool edition MUST match *)
        let assert_restrictions_match () =
                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 -> host_r.API.host_edition) host_records in
                let edition_to_int = function
                | "platinum" -> 2
-               | "enterprise" -> 1
+               | "enterprise" | "enterprise-xd" -> 1
                | "free" | _ -> 0
                in
                let int_to_edition = function
@@ -79,16 +79,6 @@ let pre_join_checks ~__context ~rpc ~session_id ~force =
                        error "Remote has %s" (int_to_edition pool_edition);
                        error "Local has  %s" (int_to_edition my_edition);
                        raise (Api_errors.Server_error(Api_errors.license_restriction, []))
-               end;
-               (* check pool restrictions *)
-               let pool_license_params = List.map (fun host_r -> host_r.API.host_license_params) host_records in
-               let pool_restrictions = Restrictions.pool_restrictions_of_list (List.map Restrictions.of_assoc_list pool_license_params) in
-               let my_restrictions = Restrictions.get() in
-               if pool_restrictions <> my_restrictions then begin
-                       error "Pool.join failed because of license restrictions mismatch";
-                       error "Remote has %s" (Restrictions.to_compact_string pool_restrictions);
-                       error "Local has  %s" (Restrictions.to_compact_string my_restrictions);
-                       raise (Api_errors.Server_error(Api_errors.license_restriction, []))
                end
        in
 
index 59bd4c562e5aab60737d3b5ce81774bab2483fd7..99687a5353f17cb0a0a914434f60f0e6ee2300f3 100644 (file)
@@ -64,7 +64,7 @@ let create ~__context ~subject_identifier ~other_config =
        (* CP-1224: Free Edition: Newly created subjects will have the Pool Administrator role. *)
        (* CP-1224: Paid-for Edition: Newly created subjects will have an empty role. *)
        let default_roles =
-               if (Restrictions.license_ok_for_rbac ~__context)
+               if (Features.is_enabled ~__context Features.RBAC)
                then (* paid-for edition: we can only create a subject with no roles*)
                        []
                else (*free edition: one fixed role of pool-admin only*)
@@ -177,7 +177,7 @@ let add_to_roles ~__context ~self ~role =
        
        (* CP-1224: Free Edition: Attempts to add or remove roles *)
        (* will fail with a LICENSE_RESTRICTION error.*)
-       if (not (Restrictions.license_ok_for_rbac ~__context)) then
+       if (not (Features.is_enabled ~__context Features.RBAC)) then
                raise (Api_errors.Server_error(Api_errors.license_restriction, []))
        else
 
@@ -215,7 +215,7 @@ let remove_from_roles ~__context ~self ~role =
 
        (* CP-1224: Free Edition: Attempts to add or remove roles *)
        (* will fail with a LICENSE_RESTRICTION error.*)
-       if (not (Restrictions.license_ok_for_rbac ~__context)) then
+       if not (Features.is_enabled ~__context Features.RBAC) then
                raise (Api_errors.Server_error(Api_errors.license_restriction, []))
        else
 
index e8249838b6245fb59b40b9c844accaa53e740b50..c0d4dbced69aecd576283f658d2e6794810e8e5e 100644 (file)
@@ -920,7 +920,7 @@ let revert ~__context ~snapshot =
 (* As the checkpoint operation modify the domain state, we take the vm_lock to do not let the event *)
 (* thread mess around with that. *)
 let checkpoint ~__context ~vm ~new_name =
-       if not (Restrictions.ok_for_checkpoint ~__context) then
+       if not (Features.is_enabled ~__context Features.Checkpoint) then
                raise (Api_errors.Server_error(Api_errors.license_restriction, []))
        else begin
                Local_work_queue.wait_in_line Local_work_queue.long_running_queue 
index 02744d536e2aa8e0b1664ade6a29cd81a2930aba..b049c698ee941ba64db232325b6622322dda85ac 100644 (file)
@@ -68,7 +68,7 @@ module Vm_memory_constraints : T = struct
        let assert_valid_for_current_context ~__context ~vm ~constraints =
          (* NB we don't want to prevent dom0 ballooning even if we do want to prevent
                 domU ballooning. *)
-               (if Db.VM.get_is_control_domain ~__context ~self:vm || (Restrictions.context_ok_for_dmc ~__context)
+               (if Db.VM.get_is_control_domain ~__context ~self:vm || (Features.is_enabled ~__context Features.DMC)
                        then assert_valid
                        else assert_valid_and_pinned_at_static_max)
                ~constraints
index 7ab9c41d1cd04ad67ea9ab6192b5690d46316bba..4555018cdcad54f23c9982b90557645b0384b6ec 100755 (executable)
@@ -170,7 +170,7 @@ _xe()
                 ;;
             edition) # for host-apply-edition (licensing)
                 IFS=$'\n,'
-                COMPREPLY=( $(compgen -W "free ,enterprise ,platinum " -- ${value}) )
+                COMPREPLY=( $(compgen -W "free ,enterprise ,platinum ,enterprise-xd " -- ${value}) )
                 return 0
                 ;;
                        *)