# HG changeset patch # User Marcus Granado # Date 1282322886 -3600 # Node ID 69e3ee3fb691d14532504fa5014451a9fad58be6 # Parent f8298e1caacd3a2a5323388ac9d44a4ae12bf98d CP-1841: add new api call vmpp.get_alerts Signed-off-by: Marcus Granado diff -r f8298e1caacd -r 69e3ee3fb691 ocaml/idl/datamodel.ml --- a/ocaml/idl/datamodel.ml +++ b/ocaml/idl/datamodel.ml @@ -5875,6 +5875,17 @@ ~allowed_roles:_R_VM_POWER_ADMIN ~result:(String, "An XMLRPC result") () +let vmpp_get_alerts = call ~flags:[`Session] + ~name:"get_alerts" + ~in_oss_since:None + ~in_product_since:rel_cowley + ~params:[Ref _vmpp, "vmpp", "The protection policy"; + DateTime, "since", "oldest record to fetch"; + ] + ~doc:"This call fetches a history of alerts for a given protection policy" + ~allowed_roles:_R_POOL_OP + ~result:(Set (String), "A list of alerts encoded in xml") + () let vmpp_backup_type = Enum ("vmpp_backup_type", [ "snapshot", "The backup is a snapshot"; @@ -6105,6 +6116,7 @@ ~messages:[ vmpp_protect_now; vmpp_archive_now; + vmpp_get_alerts; vmpp_set_is_backup_running; vmpp_set_is_archive_running; vmpp_set_backup_frequency; diff -r f8298e1caacd -r 69e3ee3fb691 ocaml/idl/ocaml_backend/rbac_audit.ml --- a/ocaml/idl/ocaml_backend/rbac_audit.ml +++ b/ocaml/idl/ocaml_backend/rbac_audit.ml @@ -81,26 +81,14 @@ let get_subject_name __context session_id = get_subject_common ~__context ~session_id ~fnname:"get_subject_name" - ~fn_if_local_session:(fun()->"") - ~fn_if_local_superuser:(fun()->"") + ~fn_if_local_session:(fun()-> + DB_Action.Session.get_auth_user_name ~__context ~self:session_id + ) + ~fn_if_local_superuser:(fun()-> + DB_Action.Session.get_auth_user_name ~__context ~self:session_id + ) ~fn_if_subject:(fun()-> DB_Action.Session.get_auth_user_name ~__context ~self:session_id - (* - let sid = - DB_Action.Session.get_auth_user_sid ~__context ~self:session_id - in - let subjs = DB_Action.Subject.get_records_where ~__context - ~expr:(Eq(Field "subject_identifier", Literal (sid))) - in - if List.length subjs > 1 then - failwith (Printf.sprintf - "More than one subject for subject_identifier %s"sid - ); - let (subj_id,subj) = List.hd subjs in - List.assoc - "subject-name" (*Auth_signature.subject_information_field_subject_name*) - subj.API.subject_other_config - *) ) (*given a ref-value, return a human-friendly value associated with that ref*) @@ -209,6 +197,8 @@ ("host.enable_external_auth",["service_name";"auth_type"]); ("subject.create",["subject_identifier";"other_config"]); ("subject.create.other_config",["subject-name"]); + (* used for VMPP alert logs *) + ("message.create",["name";"body"]); ] (* manual ref getters *) diff -r f8298e1caacd -r 69e3ee3fb691 ocaml/xapi/audit_log.ml --- a/ocaml/xapi/audit_log.ml +++ b/ocaml/xapi/audit_log.ml @@ -26,7 +26,16 @@ let timestamp_index line = try ((String.index line '[') + 1) with Not_found -> 0 -let write_line line fd since = +let went_through ?filter line = + match filter with + |None->true + |Some fs-> + List.fold_left + (fun acc f->acc&&(String.has_substr line f)) + true + fs + +let write_line line fd ?filter since = if String.length line > (line_timestamp_length + (timestamp_index line)) then @@ -35,10 +44,12 @@ in if since="" or ((String.compare line_timestamp since) >= 0) then + if went_through ?filter line + then let len = String.length line in ignore(Unix.write fd line 0 len) -let transfer_audit_file _path compression fd_out since = +let transfer_audit_file _path compression fd_out ?filter since = let path = Unixext.resolve_dot_and_dotdot _path in let in_whitelist = (String.startswith audit_log_whitelist_prefix path) in if in_whitelist then @@ -50,7 +61,7 @@ if compression="" (* uncompressed *) then begin Unixext.readfile_line - (fun line -> write_line (line^"\n") fd_out since) + (fun line -> write_line (line^"\n") fd_out ?filter since) path end else if compression="gz" @@ -63,7 +74,7 @@ try while true do let line = input_line cin in - write_line (line^"\n") fd_out since + write_line (line^"\n") fd_out ?filter since done with End_of_file -> () (* ok, expected *) ) @@ -79,7 +90,7 @@ end end -let transfer_all_audit_files fd_out since = +let transfer_all_audit_files fd_out ?filter since = let atransfer _infix _suffix = let infix = if _infix="" then "" else "."^_infix in let suffix = if _suffix="" then "" else "."^_suffix in @@ -87,6 +98,7 @@ (audit_log_whitelist_prefix^infix^suffix) _suffix fd_out + ?filter since in let atransfer_try_gz infix = diff -r f8298e1caacd -r 69e3ee3fb691 ocaml/xapi/xapi_vm_clone.ml --- a/ocaml/xapi/xapi_vm_clone.ml +++ b/ocaml/xapi/xapi_vm_clone.ml @@ -254,7 +254,7 @@ (let session = Context.get_session_id __context in let uname = Db.Session.get_auth_user_name ~__context ~self:session in let is_lsu = Db.Session.get_is_local_superuser ~__context ~self:session in - is_lsu && (uname = "__dom0__vmpr") + is_lsu && (uname = Xapi_vmpp.vmpr_username) ) with e -> debug "Error obtaining is_snapshot_from_vmpp: %s" (Printexc.to_string e); diff -r f8298e1caacd -r 69e3ee3fb691 ocaml/xapi/xapi_vmpp.ml --- a/ocaml/xapi/xapi_vmpp.ml +++ b/ocaml/xapi/xapi_vmpp.ml @@ -15,6 +15,7 @@ open D let vmpr_plugin = "vmpr" +let vmpr_username = "__dom0__vmpr" let protect_now ~__context ~vmpp = let vmpp_uuid = Db.VMPP.get_uuid ~__context ~self:vmpp in @@ -34,6 +35,69 @@ "archive_now" args +let add_alert ~__context ~vmpp ~name ~priority ~body = + () + +let get_alerts ~__context ~vmpp ~since = + let vmpp_uuid = Db.VMPP.get_uuid ~__context ~self:vmpp in + let filter=["unix-RPC|message.create";vmpr_username;vmpp_uuid] in + let tmp_filename = Filename.temp_file "vmpp-alerts-" ".dat" in + let fd = Unix.openfile tmp_filename [Unix.O_RDWR] 0o600 in + let since = Date.to_string since in + let messages=Audit_log.transfer_all_audit_files fd ~filter since in + let cout = Unix.out_channel_of_descr fd in + flush cout; + let cin = Unix.in_channel_of_descr fd in + seek_in cin 0; + let lines = ref [] in + let i = ref 0 in (* hard limit on maximum number of lines to parse *) + (try while !i<1000 do let line = input_line cin in lines:=line::!lines; i:=!i+1 done with End_of_file->()); + let lines = !lines in + Unix.close fd; + Sys.remove tmp_filename; + let alerts = List.map (fun line-> + let sexpr_init = try (String.index line ']')+1 with Not_found -> -1 in + if sexpr_init>=0 && sexpr_init<(String.length line) then ( + let sexpr_str = Stringext.String.sub_to_end line sexpr_init in + let (sroot:SExpr.t) = + (try SExpr_TS.of_string sexpr_str + with e-> + debug "error %s parsing sexpr: %s" + (ExnHelper.string_of_exn e) sexpr_str + ; + (SExpr.Node []) + ) + in + match sroot with + |SExpr.Node (SExpr.String _::SExpr.String _::SExpr.String _::SExpr.String _::SExpr.String _::SExpr.String _::SExpr.String _::SExpr.Node params::[])->( + let kvs = List.fold_right (fun (sexpr:SExpr.t) acc -> + match sexpr with + |SExpr.Node (SExpr.String name::SExpr.String value::SExpr.String _::SExpr.String _::[]) when name="name" or name="body"-> + (name,value)::acc + |_->acc + ) params [] + in + if kvs=[] then None else Some kvs + ) + |_->None + ) + else None + ) lines + in + let alerts = List.fold_right (fun a acc->match a with None->acc|Some a->a::acc) alerts [] in + let inside_data_tag str = + let tag_begin="" and tag_end="" in + let tag_begin_idx=try List.hd (Stringext.String.find_all tag_begin str) with _-> -1 in + let tag_end_idx=try List.hd (List.rev (Stringext.String.find_all tag_end str)) with _-> -1 in + try + let start=(tag_begin_idx+(String.length tag_begin)) in + let len=tag_end_idx - start in + Some (String.sub str start len) + with _->None + in + let alerts = List.fold_right (fun a acc->if List.mem_assoc "body" a then (let data=inside_data_tag(List.assoc "body" a) in match data with None->acc|Some data->data::acc) else acc) alerts [] in + alerts + let set_is_backup_running ~__context ~self ~value = Db.VMPP.set_is_backup_running ~__context ~self ~value diff -r f8298e1caacd -r 69e3ee3fb691 scripts/mail-alarm --- a/scripts/mail-alarm +++ b/scripts/mail-alarm @@ -17,6 +17,7 @@ import traceback import syslog from xml.dom import minidom +from xml.sax.saxutils import unescape from xml.parsers.expat import ExpatError from socket import getfqdn @@ -286,6 +287,29 @@ 'This alarm is set to be triggered when a host belonging to a high availability pool fails.' \ '\n' % self.text +class VmppETG(EmailTextGenerator): + def __init__(self, msg): + self.msg = msg + + def generate_subject(self): + msg = self.msg + return "[%s] XenServer Message: %s %s %s" % (msg.pool_name, msg.cls, msg.obj_uuid, msg.name) + + def generate_body(self): + msg = self.msg + msg_body = unescape(msg.body) + try: + xmldoc = minidom.parseString(msg_body) + body_message = xmldoc.getElementsByTagName('message')[0] + email_message = body_message.getElementsByTagName('email')[0].firstChild.data + return \ + "Field\t\tValue\n-----\t\t-----\nName:\t\t%s\nPriority:\t%s\nClass:\t\t%s\n" \ + "Object UUID:\t%s\nTimestamp:\t%s\nMessage UUID:\t%s\nPool name:\t%s\nBody:\t\t%s\n" % \ + (msg.name,msg.priority,msg.cls,msg.obj_uuid,msg.timestamp,msg.uuid,msg.pool_name,email_message) + except: + log_err("Badly formatted XML, or missing field") + sys.exit(1) + class XapiMessage: def __init__(self, xml): "Parse message XML" @@ -322,7 +346,9 @@ if hasattr(self,'cached_etg'): return self.cached_etg - if self.name == 'ALARM': + if self.cls == 'VMPP': + etg = VmppETG(self) + elif self.name == 'ALARM': # Extract the current level of the variable # (this will raise an exception if the 1st line of is not in the correct format, namely "value: %f\n") value_line = self.body.split("\n",2)[0]