← Back to team overview

openerp-dev-web team mailing list archive

lp:~openerp-dev/openobject-addons/trunk-mail_gateway-mailerrorhandler into lp:openobject-addons

 

Stephane Wirtel (OpenERP) has proposed merging lp:~openerp-dev/openobject-addons/trunk-mail_gateway-mailerrorhandler into lp:openobject-addons.

Requested reviews:
  OpenERP Core Team (openerp)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-mail_gateway-mailerrorhandler/+merge/51295

1. Allow to send an email if there is a bug in the OpenERP Server, avoid to lose the email.
   The email will send the full trace back with the original email as an attachment and the parameters of the mailgateway.

With the attachment, you can extract the original email and resend to the mailgateway for debugging.

Example:
parameters
==========
<Values at 0x27709e0: {'default': None, 'custom_values': None, 'userid': 1, 'port': 8069, 'host': 'localhost', 'model': 'crm.lead', 'password': 'SecretPassword', 'dbname': 'SecretDatabase'}>
traceback
=========
<type 'exceptions.ZeroDivisionError'>
Python 2.6.6: /usr/bin/python
Fri Feb 25 14:53:05 2011

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 /home/stephane/openerp/sources/trunk/addons/addons-trunk-mailgateway-mailhandler/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py in main()
  189 
  190     try:
  191         1 / 0
  192         pass
  193         #email_parser.parse(msg_txt, custom_values)

<type 'exceptions.ZeroDivisionError'>: integer division or modulo by zero
    __class__ = <type 'exceptions.ZeroDivisionError'>
    __delattr__ = <method-wrapper '__delattr__' of exceptions.ZeroDivisionError object>
    __dict__ = {}
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __format__ = <built-in method __format__ of exceptions.ZeroDivisionError object>
    __getattribute__ = <method-wrapper '__getattribute__' of exceptions.ZeroDivisionError object>
    __getitem__ = <method-wrapper '__getitem__' of exceptions.ZeroDivisionError object>
    __getslice__ = <method-wrapper '__getslice__' of exceptions.ZeroDivisionError object>
    __hash__ = <method-wrapper '__hash__' of exceptions.ZeroDivisionError object>
    __init__ = <method-wrapper '__init__' of exceptions.ZeroDivisionError object>
    __new__ = <built-in method __new__ of type object>
    __reduce__ = <built-in method __reduce__ of exceptions.ZeroDivisionError object>
    __reduce_ex__ = <built-in method __reduce_ex__ of exceptions.ZeroDivisionError object>
    __repr__ = <method-wrapper '__repr__' of exceptions.ZeroDivisionError object>
    __setattr__ = <method-wrapper '__setattr__' of exceptions.ZeroDivisionError object>
    __setstate__ = <built-in method __setstate__ of exceptions.ZeroDivisionError object>
    __sizeof__ = <built-in method __sizeof__ of exceptions.ZeroDivisionError object>
    __str__ = <method-wrapper '__str__' of exceptions.ZeroDivisionError object>
    __subclasshook__ = <built-in method __subclasshook__ of type object>
    __unicode__ = <built-in method __unicode__ of exceptions.ZeroDivisionError object>
    args = ('integer division or modulo by zero',)
    message = 'integer division or modulo by zero'

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "openerp_mailgate.py", line 191, in main
    1 / 0
ZeroDivisionError: integer division or modulo by zero




[IMP] Add a new class for the configuration
It's very useful if you want to define a specific configuration for testing


[code]
class TestingConfig(DefaultConfig):

    OPENERP_DEFAULT_PASSWORD = 'SecretPassword'
    MAIL_ERROR = 'error@xxxxxxxxxxx'
    MAIL_SERVER = 'localhost'
    MAIL_ADMINS = ('admin@xxxxxxxxxxx',)

config = TestingConfig()
[/code]

-- 
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-mail_gateway-mailerrorhandler/+merge/51295
Your team OpenERP R&D Team is subscribed to branch lp:~openerp-dev/openobject-addons/trunk-mail_gateway-mailerrorhandler.
=== modified file 'mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py'
--- mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py	2011-01-14 00:11:01 +0000
+++ mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py	2011-02-25 14:11:23 +0000
@@ -19,15 +19,71 @@
 #    You should have received a copy of the GNU Affero General Public License
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>
 #
