[autotest] Use gmail API to send emails.
Add a library that can be used by other code to send emails via
gmail api.
It can also run as a standalone script.
TEST=Ran
./site_utils/gmail_lib.py -s 2012-dut-board-inventory
[email protected] [email protected] <
/usr/local/autotest/logs/boards-2015-02-17.16.txt
BUG=chromium:465632,chromium:468622
Change-Id: Ie59986f4f8ef68fd1c2e7f62f4ec6b50e07f49bb
Reviewed-on: https://chromium-review.googlesource.com/261074
Reviewed-by: Dan Shi <[email protected]>
Reviewed-by: Richard Barnette <[email protected]>
Commit-Queue: Fang Deng <[email protected]>
Tested-by: Fang Deng <[email protected]>
diff --git a/site_utils/gmail_lib.py b/site_utils/gmail_lib.py
new file mode 100755
index 0000000..57013f4
--- /dev/null
+++ b/site_utils/gmail_lib.py
@@ -0,0 +1,151 @@
+#!/usr/bin/python
+# Copyright 2015 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Mail the content of standard input.
+
+Example usage:
+ Use pipe:
+ $ echo "Some content" |./gmail_lib.py -s "subject" [email protected] [email protected]
+
+ Manually input:
+ $ ./gmail_lib.py -s "subject" [email protected] [email protected]
+ > Line 1
+ > Line 2
+ Ctrl-D to end standard input.
+"""
+import argparse
+import base64
+import httplib2
+import logging
+import sys
+import os
+from email.mime.text import MIMEText
+
+import common
+from autotest_lib.client.common_lib import global_config
+
+try:
+ from apiclient.discovery import build as apiclient_build
+ from apiclient import errors as apiclient_errors
+ from oauth2client import file as oauth_client_fileio
+except ImportError as e:
+ apiclient_build = None
+ logging.debug("API client for gmail disabled. %s", e)
+
+
+DEFAULT_GMAIL_CREDS_PATH = global_config.global_config.get_config_value(
+ 'NOTIFICATIONS', 'gmail_api_credentials', default='')
+
+class GmailApiException(Exception):
+ """Exception raised in accessing Gmail API."""
+
+
+class Message():
+ """An email message."""
+
+ def __init__(self, to, subject, message_text):
+ """Initialize a message.
+
+ @param to: The recievers saperated by comma.
+ e.g. '[email protected],[email protected]'
+ @param subject: String, subject of the message
+ @param message_text: String, content of the message.
+ """
+ self.to = to
+ self.subject = subject
+ self.message_text = message_text
+
+
+ def get_payload(self):
+ """Get the payload that can be sent to the Gmail API.
+
+ @return: A dictionary representing the message.
+ """
+ message = MIMEText(self.message_text)
+ message['to'] = self.to
+ message['subject'] = self.subject
+ return {'raw': base64.urlsafe_b64encode(message.as_string())}
+
+
+class GmailApiClient():
+ """Client that talks to Gmail API."""
+
+ def __init__(self, oauth_credentials):
+ """Init Gmail API client
+
+ @param oauth_credentials: Path to the oauth credential token.
+ """
+ if not apiclient_build:
+ raise GmailApiException('Cannot get apiclient library.')
+
+ storage = oauth_client_fileio.Storage(oauth_credentials)
+ credentials = storage.get()
+ if not credentials or credentials.invalid:
+ raise GmailApiException('Invalid credentials for Gmail API, '
+ 'could not send email.')
+ http = credentials.authorize(httplib2.Http())
+ self._service = apiclient_build('gmail', 'v1', http=http)
+
+
+ def send_message(self, message):
+ """Send an email message.
+
+ @param message: Message to be sent.
+ """
+ try:
+ # 'me' represents the default authorized user.
+ message = self._service.users().messages().send(
+ userId='me', body=message.get_payload()).execute()
+ logging.debug('Email sent: %s' , message['id'])
+ except apiclient_errors.HttpError as error:
+ logging.error('Failed to send email: %s', error)
+
+
+def get_default_creds_abspath():
+ """Returns the abspath of the gmail api credentials file.
+
+ @return: A path to the oauth2 credentials file.
+ """
+ auth_creds = DEFAULT_GMAIL_CREDS_PATH
+ return (auth_creds if os.path.isabs(auth_creds) else
+ os.path.join(common.autotest_dir, auth_creds))
+
+
+def send_email(to, subject, message_text):
+ """Send email.
+
+ @param to: The recipients, separated by comma.
+ @param subject: Subject of the email.
+ @param message_text: Text to send.
+ """
+ auth_creds = get_default_creds_abspath()
+ if not os.path.isfile(auth_creds):
+ logging.error('Failed to send email to %s: Credential file does not'
+ 'exist: %s. If this is a prod server, puppet should'
+ 'install it. If you need to be able to send email, '
+ 'find the credential file from chromeos-admin repo and '
+ 'copy it to %s', to, auth_creds, auth_creds)
+ return
+ client = GmailApiClient(oauth_credentials=auth_creds)
+ m = Message(to, subject, message_text)
+ client.send_message(message=m)
+
+
+if __name__ == '__main__':
+ logging.basicConfig(level=logging.DEBUG)
+ parser = argparse.ArgumentParser(
+ description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
+ parser.add_argument('-s', '--subject', type=str, dest='subject',
+ required=True, help='Subject of the mail')
+ parser.add_argument('recipients', nargs='*',
+ help='Email addresses separated by space.')
+ args = parser.parse_args()
+ if not args.recipients or not args.subject:
+ print 'Requires both recipients and subject.'
+ sys.exit(1)
+
+ message_text = sys.stdin.read()
+ send_email(','.join(args.recipients), args.subject , message_text)