dpkgcheck - Überprüfe installierte Debian-Pakete
Winfried Mueller, www.reintechnisch.de, Start: 07.02.2007, Stand: 09.02.2007
Die Idee für dieses Werkzeug kam mir bei der Aufgabe, mehrere Debian oder Ubuntu Linux-Server paketmäßig synchron zu halten. Alles, was also auf einem Master-Server installiert wird, soll später auch auf mehreren anderen Servern genauso installiert sein.
Mit dem Werkzeug dpkgcheck kann man hierfür einen Schnappschuss der installierten Pakete des Mastersystems erzeugen. Dieser Schnappschuss kann in einer Datei gespeichert werden.
Das zu synchronisierende System kann nun mit diesem Schnappschuss verglichen werden. Hierbei wird angezeigt, welche Pakete auf diesem System fehlen und welche ein Update erfahren müssen.
Um sich die Arbeit noch zu erleichtern, kann das Ausgabeformat so eingestellt werden, dass direkt ein Bash-Skript ausgegeben wird, in dem die entsprechenden "apt-get install ..." Anweisungen drin stehen.
Gleichzeitig ist dpkgcheck aber auch universell gehalten, so dass es für eine Reihe weiterer Anwendungsfälle nutzbar sein wird. So kann man z.B. gelegentlich überprüfen, welche Pakete seit einem bestimmten Snapshot nachinstalliert wurden. Oder welche Pakete in der Zwischenzeit alle aktualisiert worden sind. Es ist damit ein Werkzeug, mit welchem man Paketveränderungen auf Systemen dokumentieren oder überwachen kann.
Einsetzbar ist das Werkzeug natürlich nur auf Systemen, die dpkg als zentrales Paketmanagment nutzen.
Das Werkzeug sollte sofort auf jedem Ruby 1.8.x System lauffähig sein. Es braucht keine speziellen Bibliotheken. Am besten spielt man mal mit den unter --help aufgeführten Beispielen, dann wird schnell klar, was man mit machen kann.
Übrigens: Genau sowas brauche ich nun auch noch für das Gem-Paketsystem von Ruby. Auch hier ist es nervig, wenn auf einem Master-System alles läuft, auf einem Zielsystem aber Ruby-Skripte nicht funktionieren und man mühsam schrittweise herausfinden muss, was denn nun alles nachinstalliert werden muss.
Quellcode
#!/usr/bin/ruby1.8
# dpkgcheck - Überprüfe/Erstelle dpkg-Liste
# Winfried Mueller, www.reintechnisch.de, (c) 2007, GPL
# Start: 08.02.2007, Stand: 09.02.2007
#
# Generate List
# dpkgcheck -l > myserver.lst
#
# Check List
# dpkgcheck -c myserver.lst
#
DEBUG = true
Version = "2007/02/09"
require 'optparse'
require 'ostruct'
def makelist()
getpackages.each do |package|
case $options.format
when "p"
puts package['Name']
when "pv", nil
puts package['Name'] + " " + package['Version']
when "inst"
puts "apt-get install #{package['Name']}"
else
puts "Undefined List-Format."
end
end
end
def getpackages()
l = `export COLUMNS=200;dpkg -l`
al = l.split( "\n" )
#Header löschen
l = false
10.times do
d = al.shift
if d =~ /^\+\+\+/
l = true
break
end
end
raise "Kann Anfang von dpkg -l nicht finden." unless l
packages = []
al.each do |package|
if package =~ /^(ii|hi)\s+(.*?)\s+(.*?)\s+/
packages << { 'Name' => $2, 'Version' => $3 }
end
end
packages
end
def checklist( list )
line_nr = 1
packages = getpackages
c_packages = Hash.new
packages.each do |package|
c_packages[package['Name']] = package['Version']
end
f_packages = Hash.new
File.foreach( list ) do |line|
package, version = line.split( /\s+/ )
raise "Error in list-Format line: #{line_nr}" if !package || !version
case $options.format
when "all", nil
if !c_packages[package]
puts "#{package}: Install"
else
if c_packages[package] != version
puts "#{package}: Update: #{version} => #{c_packages[package]}"
end
end
when "apt"
if !c_packages[package]
puts "apt-get install #{package}"
end
when "install"
if !c_packages[package]
puts "#{package}: Install"
end
when "update"
if (cv = c_packages[package])
if cv != version
puts "#{package}: Update: #{version} => #{c_packages[package]}"
end
end
end
f_packages[package] = version
line_nr += 1
end
packages.each do |package|
case $options.format
when "all", nil, "remove"
if !f_packages[package['Name']]
puts "#{package['Name']}: Remove"
end
when "apt"
if !f_packages[package['Name']]
puts "apt-get remove #{package['Name']}"
end
end
end
end
# ----------------------------------------------------------------
ARGV.options do |o|
o.banner = "Usage: dpkgcheck [options]"
o.separator( "dpkgcheck - Generate and Compare dpkg-list" )
o.separator( "" )
o.on( "-l", "--mklist [FORMAT]", "Make a dpkg-list to stdout. [p|pv|apt]" ) do |fmt|
$options.mklist = true
$options.format = fmt
end
o.on( "-f", "--format FORMAT", "Output Format for -c [all|apt|install|update|remove]" ) do |fmt|
$options.format = fmt
end
o.on( "-c", "--check FILELIST", "Compare installed package with dpkg-list." ) do |f|
$options.check = f
end
o.on( "-v", "--verbose", "All Check results (default only different Files)." ) do
$options.verbose = true
end
o.on_tail("-h", "--help", "This help." ) do
puts o
puts( "" )
puts( "Examples:" )
puts( " dpkgcheck -l > myserver-2007-02-07.lst # make dpkg-list snapshot" )
puts( " dpkgcheck -l p # list all installed packages" )
puts( " dpkgcheck -l apt # generate apt-get install list" )
puts( " dpkgcheck -c myserver-2007-02-07.lst # View changes from snapshot" )
puts( " dpkgcheck -f apt -c myserver-2007-02-07.lst # Make a package install script" )
puts( " dpkgcheck -f install -c myserver-2007-02-07.lst # View only packages to install" )
puts( " dpkgcheck -f update -c myserver-2007-02-07.lst # View only packages to update" )
puts( "" )
exit
end
o.on_tail("--version", "Show version") do
puts o.ver
puts "Written by Winfried Mueller, www.reintechnisch.de"
puts ""
puts "Copyright (C) 2007 Winfried Mueller"
puts "This is free software; see the source for copying conditions."
puts "There is NO warranty; not even for MERCHANTABILITY or"
puts "FITNESS FOR A PARTICULAR PURPOSE."
exit
end
end
# ----------------------------------------------------------------
begin
$options = OpenStruct.new
$options.verbose = false
err_code = 0
ARGV.parse!
if $options.mklist != nil
makelist( )
elsif $options.check != nil
checklist( $options.check )
else
STDERR.puts ARGV.options.to_s
end
rescue SystemExit
rescue Exception => exc
STDERR.puts "E: #{exc.message}"
#STDERR.puts ARGV.options.to_s
raise if DEBUG
end
exit err_code