<commit_before>(*
 * Copyright (C) 2006-2017 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.
 *)
(* Module that defines API functions for VDI objects
 * @group XenAPI functions
*)

module D=Debug.Make(struct let name="xapi" end)
open D

open Stdext
open Pervasiveext
open Printf

(**************************************************************************************)
(* current/allowed operations checking                                                *)

let check_sm_feature_error (op:API.vdi_operations) sm_features sr =
  let required_sm_feature = Smint.(match op with
  | `forget
  | `copy
  | `scan
  | `force_unlock
  | `blocked
    -> None
  | `snapshot -> Some Vdi_snapshot
  | `destroy -> Some Vdi_delete
  | `resize -> Some Vdi_resize
  | `update -> Some Vdi_update
  | `resize_online -> Some Vdi_resize_online
  | `generate_config -> Some Vdi_generate_config
  | `clone -> Some Vdi_clone
  | `mirror -> Some Vdi_mirror
  | `enable_cbt | `disable_cbt -> Some Vdi_configure_cbt
  ) in
  match required_sm_feature with
  | None -> None
  | Some feature ->
    if Smint.(has_capability feature sm_features)
    then None
    else Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])

(* Checks to see if an operation is valid in this state. Returns Some exception
   if not and None if everything is ok. *)
let check_operation_error ~__context ?(sr_records=[]) ?(pbd_records=[]) ?(vbd_records=[]) ha_enabled record _ref' op =
  let _ref = Ref.string_of _ref' in
  let current_ops = record.Db_actions.vDI_current_operations in
  let reset_on_boot = record.Db_actions.vDI_on_boot = `reset in

  (* Policy:
     	   1. any current_operation besides copy implies exclusivity; fail everything
     	      else; except vdi mirroring is in current operations and destroy is performed
     	      as part of vdi_pool_migrate.
     	   2. if a copy is ongoing, don't fail with other_operation_in_progress, as
     	      blocked operations could then get stuck behind a long-running copy.
     	      Instead, rely on the blocked_by_attach check further down to decide
     	      whether an operation should be allowed.
     	   3. if doing a VM start then assume the sharing check is done elsewhere
     	      (so VMs may share disks but our operations cannot)
     	   4. for other operations, fail if any VBD has currently-attached=true or any VBD
     	      has a current_operation itself
     	   5. HA prevents you from deleting statefiles or metadata volumes
     	   *)
  (* Don't fail with other_operation_in_progress if VDI mirroring is in progress
     	 * and destroy is called as part of VDI mirroring *)
  let is_vdi_mirroring_in_progress = (List.exists (fun (_, op) -> op = `mirror) current_ops) && (op = `destroy) in
  if (List.exists (fun (_, op) -> op <> `copy) current_ops) && not is_vdi_mirroring_in_progress
  then Some(Api_errors.other_operation_in_progress,["VDI"; _ref])
  else
    (* check to see whether it's a local cd drive *)
    let sr = record.Db_actions.vDI_SR in
    let sr_type = Db.SR.get_type ~__context ~self:sr in
    let is_tools_sr = Db.SR.get_is_tools_sr ~__context ~self:sr in

    (* Check to see if any PBDs are attached *)
    let open Db_filter_types in
    let pbds_attached = match pbd_records with
      | [] -> Db.PBD.get_records_where ~__context ~expr:(And(Eq(Field "SR", Literal (Ref.string_of sr)), Eq(Field "currently_attached", Literal "true")))
      | _ -> List.filter (fun (_, pbd_record) -> (pbd_record.API.pBD_SR = sr) && pbd_record.API.pBD_currently_attached) pbd_records
    in
    if (List.length pbds_attached = 0) && List.mem op [`resize;]
    then Some(Api_errors.sr_no_pbds, [Ref.string_of sr])
    else
      (* check to see whether VBDs exist which are using this VDI *)

      (* Only a 'live' operation can be performed if there are active (even RO) devices *)
      let my_active_vbd_records = match vbd_records with
        | [] -> List.map snd (Db.VBD.get_internal_records_where ~__context
                                ~expr:(
                                  And(Eq (Field "VDI", Literal _ref),
                                      Or(
                                        Eq (Field "currently_attached", Literal "true"),
                                        Eq (Field "reserved", Literal "true")))
                                ))
        | _ -> List.map snd (List.filter (fun (_, vbd_record) ->
            vbd_record.Db_actions.vBD_VDI = _ref' && (vbd_record.Db_actions.vBD_currently_attached || vbd_record.Db_actions.vBD_reserved)
          ) vbd_records)
      in
      let my_active_rw_vbd_records = List.filter
          (fun vbd -> vbd.Db_actions.vBD_mode = `RW)
          my_active_vbd_records
      in

      (* VBD operations (plug/unplug) (which should be transient) cause us to serialise *)
      let my_has_current_operation_vbd_records = match vbd_records with
        | [] -> List.map snd (Db.VBD.get_internal_records_where ~__context
                                ~expr:(
                                  And(Eq (Field "VDI", Literal _ref), Not (Eq (Field "current_operations", Literal "()")))
                                ))
        | _ -> List.map snd (List.filter (fun (_, vbd_record) ->
            vbd_record.Db_actions.vBD_VDI = _ref' && vbd_record.Db_actions.vBD_current_operations <> []
          ) vbd_records)
      in

      (* If the VBD is currently_attached then some operations can still be performed ie:
         			   VDI.clone (if the VM is suspended we have to have the 'allow_clone_suspended_vm'' flag)
         			   VDI.snapshot; VDI.resize_online; 'blocked' (CP-831) *)
      let operation_can_be_performed_live = match op with
        | `snapshot | `resize_online | `blocked | `clone | `mirror | `enable_cbt | `disable_cbt -> true
        | _ -> false in

      let operation_can_be_performed_with_ro_attach =
        operation_can_be_performed_live ||
        (match op with
         | `copy -> true
         | _ -> false)
      in

      (* NB RO vs RW sharing checks are done in xapi_vbd.ml *)

      let blocked_by_attach =
        if operation_can_be_performed_live
        then false
        else begin
          if operation_can_be_performed_with_ro_attach
          then (my_active_rw_vbd_records <> [])
          else (my_active_vbd_records <> [])
        end
      in
      if blocked_by_attach
      then Some (Api_errors.vdi_in_use,[_ref; (Record_util.vdi_operation_to_string op)])
      else if my_has_current_operation_vbd_records <> []
      then Some (Api_errors.other_operation_in_progress, [ "VDI"; _ref ])
      else
      let sm_features = Xapi_sr_operations.features_of_sr_internal ~__context ~_type:sr_type in
      let sm_feature_error = check_sm_feature_error op sm_features sr in
      if sm_feature_error <> None
      then sm_feature_error
      else (
        match op with
        | `forget ->
          if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
          then Some (Api_errors.ha_is_enabled, [])
          else if List.mem record.Db_actions.vDI_type [ `rrd ]
          then Some (Api_errors.vdi_has_rrds, [_ref])
          else None
        | `destroy ->
          if sr_type = "udev"
          then Some (Api_errors.vdi_is_a_physical_device, [_ref])
          else
          if is_tools_sr
          then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
          else if List.mem record.Db_actions.vDI_type [ `rrd ]
          then Some (Api_errors.vdi_has_rrds, [_ref])
          else<commit_msg>Xapi_vdi.check_operation_error: checks for cbt_metadata VDI<commit_after>(*
 * Copyright (C) 2006-2017 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.
 *)
(* Module that defines API functions for VDI objects
 * @group XenAPI functions
*)

module D=Debug.Make(struct let name="xapi" end)
open D

open Stdext
open Pervasiveext
open Printf

(**************************************************************************************)
(* current/allowed operations checking                                                *)

let check_sm_feature_error (op:API.vdi_operations) sm_features sr =
  let required_sm_feature = Smint.(match op with
  | `forget
  | `copy
  | `scan
  | `force_unlock
  | `blocked
    -> None
  | `snapshot -> Some Vdi_snapshot
  | `destroy -> Some Vdi_delete
  | `resize -> Some Vdi_resize
  | `update -> Some Vdi_update
  | `resize_online -> Some Vdi_resize_online
  | `generate_config -> Some Vdi_generate_config
  | `clone -> Some Vdi_clone
  | `mirror -> Some Vdi_mirror
  | `enable_cbt | `disable_cbt -> Some Vdi_configure_cbt
  ) in
  match required_sm_feature with
  | None -> None
  | Some feature ->
    if Smint.(has_capability feature sm_features)
    then None
    else Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])

(* Checks to see if an operation is valid in this state. Returns Some exception
   if not and None if everything is ok. *)
let check_operation_error ~__context ?(sr_records=[]) ?(pbd_records=[]) ?(vbd_records=[]) ha_enabled record _ref' op =
  let _ref = Ref.string_of _ref' in
  let current_ops = record.Db_actions.vDI_current_operations in
  let reset_on_boot = record.Db_actions.vDI_on_boot = `reset in

  (* Policy:
     	   1. any current_operation besides copy implies exclusivity; fail everything
     	      else; except vdi mirroring is in current operations and destroy is performed
     	      as part of vdi_pool_migrate.
     	   2. if a copy is ongoing, don't fail with other_operation_in_progress, as
     	      blocked operations could then get stuck behind a long-running copy.
     	      Instead, rely on the blocked_by_attach check further down to decide
     	      whether an operation should be allowed.
     	   3. if doing a VM start then assume the sharing check is done elsewhere
     	      (so VMs may share disks but our operations cannot)
     	   4. for other operations, fail if any VBD has currently-attached=true or any VBD
     	      has a current_operation itself
     	   5. HA prevents you from deleting statefiles or metadata volumes
     	   *)
  (* Don't fail with other_operation_in_progress if VDI mirroring is in progress
     	 * and destroy is called as part of VDI mirroring *)
  let is_vdi_mirroring_in_progress = (List.exists (fun (_, op) -> op = `mirror) current_ops) && (op = `destroy) in
  if (List.exists (fun (_, op) -> op <> `copy) current_ops) && not is_vdi_mirroring_in_progress
  then Some(Api_errors.other_operation_in_progress,["VDI"; _ref])
  else
    (* check to see whether it's a local cd drive *)
    let sr = record.Db_actions.vDI_SR in
    let sr_type = Db.SR.get_type ~__context ~self:sr in
    let is_tools_sr = Db.SR.get_is_tools_sr ~__context ~self:sr in

    (* Check to see if any PBDs are attached *)
    let open Db_filter_types in
    let pbds_attached = match pbd_records with
      | [] -> Db.PBD.get_records_where ~__context ~expr:(And(Eq(Field "SR", Literal (Ref.string_of sr)), Eq(Field "currently_attached", Literal "true")))
      | _ -> List.filter (fun (_, pbd_record) -> (pbd_record.API.pBD_SR = sr) && pbd_record.API.pBD_currently_attached) pbd_records
    in
    if (List.length pbds_attached = 0) && List.mem op [`resize;]
    then Some(Api_errors.sr_no_pbds, [Ref.string_of sr])
    else
      (* check to see whether VBDs exist which are using this VDI *)

      (* Only a 'live' operation can be performed if there are active (even RO) devices *)
      let my_active_vbd_records = match vbd_records with
        | [] -> List.map snd (Db.VBD.get_internal_records_where ~__context
                                ~expr:(
                                  And(Eq (Field "VDI", Literal _ref),
                                      Or(
                                        Eq (Field "currently_attached", Literal "true"),
                                        Eq (Field "reserved", Literal "true")))
                                ))
        | _ -> List.map snd (List.filter (fun (_, vbd_record) ->
            vbd_record.Db_actions.vBD_VDI = _ref' && (vbd_record.Db_actions.vBD_currently_attached || vbd_record.Db_actions.vBD_reserved)
          ) vbd_records)
      in
      let my_active_rw_vbd_records = List.filter
          (fun vbd -> vbd.Db_actions.vBD_mode = `RW)
          my_active_vbd_records
      in

      (* VBD operations (plug/unplug) (which should be transient) cause us to serialise *)
      let my_has_current_operation_vbd_records = match vbd_records with
        | [] -> List.map snd (Db.VBD.get_internal_records_where ~__context
                                ~expr:(
                                  And(Eq (Field "VDI", Literal _ref), Not (Eq (Field "current_operations", Literal "()")))
                                ))
        | _ -> List.map snd (List.filter (fun (_, vbd_record) ->
            vbd_record.Db_actions.vBD_VDI = _ref' && vbd_record.Db_actions.vBD_current_operations <> []
          ) vbd_records)
      in

      (* If the VBD is currently_attached then some operations can still be performed ie:
         			   VDI.clone (if the VM is suspended we have to have the 'allow_clone_suspended_vm'' flag)
         			   VDI.snapshot; VDI.resize_online; 'blocked' (CP-831) *)
      let operation_can_be_performed_live = match op with
        | `snapshot | `resize_online | `blocked | `clone | `mirror | `enable_cbt | `disable_cbt -> true
        | _ -> false in

      let operation_can_be_performed_with_ro_attach =
        operation_can_be_performed_live ||
        (match op with
         | `copy -> true
         | _ -> false)
      in

      (* NB RO vs RW sharing checks are done in xapi_vbd.ml *)

      let blocked_by_attach =
        if operation_can_be_performed_live
        then false
        else begin
          if operation_can_be_performed_with_ro_attach
          then (my_active_rw_vbd_records <> [])
          else (my_active_vbd_records <> [])
        end
      in
      if blocked_by_attach
      then Some (Api_errors.vdi_in_use,[_ref; (Record_util.vdi_operation_to_string op)])
      else if my_has_current_operation_vbd_records <> []
      then Some (Api_errors.other_operation_in_progress, [ "VDI"; _ref ])
      else
      let sm_features = Xapi_sr_operations.features_of_sr_internal ~__context ~_type:sr_type in
      let sm_feature_error = check_sm_feature_error op sm_features sr in
      if sm_feature_error <> None
      then sm_feature_error
      else
      let allowed_for_cbt_metadata_vdi = match op with
        | `clone | `copy | `disable_cbt | `enable_cbt | `mirror | `resize | `resize_online | `snapshot -> false
        | `blocked | `destroy | `force_unlock | `forget | `generate_config | `scan | `update -> true in
      if not allowed_for_cbt_metadata_vdi && record.Db_actions.vDI_type = `cbt_metadata
      then Some (Api_errors.vdi_incompatible_type, [ _ref; Record_util.vdi_type_to_string `cbt_metadata ])
      else (
        match op with
        | `forget ->
          if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
          then Some (Api_errors.ha_is_enabled, [])
          else if List.mem record.Db_actions.vDI_type [ `rrd ]
          then Some (Api_errors.vdi_has_rrds, [_ref])
          else None
        | `destroy ->
          if sr_type = "udev"
          then Some (Api_errors.vdi_is_a_physical_device, [_ref])
          else
          if is_tools_sr
          then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
          else if List.mem record.Db_actions.vDI_type [ `rrd ]
          then Some (Api_errors.vdi_has_rrds, [_ref])
          else
{'fraction_non_alphanumeric': 0.09290835693197566, 'fraction_numerical': 0.0022895704042899317, 'mean_word_length': 2.5965330444203683, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6026027', 'n_tokens_mistral': 5170, 'n_tokens_neox': 5034, 'n_words': 1502}