diff --git a/ocaml/idl/datamodel.ml b/ocaml/idl/datamodel.ml index e09e6b3d1bf..1a0df6e184b 100644 --- a/ocaml/idl/datamodel.ml +++ b/ocaml/idl/datamodel.ml @@ -2334,6 +2334,18 @@ let vm_set_appliance = call ~allowed_roles:_R_POOL_OP () +let vm_call_plugin = call + ~name:"call_plugin" + ~in_product_since:rel_augusta + ~doc:"Call a XenAPI plugin on this vm" + ~params:[Ref _vm, "vm", "The vm"; + String, "plugin", "The name of the plugin"; + String, "fn", "The name of the function within the plugin"; + Map(String, String), "args", "Arguments for the function"] + ~result:(String, "Result from the plugin") + ~allowed_roles:_R_VM_OP + () + (* ------------------------------------------------------------------------------------------------------------ Host Management ------------------------------------------------------------------------------------------------------------ *) @@ -6742,6 +6754,7 @@ let vm_operations = vm_migrate_send; vm_get_boot_record; vm_send_sysrq; vm_send_trigger; vm_query_services;vm_shutdown; + vm_call_plugin; ] @ [ "changing_memory_live", "Changing the memory settings"; "awaiting_memory_live", "Waiting for the memory settings to change"; @@ -6825,6 +6838,7 @@ let vm = vm_import_convert; vm_set_appliance; vm_query_services; + vm_call_plugin; ] ~contents: ([ uid _vm; diff --git a/ocaml/xapi/cli_frontend.ml b/ocaml/xapi/cli_frontend.ml index a03a905564e..bf445b88fc5 100644 --- a/ocaml/xapi/cli_frontend.ml +++ b/ocaml/xapi/cli_frontend.ml @@ -1335,6 +1335,15 @@ there are two or more empty CD devices, please use the command 'vbd-insert' and flags=[Vm_selectors]; }; + "vm-call-plugin", + { + reqd=["vm-uuid"; "plugin"; "fn"]; + optn=["args:"]; + help="Calls the function within the plugin on the given vm with optional arguments."; + implementation=No_fd Cli_operations.vm_call_plugin; + flags=[]; + }; + "snapshot-export-to-template", { reqd=["filename"; "snapshot-uuid"]; diff --git a/ocaml/xapi/cli_operations.ml b/ocaml/xapi/cli_operations.ml index 4eeb7f9ca90..833483bebb6 100644 --- a/ocaml/xapi/cli_operations.ml +++ b/ocaml/xapi/cli_operations.ml @@ -1963,6 +1963,15 @@ let vm_memory_target_wait printer rpc session_id params = let vm=vm.getref () in Client.VM.wait_memory_target_live rpc session_id vm) params []) +let vm_call_plugin printer rpc session_id params = + let vm_uuid = List.assoc "vm-uuid" params in + let vm = Client.VM.get_by_uuid rpc session_id vm_uuid in + let plugin = List.assoc "plugin" params in + let fn = List.assoc "fn" params in + let args = read_map_params "args" params in + let result = Client.VM.call_plugin rpc session_id vm plugin fn args in + printer (Cli_printer.PList [ result ]) + let data_source_to_kvs ds = ["name_label",ds.API.data_source_name_label; "name_description",ds.API.data_source_name_description; diff --git a/ocaml/xapi/message_forwarding.ml b/ocaml/xapi/message_forwarding.ml index b5b9f49d59d..71d884062c3 100644 --- a/ocaml/xapi/message_forwarding.ml +++ b/ocaml/xapi/message_forwarding.ml @@ -1306,6 +1306,13 @@ module Forward = functor(Local: Custom_actions.CUSTOM_ACTIONS) -> struct update_vbd_operations ~__context ~vm; update_vif_operations ~__context ~vm + let call_plugin ~__context ~vm ~plugin ~fn ~args = + info "VM.call_plugin: VM = '%s'" (vm_uuid ~__context vm); + let local_fn = Local.VM.call_plugin ~vm ~plugin ~fn ~args in + with_vm_operation ~__context ~self:vm ~doc:"VM.call_plugin" ~op:`call_plugin + (fun () -> + forward_vm_op ~local_fn ~__context ~vm (fun session_id rpc -> Client.VM.call_plugin rpc session_id vm plugin fn args)) + let set_xenstore_data ~__context ~self ~value = info "VM.set_xenstore_data: VM = '%s'" (vm_uuid ~__context self); Db.VM.set_xenstore_data ~__context ~self ~value; diff --git a/ocaml/xapi/xapi_vm.ml b/ocaml/xapi/xapi_vm.ml index 24a0ad64e7a..b94c96fc55a 100644 --- a/ocaml/xapi/xapi_vm.ml +++ b/ocaml/xapi/xapi_vm.ml @@ -700,6 +700,48 @@ let set_memory_dynamic_range ~__context ~self ~min ~max = if power_state = `Running then Xapi_xenops.set_memory_dynamic_range ~__context ~self min max +let request_rdp ~__context ~vm ~enabled = + let vm_gm = Db.VM.get_guest_metrics ~__context ~self:vm in + let vm_gmr = try Some (Db.VM_guest_metrics.get_record_internal ~__context ~self:vm_gm) with _ -> None in + let is_feature_ts_on = + match vm_gmr with + | None -> false + | Some vm_gmr -> + let other = vm_gmr.Db_actions.vM_guest_metrics_other in + try + List.assoc "feature-ts" other = "1" + with Not_found -> false + in + if is_feature_ts_on + then + Xapi_xenops.request_rdp ~__context ~self:vm enabled + else raise Not_found + +let request_rdp_on ~__context ~vm = + request_rdp ~__context ~vm ~enabled:true + +let request_rdp_off ~__context ~vm = + request_rdp ~__context ~vm ~enabled:false + +(* this is the generic plugin call available to xapi users *) +let call_plugin ~__context ~vm ~plugin ~fn ~args = + if plugin <> "guest-agent-operation" then + raise (Api_errors.Server_error(Api_errors.xenapi_missing_plugin, [ plugin ])); + try + match fn with + | "request-rdp-on" -> + request_rdp_on ~__context ~vm; + "" + | "request-rdp-off" -> + request_rdp_off ~__context ~vm; + "" + | _ -> + let msg = Printf.sprintf "The requested fn \"%s\" could not be found in plugin \"%s\"." fn plugin in + raise (Api_errors.Server_error(Api_errors.xenapi_plugin_failure, [ "failed to find fn"; msg; msg ])) + with Not_found -> + let msg = Printf.sprintf "The requested fn \"%s\" of plugin \"%s\" could not be executed for lack of guest agent control feature." fn plugin in + raise (Api_errors.Server_error(Api_errors.xenapi_plugin_failure, [ "failed to execute fn"; msg; msg ])) + let send_sysrq ~__context ~vm ~key = raise (Api_errors.Server_error (Api_errors.not_implemented, [ "send_sysrq" ])) diff --git a/ocaml/xapi/xapi_vm.mli b/ocaml/xapi/xapi_vm.mli index b71b668d137..70a20c737da 100644 --- a/ocaml/xapi/xapi_vm.mli +++ b/ocaml/xapi/xapi_vm.mli @@ -244,3 +244,8 @@ val import_convert : __context:Context.t -> _type:string -> username:string -> p (** [query_services __context self] returns a Map of service type -> name label provided by the specific VM. *) val query_services : __context:Context.t -> self:API.ref_VM -> (string * string) list + +val request_rdp_on : __context:Context.t -> vm:API.ref_VM -> unit +val request_rdp_off: __context:Context.t -> vm:API.ref_VM -> unit + +val call_plugin : __context:Context.t -> vm:API.ref_VM -> plugin:string -> fn:string -> args:(string * string) list -> string diff --git a/ocaml/xapi/xapi_vm_lifecycle.ml b/ocaml/xapi/xapi_vm_lifecycle.ml index 76073e5ce48..cc44df92404 100644 --- a/ocaml/xapi/xapi_vm_lifecycle.ml +++ b/ocaml/xapi/xapi_vm_lifecycle.ml @@ -51,6 +51,7 @@ let allowed_power_states ~__context ~vmr ~(op:API.vm_operations) = | `resume_on -> [`Suspended] | `awaiting_memory_live + | `call_plugin | `clean_reboot | `clean_shutdown | `changing_memory_live diff --git a/ocaml/xapi/xapi_xenops.ml b/ocaml/xapi/xapi_xenops.ml index 31140364f51..33dedb21928 100644 --- a/ocaml/xapi/xapi_xenops.ml +++ b/ocaml/xapi/xapi_xenops.ml @@ -1882,6 +1882,18 @@ let unpause ~__context ~self = check_power_state ~__context ~self ~expected:`Running ) +let request_rdp ~__context ~self enabled = + let queue_name = queue_of_vm ~__context ~self in + transform_xenops_exn ~__context queue_name + (fun () -> + let id = id_of_vm ~__context ~self in + debug "xenops: VM.request_rdp %s %b" id enabled; + let dbg = Context.string_of_task __context in + let module Client = (val make_client queue_name : XENOPS) in + Client.VM.request_rdp dbg id enabled |> sync_with_task __context queue_name; + Events_from_xenopsd.wait queue_name dbg id () + ) + let set_xenstore_data ~__context ~self xsdata = let queue_name = queue_of_vm ~__context ~self in transform_xenops_exn ~__context queue_name