~inspirated/arsenal/bug-patcher

« back to all changes in this revision

Viewing changes to arsenal/application.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 LaunchpadApplication(object):
 
88
    __metaclass__ = FlagsAndModifiers
 
89
 
 
90
    flagnames = ()
 
91
 
 
92
    modifiers = ()
 
93
 
 
94
    def __init__(self, appname, lpurl, flags, modifiers):
 
95
        self.launchpad = LaunchpadInfo(url=lpurl)
 
96
 
 
97
        self.appname = appname
 
98
        self.tmpdir = None
 
99
 
 
100
        self.parse_flags(flags)
 
101
        self.parse_modifiers(modifiers)
 
102
 
 
103
        self.parse_bugids()
 
104
        self.parse_urls()
 
105
 
 
106
        self.print_debug(self.launchpad)
 
107
 
 
108
    def print_debug(self, *args):
 
109
        if self.DEBUG:
 
110
            for arg in args:
 
111
                print arg
 
112
                print
 
113
 
 
114
    def parse_bugids(self):
 
115
        pattern = re.compile(r'^.*[^\d](\d+)$')
 
116
        result = pattern.search(self.launchpad.url)
 
117
        if result:
 
118
            self.launchpad.bugid = result.groups()[0]
 
119
 
 
120
    def parse_urls(self):
 
121
        self.launchpad.parsedurl = urlparse(self.launchpad.url)
 
122
        pattern = re.compile(r'^.*?\.(.*)\.(.*)$')
 
123
        matches = pattern.search(self.launchpad.parsedurl.netloc).groups()
 
124
        self.launchpad.api = (self.launchpad.parsedurl.scheme + '://api.' +
 
125
                              matches[0] + '.' + matches[1])
 
126
        self.launchpad.api = urljoin(self.launchpad.api, 'beta/')
 
127
 
 
128
    def sub_url_query(self, url):
 
129
        pattern = re.compile(r'^(.*\/)([^\/]*?)$')
 
130
        return pattern.sub(r'\1' + url, self.bugzilla.url)
 
131
 
 
132
    def login_launchpad(self):
 
133
        print "Logging into Launchpad"
 
134
 
 
135
        self.launchpad.service = LaunchpadService(config={
 
136
                'launchpad_client_name': self.appname,
 
137
                'launchpad_services_root': self.launchpad.api,
 
138
                })
 
139
        print ("\t[Success: Logged in as %s]" %
 
140
               self.launchpad.service.launchpad.me.display_name)
 
141
 
 
142
    def prepare_to_launch(self):
 
143
        self.tmpdir = tempfile.mkdtemp()
 
144
 
 
145
        try:
 
146
            self.login_launchpad()
 
147
        except LaunchpadServiceError as e:
 
148
            print "\t[Error: %s]" % e.msg
 
149
            return
 
150
 
 
151
    def finish_launched(self):
 
152
        shutil.rmtree(self.tmpdir)
 
153
 
 
154
    def launch(self):
 
155
        self.prepare_to_launch()
 
156
        self.run()
 
157
        self.finish_launched()
 
158
 
 
159
class LaunchpadBugzillaApplication(LaunchpadApplication):
 
160
    def __init__(self, appname, lpurl, bzurl, flags, modifiers):
 
161
        self.bugzilla = BugzillaInfo(url=bzurl,
 
162
                                     user=self.bzuser,
 
163
                                     password=self.bzpassword)
 
164
 
 
165
        LaunchpadApplication.__init__(self,
 
166
                                      appname,
 
167
                                      lpurl,
 
168
                                      flags,
 
169
                                      modifiers)
 
170
 
 
171
        self.print_debug(self.bugzilla)
 
172
 
 
173
    def parse_bugids(self):
 
174
        LaunchpadApplication.parse_bugids(self)
 
175
 
 
176
        pattern = re.compile(r'^.*[^\d](\d+)$')
 
177
        result = pattern.search(self.bugzilla.url)
 
178
        if result:
 
179
            self.bugzilla.bugid = result.groups()[0]
 
180
 
 
181
    def parse_urls(self):
 
182
        LaunchpadApplication.parse_urls(self)
 
183
 
 
184
        self.bugzilla.parsedurl = urlparse(self.bugzilla.url)
 
