[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)