add diff tools using python
[platform/upstream/swup.git] / swup.py
1 #!/usr/bin/python
2
3 import ConfigParser
4 from optparse import OptionParser
5 import urllib2
6 from lxml import etree
7 #from BeautifulSoup import *
8 import hashlib
9 import os
10 import tempfile
11 import shutil
12 import sys
13 import zipfile
14 import rpm
15 import subprocess as sub
16 import distutils
17
18 update_repo="http://www.planux.com/updates"
19 update_cache="/var/cache/updatemanager"
20
21
22 class FakeSecHead(object):
23     def __init__(self, fp):
24         self.fp = fp
25         self.sechead = '[os-release]\n'
26     def readline(self):
27         if self.sechead:
28             try: return self.sechead
29             finally: self.sechead = None
30         else: return self.fp.readline()
31
32 def get_current_version():
33     config = ConfigParser.SafeConfigParser()
34     config.readfp(FakeSecHead(open('/etc/os-release')))
35     return dict(config.items('os-release'))
36
37  
38 def checksum(fileName, checksum_type="sha256", excludeLine="", includeLine=""):
39     """Compute sha256 hash of the specified file"""
40     m = hashlib.sha256()
41     if checksum_type == "md5":
42         m = hashlib.md5()
43
44     try:
45         fd = open(fileName,"rb")
46     except IOError:
47         print "Unable to open the file in readmode:", filename
48         return
49     content = fd.readlines()
50     fd.close()
51     for eachLine in content:
52         if excludeLine and eachLine.startswith(excludeLine):
53             continue
54         m.update(eachLine)
55     m.update(includeLine)
56     return m.hexdigest()
57
58
59 def probe_updates():
60     print "Checking for new updates..."
61     response = urllib2.urlopen("%s/data/updatemd.xml" % update_repo )
62     updatemd = response.read()
63     if not os.path.exists("%s/download/metadata" %update_cache):
64         os.mkdir("%s/download/metadata" %update_cache)
65
66     fp = open("%s/download/metadata/updatemd.xml" % update_cache , "w")
67     fp.write(updatemd)
68     fp.close()
69
70     updatemd_local = open("%s/download/metadata/updatemd.xml" % update_cache )
71     root = etree.XML(updatemd_local.read())
72     data = root.xpath("//data[@type='updates']")[0]
73     loc = data.xpath("location")[0]
74     href = loc.attrib['href']
75     chksum = data.xpath("checksum")[0]
76     chksum_type = chksum.attrib['type']
77     
78     if os.path.exists("%s/download/metadata/updates.xml" % update_cache):
79         cur_sum = checksum("%s/download/metadata/updates.xml" % update_cache, checksum_type=chksum_type) 
80         if cur_sum ==  chksum.text:
81             print "Using file from cache, no new updates on server."
82         else:
83             print "Fetching new updates..."
84             get_new_update_list(href)
85     else:
86         get_new_update_list(href)
87
88
89 def parse_updates():
90
91     updates = {}
92     fp = open("%s/download/metadata/updates.xml" % update_cache , "r")
93     updates_root = etree.XML(fp.read())
94     updates_el = updates_root.xpath("//update")
95     for update in updates_el:
96         up = {}
97         attr = update.attrib
98         up['id'] = attr['id']
99         up['checksum'] = update.xpath("checksum")[0].text
100         up['title'] = update.xpath("title")[0].text 
101         loc = update.xpath("location")[0]
102         up['location'] = "%s" % ( loc.attrib['href'])
103         
104         updates[up['id']] = up
105     return updates
106
107
108 def download_update(update_data):
109     u = update_data
110     location = u['location']
111     if not os.path.exists("%s/download" % (update_cache)):
112         os.mkdir("%s/download" % (update_cache))
113     if not os.path.exists("%s/download/%s/download_done" % (update_cache, u['id'] )):
114         print "Downloading %s/%s" % (update_repo, location)
115         update_file = urllib2.urlopen("%s/%s" % (update_repo, location) )
116         location = os.path.basename(location)
117         announced_csum = u['checksum']
118         update_raw = update_file.read()
119         fp = open("%s/download/%s" % (update_cache,location) , "w")
120         fp.write(update_raw)
121         fp.close()
122         downloaded_csum = checksum("%s/download/%s" % (update_cache,location), "sha256")
123         # Verify Checksum
124         if downloaded_csum != announced_csum:
125             print "Error: Checksum mismatch"
126             os.remove("%s/download/%s" % (update_cache,location))
127     else:
128         print "%s already downloaded" % location    
129
130 def download_all_updates(update_label=None):
131     updates = parse_updates()
132
133     if update_label is not None:
134         u = updates[update_label]
135         download_update(u)
136     else:
137         for k in updates.keys():
138             u = updates[k]
139             download_update(u)
140         
141
142 def get_new_update_list(location):
143     up = urllib2.urlopen("%s/%s" % (update_repo, location) )
144     import gzip
145     update_raw = up.read()
146     fp = open("%s/download/metadata/updates.xml.gz" % update_cache , "w")
147     fp.write(update_raw)
148     fp.close()
149     f = gzip.open("%s/download/metadata/updates.xml.gz" % update_cache, 'rb')
150     file_content = f.read()
151     f.close()
152     fp = open("%s/download/metadata/updates.xml" % update_cache , "w")
153     fp.write(file_content)
154     fp.close()
155
156
157 def create_zip():
158     #distutils.archive_util.make_zipfile(base_name, base_dir[, verbose=0, dry_run=0])
159     pass
160
161 def pack(target):
162     from zipfile_infolist import print_info
163     import zipfile
164     try:
165         import zlib
166         compression = zipfile.ZIP_DEFLATED
167     except:
168         compression = zipfile.ZIP_STORED
169
170     modes = { zipfile.ZIP_DEFLATED: 'deflated',
171               zipfile.ZIP_STORED:   'stored',
172               }
173
174     print 'creating update archive'
175     zf = zipfile.ZipFile('%s.zip' %target, mode='w')
176     try:
177         print 'adding README.txt with compression mode', modes[compression]
178         zf.write('README.txt', compress_type=compression)
179     finally:
180         print 'closing'
181         zf.close()
182
183     print
184     print_info('%s.zip' %target)
185
186 def unpack(location, update_id):
187     #os.mkdir("%s/download/%s" %(update_cache, update_id))
188     zfile = zipfile.ZipFile("%s/download/%s/%s" % (update_cache,update_id, location))
189     for name in zfile.namelist():            
190         (dirname, filename) = os.path.split(name)
191         print "Decompressing " + filename + " on " + dirname
192         if not os.path.exists("%s/download/%s/%s" % (update_cache, update_id, dirname)):
193             os.mkdir("%s/download/%s/%s" % (update_cache, update_id, dirname))            
194         if filename != "":
195             fd = open("%s/download/%s/%s" % (update_cache, update_id, name),"w")
196             fd.write(zfile.read(name))
197             fd.close()
198
199     os.rename("%s/download/%s/%s" % (update_cache,update_id, update_id), "%s/download/%s/content" % (update_cache,update_id))
200
201
202 def prepare_update(update_data, download):
203     u = update_data
204     location = u['location']
205     update_id = u['id']
206     # unzip
207     if os.path.exists("%s/download/%s" % (update_cache,update_id)) and not os.path.exists("%s/download/%s/content" % (update_cache,update_id)):
208         print "Unpacking %s" %location
209         unpack(location, update_id)
210     else:
211         print "Update %s already unpacked" % update_id
212
213     repodir = "%s/repos.d" %update_cache
214     repourl = "file://%s/download/%s/content" % (update_cache, update_id)
215     if not os.path.exists("%s/%s.repo" % (repourl, update_id)):
216         os.system("zypper --quiet --reposd-dir %s ar --no-gpgcheck --no-keep-packages %s %s" %(repodir, repourl, update_id))
217     if not download:
218         os.system("zypper --quiet --non-interactive --reposd-dir %s patch --repo %s -d" % (repodir, update_id) )
219
220 def install_update(update_data):
221     u = update_data
222     location = u['location']
223     update_id = u['id']
224     # unzip
225     if not os.path.exists("%s/download/%s/content" % (update_cache,update_id)):
226         prepare_update(update_data, False)
227
228     repodir = "%s/repos.d" %update_cache
229     repourl = "file://%s/download/%s/content" % (update_cache, update_id)
230     if not os.path.exists("%s/%s.repo" % (repodir, update_id)):
231         os.system("zypper --quiet --reposd-dir %s ar --no-gpgcheck --no-keep-packages %s %s" %(repodir, repourl, update_id))
232     print "zypper -n  --reposd-dir %s patch --with-interactive  --repo %s " % (repodir, update_id) 
233     os.system("zypper -n  --reposd-dir %s patch --with-interactive --repo %s " % (repodir, update_id) )
234     if not os.path.exists("%s/installed" % (update_cache)):
235         os.mkdir("%s/installed" % (update_cache))
236     shutil.copyfile("%s/download/%s/content/%s" %(update_cache, update_id, update_id), "%s/installed/%s" % (update_cache, update_id))
237
238     print "Finished installing %s." % update_id 
239     for line in fileinput.input("/etc/os-release", inplace=1):
240         print line.replace(current_version, u["version"]),
241     for line in fileinput.input("/etc/tizen-release", inplace=1):
242         print line.replace(current_version, u["version"]),
243
244 def apply_update(update_data):
245     pass
246
247 def list_updates():
248     updates = parse_updates()
249     for k in updates.keys():
250         u = updates[k]
251         installed = "%s/installed/%s" % (update_cache, u['id'])
252         if not os.path.exists(installed):
253             print "%s" %u['id']
254             print "    %s" %u['title']
255
256
257 parser = OptionParser()
258 parser.add_option("-V", "--os-version", action="store_true", dest="osver", default=False,
259                   help="Current OS Version")
260
261 parser.add_option("-l", "--list-updates", action="store_true", dest="listupdates", default=False,
262                   help="List updates")
263
264 parser.add_option("-d", "--download-only", action="store_true", dest="downloadonly", default=False,
265                   help="Download only")
266
267 parser.add_option("-i", "--install",  dest="install", metavar="LABEL",
268                   help="Install update")
269
270 parser.add_option("-p", "--prepare",  dest="prepare", metavar="LABEL",
271                   help="Prepare update")
272
273 parser.add_option("-a", "--install-all",  dest="installall", action="store_true", default=False,
274                   help="Install all updates")
275
276 parser.add_option("-P", "--prepare-all",  dest="prepareall", action="store_true", default=False,
277                   help="prepare update")
278
279 parser.add_option("-r", "--recommended",  dest="recommended", action="store_true", default=False,
280                   help="Install recommended updates only")
281
282 parser.add_option("-q", "--quiet",
283                   action="store_false", dest="verbose", default=True,
284                   help="don't print status messages to stdout")
285
286 (options, args) = parser.parse_args()
287
288 if not os.path.exists(update_cache):
289     os.mkdir("%s" % update_cache)
290 if not os.path.exists("%s/download" % update_cache):
291     os.mkdir("%s/download" % update_cache)
292     os.mkdir("%s/download/metadata" % update_cache)
293
294 if not os.path.exists("%s/repos.d" % update_cache):
295     os.mkdir("%s/repos.d" % update_cache)
296
297 if options.osver:
298     os_release = get_current_version()
299     print os_release['version_id'].strip('"')
300
301 if options.listupdates:
302     probe_updates()
303     list_updates()
304
305 if options.downloadonly:
306     probe_updates()
307     download_all_updates()
308
309 if options.prepare is not None:
310     probe_updates()
311     updates = parse_updates()
312     if not updates.has_key(options.prepare):
313         print "%s is not available for installation. Abort." %options.prepare
314         sys.exit()
315     u = updates[options.prepare]
316     download_update(u)
317     prepare_update(u, False)
318
319 if options.install is not None:
320     #probe_updates()
321     updates = parse_updates()
322     if not updates.has_key(options.install):
323         print "%s is not available for installation. Abort." %options.install
324         sys.exit()
325     u = updates[options.install]
326     download_update(u)
327     install_update(u)