~inspirated/arsenal/bug-patcher

« back to all changes in this revision

Viewing changes to arsenal/lp_bz_app.py

  • Committer: Kamran Riaz Khan
  • Date: 2010-08-13 02:10:45 UTC
  • Revision ID: krkhan@inspirated.com-20100813021045-6glg05m8er3ixwbb
Cleaned up the library to provide LaunchpadApplication and
LaunchpadBugzillaApplication

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
 
3
 
import cgi
4
 
import gzip
5
 
import os
6
 
import pdb
7
 
import re
8
 
import shutil
9
 
import sys
10
 
import tarfile
11
 
import tempfile
12
 
 
13
 
from ConfigParser import ConfigParser
14
 
from StringIO import StringIO
15
 
from fnmatch import fnmatch
16
 
from getpass import getpass
17
 
from locale import getpreferredencoding
18
 
from optparse import OptionParser
19
 
from urlparse import (urlparse, urljoin)
20
 
from zipfile import ZipFile
21
 
 
22
 
import glib
23
 
import pycurl
24
 
import gnomekeyring as gkeyring
25
 
 
26
 
from arsenal.bugzilla_adapter import *
27
 
 
28
 
from lazr.restfulclient.errors import HTTPError
29
 
 
30
 
from lpltk import LaunchpadService
31
 
from lpltk.LaunchpadService import LaunchpadServiceError
32
 
from lpltk.attachments import *
33
 
from lpltk.bug import Bug
34
 
 
35
 
class Info(object):
36
 
    attrs = ()
37
 
    def __init__(self, **kwargs):
38
 
        for attr in self.attrs:
39
 
            setattr(self, attr, kwargs.get(attr, None))
40
 
 
41
 
    def __repr__(self):
42
 
        return "<%s>" % u" ".join(["%s: %s" % (attr, getattr(self, attr))
43
 
                                   for attr in self.attrs])
44
 
 
45
 
    def __str__(self):
46
 
        return u"\n".join(["%s: %s" % (attr, getattr(self, attr))
47
 
                           for attr in self.attrs])
48
 
 
49
 
class LaunchpadInfo(Info):
50
 
    attrs = ('url',
51
 
             'parsedurl',
52
 
             'api',
53
 
             'bugid',
54
 
             'service')
55
 
 
56
 
class BugzillaInfo(Info):
57
 
    attrs = ('url',
58
 
             'parsedurl',
59
 
             'username',
60
 
             'password',
61
 
             'bugid',
62
 
             'attachurl')
63
 
 
64
 
class FlagsAndModifiers(type):
65
 
    def __init__(cls, name, bases, dct):
66
 
        super(FlagsAndModifiers, cls).__init__(name, bases, dict)
67
 
 
68
 
        for index, flagname in enumerate(cls.flagnames):
69
 
            setattr(cls, flagname, 1 << index)
70
 
 
71
 
        for modifier in cls.modifiers:
72
 
            setattr(cls, modifier, None)
73
 
 
74
 
        def parse_flags(self, flags):
75
 
            for flagname in self.flagnames:
76
 
                flag = getattr(self, flagname)
77
 
                if not flag & flags:
78
 
                    setattr(self, flagname, 0)
79
 
 
80
 
        def parse_modifiers(self, modifiers):
81
 
            for modifier, value in modifiers.iteritems():
82
 
                setattr(self, modifier, value)
83
 
 
84
 
        setattr(cls, 'parse_flags', parse_flags)
85
 
        setattr(cls, 'parse_modifiers', parse_modifiers)
86
 
 
87
 
class LaunchpadBugzillaApp(object):
88
 
    __metaclass__ = FlagsAndModifiers
89
 
 
90
 
    flagnames = ()
91
 
 
92
 
    modifiers = ()
93
 
 
94
 
    def __init__(self, appname, lpurl, bzurl, flags, modifiers):
95
 
        self.appname = appname
96
 
        self.tmpdir = None
97
 
 
98
 
        self.parse_flags(flags)
99
 
        self.parse_modifiers(modifiers)
100
 
 
101
 
        self.launchpad = LaunchpadInfo(url=lpurl)
102
 
 
103
 
        if bzurl:
104
 
            self.bugzilla = BugzillaInfo(url=bzurl,
105
 
                                         user=self.bzuser,
106
 
                                         password=self.bzpassword)
107
 
        else:
108
 
            self.bugzilla = None
109
 
 
110
 
        self.parse_bugids()
111
 
        self.parse_urls()
112
 
 
113
 
        self.print_debug(self.launchpad, self.bugzilla)
114
 
 
115
 
    def print_debug(self, *args):
116
 
        if self.DEBUG:
117
 
            for arg in args:
118
 
                print arg
119
 
                print
120
 
 
121
 
    def parse_bugids(self):
122
 
        pattern = re.compile(r'^.*[^\d](\d+)$')
123
 
        result = pattern.search(self.launchpad.url)
124
 
        if result:
125
 
            self.launchpad.bugid = result.groups()[0]
126
 
 
127
 
        if self.bugzilla:
128
 
            result = pattern.search(self.bugzilla.url)
129
 
            if result:
130
 
                self.bugzilla.bugid = result.groups()[0]
131
 
 
132
 
    def parse_urls(self):
133
 
        self.launchpad.parsedurl = urlparse(self.launchpad.url)
134
 
        pattern = re.compile(r'^.*?\.(.*)\.(.*)$')
135
 
        matches = pattern.search(self.launchpad.parsedurl.netloc).groups()
