From dee3e35f4f3fd529261e143932b4ffc4f4fad167 Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Sun, 31 May 2009 16:14:07 +0200 Subject: [PATCH] implement Entropy Error reporting through UGC --- libraries/entropy/client/interfaces/qa.py | 86 +++++++++++++++++++ libraries/entropy/client/misc.py | 2 +- .../entropy/client/services/ugc/commands.py | 13 ++- .../entropy/client/services/ugc/interfaces.py | 3 + libraries/entropy/services/ugc/commands.py | 75 +++++++++++++++- 5 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 libraries/entropy/client/interfaces/qa.py diff --git a/libraries/entropy/client/interfaces/qa.py b/libraries/entropy/client/interfaces/qa.py new file mode 100644 index 000000000..f1a50ec88 --- /dev/null +++ b/libraries/entropy/client/interfaces/qa.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +''' + # DESCRIPTION: + # Entropy Object Oriented Interface + + Copyright (C) 2007-2009 Fabio Erculiani + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +''' +from entropy.qa import ErrorReportInterface +from entropy.client.interfaces import Client +from entropy.core import SystemSettings +from entropy.exceptions import IncorrectParameter, OnlineMirrorError, \ + PermissionDenied +from entropy.i18n import _ + +class UGCErrorReportInterface(ErrorReportInterface): + + """ + Entropy Errors Reporting Interface that works over User Generated + Content (UGC) infrastructure. This version is bound to a specific + repository which MUST provide UGC services, otherwise, the error + submission will fail. + + This class will allow Entropy repository maintainers to know about + critical errors happened during normal operation. + Here is an example on how to use this: + + error_interface = UGCErrorReportInterface('sabayonlinux.org') + error_interface.prepare() + reported = error_interface.submit() + if reported: + print("error reported succesfully") + else: + print("cannot report error") + """ + + def __init__(self, repository_id): + """ + object constructor, repository_id must be a valid repository + identifier. + + @param repository_id -- valid repository identifier + @type basestring + """ + ErrorReportInterface.__init__(self) + self.entropy = Client() + self.__repository_id = repository_id + if self.entropy.UGC == None: + # enable UGC + from entropy.client.services.ugc.interfaces import Client as ugc + self.entropy.UGC = ugc(self.entropy) + self.__system_settings = SystemSettings() + if repository_id not in self.__system_settings['repositories']['order']: + raise IncorrectParameter('invalid repository_id provided') + if not self.entropy.UGC.is_repository_eapi3_aware(repository_id): + raise OnlineMirrorError('UGC not supported by the provided repo') + + def submit(self): + """ + Overloaded method from ErrorReportInterface. + Does the actual error submission. You must call it after prepare(). + + @return submission status -- bool + """ + if self.generated: + done, err_msg = self.entropy.UGC.report_error(self.__repository_id, + self.params) + if done: + return True + return False + else: + mytxt = _("Not prepared yet") + raise PermissionDenied("PermissionDenied: %s" % (mytxt,)) diff --git a/libraries/entropy/client/misc.py b/libraries/entropy/client/misc.py index 29c90cd22..0ba88144e 100644 --- a/libraries/entropy/client/misc.py +++ b/libraries/entropy/client/misc.py @@ -286,4 +286,4 @@ class FileUpdates: mydict['automerge'] = True except: pass - return mydict \ No newline at end of file + return mydict diff --git a/libraries/entropy/client/services/ugc/commands.py b/libraries/entropy/client/services/ugc/commands.py index ba55429a4..39fc27dcd 100644 --- a/libraries/entropy/client/services/ugc/commands.py +++ b/libraries/entropy/client/services/ugc/commands.py @@ -703,4 +703,15 @@ class Client(Base): pkgkey, xml_string, ) - return self.do_generic_handler(cmd, session_id) \ No newline at end of file + return self.do_generic_handler(cmd, session_id) + + def report_error(self, session_id, error_data): + + xml_string = self.entropyTools.xml_from_dict(error_data) + + cmd = "%s %s %s" % ( + session_id, + 'ugc:report_error', + xml_string, + ) + return self.do_generic_handler(cmd, session_id) diff --git a/libraries/entropy/client/services/ugc/interfaces.py b/libraries/entropy/client/services/ugc/interfaces.py index 92662a997..84953f0eb 100644 --- a/libraries/entropy/client/services/ugc/interfaces.py +++ b/libraries/entropy/client/services/ugc/interfaces.py @@ -340,6 +340,9 @@ class Client: return self.remove_comment(repository, iddoc) return None,'type not supported locally' + def report_error(self, repository, error_data): + return self.do_cmd(repository, False, "report_error", [error_data], {}) + class AuthStore(Singleton): diff --git a/libraries/entropy/services/ugc/commands.py b/libraries/entropy/services/ugc/commands.py index 059b149e0..2ed316a9f 100644 --- a/libraries/entropy/services/ugc/commands.py +++ b/libraries/entropy/services/ugc/commands.py @@ -26,6 +26,7 @@ import shutil from entropy.services.skel import SocketCommands from entropy.const import etpConst from entropy.services.ugc.interfaces import Server +from entropy.misc import EmailSender class UGC(SocketCommands): @@ -45,7 +46,8 @@ class UGC(SocketCommands): ] self.raw_commands = [ 'ugc:add_comment', 'ugc:edit_comment', - 'ugc:register_stream','ugc:do_download_stats' + 'ugc:register_stream','ugc:do_download_stats', + 'ugc:report_error' ] self.valid_commands = { @@ -249,6 +251,16 @@ class UGC(SocketCommands): 'syntax': " ugc:do_download_stats ", 'from': unicode(self), # from what class }, + 'ugc:report_error': { + 'auth': False, + 'built_in': False, + 'cb': self.docmd_do_report_error, + 'args': ["authenticator","myargs"], + 'as_user': False, + 'desc': "submit an Entropy Error Report", + 'syntax': " ugc:report_error ", + 'from': unicode(self), # from what class + }, } def _load_ugc_interface(self): @@ -765,4 +777,63 @@ class UGC(SocketCommands): if metadata == None: return None,'no metadata available' - return metadata,'ok' \ No newline at end of file + return metadata,'ok' + + def docmd_do_report_error(self, authenticator, myargs): + + if not myargs: + return None, 'wrong arguments' + + xml_string = ' '.join(myargs) + try: + mydict = self.entropyTools.dict_from_xml(xml_string) + except Exception, e: + return None, "error: %s" % (e,) + + subject = 'Entropy Error Reporting Handler' + destination_email = 'entropy.errors@sabayon.org' + sender_email = mydict.get('email', 'anonymous@sabayon.org') + keys_to_file = ['errordata', 'processes', 'lspci', 'dmesg', 'locale'] + + # call it over + mail_txt = '' + for key in sorted(mydict): + if key in keys_to_file: + continue + mail_txt += u'%s: %s\n' % (key, mydict.get(key),) + + from datetime import datetime + import time + import tempfile + date = datetime.fromtimestamp(time.time()) + + # add ip address + ip_addr = self._get_session_ip_address(authenticator) + mail_txt += u'ip_address: %s\n' % (ip_addr,) + mail_txt += u'date: %s\n' % (date,) + + files = [] + rm_paths = [] + for key in keys_to_file: + if key not in mydict: + continue + fd, path = tempfile.mkstemp(suffix = "__%s.txt" % (key,)) + try: + f_path = open(path, "w") + f_path.write(mydict.get(key,'')) + f_path.flush() + f_path.close() + except IOError: + continue + files.append(path) + rm_paths.append(path) + + sender = EmailSender() + sender.send_mime_email(sender_email, [destination_email], subject, + mail_txt, files) + del sender + + for rm_path in rm_paths: + os.remove(rm_path) + + return True,'ok'