~cprov/britney/boottesting-arch-indep

« back to all changes in this revision

Viewing changes to britney.py

  • Committer: Celso Providelo
  • Date: 2015-01-19 19:16:46 UTC
  • Revision ID: celso.providelo@canonical.com-20150119191646-ovtr36plly0p50z9
Refactoring the existing test (autopkgtest), so its features can be re-used for other criterias (boottest) tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 
29
29
Britney's source code is split between two different but related tasks:
30
30
the first one is the generation of the update excuses, while the
31
 
second tries to update testing with the valid candidates; first 
 
31
second tries to update testing with the valid candidates; first
32
32
each package alone, then larger and even larger sets of packages
33
33
together. Each try is accepted if testing is not more uninstallable
34
34
after the update than before.
53
53
  * BugsV, which contains the list of release-critical bugs for a given
54
54
    version of a source or binary package (see Britney.read_bugs).
55
55
 
56
 
  * Dates, which contains the date of the upload of a given version 
 
56
  * Dates, which contains the date of the upload of a given version
57
57
    of a source package (see Britney.read_dates).
58
58
 
59
59
  * Urgencies, which contains the urgency of the upload of a given
72
72
= Excuses =
73
73
 
74
74
An excuse is a detailed explanation of why a package can or cannot
75
 
be updated in the testing distribution from a newer package in 
 
75
be updated in the testing distribution from a newer package in
76
76
another distribution (like for example unstable). The main purpose
77
 
of the excuses is to be written in an HTML file which will be 
 
77
of the excuses is to be written in an HTML file which will be
78
78
published over HTTP. The maintainers will be able to parse it manually
79
79
or automatically to find the explanation of why their packages have
80
80
been updated or not.
232
232
 
233
233
class Britney(object):
234
234
    """Britney, the Debian testing updater script
235
 
    
 
235
 
236
236
    This is the script that updates the testing distribution. It is executed
237
 
    each day after the installation of the updated packages. It generates the 
 
237
    each day after the installation of the updated packages. It generates the
238
238
    `Packages' files for the testing distribution, but it does so in an
239
239
    intelligent manner; it tries to avoid any inconsistency and to use only
240
240
    non-buggy packages.
384
384
        parser.add_option("", "--series", action="store", dest="series", default=None,
385
385
                               help="set distribution series name")
386
386
        (self.options, self.args) = parser.parse_args()
387
 
        
 
387
 
388
388
        # integrity checks
389
389
        if self.options.nuninst_cache and self.options.print_uninst:
390
390
            self.__log("nuninst_cache and print_uninst are mutually exclusive!", type="E")
425
425
 
426
426
    def __log(self, msg, type="I"):
427
427
        """Print info messages according to verbosity level
428
 
        
 
428
 
429
429
        An easy-and-simple log method which prints messages to the standard
430
430
        output. The type parameter controls the urgency of the message, and
431
431
        can be equal to `I' for `Information', `W' for `Warning' and `E' for
528
528
 
529
529
    def read_sources(self, basedir, intern=intern):
530
530
        """Read the list of source packages from the specified directory
531
 
        
 
531
 
532
532
        The source packages are read from the `Sources' file within the
533
533
        directory specified as `basedir' parameter. Considering the
534
534
        large amount of memory needed, not all the fields are loaded
567
567
 
568
568
    def read_binaries(self, basedir, distribution, arch, intern=intern):
569
569
        """Read the list of binary packages from the specified directory
570
 
        
 
570
 
571
571
        The binary packages are read from the `Packages_${arch}' files
572
572
        within the directory specified as `basedir' parameter, replacing
573
573
        ${arch} with the value of the arch parameter. Considering the
574
574
        large amount of memory needed, not all the fields are loaded
575
575
        in memory. The available fields are Version, Source, Multi-Arch,
576
576
        Depends, Conflicts, Provides and Architecture.
577
 
        
 
577
 
578
578
        After reading the packages, reverse dependencies are computed
