From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 88B3CC4338F for ; Mon, 9 Aug 2021 12:56:17 +0000 (UTC) Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by mail.kernel.org (Postfix) with ESMTP id 22A4D60EBD for ; Mon, 9 Aug 2021 12:56:17 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 22A4D60EBD Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=ashroe.eu Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=dpdk.org Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 4CB3941109; Mon, 9 Aug 2021 14:56:07 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by mails.dpdk.org (Postfix) with ESMTP id F130C41109 for ; Mon, 9 Aug 2021 14:56:04 +0200 (CEST) X-IronPort-AV: E=McAfee;i="6200,9189,10070"; a="211578155" X-IronPort-AV: E=Sophos;i="5.84,307,1620716400"; d="scan'208";a="211578155" Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Aug 2021 05:56:04 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.84,307,1620716400"; d="scan'208";a="514940444" Received: from silpixa00396680.ir.intel.com (HELO silpixa00396680.ger.corp.intel.com) ([10.237.223.54]) by FMSMGA003.fm.intel.com with ESMTP; 09 Aug 2021 05:56:02 -0700 From: Ray Kinsella To: dev@dpdk.org Cc: bruce.richardson@intel.com, stephen@networkplumber.org, ferruh.yigit@intel.com, thomas@monjalon.net, ktraynor@redhat.com, mdr@ashroe.eu Date: Mon, 9 Aug 2021 13:53:51 +0100 Message-Id: <20210809125351.914158-3-mdr@ashroe.eu> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20210809125351.914158-1-mdr@ashroe.eu> References: <20210618163659.85933-1-mdr@ashroe.eu> <20210809125351.914158-1-mdr@ashroe.eu> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [dpdk-dev] [PATCH v9 2/2] devtools: script to send notifications of expired symbols X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Use this script with the output of the DPDK symbol tool, to notify maintainers of expired symbols by email. You need to define the environment variable DPDK_GETMAINTAINER_PATH for this tool to work. Use terminal output to review the emails before sending. e.g. $ devtools/symbol-tool.py list-expired --format-output csv \ | DPDK_GETMAINTAINER_PATH=/get_maintainer.pl \ devtools/notify_expired_symbols.py --format-output terminal Then use email output to send the emails to the maintainers. e.g. $ devtools/symbol-tool.py list-expired --format-output csv \ | DPDK_GETMAINTAINER_PATH=/get_maintainer.pl \ devtools/notify_expired_symbols.py --format-output email \ --smtp-server --sender \ --password Signed-off-by: Ray Kinsella --- devtools/notify-symbol-maintainers.py | 234 ++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100755 devtools/notify-symbol-maintainers.py diff --git a/devtools/notify-symbol-maintainers.py b/devtools/notify-symbol-maintainers.py new file mode 100755 index 0000000000..a6c27b067c --- /dev/null +++ b/devtools/notify-symbol-maintainers.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2021 Intel Corporation +'''Tool to notify maintainers of expired symbols''' +import smtplib +import ssl +import sys +import subprocess +import argparse +from argparse import RawTextHelpFormatter +import time +from email.message import EmailMessage + +DESCRIPTION = ''' +Use this script with the output of the DPDK symbol tool, to notify maintainers +of expired symbols by email. You need to define the environment variable +DPDK_GETMAINTAINER_PATH, for this tool to work. + +Use terminal output to review the emails before sending. +e.g. +$ devtools/symbol-tool.py list-expired --format-output csv \\ +| DPDK_GETMAINTAINER_PATH=/get_maintainer.pl \\ +{s} --format-output terminal + +Then use email output to send the emails to the maintainers. +e.g. +$ devtools/symbol-tool.py list-expired --format-output csv \\ +| DPDK_GETMAINTAINER_PATH=/get_maintainer.pl \\ +{s} --format-output email \\ +--smtp-server --sender --password +''' + +EMAIL_TEMPLATE = '''Hi there, + +Please note the symbols listed below have expired. In line with the DPDK ABI +policy, they should be scheduled for removal, in the next DPDK release. + +For more information, please see the DPDK ABI Policy, section 3.5.3. +https://doc.dpdk.org/guides/contributing/abi_policy.html + +Thanks, + +The DPDK Symbol Bot + +''' + +ABI_POLICY = 'doc/guides/contributing/abi_policy.rst' +get_maintainer = ['devtools/get-maintainer.sh', \ + '--email', '-f'] + +def _get_maintainers(libpath): + '''Get the maintainers for given library''' + try: + cmd = get_maintainer + [libpath] + result = subprocess.run(cmd, \ + stdout=subprocess.PIPE, \ + stderr=subprocess.PIPE, + check=True) + except subprocess.CalledProcessError: + return None + + if result is None: + return None + + email = result.stdout.decode('utf-8') + if email == '': + return None + + email = list(filter(None,email.split('\n'))) + return email + +default_maintainers = _get_maintainers(ABI_POLICY) + +def get_maintainers(libpath): + '''Get the maintainers for given library''' + maintainers=_get_maintainers(libpath) + + if maintainers is None: + maintainers = default_maintainers + + return maintainers + +def get_message(library, symbols): + '''Build email message from symbols, config and maintainers''' + message = {} + maintainers = get_maintainers(library) + + message['To'] = maintainers + if maintainers != default_maintainers: + message['CC'] = default_maintainers + + message['Subject'] = 'Expired symbols in {}\n'.format(library) + + body = EMAIL_TEMPLATE + for sym in symbols: + body += ('{}\n'.format(sym)) + + message['Body'] = body + + return message + +class OutputEmail(): + '''Format the output for email''' + def __init__(self, config): + self.config = config + + self.terminal = OutputTerminal(config) + context = ssl.create_default_context() + + # Try to log in to server and send email + try: + self.server = smtplib.SMTP(config['smtp_server'], 587) + self.server.starttls(context=context) # Secure the connection + self.server.login(config['sender'], config['password']) + except Exception as exception: + print(exception) + raise exception + + def message(self,message): + '''send email''' + self.terminal.message(message) + + msg = EmailMessage() + msg.set_content(message.pop('Body')) + + for key in message.keys(): + msg[key] = message[key] + + msg['From'] = self.config['sender'] + msg['Reply-To'] = 'no-reply@dpdk.org' + + self.server.send_message(msg) + + time.sleep(1) + + def __del__(self): + self.server.quit() + +class OutputTerminal(): # pylint: disable=too-few-public-methods + '''Format the output for the terminal''' + def __init__(self, config): + self.config = config + + def message(self,message): + '''Print email to terminal''' + terminal = 'To:' + ', '.join(message['To']) + '\n' + if 'sender' in self.config.keys(): + terminal += 'From:' + self.config['sender'] + '\n' + + terminal += 'Reply-To:' + 'no-reply@dpdk.org' + '\n' + if 'CC' in message.keys(): + terminal += 'CC:' + ', '.join(message['CC']) + '\n' + + terminal += 'Subject:' + message['Subject'] + '\n' + terminal += 'Body:' + message['Body'] + '\n' + + print(terminal) + print('-' * 80) + +def parse_config(args): + '''put the command line args in the right places''' + config = {} + error_msg = None + + outputs = { + None : OutputTerminal, + 'terminal' : OutputTerminal, + 'email' : OutputEmail + } + + if args.format_output == 'email': + if args.smtp_server is None: + error_msg = 'SMTP server' + else: + config['smtp_server'] = args.smtp_server + + if args.sender is None: + error_msg = 'sender' + else: + config['sender'] = args.sender + + if args.password is None: + error_msg = 'password' + else: + config['password'] = args.password + + if error_msg is not None: + print('Please specify a {} for email output'.format(error_msg)) + return None + + config['output'] = outputs[args.format_output] + return config + +def main(): + '''Main entry point''' + parser = argparse.ArgumentParser(description=DESCRIPTION.format(s=__file__), \ + formatter_class=RawTextHelpFormatter) + parser.add_argument('--format-output', choices=['terminal','email'], \ + default='terminal') + parser.add_argument('--smtp-server') + parser.add_argument('--password') + parser.add_argument('--sender') + + args = parser.parse_args() + config = parse_config(args) + if config is None: + return + + symbols = [] + lastlib = library = '' + + output = config['output'](config) + + for line in sys.stdin: + line = line.rstrip('\n') + library, symbol = [line[:line.find(',')], \ + line[line.find(',') + 1: len(line)]] + if library == 'mapfile': + continue + + if library != lastlib: + message = get_message(lastlib, symbols) + output.message(message) + symbols = [] + + lastlib = library + symbols = symbols + [symbol] + + #print the last library + message = get_message(lastlib, symbols) + output.message(message) + +if __name__ == '__main__': + main() -- 2.26.2