[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: [proposal] Documenting which revision is installed



On Wed, Dec 26, 2007 at 11:53:25PM +0100, Adeodato Sim?? wrote:
> *Personally*, I like the idea of Javier Fern??ndez-Sanguino expressed in
> the mail linked above of keeping debian_version as is, and introducing
> /etc/lsb-release with detailed information like:
>   DISTRIB_ID=Debian
>   DISTRIB_RELEASE=4.0
>   DISTRIB_CODENAME=etch
>   DISTRIB_DESCRIPTION="Debian GNU/Linux 4.0 'etch'"

The problem with base-files providing /etc/debian_version is that it means
/etc/debian_version can really only tell you what version of base-files
is installed. So if you upgrade every other package but base-files from
4.0r1 to 4.0r2, you have all the functionality of 4.0r2 but get reported as
4.0r1, and if you just upgrade base-files, you get reported as 4.0r2 while
still having the bugs from 4.0r1 that were meant to have been fixed.

Maybe it would be better to have aptitude or similar actually analyse
what's installed compared to the available Packages/Release files,
and generate a /etc/debian_version or /etc/lsb-release file based on
that. It wouldn't require any additional downloads -- it'd just be a matter
of:

	- looking at /var/lib/apt/lists/*_Release to see what the latest
	  available revision is

	- looking at /var/lib/dpkg/status and /var/lib/apt/lists/*_Packages
	  to see if the packages on this system are actually updated to
	  that revision
	  
	- based on the above updating /etc/debian_version to reflect the
	  version of Debian actually installed

And that way the only things needed for getting the right version would
be (the equivalent of):

	# update Release file
	apt-get update
	apt-get dist-upgrade
	aptitude debian-version-update

with no need to worry about multiple branches of base-files or whatever,
or worrying what apt pinning says the admin's intending to install
or similar.

I'm at a bit of a loss as to how you'd do it properly (using python-apt
or something, say), but a proof of concept is attached. Example output:

$ ./debian_version.py 
Packages seem to be from Debian release lenny
     91.7% HIT RATE (1325 of 1445)
     95.5% of installed packages available
      2.2% of matching packages could be upgraded
      1.8% of matching packages are more recent

$ ./debian_version.py 
Packages seem to be from Debian release 4.0r1
     87.3% HIT RATE (517 of 592)
     97.3% of installed packages available
      0.0% of matching packages could be upgraded
     10.2% of matching packages are more recent

$ ./debian_version.py iterate
Packages seem to be from Debian release 4.0r1
     87.3% HIT RATE (517 of 592)
     97.3% of installed packages available
      0.0% of matching packages could be upgraded
     10.2% of matching packages are more recent

Remaining packages seem to be from Debian release 4.0-updates
     61.3% HIT RATE (46 of 75)
     68.0% of installed packages available
      9.8% of matching packages could be upgraded
      0.0% of matching packages are more recent

Remaining packages seem to be from Debian-Security release etch
     10.3% HIT RATE (3 of 29)
     24.1% of installed packages available
     14.3% of matching packages could be upgraded
     42.9% of matching packages are more recent

Remaining packages:
    bzr: 0.15-1
    ...

Cheers,
aj

#!/usr/bin/env python

# Copyright 2007 Anthony Towns
# GPL v2 or later.

import apt_pkg, sys, os

apt_pkg.init()

def get_installed():
    vs = {}
    i = open("/var/lib/dpkg/status", "r")
    p,v,x = None,None,None
    for l in i.xreadlines():
	l = l.rstrip()
	if l.startswith("Package: "): p = l[l.find(" ")+1:]
	if l.startswith("Version: "): v = l[l.find(" ")+1:]
	if l.startswith("Status: "):
	    x = l.split(" ")[1:]
	if l == "":
	    if x and x[0] != "hold" and x[1] == "ok" and x[2] == "installed":
		vs[p] = v
	    p,v,x = None,None,None
    i.close()
    return vs

vlal = "/var/lib/apt/lists"
arch = "".join(os.popen("dpkg --print-architecture", "r").readlines()).rstrip()

def releases():
    urs = {}
    for path in os.listdir(vlal):
        if not path.endswith("_Release.gpg"): continue
        path = path[:-4]

	o,l,s,v,c = [None] * 5
	for l in open(os.path.join(vlal,path)).xreadlines():
	    l = l.rstrip()
	    if l.startswith("Origin: "):   o = l[l.find(" ")+1:]
	    if l.startswith("Label: "):    L = l[l.find(" ")+1:]
	    if l.startswith("Suite: "):    s = l[l.find(" ")+1:]
	    if l.startswith("Version: "):  v = l[l.find(" ")+1:]
	    if l.startswith("Codename: "): c = l[l.find(" ")+1:]

	vendor = filter(None, [L,o,"unknown"])[0]
	version = filter(None, [v,c,s,"unknown"])[0]

	pkgs = [x for x in os.listdir(vlal) 
			if x.startswith(path[:-7])
			    and x.endswith("_binary-%s_Packages" % arch) ]

	k = (vendor,version)
	if k not in urs: urs[k] = ([],[])
	urs[k][0].append( path )
	urs[k][1].extend( pkgs )

    res = []
    for k,v in urs.iteritems():
	(vendor, version) = k
        (paths, pkgs) = v
	res.append( (vendor, version, paths, readlist(pkgs)) )

    return res

def readlist(files):
    vs = {}
    for file in files:
        i = open(os.path.join(vlal, file), "r")
        p,v = None,None
        for l in i.xreadlines():
  	    l = l.rstrip()
	    if l.startswith("Package: "): p = l[l.find(" ")+1:]
	    if l.startswith("Version: "): v = l[l.find(" ")+1:]
	    if l == "":
	        vs[p] = v
	        p,v = None,None
        i.close()
    return vs.items()

def compare(installed, list):
    same, new, old = 0,0,0
    tryagain = dict(installed)
    for (p, tv) in list:
        if p in installed:
            if tv == installed[p]:
	        same += 1
		del tryagain[p]
	    else:
                x = apt_pkg.VersionCompare(installed[p], tv)
	        if x > 0:
		    old += 1
	        elif x < 0:
		    new += 1
	        else:
		    same += 1
		    del tryagain[p]
    return same,new,old,tryagain

installed = get_installed()
release_pkgs = releases()

iterate = 0
if len(sys.argv) > 1 and sys.argv[1] == "iterate":
    iterate = 1

intro = "Packages"
while installed:
    best = 0,len(installed),0,"-","-","-",[]
    for vendor, version, path, packages in release_pkgs:
        same,new,old,tryagain = compare(installed, packages)
        if same > best[0]:
	    best = same,new,old,vendor,version,path,tryagain

    same,new,old,vendor,version,path,tryagain = best
    if same == 0: break

    total = same+new+old
    inst = len(installed)

    print "%s seem to be from %s release %s" % (intro, vendor, version)
    print "    %5.1f%% HIT RATE (%d of %d)" % (100.0*same/inst, same, inst)
    print "    %5.1f%% of installed packages available" % (100.0*total/inst)
    print "    %5.1f%% of matching packages could be upgraded" % (100.0*new/total)
    print "    %5.1f%% of matching packages are more recent" % (100.0*old/total)

    if iterate:
        installed = tryagain
        intro = "\nRemaining packages"
    else:
        installed = []

if installed and iterate:
    print "\nRemaining packages:"
    for p,v in installed.iteritems():
        print "    %s: %s" % (p,v)

Attachment: signature.asc
Description: Digital signature


Reply to: