import repository from arizona
[raven.git] / lib / ravenlib / package / storkpackage.py
1 #! /usr/bin/env python
2
3 import ravenlib.hash
4 import ravenlib.listutil
5 import ravenlib.report
6 import ravenlib.typecheck
7 import os
8 import tempfile
9 import shutil
10
11 import transaction
12 from ravenlib.package.exception import *
13
14
15
16 # global list that holds (a package manager name as a string, package manager module itself)
17 inited_packmanager_list = []
18 possible_packmanagers = None
19
20 # list of all dependencies satisfied by the package managers themselves
21 glo_packagemanagers_provide = []
22
23
24 def initialize():
25    """
26    <Purpose>
27       Check whether package managers have been initialized or not.
28       If not, initializes every package manager at once
29
30    <Arguments>
31       None.
32
33    <Exceptions>
34       ImportError:
35          If given package manager cannot be found.
36          Or, there is no packagemanager setting in configuration.
37
38    <Side Effects>
39       Set inited_packmanager_list and possible_packmanagers.
40       Build glo_packagemanagers_provide
41
42    <Returns>
43       None.
44    """
45    global possible_packmanagers
46    global glo_packagemanagers_provide
47    global inited_packmanager_list
48
49    # if it hasn't been initialized
50    if possible_packmanagers == None:
51       inited_packmanager_list = []
52
53       # get a list of strings which indicate what package manages are available
54       packmanagers = ["rpm", "tar"]
55
56       # if there is no setting for packagemanagers option in arizonaconfig
57       if packmanagers == None:
58          # raise exception
59          raise StorkNoPackageManagersException()
60
61       possible_packmanagers = packmanagers
62
63       # reset internal provides list
64       glo_packagemanagers_provide = []
65
66       # for each package manager
67       for manager in possible_packmanagers:
68          ravenlib.report.debug("Trying to install package manager '" + manager + "'");
69          # import each package manager
70          try:
71             # TODO possible security problem?   For example, method="rpm as chosen_packmanager; hostile code...; #"
72             exec('import ' + manager + ' as chosen_packmanager')
73             provides = chosen_packmanager.initialize()
74
75             # handle initialization errors
76             if provides == None:
77                ravenlib.report.warning("Package manager `" + manager + "' failed initialization, skipping.")
78                continue
79
80             glo_packagemanagers_provide += provides
81
82             # store package manager name and package manager module into a list
83             inited_packmanager_list.append(('stork' + manager, chosen_packmanager))
84          # if given manager is invalid
85          except ImportError, (sterr):
86             ravenlib.report.warning("Package manager `" + manager + "' could not be imported, skipping.")
87             continue
88          # storknestrpm tends to throw IOErrors when there are problems
89          except IOError, e:
90             ravenlib.report.warning("Package manager `" + manager + "' generated IO error, skipping.")
91             continue
92          except Exception, e:
93             ravenlib.report.warning("Package manager `" + manager + "' generated exception " + str(e))
94             continue
95
96       # did we end up having any package managers?
97       if len(inited_packmanager_list) < 1:
98          # raise exception
99          raise StorkNoUsablePackageManagersException()
100
101
102
103
104
105 def get_packagemanagers_provide():
106    """
107    <Purpose>
108       Returns a string list of all dependencies fulfilled internally
109       by the installed package managers.
110
111    <Arguments>
112       None.
113
114    <Exceptions>
115       None.
116
117    <Side Effects>
118       None.
119
120    <Returns>
121       String list of all dependencies fulfilled internally by the 
122       installed package managers.
123    """
124    # initialize package managers
125    initialize()
126
127    return glo_packagemanagers_provide   
128
129
130
131
132   
133 def is_package_understood(filename): 
134    """
135    <Purpose>
136       Given a filename, checks whether it is valid by any of given package
137       managers.
138
139    <Arguments>
140       filename:
141          Filename of the package to check.
142
143    <Exceptions>
144       TypeError:
145          If a type mismatch or parameter error is detected.
146
147    <Side Effects>
148       None.
149
150    <Returns>
151       True if the package is valid and understood, False otherwise.
152    """
153    # check params
154    ravenlib.typecheck.simple(filename, "filename", str, "ravenlib.package.all.is_package_understood")
155
156    # initialize package managers
157    initialize()
158
159    # go through every possible package manager
160    for packagemanager in inited_packmanager_list:      
161       if packagemanager[1].is_package_understood(filename):
162          return True
163
164    return False
165
166
167
168
169
170 def get_package_metadata(filename, original_filename=None):
171    """
172    <Purpose>
173       Given a package filename, returns a dict of the package metadata.
174
175    <Arguments>
176       filename:
177               This is the package file we wish to extract metadata from
178       original_filename:
179               This is the original name, to be used when getting the hash
180               on a file that has been temporarily renamed.
181
182    <Exceptions>
183       TypeError:
184               If a type mismatch or parameter error is detected (not a package 
185               file).
186       IOError:
187               If an error occurred opening the file, etc.
188
189    <Side Effects>
190       None.
191
192    <Returns>
193       String list which contains the metadata for the package file 
194    """
195    # check params
196    ravenlib.typecheck.simple(filename, "filename", str, "ravenlib.package.all.get_package_metadata")
197
198    ravenlib.report.debug("get_package_metadata: filename = " + filename)
199
200    if not os.path.exists(filename):
201       raise TypeError, "File does not exist: " + str(filename)
202
203    package_dict = {}
204    if original_filename != None:
205       package_dict['filename'] = original_filename
206    else:
207       package_dict['filename'] = os.path.basename(filename)
208
209    # This fetches a list containing the name, version, release, and size
210    info = get_package_info(filename)
211    if info == None:
212       raise TypeError, "Unrecognized package format: " + str(filename)
213
214    package_dict['name'] = info[0]
215    package_dict['version'] = info[1]
216    package_dict['release'] = info[2]
217    package_dict['size'] = info[3]
218    
219    # may throw IOError or TypeError 
220    package_dict['hash'] = ravenlib.hash.get_file_hash(filename)
221    package_dict['provides'] = get_packages_provide([filename])
222    package_dict['requires'] = get_packages_require([filename])
223    package_dict['files'] = get_packages_files([filename])
224
225    return package_dict
226
227
228
229 def package_metadata_dict_to_fn(metadict, dest_dir, dest_fn=None):
230    """
231    <Purpose>
232       This takes a metadata dict for a filename and writes it to a file. If
233       dest_fn is specified, then the file will be written to that filename.
234       Otherwise, if dest_fn==None, then a filename will be generated based
235       on the metahash.
236
237    <Arguments>
238       metadict:
239          The metadata dictionary with the data we want to write out
240       dest_dir:
241          The directory where the file should be written
242       dest_fn:
243          The filename where the file should be written (optional)
244
245    <Exceptions>
246       TypeError is raised when given an invalid argument.
247       OError may be raised if writing the file fails
248
249    <Side Effects>
250       None
251
252    <Returns>
253       The file created
254    """
255    ravenlib.typecheck.simple(metadict, "metadict", dict, "package_metadata_dict_to_fn")
256    if dest_dir:
257       ravenlib.typecheck.simple(dest_dir, "dest_dir", str, "package_metadata_dict_to_fn")
258    if dest_fn:
259       ravenlib.typecheck.simple(dest_fn, "dest_fn", str, "package_metadata_dict_to_fn")
260
261    (metadatahash, thisfilename) = package_metadata_dict_get_hash(metadict, True)
262
263    # if dest_fn was specified, then use that filename
264    if dest_fn:
265       # if the caller specified both dest_dir and dest_fn, then splice them
266       # together.
267       if dest_dir:
268           outfn = dest_dir + "/" + os.path.basename(dest_fn)
269       else:
270           outfn = dest_fn
271    else:
272       # no dest_fn was specified, so generate a filename based on the metahash
273       outfn = dest_dir + "/" + metadatahash
274
275    shutil.move(thisfilename, outfn)
276
277    return outfn
278
279
280
281
282
283 def package_metadata_dict_get_hash(metadict, keep_temp_file=False):
284    """
285    <Purpose>
286       Get the hash from a metadata file.
287
288       This function strips out any temporary/generated entries of the metadict
289       (entries that start with _) and then generates the hash.
290
291    <Arguments>
292       metadict - dictionary containing metadata
293       keep_temp_file - True to keep the temporary disk file that contains the
294                        metadata. False to automatically delete.
295
296    <Returns>
297       if keep_temp_file is true:
298          a tuple (metadatahash, tempfilename)
299       otherwise:
300          metadatahash
301    """
302    mydict = metadict.copy()
303
304    # Remove keys that start with _ as these are generated on the fly and not
305    # included in the hash.
306    for key in mydict.keys():
307       if key[0] == '_':
308          del mydict[key]
309
310    kvpairs = []
311    for pair in mydict.iteritems():
312      kvpairs.append(pair)
313    kvpairs.sort()
314
315    # I now have a sorted kvpair list (sorting is important because dict order
316    # is arbritrary and it needs to be written in the same order so the hashes
317    # will match)
318
319    # we will write the metadata to a temporary file, then compute the hash and
320    # continue
321    (thisfilefd, thisfilename) = tempfile.mkstemp(suffix=".xml_original_file.temp")
322
323    for kvpair in kvpairs:
324       os.write(thisfilefd, kvpair[0] + ":" + repr(kvpair[1]) + "\n")
325
326    os.close(thisfilefd)
327
328    # There might be a race condition here (security flaw) TODO NEEDS WORK FIXME
329    metadatahash = ravenlib.hash.get_file_hash(thisfilename)
330
331    if not keep_temp_file:
332       os.remove(thisfilename)
333       return metadatahash
334
335    return (metadatahash, thisfilename)
336
337
338
339
340
341 def package_metadata_dict_validate(metadict):
342    """
343    <Purpose>
344       Given a metadata dictionary, verify that it is valid
345
346    <Arguments>
347       metadict:
348               Metadata dictionary
349
350    <Side Effects>
351       Sets metadict['_valid'] to True if it is valid
352
353    <Returns>
354       True if metadict is valid, false otherwise
355    """
356    if metadict.get('_valid', False):
357       return True
358
359    # compute the hash
360    hash = package_metadata_dict_get_hash(metadict)
361
362    if not '_metadatahash' in metadict:
363       ravenlib.report.error("metadata missing hash for " + metadict.get('name', 'unknown'))
364       return False
365
366    if metadict['_metadatahash'] != hash:
367       ravenlib.report.error("invalid metadata detected for " + metadict.get('name', 'unknown'))
368       return False
369
370    metadict['_valid'] = True
371
372    return True
373
374
375
376
377
378 def get_package_metadata_hash(filename, createhashfile=False, original_filename=None):
379    """
380    <Purpose>
381       Given a package filename, returns the hash of the package metadata.
382
383    <Arguments>
384       filename:
385               This is the package file we wish to extract the hash of the
386               metadata from
387
388       createhashfile:
389               If True, after the hash has been computed, a file will be
390               written, filename.metahash, containing the metahash.  If
391               file creation fails, it will not throw exceptions.
392
393    <Exceptions>
394       TypeError:
395               If a type mismatch or parameter error is detected (not a package
396               file).
397       IOError:
398               If an error occurred opening the file, etc.
399
400    <Side Effects>
401       Creates and then removes a temp file
402
403    <Returns>
404       The hash of the metadata (as a string)
405    """
406    # check params
407    ravenlib.typecheck.simple(filename, "filename", str, "ravenlib.package.all.get_package_metadata_hash")
408
409    ravenlib.report.debug("get_package_metadata_hash: filename = " + filename)
410
411    try:
412       package_dict = get_package_metadata(filename, original_filename)
413    except TypeError:
414       ravenlib.report.error("Error: Unrecognized Package Format - " + filename)
415       # re-raise the TypeError, because we can't proceed without package_dict
416       raise TypeError, "Error: Unrecognized Package Format - " + filename
417
418    outfn = package_metadata_dict_to_fn(package_dict, "/tmp")
419    
420    if os.path.isfile(outfn):
421        os.unlink(outfn)
422
423    metahash = os.path.basename(outfn)
424    ravenlib.report.debug("get_package_metadata_hash(" + filename + ") = " + metahash)
425    
426    # create the filename.metahash file, if requested
427    if createhashfile:
428       try:
429          f = open(filename + ".metahash", "w")
430          f.write(metahash + "\n")
431          f.close()         
432       except IOError: #changed from a general exception
433          ravenlib.report.debug("get_package_metadata_hash: couldn't create file: " + filename + ".metahash")
434    
435    return metahash
436
437
438
439
440
441 def get_packages_provide(filename_list):
442    """
443    <Purpose>
444       Given a string list of package filenames, returns a string list of 
445       dependencies that those packages can provide.
446
447    <Arguments>
448       filename_list:
449               String list of package filenames.
450
451    <Exceptions>
452       TypeError:
453               If a type mismatch or parameter error is detected.
454
455    <Side Effects>
456       None.
457
458    <Returns>
459       String list of dependencies provided by the given package files.
460       In the format "name = version" or "name".
461    """
462    # check params
463    ravenlib.typecheck.stringlist(filename_list, "filename_list", "ravenlib.package.all.get_packages_provide")
464
465    if len(filename_list) < 1:
466       return []
467
468    # a list that holds provided packages   
469    providelist = []
470
471    # initialize package managers
472    initialize()
473
474    # for each file, locate a package manager which can handle
475    # that package, get the list, and continue to the next file.
476    for filename in filename_list:
477       for packagemanager in inited_packmanager_list:
478          if packagemanager[1].is_package_understood(filename):
479             # get a list of provided packages, and break
480             providelist += packagemanager[1].get_packages_provide([filename])
481             break
482
483    return providelist
484
485
486
487
488
489 def get_packages_require(filename_list):
490    """
491    <Purpose>
492       Given a string list of package filenames, returns a string list of 
493       dependencies that those packages require.
494
495    <Arguments>
496       filename_list:
497               String list of package filenames.
498
499    <Exceptions>
500       TypeError:
501               If a type mismatch or parameter error is detected.
502
503    <Side Effects>
504       None.
505
506    <Returns>
507       String list of dependencies required by the given package files.
508    """
509    # check params
510    ravenlib.typecheck.stringlist(filename_list, "filename_list", "ravenlib.package.all.get_packages_require")
511
512    if len(filename_list) < 1:
513       return []
514
515    # a list that holds required packages   
516    requirelist = []
517
518    # initialize package managers
519    initialize()
520
521    # for each file, locate a package manager which can handle
522    # that package, get the list, and continue to the next file.
523    for filename in filename_list:
524       for packagemanager in inited_packmanager_list:
525          if packagemanager[1].is_package_understood(filename):
526             # get a list of required packages, and break
527             requirelist += packagemanager[1].get_packages_require([filename])
528             break
529
530    return requirelist
531
532
533
534
535
536 def get_packages_files(filename_list):
537    """
538    <Purpose>
539       Given a string list of package filenames, returns a string list of 
540       files that are installed by those packages.
541
542    <Arguments>
543       filename_list:
544               String list of package filenames.
545
546    <Exceptions>
547       TypeError:
548               If a type mismatch or parameter error is detected.
549
550    <Side Effects>
551       None.
552
553    <Returns>
554       String list of files in the given package files.
555    """
556    # check params
557    ravenlib.typecheck.stringlist(filename_list, "filename_list", "ravenlib.package.all.get_packages_files")
558
559    if len(filename_list) < 1:
560       return []
561
562    # a list that holds which files are provided in packages 
563    fileslist = []
564
565    # initialize package managers
566    initialize()
567
568    # for each file, locate a package manager which can handle
569    # that package, get the list, and continue to the next file.
570    for filename in filename_list:
571       for packagemanager in inited_packmanager_list:
572          if packagemanager[1].is_package_understood(filename):
573             # get a list of files provided, and break
574             fileslist += packagemanager[1].get_packages_files([filename])
575             break
576
577    return fileslist
578
579
580
581
582
583 def get_package_info(filename):
584    """
585    <Purpose>
586       Given a package filename, returns a string list of package 
587       information of the form: 
588         [NAME, VERSION, RELEASE, SIZE]
589
590    <Arguments>
591       filename:
592               Package filename.
593
594    <Exceptions>
595       TypeError:
596               If a type mismatch or parameter error is detected.
597
598    <Side Effects>
599       None.
600
601    <Returns>
602       String list containing package information, or None on error.
603    """
604    # check params
605    ravenlib.typecheck.simple(filename, "filename", str, "ravenlib.package.all.get_package_info")
606
607    ravenlib.report.debug("get_package_info: filename = " + filename)
608
609    # initialize package managers
610    initialize()
611
612    # go through every possible package manager
613    ravenlib.report.debug("get_package_metadata: inited_packmanager_list = " + str(inited_packmanager_list))
614    for packagemanager in inited_packmanager_list: 
615       ravenlib.report.debug("get_package_info: packagemanager = " + str(packagemanager))
616       if packagemanager[1].is_package_understood(filename):
617          ravenlib.report.debug("get_package_info: is_package_understood = True")
618          info = packagemanager[1].get_package_info(filename)
619          ravenlib.report.debug("get_package_info: info = " + str(info))
620          if info != None:
621             return info
622             
623    return None
624
625
626
627
628
629 def get_installed_versions(package_list):
630    """
631    <Purpose>
632       Given a package list, returns a list containing the name and version
633       of each package if installed.
634
635    <Arguments>
636       package_list:
637          List of strings containing the names of the packages to get 
638          version information for.
639
640    <Exceptions>
641       TypeError:
642          If a type mismatch or parameter error is detected.
643
644    <Side Effects>
645       None.
646
647    <Returns>
648       String list containing each package name and version (in the format
649       "name = version-release"). Packages that are not installed are not
650       listed. If a package has more than one installed version, then multiple
651       results may be returned for that package.
652    """
653    # check params
654    ravenlib.typecheck.stringlist(package_list, "package_list", "ravenlib.package.all.get_installed_versions")
655
656    # initialize package managers
657    initialize()
658    
659    # initialize the list
660    installed_list = []
661
662    for packagemanager in inited_packmanager_list:      
663       installed_list += packagemanager[1].get_installed_versions(package_list)
664
665    # both rpm and nestrpm will report a match for the same version, so we need
666    # to uniquify the list
667    installed_list = ravenlib.listutil.uniq(installed_list)
668
669    return installed_list
670
671
672
673
674
675 def get_installedpackages_fulfilling(dep_list):
676    """
677    <Purpose>
678       Given a string list of dependencies, returns a string list of 
679       installed packages that fulfill those package dependencies.
680       
681    <Arguments>
682       dep_list:
683          String list of package dependencies.
684
685    <Exceptions>
686       TypeError:
687          If given argument is not a list of strings.
688
689    <Side Effects>
690       None.
691
692    <Returns>
693       String list of installed packages that meet the given dependencies.
694    """
695    
696    # type checking on the given list
697    ravenlib.typecheck.stringlist(dep_list, "dep_list", "ravenlib.package.all.get_installedpackages_fulfilling")
698    
699    if len(dep_list) < 1:
700       return []
701
702    # a list that holds installed packages   
703    fulfillinglist = []
704
705    # initialize package managers
706    initialize()
707
708    # go through every possible package manager
709    for packagemanager in inited_packmanager_list:      
710       # for each manager, get a list of installed packages
711       fulfillinglist += packagemanager[1].get_installedpackages_fulfilling(dep_list)         
712
713    fulfillinglist = ravenlib.listutil.uniq(fulfillinglist)
714
715    return fulfillinglist
716
717
718    
719 def get_installedpackages_requiring(dep_list):
720    """
721    <Purpose>
722       Given a string list of dependencies, returns a string list of 
723       installed packages that require those package dependencies.
724       
725    <Arguments>
726       dep_list:
727          String list of package dependencies.
728
729    <Exceptions>
730       TypeError:
731          If given argument is not a list of strings.
732
733    <Side Effects>
734       None.
735
736    <Returns>
737       String list of installed packages that meet the given dependencies.
738    """
739    
740    # type checking on the given list
741    ravenlib.typecheck.stringlist(dep_list, "dep_list", "ravenlib.package.all.get_installedpackages_requiring")
742    
743    if len(dep_list) < 1:
744       return []
745
746    # a list that holds installed packages   
747    requiringlist = []
748
749    # initialize package managers
750    initialize()
751
752    # go through every possible package manager
753    for packagemanager in inited_packmanager_list:      
754       # for each manager, get a list of installed packages
755       requiringlist += packagemanager[1].get_installedpackages_requiring(dep_list)  
756
757    requiringlist = ravenlib.listutil.uniq(requiringlist)
758
759    return requiringlist
760
761
762    
763 def get_installedpackages():
764    """
765    <Purpose>
766       Return a list of all installed packages.
767       
768    <Arguments>
769
770    <Exceptions>
771
772    <Side Effects>
773       None.
774
775    <Returns>
776       String list of installed packages.
777    """
778    
779    # a list that holds installed packages   
780    installedlist = []
781
782    # initialize package managers
783    initialize()
784
785    # go through every possible package manager
786    for packagemanager in inited_packmanager_list:      
787       # for each manager, get a list of installed packages
788       installedlist += packagemanager[1].get_installedpackages()         
789
790    installedlist = ravenlib.listutil.uniq(installedlist)
791       
792    return installedlist
793
794
795
796 def get_installedpackages_provide(package_list):
797    """
798    <Purpose>
799       Given a string list of installed package names, returns a string 
800       list of all dependencies fulfilled by those packages.
801
802    <Arguments>
803       package_list:
804          String list of installed package names.
805
806    <Exceptions>
807       TypeError:
808          If given argument is not a list of strings.
809
810       IOError: 
811          If asked to report on a package that is not installed.
812
813    <Side Effects>
814       None.
815
816    <Returns>
817       String list of all dependencies fulfilled by the given packages.
818    """
819
820    # type checking on the given list
821    ravenlib.typecheck.stringlist(package_list, "package_list", "ravenlib.package.all.get_installedpackages_provide")
822
823    if len(package_list) < 1:
824       return []
825
826    # a list that holds all dependencies
827    providelist = []
828
829    # initialize package managers
830    initialize()
831
832    # go thought every possible package manager
833    for packagemanager in inited_packmanager_list: 
834       # for each manager, get a list of all dependencies
835       try:
836          providelist += packagemanager[1].get_installedpackages_provide(package_list)
837
838       # If asked to report on a package that is not installed.
839       except IOError, (errno,strerr):
840          ravenlib.report.warning("get_installedpackages_provide: I/O error(" + str(errno) + "): " + str(strerror))
841          # return the list we got so far
842          return providelist
843
844    providelist = ravenlib.listutil.uniq(providelist)
845
846    return providelist
847
848
849
850
851 def get_installedpackages_requires(package_list):
852    """
853    <Purpose>
854       Given a string list of installed package names, returns a string 
855       list of all dependencies required by those packages.
856
857    <Arguments>
858       package_list:
859          String list of installed package names.
860
861    <Exceptions>
862       TypeError:
863          If given argument is not a list of strings.
864
865       IOError: 
866          If asked to report on a package that is not installed.
867
868    <Side Effects>
869       None.
870
871    <Returns>
872       String list of all dependencies fulfilled by the given packages.
873    """
874
875    # type checking on the given list
876    ravenlib.typecheck.stringlist(package_list, "package_list", "ravenlib.package.all.get_installedpackages_provide")
877
878    if len(package_list) < 1:
879       return []
880
881    # a list that holds all dependencies
882    providelist = []
883
884    # initialize package managers
885    initialize()
886
887    # go thought every possible package manager
888    for packagemanager in inited_packmanager_list: 
889       # for each manager, get a list of all dependencies
890       try:
891          providelist += packagemanager[1].get_installedpackages_requires(package_list)
892
893       # If asked to report on a package that is not installed.
894       except IOError, (errno,strerr):
895          ravenlib.report.warning("get_installedpackages_provide: I/O error(" + str(errno) + "): " + str(strerror))
896          # return the list we got so far
897          return providelist
898
899    providelist = ravenlib.listutil.uniq(providelist)
900
901    return providelist
902
903
904
905
906    
907 def execute(trans_list):
908    """
909    <Purpose>
910       Installs packages with the given filenames.
911
912    <Arguments>
913       filename_list:
914          String list of filenames representing packages to install.
915
916    <Exceptions>
917       TypeError:
918          If given argument is not a list of strings.
919
920    <Side Effects>
921       None.
922
923    <Returns>
924       None.
925    """
926    # check params
927    ravenlib.typecheck.simple(trans_list, "trans_list", list, "ravenlib.package.all.install")
928
929    if (len(trans_list) < 1):
930       # Error?
931       return
932
933    transaction.tl_clear_status(trans_list)
934
935    # initialize package managers
936    initialize()
937
938    # Step 1: Go through the transaction list and figure out which package
939    #   manager can handle each transaction entry. Put the package manager
940    #   in trans["packagemanager"]
941
942    for trans in trans_list:
943       check_flag = False
944
945       if (trans["op"] == transaction.REMOVE):
946           for packagemanager in inited_packmanager_list:
947               installed_versions = packagemanager[1].get_installed_versions([trans["packname"]])
948               if len(installed_versions)>0:
949                   trans["packagemanager"] = packagemanager[1]
950                   check_flag = True
951                   # let's move to the next file
952                   break
953
954           # if there is no manager that thinks the package is installed
955           if not check_flag:
956              transaction.trans_set_status(trans, "failure", "not installed")
957              raise StorkNotInstalledException(trans)
958       else:
959           for packagemanager in inited_packmanager_list:
960               if (packagemanager[1].is_package_understood(trans['filename'])):
961                   trans["packagemanager"] = packagemanager[1]
962                   check_flag = True
963                   # let's move to the next file
964                   break
965
966           # if there is no manager that understands given file
967           if not check_flag:
968              transaction.trans_set_status(trans, "failure", "unknown package type")
969              raise StorkUnknownPackageTypeException(trans)
970
971    # Step 2: Build a per-package-manager list of transactions that the
972    #   packagemanager can handle. The list consisting of grouped filenames
973    #   with a supporting package manager
974    #         [(transaction_list, package module),...]
975
976    final_valid_pack_list = []
977
978    for packagemanager in inited_packmanager_list:
979       # a temp list which holds filenames that can be installed by given package manager
980       temp_trans_list = []
981
982       for trans in trans_list:
983          if (trans["packagemanager"] == packagemanager[1]):
984             # if this transaction belongs to the package manager we are building
985             # the list for, then add it to the list
986             temp_trans_list.append(trans)
987
988       # append the tuple that holds installable filename and its package manager to the final list
989       final_valid_pack_list.append((temp_trans_list, packagemanager[1]))
990
991    # Step 3: Execute each set of transactions on the appropriate manager
992
993    for item in final_valid_pack_list:
994       (this_trans_list, this_packman) = item
995
996       if (this_trans_list == []):
997          continue
998
999       this_packman.execute(this_trans_list)
1000
1001
1002
1003 def reset():
1004    global possible_packmanagers
1005    global glo_packagemanagers_provide
1006    global inited_packmanager_list
1007
1008    possible_packmanagers = None
1009    glo_packagemanagers_provide = []
1010    inited_packmanager_list = []