<commit_before>(*
 * Copyright (C) 2006-2009 Citrix Systems Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; version 2.1 only. with the special
 * exception on linking described in file LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *)
(** Module that defines API functions for VDI objects
 * @group XenAPI functions
 *)
 
module D=Debug.Make(struct let name="xapi" end)
open D

open Pervasiveext
open Printf

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

(** 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
	   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
	   *)
	if List.exists (fun (_, op) -> op <> `copy) current_ops
	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 = Helpers.is_tools_sr ~__context ~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 -> 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 sm_features = Xapi_sr_operations.features_of_sr_internal ~__context ~_type:sr_type in

			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 (
				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
							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 [`ha_statefile; `metadata ] && Xapi_pool_helpers.ha_enable_in_progress ~__context
							then Some (Api_errors.ha_enable_in_progress, [])
							else if List.mem record.Db_actions.vDI_type [`ha_statefile; `metadata ] && Xapi_pool_helpers.ha_disable_in_progress ~__context
							then Some (Api_errors.ha_disable_in_progress, [])
							else
								if not Smint.(has_capability Vdi_delete sm_features)
								then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
								else None
				| `resize ->
					if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
					then Some (Api_errors.ha_is_enabled, [])
					else
						if not Smint.(has_capability Vdi_resize sm_features)
						then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
						else None
				| `update ->
					if not Smint.(has_capability Vdi_update sm_features)
					then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
					else None
				| `resize_online ->
					if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
					then Some (Api_errors.ha_is_enabled, [])
					else
						if not Smint.(has_capability Vdi_resize_online sm_features)
						then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
						else None
				| `generate_config ->
					if not Smint.(has_capability Vdi_generate_config sm_features)
					then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
					else None
				| `snapshot when record.Db_actions.vDI_sharable ->
					Some (Api_errors.vdi_is_sharable, [ _ref ])
				| `snapshot when reset_on_boot ->
					Some (Api_errors.vdi_on_boot_mode_incompatible_with_operation, [])
				| `copy ->
					if List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
					then Some (Api_errors.operation_not_allowed, ["VDI containing HA statefile or redo log cannot be copied (check the VDI's allowed operations)."])<commit_msg>CA-145540: Restricting VDI snapshot during VDI copy<commit_after>(*
 * Copyright (C) 2006-2009 Citrix Systems Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; version 2.1 only. with the special
 * exception on linking described in file LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *)
(** Module that defines API functions for VDI objects
 * @group XenAPI functions
 *)
 
module D=Debug.Make(struct let name="xapi" end)
open D

open Pervasiveext
open Printf

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

(** 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
	   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
	   *)
	if List.exists (fun (_, op) -> op <> `copy) current_ops
	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 = Helpers.is_tools_sr ~__context ~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 -> 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 sm_features = Xapi_sr_operations.features_of_sr_internal ~__context ~_type:sr_type in

			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 (
				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
							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 [`ha_statefile; `metadata ] && Xapi_pool_helpers.ha_enable_in_progress ~__context
							then Some (Api_errors.ha_enable_in_progress, [])
							else if List.mem record.Db_actions.vDI_type [`ha_statefile; `metadata ] && Xapi_pool_helpers.ha_disable_in_progress ~__context
							then Some (Api_errors.ha_disable_in_progress, [])
							else
								if not Smint.(has_capability Vdi_delete sm_features)
								then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
								else None
				| `resize ->
					if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
					then Some (Api_errors.ha_is_enabled, [])
					else
						if not Smint.(has_capability Vdi_resize sm_features)
						then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
						else None
				| `update ->
					if not Smint.(has_capability Vdi_update sm_features)
					then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
					else None
				| `resize_online ->
					if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
					then Some (Api_errors.ha_is_enabled, [])
					else
						if not Smint.(has_capability Vdi_resize_online sm_features)
						then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
						else None
				| `generate_config ->
					if not Smint.(has_capability Vdi_generate_config sm_features)
					then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
					else None
				| `snapshot when record.Db_actions.vDI_sharable ->
					Some (Api_errors.vdi_is_sharable, [ _ref ])
				| `snapshot when reset_on_boot ->
					Some (Api_errors.vdi_on_boot_mode_incompatible_with_operation, [])
				| `snapshot ->
				  if List.exists (fun (_, op) -> op = `copy) current_ops
					then Some (Api_errors.operation_not_allowed, ["Snapshot operation not allowed during copy."])
				  else None
				| `copy ->
					if List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
					then Some (Api_errors.operation_not_allowed, ["VDI containing HA statefile or redo log cannot be copied (check the VDI's allowed operations)."])
{'fraction_non_alphanumeric': 0.1004733089689054, 'fraction_numerical': 0.002636151219219939, 'mean_word_length': 3.467880085653105, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7622365', 'n_tokens_mistral': 6441, 'n_tokens_neox': 5362, 'n_words': 1536}