Howdy - my name's Arjun Roy, and this is my first time posting on this list.
I'm an undergraduate cs student, and am interested in yum (among various
other things).
Alright, with that out of the way, I'd like to submit a small plugin for
yum. It's a tiny
history plugin - it serializes the record of a yum transaction after the
transaction completes,
and can pretty print the record on demand.
For example:
yum install AllegroOgg --note 'installed allegroOgg'
This will install allegroogg and the dependencies, and save the
results to an xml file in a place
specified by the plugin conf file (default /var/lib/yum/history/).
yum history --date=Jan-26-2008
This will tell you what happened on the 26th of Jan 2008. So if we
installed AllegroOgg on that day,
it will tell you.
My coding style might be a bit out of whack, I might have done some
kooky things to get it to work, (though
I like to think that I didn't) so feel free to flame it / critique it /
submit feature requests. Hope it is useful to
somebody.
On an unrelated note, conduit.registerCommand needs more documentation.
INSTALLATION INSTRUCTIONS:
I was too lazy to make a script, but it is easy.
1. Copy history.conf to /etc/yum/pluginconf.d
2. Copy history.py to /usr/lib/yum/yum-plugins
3. mkdir /var/lib/yum/history
Make sure the directory in step3 matches the directory specified in
history.conf, and
also make sure the directory specified in history.conf ends with a '/'.
Thanks,
-Arjun Roy
[main]
enabled = 1
history_directory = /var/lib/yum/history/
'''
Yum History Plugin
Author: Arjun Roy
Date: 1/11/2008
License: GPL2
A simple history plugin that serializes relevant transaction data from yum to xml,
and pretty prints it on command.
TODO:
1. Find user initiating action, so we can store that too.
2. Write prettier printer for history viewer
3. Cleanup
'''
from yum.plugins import PluginYumExit, TYPE_CORE, TYPE_INTERACTIVE
from yum.transactioninfo import TransactionMember
from xml.dom.minidom import Document, parse
import sys
from ConfigParser import ConfigParser
requires_api_version = '2.6'
plugin_type = (TYPE_CORE, TYPE_INTERACTIVE)
#Is there a better way to do this, without depending on yum-cli? Enlighten me...
sys.path.insert(0, '/usr/share/yum-cli')
from yumcommands import YumCommand
class HistoryCommand(YumCommand):
def getNames(self):
return ['history']
def getUsage(self):
return 'yum history --date=16-Jan-2008'
def doCommand(self, base, basecmd, extcmds):
'''
Check if a history date was specified. If so, and if format is correct, print it.
'''
import re
date_regex = re.compile('\d\d-\D\D\D-\d\d\d\d') #16-Jan-2008
opts, command = base.optparser.parse_args()
if opts.date:
if not date_regex.match(opts.date):
print "Required date format for history is dd-mon-yyyy eg. 16-Jan-2008"
else:
try:
conf_parser = ConfigParser()
conf_parser.read('/etc/yum/pluginconf.d/history.conf')
history_directory = conf_parser.get('main', 'history_directory')
except (NoSectionError, NoOptionError, IOError):
history_directory = '/var/lib/yum/history/'
printHistory(date=opts.date, history_directory=history_directory)
return 0, ['history command has executed.']
#Here we register command line options for our plugin, and the history command itself
#'yum history --date 02/19/2007'
#'yum install AllegroOGG --note 'installed allegroOgg''
def config_hook(conduit):
'''
Register command line options.
'''
parser = conduit.getOptParser()
parser.add_option('--date', dest='date', help='Specify date in dd-mon-yyyy format to get history information.')
parser.add_option('-n', '--note', dest='notes', help='Specify some notes describing this yum transaction.')
conduit.registerCommand(HistoryCommand())
def printHistory(date, history_directory):
'''
Pretty Print what happened on date. Looks in history directory as specified.
'''
path = history_directory + date
try:
xml = parse(path)
root = xml.childNodes[0]
for pkg in root.childNodes:
#The following are guaranteed to be here
action = pkg.getElementsByTagName('action')[0].getAttribute('value')
name = pkg.getElementsByTagName('name')[0].getAttribute('value')
arch = pkg.getElementsByTagName('arch')[0].getAttribute('value')
epoch = pkg.getElementsByTagName('epoch')[0].getAttribute('value')
version = pkg.getElementsByTagName('version')[0].getAttribute('value')
release = pkg.getElementsByTagName('release')[0].getAttribute('value')
time = pkg.getElementsByTagName('time')[0].getAttribute('value')
repo = pkg.getElementsByTagName('repo')[0].getAttribute('value')
#There might be 0 or more dependency reasons
reasons = pkg.getElementsByTagName('reason')
#These might or might not be specified
user = pkg.getElementsByTagName('user') #TO BE IMPLEMENTED
notes = pkg.getElementsByTagName('notes')
print 'Package ' + name + ' was ' + action + ' at ' + time
print '\tDetails: '
print '\tName: ' + name
print '\tArch: ' + arch
print '\tEpoch: ' + epoch
print '\tVersion: ' + version
print '\tRelease: ' + release
print '\tTime: ' + time
print '\tRepo: ' + repo
for reason in reasons:
print '\tbecause it ' + reason.getAttribute('relationType') + ' ' + reason.getAttribute('relatedTo')
if(len(user) != 0):
print '\tUser: ' + user[0].getAttribute('value')
if(len(notes) != 0):
print '\tNotes: ' + notes[0].getAttribute('value')
except IOError:
print "No history data available for " + date
#Here we add the dependencies to the transaction history
def posttrans_hook(conduit):
'''
This hook executes after a successful transaction.
We already have the user requested packages in tsInfo,
awaiting the following information to be added:
1. The current system time
2. The installing user
3. A short, optional logfile that the user is asked to supply at runtime
'''
from time import localtime, strftime
completion_time = strftime("%d-%b-%Y,%H:%M:%S", localtime())
tsInfo = conduit.getTsInfo()
opts, commands = conduit.getCmdLine()
notes = opts.notes
#Create list of lists containing transaction archive objects
history = map(lambda (x, y): createTransactionArchive(x, y, completion_time, notes),
((tsInfo.removed, 'removed'),
(tsInfo.depremoved, 'depremoved'),
(tsInfo.installed, 'installed'),
(tsInfo.depinstalled, 'depinstalled'),
(tsInfo.updated, 'updated'),
(tsInfo.depupdated, 'depupdated'),
(tsInfo.obsoleted, 'obsoleted')
)
)
#Flatten list
history = reduce(lambda x, y: x + y, history)
history_directory = conduit.confString('main', 'history_directory',
default='/var/lib/yum/history/')
#Write to file with name specified by date
writeXML(date=completion_time.split(',')[0],
history=history,
history_directory=history_directory)
def createTransactionArchive(transactionList, action, time, notes):
'''
Creates a list of transaction archive objects for a
transaction with a given action and time.
'''
transactions = []
for pkg in transactionList:
naevr = (pkg.name, pkg.arch, pkg.epoch, pkg.version, pkg.release)
archive = TransactionArchive(action=action,
naevr=naevr,
reason=[],
repo=getattr(pkg.po, 'repoid'),
notes=notes)
archive.time = time
archive.reason = pkg.relatedto
transactions.append(archive)
return transactions
def writeToFile(date, history, history_directory):
'''
DEPRECATED
Writes the transaction record to file for future access.
'''
path = history_directory + date
file = open(path, 'a')
for pkg in history:
file.write('!\n')
file.write('name='+pkg.pkg_naevr[0])
file.write('\narch='+pkg.pkg_naevr[1])
file.write('\nepoch='+pkg.pkg_naevr[2])
file.write('\nversion='+pkg.pkg_naevr[3])
file.write('\nrelease='+pkg.pkg_naevr[4])
file.write('\naction='+pkg.action)
file.write('\ntime='+pkg.time.__str__())
for reason in pkg.reason:
file.write('\n'+reason.__str__())
file.write('\n#\n')
file.close()
def writeXML(date, history, history_directory):
'''
Serializes the transaction record to xml for future access.
'''
path = history_directory + date
try:
xml = parse(path)
root = xml.childNodes[0]
except IOError:
xml = Document()
root = xml.createElement('root')
for pkg in history:
pkg_xml, pkg_root = pkg.toXML()
root.appendChild(pkg_root)
file = open(path, 'w')
file.write(root.toxml())
file.close()
class TransactionArchive:
'''
Wrapper class for package info for yum history plugin.
'''
def __init__(self,
action, #(u)pgrade, (i)nstall, (e)rase
naevr, #Name, Arch, Epoch, Version, Release
reason=[], #removed, depremoved, installed,
#depinstalled, updated, depupdated, obsoleted
time=None, #date-month-year,hour:minute:second
user=None, #The installer
notes=None,
repo='unknown'): #User supplied Notes
self.action = action
self.pkg_naevr = naevr
self.reason = reason
self.time = time
self.user = user
self.notes = notes
self.repo = repo
def toXML(self):
'''
Return an XML representation of this TransactionArchive
'''
xml = Document()
root = xml.createElement('TransactionArchive')
#Set action
action = xml.createElement('action')
action.setAttribute('value', self.action)
root.appendChild(action)
#Set package naevr
n = xml.createElement('name')
n.setAttribute('value', self.pkg_naevr[0])
root.appendChild(n)
a = xml.createElement('arch')
a.setAttribute('value', self.pkg_naevr[1])
root.appendChild(a)
e = xml.createElement('epoch')
e.setAttribute('value', self.pkg_naevr[2])
root.appendChild(e)
v = xml.createElement('version')
v.setAttribute('value', self.pkg_naevr[3])
root.appendChild(v)
r = xml.createElement('release')
r.setAttribute('value', self.pkg_naevr[4])
root.appendChild(r)
#Set time
time = xml.createElement('time')
time.setAttribute('value', self.time)
root.appendChild(time)
#Set repo
repo = xml.createElement('repo')
repo.setAttribute('value', self.repo)
root.appendChild(repo)
#If we tracked it, set user
if self.user != None:
user = xml.createElement('user')
user.setAttribute('value', self.user)
root.appendChild(user)
#If we wrote one, set note
if self.notes != None:
notes = xml.createElement('notes')
notes.setAttribute('value', self.notes)
root.appendChild(notes)
#Set reasons for transaction
for reason in self.reason:
reason_node = xml.createElement('reason')
reason_node.setAttribute('relatedTo', ';'.join(reason[0]))
reason_node.setAttribute('relationType', reason[1])
root.appendChild(reason_node)
#Return what we have done
return (xml, root)
_______________________________________________
Yum-devel mailing list
[email protected]
https://lists.dulug.duke.edu/mailman/listinfo/yum-devel