579
579
        and saved in the `rdepends' keys, and the `Provides' field is
580
580
        used to populate the virtual packages list.
756
756
 
757
757
    def read_bugs(self, basedir):
758
758
        """Read the release critial bug summary from the specified directory
759
 
        
 
759
 
760
760
        The RC bug summaries are read from the `BugsV' file within the
761
761
        directory specified in the `basedir' parameter. The file contains
762
762
        rows with the format:
784
784
 
785
785
    def __maxver(self, pkg, dist):
786
786
        """Return the maximum version for a given package name
787
 
        
 
787
 
788
788
        This method returns None if the specified source package
789
789
        is not available in the `dist' distribution. If the package
790
790
        exists, then it returns the maximum version between the
802
802
 
803
803
    def normalize_bugs(self):
804
804
        """Normalize the release critical bug summaries for testing and unstable
805
 
        
 
805
 
806
806
        The method doesn't return any value: it directly modifies the
807
807
        object attribute `bugs'.
808
808
        """
828
828
 
829
829
    def read_dates(self, basedir):
830
830
        """Read the upload date for the packages from the specified directory
831
 
        
 
831
 
832
832
        The upload dates are read from the `Dates' file within the directory
833
833
        specified as `basedir' parameter. The file contains rows with the
834
834
        format:
874
874
 
875
875
    def read_urgencies(self, basedir):
876
876
        """Read the upload urgency of the packages from the specified directory
877
 
        
 
877
 
878
878
        The upload urgencies are read from the `Urgency' file within the
879
879
        directory specified as `basedir' parameter. The file contains rows
880
880
        with the format:
922
922
 
923
923
    def read_hints(self, basedir):
924
924
        """Read the hint commands from the specified directory
925
 
        
 
925
 
926
926
        The hint commands are read from the files contained in the `Hints'
927
 
        directory within the directory specified as `basedir' parameter. 
 
927
        directory within the directory specified as `basedir' parameter.
928
928
        The names of the files have to be the same as the authorized users
929
929
        for the hints.
930
 
        
 
930
 
931
931
        The file contains rows with the format:
932
932
 
933
933
        <command> <package-name>[/<version>]
1146
1146
 
1147
1147
    def should_remove_source(self, pkg):
1148
1148
        """Check if a source package should be removed from testing
1149
 
        
 
1149
 
1150
1150
        This method checks if a source package should be removed from the
1151
1151
        testing distribution; this happens if the source package is not
1152
1152
        present in the unstable distribution anymore.
1187
1187
        This method checks if the binary packages produced by the source
1188
1188
        package on the given architecture should be upgraded; this can
1189
1189
        happen also if the migration is a binary-NMU for the given arch.
1190
 
       
 
1190
 
1191
1191
        It returns False if the given packages don't need to be upgraded,
1192
1192
        True otherwise. In the former case, a new excuse is appended to
1193
1193
        the object attribute excuses.
1205
1205
        excuse.set_vers(source_t[VERSION], source_t[VERSION])
1206
1206
        source_u[MAINTAINER] and excuse.set_maint(source_u[MAINTAINER].strip())
1207
1207
        source_u[SECTION] and excuse.set_section(source_u[SECTION].strip())
1208
 
        
 
1208
 
1209
1209
        # if there is a `remove' hint and the requested version is the same as the
1210
1210
        # version in testing, then stop here and return False
1211
1211
        # (as a side effect, a removal may generate such excuses for both the source
1335
1335
        """Check if source package should be upgraded
1336
1336
 
1337
1337
        This method checks if a source package should be upgraded. The analysis
1338
 
        is performed for the source package specified by the `src' parameter, 
 
1338
        is performed for the source package specified by the `src' parameter,
1339
1339
        for the distribution `suite'.
1340
 
       
 
1340
 
1341
1341
        It returns False if the given package doesn't need to be upgraded,
1342
1342
        True otherwise. In the former case, a new excuse is appended to
1343
1343
        the object attribute excuses.
1366
1366
        # the starting point is that we will update the candidate and run autopkgtests
1367
1367
        update_candidate = True
1368
1368
        run_autopkgtest = True
1369
 
        
 
1369
 
1370
1370
        # if the version in unstable is older, then stop here with a warning in the excuse and return False
1371
1371
        if source_t and apt_pkg.version_compare(source_u[VERSION], source_t[VERSION]) < 0:
1372
1372
            excuse.addhtml("ALERT: %s is newer in testing (%s %s)" % (src, source_t[VERSION], source_u[VERSION]))
1483
1483
            for arch in self.options.architectures:
1484
1484
                if src not in self.sources["testing"]:
1485
1485
                    continue
1486
 
                    
 
1486
 
1487
1487
                # if the package in testing has no binaries on this
1488
1488
                # architecture, it can't be out-of-date
1489
1489
                if not any(x for x in self.sources["testing"][src][BINARIES]
1490
1490
                           if x.endswith("/"+arch) and self.binaries["testing"][arch][0][x.split("/")[0]][ARCHITECTURE] != 'all'):
1491
1491
                    continue
1492
 
                    
 
1492
 
1493
1493
                # if the (t-)p-u package has produced any binaries on
1494
1494
                # this architecture then we assume it's ok. this allows for
1495
1495
                # uploads to (t-)p-u which intentionally drop binary
1619
1619
                        bugs_t.extend(self.bugs['testing'][spkg])
1620
1620
                    if spkg in self.bugs['unstable']:
1621
1621
                        bugs_u.extend(self.bugs['unstable'][spkg])
1622
 
 
 
1622
 
1623
1623
                new_bugs = sorted(set(bugs_u).difference(bugs_t))
1624
1624
                old_bugs = sorted(set(bugs_t).difference(bugs_u))
1625
1625
 
1720
1720
                    exclookup[x].addreason("depends")
1721
1721
                    exclookup[x].is_valid = False
1722
1722
            i = i + 1
1723
 
 
 
1723
 
1724
1724
    def write_excuses(self, same_source=same_source):
1725
1725
        """Produce and write the update excuses
1726
1726
 
2173
2173
                         suite != 'unstable' and \
2174
2174
                         binaries_t[parch][0][binary][ARCHITECTURE] == 'all':
2175
2175
                        continue
2176
 
                    else:    
 
2176
                    else:
2177
2177
                        rms.add((binary, version, parch))
2178
2178
 
2179
2179
        # single binary removal; used for clearing up after smooth
2623
2623
                    return None
2624
2624
                selected.append(x)
2625
2625
                upgrade_me.remove(x)
2626
 
        
 
2626
 
2627
2627
        self.output_write("start: %s\n" % self.eval_nuninst(nuninst_start))
2628
2628
        if not force:
2629
2629
            self.output_write("orig: %s\n" % self.eval_nuninst(nuninst_start))
2782
2782
            if len(removals) > 0:
2783
2783
                self.output_write("Removing obsolete source packages from testing (%d):\n" % (len(removals)))
2784
2784
                self.do_all(actions=removals)
2785
 
                                                                                                                                     
 
2785
 
2786
2786
        # smooth updates
2787
2787
        if self.options.smooth_updates:
2788
2788
            self.__log("> Removing old packages left in testing from smooth updates", type="I")
2977
2977
    def auto_hinter(self):
2978
2978
        """Auto-generate "easy" hints.
2979
2979
 
2980
 
        This method attempts to generate "easy" hints for sets of packages which    
 
2980
        This method attempts to generate "easy" hints for sets of packages which
2981
2981
        must migrate together. Beginning with a package which does not depend on
2982
2982
        any other package (in terms of excuses), a list of dependencies and
2983
2983
        reverse dependencies is recursively created.
3100
3100
 
3101
3101
    def main(self):
3102
3102
        """Main method
3103
 
        
 
3103
 
3104
3104
        This is the entry point for the class: it includes the list of calls
3105
3105
        for the member methods which will produce the output files.
3106
3106
        """