-###########################################################################################
+##############################################################################
+"""
+    openerp_mailgate.py
+"""
 
-import logging
+import cgitb
+import time
 import optparse
 import sys
 import xmlrpclib
-
-class rpc_proxy(object):
-    def __init__(self, uid, passwd, host='localhost', port=8069, path='object', dbname='openerp'):
+import smtplib
+from email.MIMEMultipart import MIMEMultipart
+from email.MIMEBase import MIMEBase
+from email.MIMEText import MIMEText
+from email.Utils import COMMASPACE, formatdate
+from email import Encoders
+
+class DefaultConfig(object):
+    """
+    Default configuration
+    """
+    OPENERP_DEFAULT_USER_ID = 1
+    OPENERP_DEFAULT_PASSWORD = 'admin'
+    OPENERP_HOSTNAME = 'localhost'
+    OPENERP_PORT = 8069
+    OPENERP_DEFAULT_DATABASE = 'openerp'
+    MAIL_ERROR = 'error@xxxxxxxxxxx'
+    MAIL_SERVER = 'localhost'
+    MAIL_ADMINS = ('admin@xxxxxxxxxxx',)
+
+config = DefaultConfig()
+
+
+def send_mail(_from_, to_, subject, text, files=None, server=config.MAIL_SERVER):
+    assert isinstance(to_, (list, tuple))
+
+    if files is None:
+        files = []
+
+    msg = MIMEMultipart()
+    msg['From'] = _from_
+    msg['To'] = COMMASPACE.join(to_)
+    msg['Date'] = formatdate(localtime=True)
+    msg['Subject'] = subject
+
+    msg.attach( MIMEText(text) )
+
+    for file_name, file_content in files:
+        part = MIMEBase('application', "octet-stream")
+        part.set_payload( file_content )
+        Encoders.encode_base64(part)
+        part.add_header('Content-Disposition', 'attachment; filename="%s"'
+                       % file_name)
+        msg.attach(part)
+
+    smtp = smtplib.SMTP(server)
+    smtp.sendmail(_from_, to_, msg.as_string() )
+    smtp.close()
+
+class RPCProxy(object):
+    def __init__(self, uid, passwd,
+                 host=config.OPENERP_HOSTNAME,
+                 port=config.OPENERP_PORT,
+                 path='object',
+                 dbname=config.OPENERP_DEFAULT_DATABASE):
         self.rpc = xmlrpclib.ServerProxy('http://%s:%s/xmlrpc/%s' % (host, port, path), allow_none=True)
         self.user_id = uid
         self.passwd = passwd
@@ -36,9 +92,9 @@
     def __call__(self, *request, **kwargs):
         return self.rpc.execute(self.dbname, self.user_id, self.passwd, *request, **kwargs)
 
-class email_parser(object):
+class EmailParser(object):
     def __init__(self, uid, password, model, email_default, dbname, host, port):
-        self.rpc = rpc_proxy(uid, password, host=host, port=port, dbname=dbname)
+        self.rpc = RPCProxy(uid, password, host=host, port=port, dbname=dbname)
         try:
             self.model_id = int(model)
             self.model = str(model)
@@ -51,44 +107,92 @@
     def parse(self, message, custom_values=None):
         if custom_values is None:
             custom_values = {}
-        try:
-            # pass message as bytes because we don't know its encoding until we parse its headers
-            # and hence can't convert it to utf-8 for transport
-            res_id = self.rpc('email.server.tools', 'process_email', self.model, xmlrpclib.Binary(message), custom_values)
-        except Exception:
-            logger = logging.getLogger('mail-gateway')
-            logger.warning('Failed to process incoming email. Source of the failed mail is available at debug level.', exc_info=True)
-            logger.debug('Source of the mail that failed to parse:', message)
+        # pass message as bytes because we don't know its encoding until we parse its headers
+        # and hence can't convert it to utf-8 for transport
+        self.rpc('email.server.tools',
+                 'process_email',
+                 self.model,
+                 xmlrpclib.Binary(message),
+                 custom_values)
 
