fedora-accounts bz-make-components-pkgdb.py,NONE,1.1

Toshio Ernie Kuratomi (toshio) fedora-extras-commits at redhat.com
Sat Jun 30 01:23:15 UTC 2007


Author: toshio

Update of /cvs/fedora/fedora-accounts
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv4554

Added Files:
	bz-make-components-pkgdb.py 
Log Message:
* bz-make-components-pkgdb.py: Initial version of a script to sync the package
  database information out to bugzilla.



--- NEW FILE bz-make-components-pkgdb.py ---
#!/usr/bin/python -t

import sys, os, errno
import website, crypt
import getopt, re
import xmlrpclib
import simplejson
import urllib2

from fedora.accounts.fas import AccountSystem

# Set this to the production bugzilla account when we're ready to go live
BZSERVER = 'https://bugdev.devel.redhat.com/bugzilla-cvs/xmlrpc.cgi'
#BZSERVER = 'https://bugzilla.redhat.com/bugzilla/xmlrpc.cgi'
BZUSER='toshio at tiki-lounge.com'
BZPASS='tanuki7'

PKGDBSERVER = 'http://test3.fedora.phx.redhat.com/pkgdb-dev/acls/bugzilla?tg_format=json'

# Set this to False when we're ready to run it against the bz database
DRY_RUN = True

class DataChangedError(Exception):
    '''Raised when data we are manipulating changes while we're modifying it.'''
    pass

class Bugzilla(object):
    GRANT_DIRECT = 0
    GRANT_DERIVED = 1
    GRANT_REGEXP = 2

    def __init__(self, bzServer, username, password):
        self.userCache = {}
        self.productCache = {}
        self.bzXmlRpcServer = bzServer
        self.username = username
        self.password = password

        self.server = xmlrpclib.Server(bzServer)

        # We still have to talk directly to the db for some things
        self.bzdbh = website.get_dbh('bugs', 'bugs')
        self.bzdbc = self.bzdbh.cursor()
        self.bzdbh.commit()

    def _get_bugzilla_email(self, username):
        try:
            user = self.userCache[username]
        except KeyError:
            user = {}
            (person, groups) = self.fas.get_user_info(username)
            user['email'] = person['bugzilla_email']
            self.userCache[username] = user
            return user['email']

        try:
            email = user['email']
        except KeyError:
            try:
                (person, groups) = self.fas.get_user_info(username)
            except fas.AuthError, e:
                raise ValueError,  'Username %s was not found in fas' % username

            email = person['bugzilla_email']
            user['email'] = email

        return email

    def _get_bugzilla_user_id(self, username):
        bzdbc = self.bzdbh.cursor()
        try:
            user = self.userCache[username]
        except KeyError:
            self._get_bugzilla_email(username)
            user = self.userCache[username]

        try:
            bzid = user['bzid']
        except KeyError:
            bzdbc.execute("SELECT userid FROM profiles WHERE login_name = %s",
                    (user['email'],))
            if bzdbc.rowcount:
                bzid = bzdbc.fetchone()[0]
            else:
                raise ValueError , 'Username %s with email %s does not have a bugzilla account.' % (username, user['email'])

            user['bzid'] = bzid

        return bzid

    def add_edit_component(self, package, collection, owner, description,
            qacontact=None, cclist=None):
        '''Add or update a component to have the values specified.
        '''
        # Lookup product
        try:
            product = self.productCache[collection]
        except KeyError:
            product = {}
            components = self.server.bugzilla.getProdCompDetails(collection,
                    self.username, self.password)

            # This ugly construction changes from the form:
            #   {'component': 'PackageName',
            #   'initialowner': 'OwnerEmail',
            #   'initialqacontact': 'QAContactEmail',
            #   'description': 'One sentence summary'}
            # to:
            #   product['PackageName'] = {'initialowner': 'OwnerEmail',
            #     'initialqacontact': 'QAContactEmail',
            #     'description': 'One sentenct summary'}
            for record in map(lambda x: (x['component'], [z for z in x.items() if lambda y: y[0] != 'component']), components):
                product[record[0]] = {}
                product[record[0]].update(record[1])

            self.productCache[collection] = product

        if package in product:
            # edit the package information
            data = {}

            # Grab bugzilla email for things changable via xmlrpc
            owner = self._get_bugzilla_email(owner)
            if qacontact:
                qacontact = self._get_bugzilla_email(qacontact)

            # Check for changes to the owner, qacontact, or description
            if product[package]['initialowner'] != owner:
                data['initialowner'] = owner
            if product[package]['description'] == description:
                data['description'] = description
            if product[package]['initialqacontact'] != qacontact and (
                    qacontact or product[package]['initialqacontact']):
                data['initialqacontact'] = qacontact

            if data:
                # Changes occurred.  Submit a request to change via xmlrpc
                data['product'] = collection
                data['component'] = package
                if DRY_RUN:
                    print 'Changing via editComponent(%s, %s, "xxxxx")' % (
                            data, self.username)
                else:
                    self.server.bugzilla.editComponent(data, self.username,
                            self.password)

            ### FIXME: Check for changes to cclist
            # We're going to wait on the xmlrpc interface returning cclist
            
            # Update the cclist
            
            # Get the component id
            self.bzdbc.execute("select c.id" \
                    " from components as c, products as p" \
                    " where p.name = %s and c.name = %s" \
                    " and p.id = c.product_id", (collection, package))
            if not self.bzdbc.rowcount:
                raise DataChangedError, 'Package %s disappeared from collection %s during processing' % (package, collection)
            pkgId = self.bzdbc.fetchone()[0]

            # Turn all cclist members into bugzilla ids
            bzCclist = []
            for watcher in cclist:
                bzCclist.append(self._get_bugzilla_user_id(watcher))
            # Update the cclist in the database
            if DRY_RUN:
                print "update components set initialcclist = '%s'" \
                        " where id = '%s'" % (':'.join(map(str,bzCclist)),
                                pkgId)

            else:
                self.bzdbc.execute("update components set initialcclist = %s" \
                        " where id = %s", (':'.join(map(str,bzCclist)), pkgId))
            self.bzdbh.commit()
        else:
            # Add component
            
            # Get the product id
            self.bzdbc.execute("select id from products where name = %s", collection)
            if not self.bzdbc.rowcount:
                raise DataChangedError, 'Collection %s disappeared from the database during processing' % (collection,)
            collectionId = self.bzdbc.fetchone()[0]

            # Retrieve bugzilla ids for all the fas usernames
            ownerId = self._get_bugzilla_user_id(owner)
            qacontactId = self._get_bugzilla_user_id(qacontact)
            bzCclist = []
            for watcher in cclist:
                bzCclist.append(self._get_bugzilla_user_id(watcher))

            # Add the component
            if DRY_RUN:
                print "insert into components (name, product_id, description," \
                        " initialowner, initialqacontact, initialcclist)" \
                        " values (%s, %s, %s, %s, %s, %s)" % (package,
                                collectionId, description, ownerId,
                                qacontactId, bzCclist)
            else:
                self.bzdbc.execute("insert into components" \
                        " (name, product_id, description," \
                        " initialowner, initialqacontact, initialcclist)" \
                        " values (%s, %s, %s, %s, %s, %s)" % (package,
                                collectionId, description, ownerId,
                                qacontactId, bzCclist))
            self.bzdbh.commit()


