[Asterisk-code-review] Adds option to send crash reports via email (testsuite[master])

Joshua Colp asteriskteam at digium.com
Wed Oct 28 14:40:55 CDT 2015


Joshua Colp has submitted this change and it was merged.

Change subject: Adds option to send crash reports via email
......................................................................


Adds option to send crash reports via email

--email-on-crash enables email on crash behavior. When this
behavior is enabled, the testsuite will attempt to email crash
reports (currently just the path to the test and a backtrace).
Configuration for sending the email (smtp server, who to send as,
who to send to, etc) is handled via crash-mail-config.yaml which
goes in the base folder for the testsuite when in use. There is
a sample configuration for it stored in the sample-yaml folder
(crash-mail-config.yaml.sample)

Change-Id: I17def41f15294a48754269d9c42aa649389840ad
---
M .gitignore
A lib/python/mailer.py
M runtests.py
A sample-yaml/crash-mail-config.yaml.sample
4 files changed, 129 insertions(+), 15 deletions(-)

Approvals:
  Anonymous Coward #1000019: Verified
  Ashley Sanders: Looks good to me, but someone else must approve
  Matt Jordan: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, approved



diff --git a/.gitignore b/.gitignore
index c37e120..fdbbcf0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 *.pyc
 asterisk-test-suite-report.xml
+crash-mail-config.yaml
 /astroot
 /contrib/valgrind/suppressions.txt
 /logs
