findmissing: abandon/restore CL + update db

Added gerrit endpoints to abandon and restore CL's in Gerrit.
Implemented functions to interface with cloudsql and update the table
to track abandoned/restored CL's.

Moved get_current_time to common.py.

Implemented top level main functions which can be called to
abandon/restore a fix. In a later CL I will be setting up the webserver
to integrate with these functions and update the gerrit CL and
reflect that update in the database.

Formatted touched files to have correct line spacing.

BUG=None
TEST=None

Change-Id: I8c78470c4be8ccfd7ae6921f2d988047da3317da
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/2128681
Tested-by: Hirthanan Subenderan <hirthanan@google.com>
Commit-Queue: Guenter Roeck <groeck@chromium.org>
Reviewed-by: Curtis Malainey <cujomalainey@chromium.org>
diff --git a/contrib/findmissing/cloudsql_interface.py b/contrib/findmissing/cloudsql_interface.py
new file mode 100755
index 0000000..c6e6df9
--- /dev/null
+++ b/contrib/findmissing/cloudsql_interface.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-"
+#
+# Copyright 2020 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.
+
+"""Find missing stable and backported mainline fix patches in chromeos."""
+
+from __future__ import print_function
+
+import MySQLdb
+
+import common
+
+
+def get_fix_status_and_changeid(db, fixes_table, kernel_sha, fixedby_upstream_sha):
+    """Get initial_status, status, and fix_change_id row from fixes table."""
+    c = db.cursor(MySQLdb.cursors.DictCursor)
+
+    q = """SELECT initial_status, status, fix_change_id
+            FROM {fixes_table}
+            WHERE kernel_sha = %s
+            AND fixedby_upstream_sha = %s""".format(fixes_table=fixes_table)
+
+    c.execute(q, [kernel_sha, fixedby_upstream_sha])
+    row = c.fetchone()
+    return row
+
+
+def update_change_abandoned(db, fixes_table, kernel_sha, fixedby_upstream_sha):
+    """Updates fixes_table unique fix row to indicate fix cl has been abandoned.
+
+    Function will only abandon rows in the table which have status OPEN or CONFLICT.
+    """
+    c = db.cursor()
+    q = """UPDATE {fixes_table}
+            SET status = 'ABANDONED', close_time = %s
+            WHERE kernel_sha = %s
+            AND fixedby_upstream_sha = %s
+            AND (status = 'OPEN' OR status = 'CONFLICT')""".format(fixes_table=fixes_table)
+    close_time = common.get_current_time()
+    c.execute(q, [close_time, kernel_sha, fixedby_upstream_sha])
+    db.commit()
+
+
+def update_change_restored(db, fixes_table, kernel_sha, fixedby_upstream_sha):
+    """Updates fixes_table unique fix row to indicate fix cl has been reopened."""
+    row = get_fix_status_and_changeid(db, fixes_table, kernel_sha, fixedby_upstream_sha)
+    initial_status = row['initial_status']
+
+    c = db.cursor()
+    q = """UPDATE {fixes_table}
+            SET status = %s, close_time = %s
+            WHERE kernel_sha = %s
+            AND fixedby_upstream_sha = %s
+            AND status = 'ABANDONED'""".format(fixes_table=fixes_table)
+    close_time = None
+    c.execute(q, [initial_status, close_time, kernel_sha, fixedby_upstream_sha])
+    db.commit()