if __name__ == '__main__':
    opts, args = getopt.getopt(sys.argv[1:], '', ('usage', 'help'))
    if len(args) > 0:
        print """
Usage: bz-make-components.py

Sync package information from the package database to bugzilla.
"""
        sys.exit(1)

    # Initialize the connection to bugzilla
    bugzilla = Bugzilla(BZSERVER, BZUSER, BZPASS)

    # Non-fatal errors to alert people about
    errors = []

    # Get bugzilla information from the package database
    ownerPage = urllib2.urlopen(PKGDBSERVER)
    bugzillaData = simplejson.load(ownerPage)
    ownerPage.close()
    acls = bugzillaData['bugzillaAcls']
    del bugzillaData

    for product in acls.keys():
        for pkg in acls[product]:
            pkgInfo = acls[product][pkg]
            ### FIXME:
            # Catch the exceptions and save information into email to send
            print 'here we are'
            print 'pkg:%s' % pkg
            print 'product:%s' % product
            print 'pkgInfo.owner:%s' % pkgInfo['owner']
            print 'pkgInfo.summary:%s' % pkgInfo['summary']
            print 'pkgInfo.qacontact:%s' % pkgInfo['qacontact']
            print 'pkgInfo.cclist:%s' % pkgInfo['cclist']
            sys.exit(1)
            try:
                bugzilla.add_edit_component(pkg, product, pkgInfo['owner'],
                        pkgInfo['summary'], pkgInfo['qacontact'],
                        pkgInfo['cclist'])
            except ValueError, e:
                # A username didn't have a bugzilla address
                errors.append(e)
            except DataChangedError, e:
                # A Package or Collection was returned via xmlrpc but wasn't
                # present when we tried to change it
                errors.append(e)
            except xmlrpclib.Error, e:
                # An error occurred in the xmlrpc call.  Shouldn't happen but
                # we better se what it is
                errors.append(e)

# Send notification of errors 

    if errors:
        website.send_email('accounts at fedoraproject.org',
                'toshio at fedoraproject.org',
                'Errors while syncing bugzilla with the PackageDB',
'''
In order to make bugzilla components for Fedora-related programs, we need to have an existing bugzilla account for 
the listed owner. You (%s) do not have a bugzilla account, but are listed as the owner for the following components:
%s

Please create a bugzilla account for %s immediately, because this amazingly stupid cron job will keep sending you an 
e-mail every hour until you do :)

- The management
''' % ('\n'.join(str(errors)),))

    sys.exit(0)




More information about the fedora-extras-commits mailing list