diff --git a/lib/python/mailer.py b/lib/python/mailer.py
new file mode 100644
index 0000000..9c6d726
--- /dev/null
+++ b/lib/python/mailer.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+"""Simple email via SMTP
+
+Copyright (C) 2015, Digium, Inc.
+Jonathan Rose <jrose at digium.com>
+
+This program is free software, distributed under the terms of
+the GNU General Public License Version 2.
+"""
+
+try:
+    import smtplib
+    SMTP_AVAILABLE = True
+except ImportError:
+    SMTP_AVAILABLE = False
+
+
+def send_email(server, sender, recipients, message, debug=0):
+    """Send an email using SMTP
+
+    Keyword Arguments:
+    server - host address of SMTP server
+    sender - email address to use as the sender
+    recipients - array of email addresses to send the message to
+    message - dictionary containing a body and a subject for the message
+    debug - optional argument sets debug level for smtp interface
+
+    Note: Throws exceptions if SMTP server connection fails in some manner
+          or if smtplib is not available when this function is imported.
+    """
+    if not SMTP_AVAILABLE:
+        raise Exception("smtplib could not be loaded, email functionality "
+                        "is unavailable")
+
+    subject = message.get('subject')
+    if subject:
+        email_text = "Subject: {0}\n\n{1}".format(subject,
+                                                  message['body'])
+    else:
+        email_text = message['body']
+
+    smtp_obj = smtplib.SMTP(server)
+    smtp_obj.set_debuglevel(debug)
+    smtp_obj.sendmail(sender, recipients, email_text)
diff --git a/runtests.py b/runtests.py
index 88b0e23..b971004 100755
--- a/runtests.py
+++ b/runtests.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 '''Asterisk external test suite driver.
 
-Copyright (C) 2010, Digium, Inc.
+Copyright (C) 2015, Digium, Inc.
 Russell Bryant <russell at digium.com>
 
 This program is free software, distributed under the terms of
@@ -43,6 +43,7 @@
 from asterisk.version import AsteriskVersion
 from asterisk.asterisk import Asterisk
 from asterisk.test_config import TestConfig
+from mailer import send_email
 
 TESTS_CONFIG = "tests.yaml"
 TEST_RESULTS = "asterisk-test-suite-report.xml"
@@ -184,6 +185,37 @@
 
         return core_files
 
+    def _email_crash_report(self, dest_file_name):
+        email_config = load_yaml_config("crash-mail-config.yaml")
+
+        smtp_server = email_config.get('smtp-server')
+        sender = email_config.get('sender')
+        recipients = email_config.get('recipients')
+        subject = email_config.get('subject', "Testsuite crash detected")
+        max_bt_len = email_config.get('truncate-backtrace', 0)
+        debug_level = email_config.get('debug', 0)
+
+        if not sender or len(recipients) == 0:
+            print "--email-on-crash requires sender and 1+ recipients"
+            return
+
+        with open(dest_file_name, 'r') as bt_file:
+            backtrace_text = bt_file.read()
+
+        if max_bt_len > 0 and backtrace_text > max_bt_len:
+            truncated_text = "\n--truncated to {0} chars--".format(max_bt_len)
+            backtrace_text = backtrace_text[0:max_bt_len] + truncated_text
+
+        email_text = "{0}\n\n{1}".format(dest_file_name, backtrace_text)
+
+        message = {'body': email_text, 'subject': subject}
+
+        try:
+            send_email(smtp_server, sender, recipients, message,
+                       debug=debug_level)
+        except Exception as exception:
+            print "Failed to send email\nError: {0}".format(exception)
+
     def _archive_core_dumps(self, core_dumps):
         for core in core_dumps:
             if not os.path.exists(core):
@@ -209,6 +241,10 @@
                     print "error analyzing core dump; gdb exited with %d" % res
                 # Copy the backtrace over to the logs
                 print "Archived backtrace: {0}".format(dest_file_name)
+
+                if self.options.email_on_crash:
+                    self._email_crash_report(dest_file_name)
+
             except OSError, ose:
                 print "OSError ([%d]: %s) occurred while executing %r" % \
                     (ose.errno, ose.strerror, gdb_cmd)
@@ -393,18 +429,10 @@
 
     def _parse_test_yaml(self, test_dir, ast_version):
         tests = []
-        try:
-            f = open("%s/%s" % (test_dir, TESTS_CONFIG), "r")
-        except IOError:
-            if test_dir != "tests/custom":
-                print "Failed to open %s/%s" % (test_dir, TESTS_CONFIG)
-            return tests
-        except:
-            print "Unexpected error: %s" % sys.exc_info()[0]
-            return tests
 
-        config = yaml.load(f)
-        f.close()
+        config = load_yaml_config("%s/%s" % (test_dir, TESTS_CONFIG))
+        if not config:
+            return tests
 
         for t in config["tests"]:
             for val in t:
@@ -616,6 +644,23 @@
             tc.appendChild(failure)
 
 
+def load_yaml_config(path):
+    """Load contents of a YAML config file to a dictionary"""
+    try:
+        f = open(path, "r")
+    except IOError:
+        print "Failed to open %s" % path
+        return None
+    except:
+        print "Unexpected error: %s" % sys.exc_info()[0]
+        return None
+
+    config = yaml.load(f)
+    f.close()
+
+    return config
+
+
 def handle_usr1(sig, stack):
     """Handle the SIGUSR1 signal
 
@@ -668,14 +713,18 @@
                       help="List available tags")
     parser.add_option("-t", "--test", action="append", default=[],
                       dest="tests",
-                      help=("Run a single specified test (directory) instead "
-                            "of all tests.  May be specified more than once."))
+                      help="Run a single specified test (directory) instead "
+                           "of all tests.  May be specified more than once.")
     parser.add_option("-v", "--version",
                       dest="version", default=None,
                       help="Specify the version of Asterisk rather then detecting it.")
     parser.add_option("-V", "--valgrind", action="store_true",
                       dest="valgrind", default=False,
                       help="Run Asterisk under Valgrind")
+    parser.add_option("--email-on-crash", action="store_true",
+                      dest="email_on_crash", default=False,
+                      help="Send email on crash with a backtrace. See "
+                           "crash-mail-config.yaml.sample for details.")
     parser.add_option("--number", metavar="int", type=int,
                       dest="number", default=1,
                       help="Number of times to run the test suite. If a value of "
@@ -686,7 +735,6 @@
     parser.add_option("--timeout", metavar='int', type=int,
                       dest="timeout", default=-1,
                       help="Abort test after n seconds of no output.")
-
     (options, args) = parser.parse_args(argv)
 
     # Install a signal handler for USR1/TERM, and use it to bail out of running
diff --git a/sample-yaml/crash-mail-config.yaml.sample b/sample-yaml/crash-mail-config.yaml.sample
new file mode 100644
index 0000000..9674166
--- /dev/null
+++ b/sample-yaml/crash-mail-config.yaml.sample
@@ -0,0 +1,21 @@
+# Configuration sample for mail on crash functionality. Place this in the
+# testsuite base directory as crash-mail-config.yaml and use the
+# --email-on-crash option for runtests.py to send reports to the recipients
+# using the configured parameters here.
+
+# The smtp server to send the message
+smtp-server: 'example-domain.com'
+# The email address that should act as the sender
+sender: 'bigboss at example-domain.com'
+# Recipients for the email
+recipients:
+    -
+        'sigint at example-domain.com'
+    -
+        'paramedic at example-domain.com'
+# (optional) If set, backtrace will be truncated to the first N characters
+truncate-backtrace: 10000
+# (options) Subject line for the email (default: 'Testsuite crash detected')
+subject: 'Testsuite crash detected'
+# (optional) Sets debug level for SMTP interactions (default: 0)
+debug: 0

-- 
To view, visit https://gerrit.asterisk.org/1539
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I17def41f15294a48754269d9c42aa649389840ad
Gerrit-PatchSet: 6
Gerrit-Project: testsuite
Gerrit-Branch: master
Gerrit-Owner: Jonathan Rose <jrose at digium.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: Ashley Sanders <asanders at digium.com>
Gerrit-Reviewer: Corey Farrell <git at cfware.com>
Gerrit-Reviewer: Jonathan Rose <jrose at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Mark Michelson <mmichelson at digium.com>
Gerrit-Reviewer: Matt Jordan <mjordan at digium.com>



More information about the asterisk-code-review mailing list