-if __name__ == '__main__':
-    parser = optparse.OptionParser(usage='usage: %prog [options]', version='%prog v1.0')
+def configure_parser():
+    parser = optparse.OptionParser(usage='usage: %prog [options]', version='%prog v1.1')
     group = optparse.OptionGroup(parser, "Note",
         "This program parse a mail from standard input and communicate "
         "with the OpenERP server for case management in the CRM module.")
     parser.add_option_group(group)
-    parser.add_option("-u", "--user", dest="userid", help="ID of the user in OpenERP", default=1, type='int')
-    parser.add_option("-p", "--password", dest="password", help="Password of the user in OpenERP", default='admin')
-    parser.add_option("-o", "--model", dest="model", help="Name or ID of crm model", default="crm.lead")
-    parser.add_option("-m", "--default", dest="default", help="Default eMail in case of any trouble.", default=None)
-    parser.add_option("-d", "--dbname", dest="dbname", help="Database name (default: openerp)", default='openerp')
-    parser.add_option("--host", dest="host", help="Hostname of the OpenERP Server", default="localhost")
-    parser.add_option("--port", dest="port", help="Port of the OpenERP Server", default="8069")
-    parser.add_option("--custom-values", dest="custom_values", help="Add Custom Values to the object", default=None)
-
+    parser.add_option("-u", "--user", dest="userid",
+                      help="ID of the user in OpenERP",
+                      default=config.OPENERP_DEFAULT_USER_ID, type='int')
+    parser.add_option("-p", "--password", dest="password",
+                      help="Password of the user in OpenERP",
+                      default=config.OPENERP_DEFAULT_PASSWORD)
+    parser.add_option("-o", "--model", dest="model",
+                      help="Name or ID of crm model",
+                      default="crm.lead")
+    parser.add_option("-m", "--default", dest="default",
+                      help="Default eMail in case of any trouble.",
+                      default=None)
+    parser.add_option("-d", "--dbname", dest="dbname",
+                      help="Database name (default: %default)",
+                      default=config.OPENERP_DEFAULT_DATABASE)
+    parser.add_option("--host", dest="host",
+                      help="Hostname of the OpenERP Server",
+                      default=config.OPENERP_HOSTNAME)
+    parser.add_option("--port", dest="port",
+                      help="Port of the OpenERP Server",
+                      default=config.OPENERP_PORT)
+    parser.add_option("--custom-values", dest="custom_values",
+                      help="Add Custom Values to the object",
+                      default=None)
+
+    return parser
+
+def main():
+    """
+    Receive the email via the stdin and send it to the OpenERP Server
+    """
+    parser = configure_parser()
     (options, args) = parser.parse_args()
 
-    logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(message)s")
-
-    parser = email_parser(options.userid, options.password, options.model, options.default, dbname=options.dbname, host=options.host, port=options.port)
+
+    email_parser = EmailParser(options.userid,
+                               options.password,
+                               options.model,
+                               options.default,
+                               dbname=options.dbname,
+                               host=options.host,
+                               port=options.port)
+
 
     msg_txt = sys.stdin.read()
 
     custom_values = {}
     try:
-        custom_values = dict(eval(options.custom_values))
+        custom_values = dict(eval(options.custom_values or {} ))
     except:
         pass
 
-    parser.parse(msg_txt, custom_values)
+    try:
+        email_parser.parse(msg_txt, custom_values)
+    except Exception:
+        msg = '\n'.join([
+            'parameters',
+            '==========',
+            '%r' % (options,),
+            'traceback',
+            '=========',
+            '%s' % (cgitb.text(sys.exc_info())),
+        ])
+
+        subject = '[OPENERP]:ERROR: Mailgateway - %s' % time.strftime('%Y-%m-%d %H:%M:%S')
+        send_mail(
+            config.MAIL_ERROR,
+            config.MAIL_ADMINS,
+            subject, msg, files=[('message.txt', msg_txt)]
+        )
+
+if __name__ == '__main__':
+    main()
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:


Follow ups