136
 
        self.launchpad.api = (self.launchpad.parsedurl.scheme + '://api.' +
137
 
                              matches[0] + '.' + matches[1])
138
 
        self.launchpad.api = urljoin(self.launchpad.api, 'beta/')
139
 
 
140
 
        if self.bugzilla:
141
 
            self.bugzilla.parsedurl = urlparse(self.bugzilla.url)
142
 
 
143
 
            pattern = re.compile(r'^(.*)show_bug\.cgi\?id=(\d+)$')
144
 
            self.bugzilla.attachurl = pattern.sub(
145
 
                r'\1attachment.cgi?bugid=\2&action=enter', self.bugzilla.url)
146
 
 
147
 
    def sub_url_query(self, url):
148
 
        pattern = re.compile(r'^(.*\/)([^\/]*?)$')
149
 
        return pattern.sub(r'\1' + url, self.bugzilla.url)
150
 
 
151
 
    def get_user_from_config(self):
152
 
        path = os.path.expanduser('~/.config/arsenal')
153
 
 
154
 
        if not os.path.isdir(path):
155
 
            os.makedirs(path)
156
 
        fd = open(os.path.join(path, 'bugzilla.ini'), 'a+')
157
 
 
158
 
        config = ConfigParser()
159
 
        config.readfp(fd)
160
 
 
161
 
        section = self.bugzilla.parsedurl.netloc
162
 
        if not config.has_section(section):
163
 
            config.add_section(section)
164
 
 
165
 
        if not config.has_option(section, 'user'):
166
 
            config.set(section, 'user',
167
 
                       raw_input('\tEnter Bugzilla username: '))
168
 
            config.write(fd)
169
 
 
170
 
        fd.close()
171
 
 
172
 
        return config.get(section, 'user')
173
 
 
174
 
    def get_password_from_keyring(self):
175
 
        keyring = 'arsenal'
176
 
        glib.set_application_name(keyring)
177
 
 
178
 
        if not keyring in gkeyring.list_keyring_names_sync():
179
 
            print "\tCreating new keyring for Arsenal"
180
 
            gkeyring.create_sync(keyring, getpass("\tKeyring Password: "))
181
 
 
182
 
        info = gkeyring.get_info_sync(keyring)
183
 
        if info.get_is_locked():
184
 
            print "\tUnlocking keyring for Arsenal"
185
 
            gkeyring.unlock_sync(keyring, getpass("\tKeyring Password: "))
186
 
 
187
 
        secret = None
188
 
        for id in gkeyring.list_item_ids_sync(keyring):
189
 
            item = gkeyring.item_get_info_sync(keyring, id)
190
 
            if self.bugzilla.username == item.get_display_name():
191
 
                attrs = gkeyring.item_get_attributes_sync(keyring, id)
192
 
                if attrs['netloc'] == self.bugzilla.parsedurl.netloc:
193
 
                    secret = item.get_secret()
194
 
 
195
 
        if not secret:
196
 
            attrs = {'netloc' : self.bugzilla.parsedurl.netloc}
197
 
            id = gkeyring.item_create_sync(keyring,
198
 
                                  gkeyring.ITEM_GENERIC_SECRET,
199
 
                                  self.bugzilla.username,
200
 
                                  attrs,
201
 
                                  getpass("\tEnter Bugzilla password: "),
202
 
                                  True)
203
 
            item = gkeyring.item_get_info_sync(keyring, id)
204
 
            secret = item.get_secret()
205
 
 
206
 
        return secret
207
 
 
208
 
    def login_launchpad(self):
209
 
        print "Logging into Launchpad"
210
 
 
211
 
        self.launchpad.service = LaunchpadService(config={
212
 
                'launchpad_client_name': self.appname,
213
 
                'launchpad_services_root': self.launchpad.api,
214
 
                })
215
 
        print ("\t[Success: Logged in as %s]" %
216
 
               self.launchpad.service.launchpad.me.display_name)
217
 
 
218
 
    def login_bugzilla(self, bzadapter):
219
 
        if not self.bugzilla.username:
220
 
            self.bugzilla.username = self.get_user_from_config()
221
 
 
222
 
        if not self.bugzilla.password:
223
 
            self.bugzilla.password = self.get_password_from_keyring()
224
 
 
225
 
        if self.bugzilla.username and self.bugzilla.password:
226
 
            return bzadapter.login(self.bugzilla.attachurl,
227
 
                                        self.bugzilla.username,
228
 
                                        self.bugzilla.password)
229
 
        else:
230
 
            raise BugzillaLoginError("Invalid username/password")
231
 
 
232
 
    def launch(self):
233
 
        self.tmpdir = tempfile.mkdtemp()
234
 
 
235
 
        try:
236
 
            self.login_launchpad()
237
 
        except LaunchpadServiceError as e:
238
 
            print "\t[Error: %s]" % e.msg
239
 
            return
240
 
 
241
 
        if self.bugzilla:
242
 
            self.bzadapter = BugzillaAdapter(self.tmpdir)
243
 
            if self.bzadapter.is_login_required(self.bugzilla.attachurl):
244
 
                try:
245
 
                    print "Logging into Bugzilla"
246
 
                    username = self.login_bugzilla(self.bzadapter)
247
 
                    if username:
248
 
                        print "\t[Success: Logged in as %s]" % username
249
 
                except BugzillaLoginError as e:
250
 
                    print "\t[Error: %s]" % e
251
 
                    return
252
 
 
253
 
        self.run()
254
 
 
255
 
        shutil.rmtree(self.tmpdir)
256