Add script to post a gerrit welcome message

This script, to be triggered by the patchset-created hook in gerrit,
will post a welcoming message on new contributions from people who
have never contributed to OpenStack before.

The aim is to increase awareness of the place of code review in our
community, and encourage users to participate, while managing
expectations.

The plan is to merge this, then test thoroughly on review-dev with
a range of cases, including:
* First time patch (post message)
* Patch after change of email (no message)
* Patch after existing patch (no message)

Change-Id: I7bb2baeb37d3700ff07f39a8a29b4eaf41f5cd40
Partial-Bug: 1246879
diff --git a/jeepyb/cmd/welcome_message.py b/jeepyb/cmd/welcome_message.py
new file mode 100644
index 0000000..31df892
--- /dev/null
+++ b/jeepyb/cmd/welcome_message.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# This is designed to be called by a gerrit hook.  It searched new
+# patchsets for those from a first time commiter, then posts a helpful
+# message welcoming them to the community and explaining the review process
+#
+# For example, this might be called as follows
+# python welcome_message.py -change Ia1fea1eab3976f1a9cb89ceb3ce1c6c6a7e79c42
+# --change-url \ https://review-dev.openstack.org/81 --project gtest-org/test \
+# --branch master --uploader User A. Example (user@example.com) --commit \
+# 05508ae633852469d2fd7786a3d6f1d06f87055b --patchset 1 patchset-merged user \
+# ~/.ssh/id_rsa
+# and if this was the first commit from "user@example.com", a message
+# would be posted on review 81.
+
+
+import argparse
+import logging
+import paramiko
+
+import jeepyb.gerritdb
+
+BASE_DIR = '/home/gerrit2/review_site'
+
+logger = logging.getLogger('welcome_reviews')
+logger.setLevel(logging.INFO)
+
+
+def is_newbie(uploader):
+    """Determine if the owner of the patch is a first-timer."""
+
+    # Retrieve uploader email
+    try:
+        searchkey = uploader[uploader.rindex("(") + 1:-1]
+    except ValueError:
+        logger.info('Couldnt get email for %s', uploader)
+        return False
+
+    # this query looks for all distinct patchsets for the given
+    # user. If there's only 1, they're a first-timer.
+    query = """SELECT COUNT(DISTINCT p.change_id + p.patch_set_id)
+               FROM patch_sets p, account_external_ids a
+               WHERE a.email_address = %s
+               AND a.account_id = p.uploader_account_id;"""
+
+    cursor = jeepyb.gerritdb.connect().cursor()
+    cursor.execute(query, searchkey)
+    data = cursor.fetchone()
+    if data:
+        if data[0] == "1":
+            return True
+        else:
+            return False
+
+
+def post_message(change_id, gerrit_user, gerrit_ssh_key):
+    """Post a welcome message on the patch set specified by the change_id."""
+
+    welcome_text = """Thank you for your first contribution to OpenStack.
+
+    Your patch will now be tested automatically by OpenStack testing frameworks
+    and once the automatic tests pass, it will be reviewed by other friendly
+    developers. They will give you feedback and may require you to refine it.
+
+    People seldom get their patch approved on the first try, so don't be
+    concerned if requested to make corrections. Feel free to modify your patch
+    and resubmit a new change-set.
+
+    Patches usually take 3 to 7 days to be reviewed so be patient and be
+    available on IRC to ask and answer questions about your work. The more you
+    participate in the community the more rewarding it is for you. You may also
+    notice that the more you get to know people and get to be known, the faster
+    your patches will be reviewed and eventually approved. Get to know others
+    and become known by doing code reviews: anybody can do it, and it's a
+    great way to learn the code base.
+
+    Thanks again for supporting OpenStack, we look forward to working with you.
+
+    IRC: https://wiki.openstack.org/wiki/IRC
+    Workflow: https://wiki.openstack.org/wiki/Gerrit_Workflow
+    """
+    # get the patch_id
+    query = """SELECT change_id FROM patch_sets WHERE change_id = %s;"""
+
+    cursor = jeepyb.gerritdb.connect().cursor()
+    cursor.execute(query, change_id)
+    data = cursor.fetchone()
+    if data[0]:
+        # post the above message, using ssh.
+        command = ('gerrit review '
+                   '--message="{message}" {patch_id}').format(
+                       message=welcome_text,
+                       patch_id=data)
+        logger.info('Welcoming: %s', data[0])
+        ssh = paramiko.SSHClient()
+        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+        ssh.connect('localhost', username=gerrit_user,
+                    key_filename=gerrit_ssh_key, port=29418)
+        stdin, stdout, stderr = ssh.exec_command(command)
+
+    else:
+        logger.info('Unable to find patch_id for change_id=%s', change_id)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('hook')
+    # common
+    parser.add_argument('--change', default=None)
+    parser.add_argument('--change-url', default=None)
+    parser.add_argument('--project', default=None)
+    parser.add_argument('--branch', default=None)
+    parser.add_argument('--commit', default=None)
+    # change-merged
+    parser.add_argument('--submitter', default=None)
+    # patchset-created
+    parser.add_argument('--uploader', default=None)
+    parser.add_argument('--patchset', default=None)
+    # for Welcome Message
+    parser.add_argument('user', help='The gerrit admin user')
+    parser.add_argument('ssh_key', help='The gerrit admin SSH key file')
+
+    args = parser.parse_args()
+
+    # they're a first-timer, post the message on 1st patchset
+    if is_newbie(args.uploader) and args.patchset == 1:
+        post_message(args.change, args.user, args.ssh_key)
+
+if __name__ == "__main__":
+    main()