Skip to content
This repository
Newer
Older
100755 335 lines (284 sloc) 11.008 kb
dfe4c217 »
2007-10-19 Updates
1 #!/usr/bin/python
2
3 ### This program is free software; you can redistribute it and/or modify
4 ### it under the terms of the GNU Library General Public License as published by
5 ### the Free Software Foundation; version 2 only
6 ###
7 ### This program is distributed in the hope that it will be useful,
8 ### but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 ### GNU Library General Public License for more details.
11 ###
12 ### You should have received a copy of the GNU Library General Public License
13 ### along with this program; if not, write to the Free Software
14 ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 ### Copyright 2004-2006 Dag Wieers <dag@wieers.com>
16
17 import os, sys, shutil, getopt, ConfigParser, urlparse, types
18 import signal, xmlrpclib, getpass, glob, fnmatch, urllib2
19 import gzip
20 import cElementTree as ElementTree
21
22 __version__ = "$Revision: 4786 $"
23 # $Source$
24
25 VERSION = '0.8.4svn'
26
27 class Options:
28 def __init__(self, args):
29 self.cleanup = False
30 self.credpath = None
31 self.downloadall = False
32 self.dryrun = False
33 self.filter = None
34 self.list = None
35 self.quiet = False
36 self.password = None
37 self.username = None
38 self.source = False
39 self.verbose = 1
40
41 try:
42 opts, args = getopt.getopt (args, 'hlnqp:s:u:v',
43 ('credpath=', 'delete', 'download-all', 'dryrun', 'filter=', 'help', 'list',
44 'password=', 'quiet', 'source', 'systemid=', 'username=', 'verbose', 'version' ))
45 except getopt.error, exc:
46 print 'youget: %s, try youget -h for a list of all the options' % str(exc)
47 sys.exit(1)
48
49 for opt, arg in opts:
50 if opt in ('--credpath', ):
51 self.credpath = arg
52 elif opt in ('--delete', ):
53 self.cleanup = True
54 elif opt in ('--download-all', ):
55 self.downloadall = True
56 elif opt in ('--filter', ):
57 self.filter = arg
58 self.downloadall = True
59 elif opt in ('-h', '--help'):
60 self.usage()
61 print
62 self.help()
63 sys.exit(0)
64 elif opt in ('-l', '--list'):
65 self.list = True
66 self.downloadall = True
67 elif opt in ('-n', '--dry-run'):
68 self.dryrun = True
69 elif opt in ['-p', '--password']:
70 self.password = arg
71 elif opt in ('-q', '--quiet'):
72 self.quiet = True
73 elif opt in ('--source'):
74 self.source = True
75 elif opt in ('-s', '--systemid'):
76 self.systemid = os.path.abspath(arg)
77 elif opt in ['-u', '--username']:
78 self.username = arg
79 elif opt in ('-v', '--verbose'):
80 self.verbose = self.verbose + 1
81 elif opt in ('--version', ):
82 self.version()
83 sys.exit(0)
84
85 if len(args) < 1:
86 self.usage()
87 print
88 self.help()
89 sys.exit(1)
90
91 self.uri = args[0]
92
93 if not self.username and not self.password:
94 if self.credpath:
95 try:
96 self.username = open(os.path.join(self.credpath, 'mcookie')).read().rstrip()
97 self.password = open(os.path.join(self.credpath, 'partnernet')).read().rstrip()
98 except:
99 die(1, 'Credentials directory %s does not contain mcookie and partnernet files.' % op.credpath)
100 elif os.path.isdir('/etc/ximian'):
101 try:
102 self.username = open('/etc/ximian/mcookie').read().rstrip()
103 self.password = open('/etc/ximian/partnernet').read().rstrip()
104 except:
105 die(1, 'Credentials directory /etc/ximian does not contain mcookie and partnernet files.')
106
107 if not self.username:
108 self.username = raw_input('YOU Username: ')
109
110 if self.username and not self.password:
111 self.password = getpass.getpass('YOU Password for user %s: ' % self.username)
112
113 if len(args) == 2:
114 self.destination = args[1]
115 else:
116 self.destination = os.getcwd()
117
118 if self.quiet:
119 self.verbose = 0
120
121 if self.verbose >= 3:
122 print 'Verbosity set to level %d' % (self.verbose - 1)
123
124 def version(self):
125 print 'youget %s' % VERSION
126 print 'Written by Dag Wieers <dag@wieers.com>'
127 print
128 print 'platform %s/%s' % (os.name, sys.platform)
129 print 'python %s' % sys.version
130 print
131 print 'build revision $Rev: 4786 $'
132
133 def usage(self):
134 print 'usage: youget [options] URL'
135
136 def help(self):
137 print '''Download packages from Yast Online Update (YOU)
138
139 youget options:
140 -d, --credpath=dir credentials directory
141 --delete delete files that are not on the sender side
142 --download-all download all package versions available
143 --filter filter packages based on regexp
144 -l, --list list the available packages
145 -n, --dry-run show what would have been done
146 -q, --quiet minimal output
147 --source download source packages
148 -v, --verbose increase verbosity
149 -vv, -vvv, -vvvv.. increase verbosity more
150 '''
151
152 class Set:
153 def __init__(self):
154 self.list = []
155
156 def add(self, input):
157 if input not in self.list:
158 self.list.append(input)
159
160 def delete(self, input):
161 if input in self.list:
162 self.list.removed(input)
163
164 def difference(self, other):
165 newlist = Set()
166 for element in self.list:
167 if element not in other.list:
168 newlist.add(element)
169 return newlist
170
171 def sort(self):
172 return self.list.sort()
173
174 def __str__(self):
175 return '\n\t' + '\n\t'.join([element[0] for element in self.list])
176
177 def __len__(self):
178 return len(self.list)
179
180 class MirrorException(Exception):
181 def __init__(self, value):
182 self.value = value
183 def __str__(self):
184 return repr(self.value)
185
186 def error(level, str):
187 "Output error message"
188 if level <= op.verbose:
189 sys.stderr.write('youget: %s\n' % str)
190
191 def info(level, str):
192 "Output info message"
193 if level <= op.verbose:
194 sys.stdout.write('%s\n' % str)
195
196 def die(ret, str):
197 "Print error and exit with errorcode"
198 error(0, str)
199 sys.exit(ret)
200
97e75df5 »
2007-10-22 Updates
201 def remove(file):
202 "Remove files or directories"
203 if isinstance(file, types.StringType):
204 if op.dryrun:
205 return
206 if os.path.islink(file):
207 os.unlink(file)
208 elif os.path.isdir(file):
209 try:
210 os.rmdir(file)
211 except:
212 os.path.walk(file, removedir, ())
213 os.rmdir(file)
214 elif os.path.isfile(file) or os.path.islink(file):
215 os.unlink(file)
216 else:
217 for f in file:
218 remove(f)
219
dfe4c217 »
2007-10-19 Updates
220 def mirroryou(url, path):
221 'Mirror a channel from YOU'
222
223 ### Download packagelist for this channel
224 info(2, 'Downloading packagelist from %s' % url)
225 host = urlparse.urlparse(url)[1]
226 auth_handler = urllib2.HTTPDigestAuthHandler()
227 auth_handler.add_password('Express', host, op.username, op.password)
228 opener = urllib2.build_opener(auth_handler)
229 opener.addheaders = [('User-agent', 'Mozilla/5.0')]
230 fdin = opener.open(os.path.join(url, 'packageinfo.xml.gz'))
231 fdout = open(os.path.join(path,'packageinfo.xml.gz'), 'w')
232 fdout.write(fdin.read())
233 fdin.close()
234 fdout.close()
235
236 ### Parse packagelist
237 fd = gzip.open(os.path.join(path,'packageinfo.xml.gz'), 'r')
238 tree = ElementTree.ElementTree(file=fd)
239 root = tree.getroot()
240 package_list = Set()
241 for elem in root.getiterator('package'):
242 pkgname = elem.findtext('history/update/filename')
243 pkgsize = elem.findtext('history/update/filesize')
244 package_list.add( (pkgname, pkgsize) )
245 fd.close()
246 package_list.sort()
247
248 ### Download packages from the packagelist
249 for filename, filesize in package_list.list:
250
251 ### Filter packagelist
252 if op.filter and not fnmatch.fnmatch(filename, op.filter):
253 info(4, 'Packages %s excluded by filter' % filename)
254 continue
255
256 ### List only files if requested
257 if op.list:
258 info(0, filename)
259 continue
260
261 ### If file (or symlink target) exists
262 if os.path.isfile(os.path.join(path, filename)):
263 stat = os.stat(os.path.join(path, filename))
264 if stat.st_size == int(filesize):
265 info(3, 'File %s is already in %s' % (filename, path))
266 continue
267 else:
268 info(2, 'File %s has wrong size (found: %s, expected: %s), refetching.' % (filename, stat.st_size, filesize))
269 remove(os.path.join(path, filename))
270
271 ### If symlink target does not exist, remove symlink
272 elif os.path.islink(os.path.join(path, filename)):
273 remove(os.path.join(path, filename))
274
275 if op.dryrun:
276 info(1, 'Not downloading package %s' % filename)
277 continue
278
279 info(2, 'Download %s (%s)' % (filename, filesize))
280 fdin = opener.open(os.path.join(url, filename))
281 fdout = open(os.path.join(path, filename), 'w')
282 fdout.write(fdin.read())
283 fdin.close()
284 fdout.close()
285
286 ### Remove packages on the receiver side that are not on the sender side
287 if op.cleanup:
288
289 ### Collect receiver side
290 receiver = Set()
291 for file in glob.glob(os.path.join(path, '*.rpm')):
292 if os.path.exists(file):
293 filename = os.path.basename(file)
294 filesize = os.stat(file).st_size
295 receiver.add( (filename, filesize) )
296 receiver.sort()
297
298 ### Collect sender side
299 sender = package_list
300
301 ### Remove difference between receiver and sender
302 cleanse = receiver.difference(sender)
303 for filename, filesize in cleanse.list:
304 info(3, 'Cleaning up obsolete file %s (%d kiB)' % (filename, filesize))
305 remove(os.path.join(path, filename))
306
307 def main():
308 try:
309 mirroryou(op.uri, op.destination)
310 except Exception, e:
311 die(1, e)
312
313 ### Unbuffered sys.stdout
314 sys.stdout = os.fdopen(1, 'w', 0)
315 sys.stderr = os.fdopen(2, 'w', 0)
316
317 ### Workaround for python <= 2.2.1
318 try:
319 True, False
320 except NameError:
321 True = 1
322 False = 0
323
324 ### Main entrance
325 if __name__ == '__main__':
326 exitcode = 0
327
328 op = Options(sys.argv[1:])
329 try:
330 main()
331 except KeyboardInterrupt, e:
332 die(6, 'Exiting on user request')
333 sys.exit(exitcode)
334
335 # vim:ts=4:sw=4:et
Something went wrong with that request. Please try again.