#!/usr/bin/env python # Copyright 2016 Red Hat, Inc. # # 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. import MySQLdb import ConfigParser from prettytable import PrettyTable import argparse parser = argparse.ArgumentParser(description= 'Delete spam comments from Gerrit.') parser.add_argument('account_id', help='The numeric account id of the spammer') parser.add_argument('--delete', action='store_true', help='actually perform the deletion (the default' 'behavior is a dry run)') args = parser.parse_args() config = ConfigParser.RawConfigParser() config.read('/root/.my.cnf') host = config.get('client', 'host') user = config.get('client', 'user') pw = config.get('client', 'password') db = MySQLdb.connect(host=host, user=user, passwd=pw, db='reviewdb') cur = db.cursor() # First, display / delete patch comments. t = PrettyTable(['Change', 'Patchset', 'File', 'UUID', 'Date', 'Message']) t.align = 'l' cur.execute('select change_id, patch_set_id, file_name, uuid, written_on, ' 'message from patch_comments where author_id=%s', args.account_id) # Any of the patch comments we delete might also be a parent of some # other comment. Keep track of the UUID of the comments we delete and # check later to see if they are parents. potential_parents = set() for row in cur.fetchall(): t.add_row(row) potential_parents.add(row[3]) if cur.rowcount: print "Patch Comments -- To Be Deleted" print t if args.delete: cur.execute('delete from patch_comments where author_id=%s', args.account_id) print "Deleted %s rows." % cur.rowcount # If we are deleting some patch comments above, see if any of them are # parents of other comments. If so, unparent the child comments so # that they don't have a 'parent_uuid' field pointing to a nonexistent # entry. if potential_parents: # This query formatting is so that we can let the client library # substitute the value for each member of the UUID set we created # above. placeholders = ','.join(['%s'] * len(potential_parents)) query = ('select change_id, patch_set_id, file_name, uuid, written_on, ' 'message from patch_comments where parent_uuid in (%s)' % placeholders) cur.execute(query, list(potential_parents)) t = PrettyTable(['Change', 'Patchset', 'File', 'UUID', 'Date', 'Message']) t.align = 'l' for row in cur.fetchall(): t.add_row(row) if cur.rowcount: print "Patch Comment Children -- To Be Unparented" print t if args.delete: query = ('update patch_comments set parent_uuid=NULL where ' 'parent_uuid in (%s)' % placeholders) cur.execute(query, list(potential_parents)) print "Updated %s rows." % cur.rowcount # Finally, display / delete any change messages. t = PrettyTable(['Change', 'UUID', 'Date', 'Message']) t.align = 'l' cur.execute('select change_id, uuid, written_on, message from change_messages ' 'where author_id=%s', args.account_id) for row in cur.fetchall(): t.add_row(row) if cur.rowcount: print "Change Messages -- To Be Deleted" print t if args.delete: cur.execute('delete from change_messages where author_id=%s', args.account_id) print "Deleted %s rows." % cur.rowcount db.commit()