185
 
 
186
        pattern = re.compile(r'^(.*)show_bug\.cgi\?id=(\d+)$')
 
187
        self.bugzilla.attachurl = pattern.sub(
 
188
            r'\1attachment.cgi?bugid=\2&action=enter', self.bugzilla.url)
 
189
 
 
190
    def get_user_from_config(self):
 
191
        path = os.path.expanduser('~/.config/arsenal')
 
192
 
 
193
        if not os.path.isdir(path):
 
194
            os.makedirs(path)
 
195
        fd = open(os.path.join(path, 'bugzilla.ini'), 'a+')
 
196
 
 
197
        config = ConfigParser()
 
198
        config.readfp(fd)
 
199
 
 
200
        section = self.bugzilla.parsedurl.netloc
 
201
        if not config.has_section(section):
 
202
            config.add_section(section)
 
203
 
 
204
        if not config.has_option(section, 'user'):
 
205
            config.set(section, 'user',
 
206
                       raw_input('\tEnter Bugzilla username: '))
 
207
            config.write(fd)
 
208
 
 
209
        fd.close()
 
210
 
 
211
        return config.get(section, 'user')
 
212
 
 
213
    def get_password_from_keyring(self):
 
214
        keyring = 'arsenal'
 
215
        glib.set_application_name(keyring)
 
216
 
 
217
        if not keyring in gkeyring.list_keyring_names_sync():
 
218
            print "\tCreating new keyring for Arsenal"
 
219
            gkeyring.create_sync(keyring, getpass("\tKeyring Password: "))
 
220
 
 
221
        info = gkeyring.get_info_sync(keyring)
 
222
        if info.get_is_locked():
 
223
            print "\tUnlocking keyring for Arsenal"
 
224
            gkeyring.unlock_sync(keyring, getpass("\tKeyring Password: "))
 
225
 
 
226
        secret = None
 
227
        for id in gkeyring.list_item_ids_sync(keyring):
 
228
            item = gkeyring.item_get_info_sync(keyring, id)
 
229
            if self.bugzilla.username == item.get_display_name():
 
230
                attrs = gkeyring.item_get_attributes_sync(keyring, id)
 
231
                if attrs['netloc'] == self.bugzilla.parsedurl.netloc:
 
232
                    secret = item.get_secret()
 
233
 
 
234
        if not secret:
 
235
            attrs = {'netloc' : self.bugzilla.parsedurl.netloc}
 
236
            id = gkeyring.item_create_sync(keyring,
 
237
                                  gkeyring.ITEM_GENERIC_SECRET,
 
238
                                  self.bugzilla.username,
 
239
                                  attrs,
 
240
                                  getpass("\tEnter Bugzilla password: "),
 
241
                                  True)
 
242
            item = gkeyring.item_get_info_sync(keyring, id)
 
243
            secret = item.get_secret()
 
244
 
 
245
        return secret
 
246
 
 
247
    def login_bugzilla(self, bzadapter):
 
248
        if not self.bugzilla.username:
 
249
            self.bugzilla.username = self.get_user_from_config()
 
250
 
 
251
        if not self.bugzilla.password:
 
252
            self.bugzilla.password = self.get_password_from_keyring()
 
253
 
 
254
        if self.bugzilla.username and self.bugzilla.password:
 
255
            return bzadapter.login(self.bugzilla.attachurl,
 
256
                                        self.bugzilla.username,
 
257
                                        self.bugzilla.password)
 
258
        else:
 
259
            raise BugzillaLoginError("Invalid username/password")
 
260
 
 
261
    def prepare_to_launch(self):
 
262
        LaunchpadApplication.prepare_to_launch(self)
 
263
 
 
264
        self.bzadapter = BugzillaAdapter(self.tmpdir)
 
265
        if self.bzadapter.is_login_required(self.bugzilla.attachurl):
 
266
            try:
 
267
                print "Logging into Bugzilla"
 
268
                username = self.login_bugzilla(self.bzadapter)
 
269
                if username:
 
270
                    print "\t[Success: Logged in as %s]" % username
 
271
            except BugzillaLoginError as e:
 
272
                print "\t[Error: %s]" % e
 
273
                return
 
274