Merge "Another method of install tpk." into devel
[tools/mic.git] / plugins / backend / zypppkgmgr.py
1 #!/usr/bin/python -tt
2 #
3 # Copyright (c) 2010, 2011 Intel, Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation; version 2 of the License
8 #
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 # for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc., 59
16 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 import os
19 import shutil
20 import urlparse
21 import rpm
22 import glob
23
24 import zypp
25 if not hasattr(zypp, 'PoolQuery') or \
26    not hasattr(zypp.RepoManager, 'loadSolvFile'):
27     raise ImportError("python-zypp in host system cannot support PoolQuery or "
28                       "loadSolvFile interface, please update it to enhanced "
29                       "version which can be found in download.tizen.org/tools")
30
31 from mic import msger
32 from mic.kickstart import ksparser
33 from mic.utils import misc, rpmmisc, runner, fs_related
34 from mic.utils.grabber import myurlgrab, TextProgress
35 from mic.utils.proxy import get_proxy_for
36 from mic.utils.errors import CreatorError, RepoError, RpmError
37 from mic.conf import configmgr
38
39 class RepositoryStub:
40     def __init__(self):
41         self.name = None
42         self.baseurl = []
43         self.mirrorlist = None
44         self.proxy = None
45         self.proxy_username = None
46         self.proxy_password = None
47         self.nocache = False
48
49         self.enabled = True
50         self.autorefresh = True
51         self.keeppackages = True
52         self.priority = None
53
54 from mic.pluginbase import BackendPlugin
55 class Zypp(BackendPlugin):
56     name = 'zypp'
57
58     def __init__(self, target_arch, instroot, cachedir, strict_mode = False):
59         self.cachedir = cachedir
60         self.instroot  = instroot
61         self.target_arch = target_arch
62         self.strict_mode = strict_mode
63
64         self.__pkgs_license = {}
65         self.__pkgs_content = {}
66         self.__pkgs_vcsinfo = {}
67         self.repos = []
68         self.to_deselect = []
69         self.localpkgs = {}
70         self.repo_manager = None
71         self.repo_manager_options = None
72         self.Z = None
73         self.ts = None
74         self.ts_pre = None
75         self.incpkgs = {}
76         self.excpkgs = {}
77         self.pre_pkgs = []
78         self.check_pkgs = []
79         self.probFilterFlags = [ rpm.RPMPROB_FILTER_OLDPACKAGE,
80                                  rpm.RPMPROB_FILTER_REPLACEPKG ]
81
82         self.has_prov_query = True
83         self.install_debuginfo = False
84         # this can't be changed, it is used by zypp
85         self.tmp_file_path = '/var/tmp'
86
87     def doFileLogSetup(self, uid, logfile):
88         # don't do the file log for the livecd as it can lead to open fds
89         # being left and an inability to clean up after ourself
90         pass
91
92     def closeRpmDB(self):
93         pass
94
95     def close(self):
96         if self.ts:
97             self.ts.closeDB()
98             self.ts = None
99
100         if self.ts_pre:
101             self.ts_pre.closeDB()
102             self.ts = None
103
104         self.closeRpmDB()
105
106     def __del__(self):
107         self.close()
108
109     def _cleanupRpmdbLocks(self, installroot):
110         # cleans up temporary files left by bdb so that differing
111         # versions of rpm don't cause problems
112         import glob
113         for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
114             os.unlink(f)
115
116     def _cleanupZyppJunk(self, installroot):
117         try:
118             shutil.rmtree(os.path.join(installroot, '.zypp'))
119         except:
120             pass
121
122     def setup(self):
123         self._cleanupRpmdbLocks(self.instroot)
124         # '/var/tmp' is used by zypp to build cache, so make sure
125         # if it exists
126         if not os.path.exists(self.tmp_file_path ):
127             os.makedirs(self.tmp_file_path)
128
129     def whatObsolete(self, pkg):
130         query = zypp.PoolQuery()
131         query.addKind(zypp.ResKind.package)
132         query.addDependency(zypp.SolvAttr.obsoletes, pkg.name(), pkg.edition())
133         query.setMatchExact()
134         for pi in query.queryResults(self.Z.pool()):
135             return pi
136         return None
137
138     def _zyppQueryPackage(self, pkg):
139         query = zypp.PoolQuery()
140         query.addKind(zypp.ResKind.package)
141         query.addAttribute(zypp.SolvAttr.name, pkg)
142         query.setMatchExact()
143         for pi in query.queryResults(self.Z.pool()):
144             return pi
145         return None
146
147     def _splitPkgString(self, pkg):
148         sp = pkg.rsplit(".", 1)
149         name = sp[0]
150         arch = None
151         if len(sp) == 2:
152             arch = sp[1]
153             sysarch = zypp.Arch(self.target_arch)
154             if not zypp.Arch(arch).compatible_with (sysarch):
155                 arch = None
156                 name = ".".join(sp)
157         return name, arch
158
159     def selectPackage(self, pkg):
160         """Select a given package or package pattern, can be specified
161         with name.arch or name* or *name
162         """
163
164         if not self.Z:
165             self.__initialize_zypp()
166
167         def markPoolItem(obs, pi):
168             if obs == None:
169                 pi.status().setToBeInstalled (zypp.ResStatus.USER)
170             else:
171                 obs.status().setToBeInstalled (zypp.ResStatus.USER)
172
173         def cmpEVR(p1, p2):
174             # compare criterion: arch compatibility first, then repo
175             # priority, and version last
176             a1 = p1.arch()
177             a2 = p2.arch()
178             if str(a1) != str(a2):
179                 if a1.compatible_with(a2):
180                     return -1
181                 else:
182                     return 1
183             # Priority of a repository is an integer value between 0 (the
184             # highest priority) and 99 (the lowest priority)
185             pr1 = int(p1.repoInfo().priority())
186             pr2 = int(p2.repoInfo().priority())
187             if pr1 > pr2:
188                 return -1
189             elif pr1 < pr2:
190                 return 1
191
192             ed1 = p1.edition()
193             ed2 = p2.edition()
194             (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()])
195             (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()])
196             return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
197
198         found = False
199         startx = pkg.startswith("*")
200         endx = pkg.endswith("*")
201         ispattern = startx or endx
202         name, arch = self._splitPkgString(pkg)
203
204         q = zypp.PoolQuery()
205         q.addKind(zypp.ResKind.package)
206
207         if ispattern:
208             if startx and not endx:
209                 pattern = '%s$' % (pkg[1:])
210             if endx and not startx:
211                 pattern = '^%s' % (pkg[0:-1])
212             if endx and startx:
213                 pattern = '%s' % (pkg[1:-1])
214             q.setMatchRegex()
215             q.addAttribute(zypp.SolvAttr.name, pattern)
216
217         elif arch:
218             q.setMatchExact()
219             q.addAttribute(zypp.SolvAttr.name, name)
220
221         else:
222             q.setMatchExact()
223             q.addAttribute(zypp.SolvAttr.name, pkg)
224
225         for pitem in sorted(
226                         q.queryResults(self.Z.pool()),
227                         cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)),
228                         reverse=True):
229             item = zypp.asKindPackage(pitem)
230             if item.name() in self.excpkgs.keys() and \
231                self.excpkgs[item.name()] == item.repoInfo().name():
232                 continue
233             if item.name() in self.incpkgs.keys() and \
234                self.incpkgs[item.name()] != item.repoInfo().name():
235                 continue
236
237             found = True
238             obspkg = self.whatObsolete(item)
239             if arch:
240                 if arch == str(item.arch()):
241                     pitem.status().setToBeInstalled (zypp.ResStatus.USER)
242             else:
243                 markPoolItem(obspkg, pitem)
244             if not ispattern:
245                 break
246
247         # Can't match using package name, then search from packge
248         # provides infomation
249         if found == False and not ispattern:
250             q.addAttribute(zypp.SolvAttr.provides, pkg)
251             q.addAttribute(zypp.SolvAttr.name,'')
252
253             for pitem in sorted(
254                             q.queryResults(self.Z.pool()),
255                             cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)),
256                             reverse=True):
257                 item = zypp.asKindPackage(pitem)
258                 if item.name() in self.excpkgs.keys() and \
259                    self.excpkgs[item.name()] == item.repoInfo().name():
260                     continue
261                 if item.name() in self.incpkgs.keys() and \
262                    self.incpkgs[item.name()] != item.repoInfo().name():
263                     continue
264
265                 found = True
266                 obspkg = self.whatObsolete(item)
267                 markPoolItem(obspkg, pitem)
268                 break
269
270         if found:
271             return None
272         else:
273             raise CreatorError("Unable to find package: %s" % (pkg,))
274
275     def inDeselectPackages(self, pitem):
276         """check if specified pacakges are in the list of inDeselectPackages
277         """
278         item = zypp.asKindPackage(pitem)
279         name = item.name()
280         for pkg in self.to_deselect:
281             startx = pkg.startswith("*")
282             endx = pkg.endswith("*")
283             ispattern = startx or endx
284             pkgname, pkgarch = self._splitPkgString(pkg)
285             if not ispattern:
286                 if pkgarch:
287                     if name == pkgname and str(item.arch()) == pkgarch:
288                         return True
289                 else:
290                     if name == pkgname:
291                         return True
292             else:
293                 if startx and name.endswith(pkg[1:]):
294                     return True
295                 if endx and name.startswith(pkg[:-1]):
296                     return True
297
298         return False
299
300     def deselectPackage(self, pkg):
301         """collect packages should not be installed"""
302         self.to_deselect.append(pkg)
303
304     def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
305         def compareGroup(pitem):
306             item = zypp.asKindPattern(pitem)
307             return item.repoInfo().priority()
308         if not self.Z:
309             self.__initialize_zypp()
310         found = False
311         q = zypp.PoolQuery()
312         q.addKind(zypp.ResKind.pattern)
313         for pitem in sorted(q.queryResults(self.Z.pool()), key=compareGroup):
314             item = zypp.asKindPattern(pitem)
315             summary = "%s" % item.summary()
316             name = "%s" % item.name()
317             if name == grp or summary == grp:
318                 found = True
319                 pitem.status().setToBeInstalled (zypp.ResStatus.USER)
320                 break
321
322         if found:
323             if include == ksparser.GROUP_REQUIRED:
324                 map(
325                     lambda p: self.deselectPackage(p),
326                     grp.default_packages.keys())
327
328             return None
329         else:
330             raise CreatorError("Unable to find pattern: %s" % (grp,))
331
332     def addRepository(self, name,
333                             url = None,
334                             mirrorlist = None,
335                             proxy = None,
336                             proxy_username = None,
337                             proxy_password = None,
338                             inc = None,
339                             exc = None,
340                             ssl_verify = True,
341                             nocache = False,
342                             cost=None,
343                             priority=None):
344         # TODO: Handle cost attribute for repos
345
346         if not self.repo_manager:
347             self.__initialize_repo_manager()
348
349         if not proxy and url:
350             proxy = get_proxy_for(url)
351
352         repo = RepositoryStub()
353         repo.name = name
354         repo.id = name
355         repo.proxy = proxy
356         repo.proxy_username = proxy_username
357         repo.proxy_password = proxy_password
358         repo.ssl_verify = ssl_verify
359         repo.nocache = nocache
360         repo.baseurl.append(url)
361         if inc:
362             for pkg in inc:
363                 self.incpkgs[pkg] = name
364         if exc:
365             for pkg in exc:
366                 self.excpkgs[pkg] = name
367
368         if mirrorlist:
369             repo.mirrorlist = mirrorlist
370
371         # Enable gpg check for verifying corrupt packages
372         repo.gpgcheck = 1
373         if priority is not None:
374             # priority 0 has issue in RepoInfo.setPriority
375             repo.priority = priority + 1
376
377         try:
378             repo_info = zypp.RepoInfo()
379             repo_info.setAlias(repo.name)
380             repo_info.setName(repo.name)
381             repo_info.setEnabled(repo.enabled)
382             repo_info.setAutorefresh(repo.autorefresh)
383             repo_info.setKeepPackages(repo.keeppackages)
384             baseurl = zypp.Url(repo.baseurl[0].full)
385             if not ssl_verify:
386                 baseurl.setQueryParam("ssl_verify", "no")
387             if proxy:
388                 host = urlparse.urlparse(proxy)[1]
389                 # scheme, host, path, parm, query, frag = urlparse.urlparse(proxy)
390
391                 proxyinfo = host.rsplit(":", 1)
392                 host = proxyinfo[0]
393
394                 port = "80"
395                 if len(proxyinfo) > 1:
396                     port = proxyinfo[1]
397
398                 if proxy.startswith("socks") and len(proxy.rsplit(':', 1)) == 2:
399                     host = proxy.rsplit(':', 1)[0]
400                     port = proxy.rsplit(':', 1)[1]
401
402                 # parse user/pass from proxy host
403                 proxyinfo = host.rsplit("@", 1)
404                 if len(proxyinfo) == 2:
405                     host = proxyinfo[1]
406                     # Known Issue: If password contains ":", which should be
407                     # quoted, for example, use '123%3Aabc' instead of 123:abc
408                     userpassinfo = proxyinfo[0].rsplit(":", 1)
409                     if len(userpassinfo) == 2:
410                         proxy_username = userpassinfo[0]
411                         proxy_password = userpassinfo[1]
412                     elif len(userpassinfo) == 1:
413                         proxy_username = userpassinfo[0]
414
415                 baseurl.setQueryParam ("proxy", host)
416                 baseurl.setQueryParam ("proxyport", port)
417                 if proxy_username:
418                     baseurl.setQueryParam ("proxyuser", proxy_username)
419                 if proxy_password:
420                     baseurl.setQueryParam ("proxypass", proxy_password)
421             else:
422                 baseurl.setQueryParam ("proxy", "_none_")
423
424             self.repos.append(repo)
425
426             repo_info.addBaseUrl(baseurl)
427
428             if repo.priority is not None:
429                 repo_info.setPriority(repo.priority)
430
431             # this hack is used to change zypp credential file location
432             # the default one is $HOME/.zypp, which cause conflicts when
433             # installing some basic packages, and the location doesn't
434             # have any interface actually, so use a tricky way anyway
435             homedir = None
436             if 'HOME' in os.environ:
437                 homedir = os.environ['HOME']
438                 os.environ['HOME'] = '/'
439             else:
440                 os.environ['HOME'] = '/'
441
442             self.repo_manager.addRepository(repo_info)
443
444             # save back the $HOME env
445             if homedir:
446                 os.environ['HOME'] = homedir
447             else:
448                 del os.environ['HOME']
449
450             self.__build_repo_cache(name)
451
452         except RuntimeError, e:
453             raise CreatorError(str(e))
454
455         msger.verbose('repo: %s was added' % name)
456         return repo
457
458     def installHasFile(self, file):
459         return False
460
461     def preInstall(self, pkg):
462         self.pre_pkgs.append(pkg)
463
464     def checkPackage(self, pkg):
465         self.check_pkgs.append(pkg)
466
467     def _get_local_packages(self):
468         """Return a list of rpm path to be local installed.
469         This is the hook where subclasses may specify a set of rpms which
470         it requires to be installed locally.
471         This returns an empty list by default.
472         Note, subclasses should usually chain up to the base class
473         implementation of this hook.
474         """
475         cropts = configmgr.create
476         if cropts['local_pkgs_path']:
477             if os.path.isdir(cropts['local_pkgs_path']):
478                 return glob.glob(
479                         os.path.join(cropts['local_pkgs_path'], '*.rpm'))
480             elif os.path.splitext(cropts['local_pkgs_path'])[-1] == '.rpm':
481                 return [cropts['local_pkgs_path']]
482         return []
483     def __localinst_packages(self):
484         for rpm_path in self._get_local_packages():
485             self.installLocal(rpm_path)
486     def runInstall(self, checksize = 0):
487         os.environ["HOME"] = "/"
488         os.environ["LD_PRELOAD"] = ""
489         self.buildTransaction()
490         self.__localinst_packages()
491
492         todo = zypp.GetResolvablesToInsDel(self.Z.pool())
493         installed_pkgs = todo._toInstall
494         dlpkgs = []
495
496         for pitem in installed_pkgs:
497             if not zypp.isKindPattern(pitem) and \
498               not self.inDeselectPackages(pitem):
499                 item = zypp.asKindPackage(pitem)
500                 dlpkgs.append(item)
501
502                 if item.name() in self.check_pkgs:
503                     self.check_pkgs.remove(item.name())
504
505                 if not self.install_debuginfo or str(item.arch()) == "noarch":
506                     continue
507
508                 dipkg = self._zyppQueryPackage("%s-debuginfo" % item.name())
509                 if dipkg:
510                     ditem = zypp.asKindPackage(dipkg)
511                     dlpkgs.append(ditem)
512                 else:
513                     msger.warning("No debuginfo rpm found for: %s" \
514                                   % item.name())
515
516         if self.check_pkgs:
517             raise CreatorError('Packages absent in image: %s' % ','.join(self.check_pkgs))
518
519         # record all pkg and the content
520         localpkgs = self.localpkgs.keys()
521         for pkg in dlpkgs:
522             license = ''
523             if pkg.name() in localpkgs:
524                 hdr = rpmmisc.readRpmHeader(self.ts, self.localpkgs[pkg.name()])
525                 pkg_long_name = misc.RPM_FMT % {
526                                     'name': hdr['name'],
527                                     'arch': hdr['arch'],
528                                     'version': hdr['version'],
529                                     'release': hdr['release']
530                                 }
531                 license = hdr['license']
532
533             else:
534                 pkg_long_name = misc.RPM_FMT % {
535                                     'name': pkg.name(),
536                                     'arch': pkg.arch(),
537                                     'version': pkg.edition().version(),
538                                     'release': pkg.edition().release()
539                                 }
540
541                 license = pkg.license()
542
543             if license in self.__pkgs_license.keys():
544                 self.__pkgs_license[license].append(pkg_long_name)
545             else:
546                 self.__pkgs_license[license] = [pkg_long_name]
547
548         total_count = len(dlpkgs)
549         cached_count = 0
550         download_total_size = sum(map(lambda x: int(x.downloadSize()), dlpkgs))
551         localpkgs = self.localpkgs.keys()
552
553         msger.info("Checking packages cached ...")
554         for po in dlpkgs:
555             # Check if it is cached locally
556             if po.name() in localpkgs:
557                 cached_count += 1
558             else:
559                 local = self.getLocalPkgPath(po)
560                 name = str(po.repoInfo().name())
561                 try:
562                     repo = filter(lambda r: r.name == name, self.repos)[0]
563                 except IndexError:
564                     repo = None
565                 nocache = repo.nocache if repo else False
566
567                 if os.path.exists(local):
568                     if nocache or self.checkPkg(local) !=0:
569                         os.unlink(local)
570                     else:
571                         download_total_size -= int(po.downloadSize())
572                         cached_count += 1
573         cache_avail_size = misc.get_filesystem_avail(self.cachedir)
574         if cache_avail_size < download_total_size:
575             raise CreatorError("No enough space used for downloading.")
576
577         # record the total size of installed pkgs
578         install_total_size = sum(map(lambda x: int(x.installSize()), dlpkgs))
579         # check needed size before actually download and install
580
581         # FIXME: for multiple partitions for loop type, check fails
582         #        skip the check temporarily
583         #if checksize and install_total_size > checksize:
584         #    raise CreatorError("No enough space used for installing, "
585         #                       "please resize partition size in ks file")
586
587         download_count =  total_count - cached_count
588         msger.info("Packages: %d Total, %d Cached, %d Missed" \
589                    % (total_count, cached_count, download_count))
590
591         try:
592             if download_count > 0:
593                 msger.info("Downloading packages ...")
594             self.downloadPkgs(dlpkgs, download_count)
595         except CreatorError, e:
596             raise CreatorError("Package download failed: %s" %(e,))
597
598         try:
599             self.installPkgs(dlpkgs)
600         except (RepoError, RpmError):
601             raise
602         except Exception, e:
603             raise CreatorError("Package installation failed: %s" % (e,))
604
605     def getVcsInfo(self):
606         if self.__pkgs_vcsinfo:
607             return
608
609         if not self.ts:
610             self.__initialize_transaction()
611
612         mi = self.ts.dbMatch()
613         for hdr in mi:
614             lname = misc.RPM_FMT % {
615                         'name': hdr['name'],
616                         'arch': hdr['arch'],
617                         'version': hdr['version'],
618                         'release': hdr['release']
619                     }
620             try:
621                 self.__pkgs_vcsinfo[lname] = hdr['VCS']
622             except ValueError:
623                 # if rpm not support VCS, set to None
624                 self.__pkgs_vcsinfo[lname] = None
625
626         return self.__pkgs_vcsinfo
627
628     def getAllContent(self):
629         if self.__pkgs_content:
630             return self.__pkgs_content
631
632         if not self.ts:
633             self.__initialize_transaction()
634
635         mi = self.ts.dbMatch()
636         for hdr in mi:
637             lname = misc.RPM_FMT % {
638                         'name': hdr['name'],
639                         'arch': hdr['arch'],
640                         'version': hdr['version'],
641                         'release': hdr['release']
642                     }
643             self.__pkgs_content[lname] = hdr['FILENAMES']
644
645         return self.__pkgs_content
646
647     def getPkgsLicense(self):
648         return self.__pkgs_license
649
650     def getFilelist(self, pkgname):
651         if not pkgname:
652             return None
653
654         if not self.ts:
655             self.__initialize_transaction()
656
657         mi = self.ts.dbMatch('name', pkgname)
658         for header in mi:
659             return header['FILENAMES']
660
661     def __initialize_repo_manager(self):
662         if self.repo_manager:
663             return
664
665         # Clean up repo metadata
666         shutil.rmtree(self.cachedir + "/etc", ignore_errors = True)
667         shutil.rmtree(self.cachedir + "/solv", ignore_errors = True)
668         shutil.rmtree(self.cachedir + "/raw", ignore_errors = True)
669
670         zypp.KeyRing.setDefaultAccept( zypp.KeyRing.ACCEPT_UNSIGNED_FILE
671                                      | zypp.KeyRing.ACCEPT_VERIFICATION_FAILED
672                                      | zypp.KeyRing.ACCEPT_UNKNOWNKEY
673                                      | zypp.KeyRing.TRUST_KEY_TEMPORARILY
674                                      )
675
676         self.repo_manager_options = \
677                 zypp.RepoManagerOptions(zypp.Pathname(self.instroot))
678
679         self.repo_manager_options.knownReposPath = \
680                 zypp.Pathname(self.cachedir + "/etc/zypp/repos.d")
681
682         self.repo_manager_options.repoCachePath = \
683                 zypp.Pathname(self.cachedir)
684
685         self.repo_manager_options.repoRawCachePath = \
686                 zypp.Pathname(self.cachedir + "/raw")
687
688         self.repo_manager_options.repoSolvCachePath = \
689                 zypp.Pathname(self.cachedir + "/solv")
690
691         self.repo_manager_options.repoPackagesCachePath = \
692                 zypp.Pathname(self.cachedir + "/packages")
693
694         self.repo_manager = zypp.RepoManager(self.repo_manager_options)
695
696     def __build_repo_cache(self, name):
697         repo = self.repo_manager.getRepositoryInfo(name)
698         if self.repo_manager.isCached(repo) or not repo.enabled():
699             return
700
701         msger.info('Refreshing repository: %s ...' % name)
702         self.repo_manager.buildCache(repo, zypp.RepoManager.BuildIfNeeded)
703
704     def __initialize_zypp(self):
705         if self.Z:
706             return
707
708         zconfig = zypp.ZConfig_instance()
709
710         # Set system architecture
711         if self.target_arch:
712             zconfig.setSystemArchitecture(zypp.Arch(self.target_arch))
713
714         msger.info("zypp architecture is <%s>" % zconfig.systemArchitecture())
715
716         # repoPackagesCachePath is corrected by this
717         self.repo_manager = zypp.RepoManager(self.repo_manager_options)
718         repos = self.repo_manager.knownRepositories()
719         for repo in repos:
720             if not repo.enabled():
721                 continue
722             self.repo_manager.loadFromCache(repo)
723
724         self.Z = zypp.ZYppFactory_instance().getZYpp()
725         self.Z.initializeTarget(zypp.Pathname(self.instroot))
726         self.Z.target().load()
727
728     def buildTransaction(self):
729         if not self.Z.resolver().resolvePool():
730             probs = self.Z.resolver().problems()
731
732             for problem in probs:
733                 msger.warning("repo problem: %s, %s" \
734                               % (problem.description().decode("utf-8"),
735                                  problem.details().decode("utf-8")))
736
737             raise RepoError("found %d resolver problem, abort!" \
738                             % len(probs))
739
740     def getLocalPkgPath(self, po):
741         repoinfo = po.repoInfo()
742         cacheroot = repoinfo.packagesPath()
743         location = po.location()
744         rpmpath = str(location.filename())
745         pkgpath = "%s/%s" % (cacheroot, os.path.basename(rpmpath))
746         return pkgpath
747
748     def installLocal(self, pkg, po=None, updateonly=False):
749         if not self.ts:
750             self.__initialize_transaction()
751
752         solvfile = "%s/.solv" % (self.cachedir)
753
754         rc, out = runner.runtool([fs_related.find_binary_path("rpms2solv"),
755                                   pkg])
756         if rc == 0:
757             f = open(solvfile, "w+")
758             f.write(out)
759             f.close()
760
761             warnmsg = self.repo_manager.loadSolvFile(solvfile,
762                                                      os.path.basename(pkg))
763             if warnmsg:
764                 msger.warning(warnmsg)
765
766             os.unlink(solvfile)
767         else:
768             msger.warning('Can not get %s solv data.' % pkg)
769
770         hdr = rpmmisc.readRpmHeader(self.ts, pkg)
771         arch = zypp.Arch(hdr['arch'])
772         sysarch = zypp.Arch(self.target_arch)
773
774         if arch.compatible_with (sysarch):
775             pkgname = hdr['name']
776             self.localpkgs[pkgname] = pkg
777             self.selectPackage(pkgname)
778             msger.info("Marking %s to be installed" % (pkg))
779
780         else:
781             msger.warning("Cannot add package %s to transaction. "
782                           "Not a compatible architecture: %s" \
783                           % (pkg, hdr['arch']))
784
785     def downloadPkgs(self, package_objects, count):
786         localpkgs = self.localpkgs.keys()
787         progress_obj = TextProgress(count)
788
789         for po in package_objects:
790             if po.name() in localpkgs:
791                 continue
792
793             filename = self.getLocalPkgPath(po)
794             if os.path.exists(filename):
795                 if self.checkPkg(filename) == 0:
796                     continue
797
798             dirn = os.path.dirname(filename)
799             if not os.path.exists(dirn):
800                 os.makedirs(dirn)
801
802             url = self.get_url(po)
803             proxies = self.get_proxies(po)
804
805             try:
806                 filename = myurlgrab(url.full, filename, proxies, progress_obj)
807             except CreatorError:
808                 self.close()
809                 raise
810
811     def preinstallPkgs(self):
812         if not self.ts_pre:
813             self.__initialize_transaction()
814
815         self.ts_pre.order()
816         cb = rpmmisc.RPMInstallCallback(self.ts_pre)
817         cb.headmsg = "Preinstall"
818         installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
819
820         # start to catch stderr output from librpm
821         msger.enable_logstderr(installlogfile)
822
823         errors = self.ts_pre.run(cb.callback, '')
824         # stop catch
825         msger.disable_logstderr()
826         self.ts_pre.closeDB()
827         self.ts_pre = None
828
829         if errors is not None:
830             if len(errors) == 0:
831                 msger.warning('scriptlet or other non-fatal errors occurred '
832                               'during transaction.')
833
834             else:
835                 for e in errors:
836                     msger.warning(e[0])
837                 raise RepoError('Could not run transaction.')
838
839     def installPkgs(self, package_objects):
840         if not self.ts:
841             self.__initialize_transaction()
842
843         # clean rpm lock
844         self._cleanupRpmdbLocks(self.instroot)
845         self._cleanupZyppJunk(self.instroot)
846         # Set filters
847         probfilter = 0
848         for flag in self.probFilterFlags:
849             probfilter |= flag
850         self.ts.setProbFilter(probfilter)
851         self.ts_pre.setProbFilter(probfilter)
852
853         localpkgs = self.localpkgs.keys()
854
855         for po in package_objects:
856             pkgname = po.name()
857             if pkgname in localpkgs:
858                 rpmpath = self.localpkgs[pkgname]
859             else:
860                 rpmpath = self.getLocalPkgPath(po)
861
862             if not os.path.exists(rpmpath):
863                 # Maybe it is a local repo
864                 rpmuri = self.get_url(po)
865                 if rpmuri.startswith("file:/"):
866                     rpmpath = rpmuri[5:]
867
868             if not os.path.exists(rpmpath):
869                 raise RpmError("Error: %s doesn't exist" % rpmpath)
870
871             h = rpmmisc.readRpmHeader(self.ts, rpmpath)
872
873             if pkgname in self.pre_pkgs:
874                 msger.verbose("pre-install package added: %s" % pkgname)
875                 self.ts_pre.addInstall(h, rpmpath, 'u')
876
877             self.ts.addInstall(h, rpmpath, 'u')
878
879         unresolved_dependencies = self.ts.check()
880         if not unresolved_dependencies:
881             if self.pre_pkgs:
882                 self.preinstallPkgs()
883
884             self.ts.order()
885             cb = rpmmisc.RPMInstallCallback(self.ts)
886             installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
887
888             # start to catch stderr output from librpm
889             msger.enable_logstderr(installlogfile)
890
891             errors = self.ts.run(cb.callback, '')
892             # stop catch
893             msger.disable_logstderr()
894             self.ts.closeDB()
895             self.ts = None
896
897             if errors is not None:
898                 if len(errors) == 0:
899                     msger.warning('scriptlet or other non-fatal errors occurred '
900                                   'during transaction.')
901                     if self.strict_mode:
902                         raise CreatorError("mic failes to install some packages")
903                 else:
904                     for e in errors:
905                         msger.warning(e[0])
906                     raise RepoError('Could not run transaction.')
907
908         else:
909             for pkg, need, needflags, sense, key in unresolved_dependencies:
910                 package = '-'.join(pkg)
911
912                 if needflags == rpm.RPMSENSE_LESS:
913                     deppkg = ' < '.join(need)
914                 elif needflags == rpm.RPMSENSE_EQUAL:
915                     deppkg = ' = '.join(need)
916                 elif needflags == rpm.RPMSENSE_GREATER:
917                     deppkg = ' > '.join(need)
918                 else:
919                     deppkg = '-'.join(need)
920
921                 if sense == rpm.RPMDEP_SENSE_REQUIRES:
922                     msger.warning("[%s] Requires [%s], which is not provided" \
923                                   % (package, deppkg))
924
925                 elif sense == rpm.RPMDEP_SENSE_CONFLICTS:
926                     msger.warning("[%s] Conflicts with [%s]" % (package, deppkg))
927
928             raise RepoError("Unresolved dependencies, transaction failed.")
929
930     def __initialize_transaction(self):
931         if not self.ts:
932             self.ts = rpm.TransactionSet(self.instroot)
933             # Set to not verify DSA signatures.
934             self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
935
936         if not self.ts_pre:
937             self.ts_pre = rpm.TransactionSet(self.instroot)
938             # Just unpack the files, don't run scripts
939             self.ts_pre.setFlags(rpm.RPMTRANS_FLAG_ALLFILES | rpm.RPMTRANS_FLAG_NOSCRIPTS)
940             # Set to not verify DSA signatures.
941             self.ts_pre.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
942
943     def checkPkg(self, pkg):
944         ret = 1
945         if not os.path.exists(pkg):
946             return ret
947         ret = rpmmisc.checkRpmIntegrity('rpm', pkg)
948         if ret != 0:
949             msger.warning("package %s is damaged: %s" \
950                           % (os.path.basename(pkg), pkg))
951
952         return ret
953
954     def _add_prob_flags(self, *flags):
955         for flag in flags:
956             if flag not in self.probFilterFlags:
957                 self.probFilterFlags.append(flag)
958
959     def get_proxies(self, pobj):
960         if not pobj:
961             return None
962
963         proxy = None
964         proxies = None
965         repoinfo = pobj.repoInfo()
966         reponame = "%s" % repoinfo.name()
967         repos = filter(lambda r: r.name == reponame, self.repos)
968         repourl = str(repoinfo.baseUrls()[0])
969
970         if repos:
971             proxy = repos[0].proxy
972         if not proxy:
973             proxy = get_proxy_for(repourl)
974         if proxy:
975             proxies = {str(repourl.split(':')[0]): str(proxy)}
976
977         return proxies
978
979     def get_url(self, pobj):
980         if not pobj:
981             return None
982
983         name = str(pobj.repoInfo().name())
984         try:
985             repo = filter(lambda r: r.name == name, self.repos)[0]
986         except IndexError:
987             return None
988
989         location = pobj.location()
990         location = str(location.filename())
991         if location.startswith("./"):
992             location = location[2:]
993
994         return repo.baseurl[0].join(location)
995
996     def package_url(self, pkgname):
997
998         def cmpEVR(p1, p2):
999             ed1 = p1.edition()
1000             ed2 = p2.edition()
1001             (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()])
1002             (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()])
1003             return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
1004
1005         if not self.Z:
1006             self.__initialize_zypp()
1007
1008         q = zypp.PoolQuery()
1009         q.addKind(zypp.ResKind.package)
1010         q.setMatchExact()
1011         q.addAttribute(zypp.SolvAttr.name, pkgname)
1012         items = sorted(q.queryResults(self.Z.pool()),
1013                        cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)),
1014                        reverse=True)
1015
1016         if items:
1017             item = zypp.asKindPackage(items[0])
1018             url = self.get_url(item)
1019             proxies = self.get_proxies(item)
1020             return (url, proxies)
1021
1022         return (None, None)