[yum-commits] Branch 'yum-3_2_X' - 29 commits - cli.py docs/yum.8 docs/yum.conf.5 output.py yumcommands.py yum/config.py yum/history.py yum/__init__.py yum/misc.py yum/packageSack.py yum/plugins.py yum/rpmsack.py yum/rpmtrans.py yum/sqlutils.py yum/transactioninfo.py
James Antill
james at osuosl.org
Mon Sep 21 16:09:05 UTC 2009
cli.py | 4
docs/yum.8 | 26 +-
docs/yum.conf.5 | 15 +
output.py | 324 +++++++++++++++++++++++++
yum/__init__.py | 119 +++++++++
yum/config.py | 2
yum/history.py | 622 +++++++++++++++++++++++++++++++++++++++++++++++++
yum/misc.py | 15 +
yum/packageSack.py | 2
yum/plugins.py | 4
yum/rpmsack.py | 5
yum/rpmtrans.py | 17 +
yum/sqlutils.py | 5
yum/transactioninfo.py | 3
yumcommands.py | 91 ++++++-
15 files changed, 1239 insertions(+), 15 deletions(-)
New commits:
commit 2d1f0712ce2b4604aec141f87ef9ec425ee77cf5
Author: James Antill <james at and.org>
Date: Mon Sep 21 11:18:14 2009 -0400
Add documentation for history, and version command
diff --git a/docs/yum.8 b/docs/yum.8
index 3520912..d0dfb79 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -67,6 +67,10 @@ gnome\-packagekit application\&.
.br
.I \fR * repolist [all|enabled|disabled]
.br
+.I \fR * version [all]
+.br
+.I \fR * history [info|list|summary|redo|undo|new]
+.br
.I \fR * help [command]
.br
.PP
@@ -225,6 +229,25 @@ Produces a list of configured repositories. The default is to list all
enabled repositories. If you pass \-v, for verbose mode, more information is
listed.
.IP
+.IP "\fBversion\fP"
+Produces a "version" of the rpmdb, and of the enabled repositories if "all" is
+given as the first argument. If you pass \-v, for verbose mode, more
+information is listed. The version is calculated by taking a sha1 hash of the
+packages (in sorted order), and the checksum_type/checksum_data entries from
+the yumdb. Note that this rpmdb version is now also used significantly within
+yum (esp. in yum history).
+.IP
+.IP "\fBhistory\fP"
+The history command allows the user to view what has happened in past
+transactions (assuming the history_record config. option is set). You can use
+info/list/summary to view what happend, undo/redo to act on that information
+and new to start a new history file.
+
+The info/list/summary commands take either a transactions id or a package (with
+wildcards, as in \fBSpecifying package names\fP), all three can also be passed
+no arguments. list can be passed the keyword "all" to list all the transactions.
+undo/redo just take a transaction id.
+.IP
.IP "\fBhelp\fP"
Produces help, either for all commands or if given a command name then the help
for that particular command\&.
@@ -266,7 +289,8 @@ Sets the maximum amount of time yum will wait before performing a command \- it
Tells yum to run entirely from cache - does not download or update any
headers unless it has to to perform the requested action.
.IP "\fB\-\-version\fP"
-Reports the \fByum\fP version number and exits.
+Reports the \fByum\fP version number and installed package versions for
+everything in history_record_packages (can be added to by plugins).
.IP "\fB\-\-showduplicates\fP"
Doesn't limit packages to their latest versions in the info, list and search
commands (will also affect plugins which use the doPackageLists() API).
diff --git a/docs/yum.conf.5 b/docs/yum.conf.5
index 69b601d..6295b1e 100644
--- a/docs/yum.conf.5
+++ b/docs/yum.conf.5
@@ -274,6 +274,21 @@ Path to the ssl client key yum should use to connect to repos/remote sites
Defaults to none.
.IP
+\fBhistory_record \fR
+Boolean - should yum record history entries for transactions. This takes some
+disk space, and some extra time in the transactions. But it allos how to know a
+lot of information about what has happened before, and display it to the user
+with the history info/list/summary commands. yum also provides the
+history undo/redo commands. Defaults to True.
+
+.IP
+\fBhistory_record_packages \fR
+This is a list of package names that should be recorded as having helped the
+transaction. yum plugins have an API to add themself to this, so it should not
+normally be necessary to add packages here. Not that this is also used for the
+packages to look for in --version. Defaults to rpm, yum, yum-metadata-parser.
+
+.IP
\fBcommands\fR
List of functional commands to run if no functional commands are specified
on the command line (eg. "update foo bar baz quux"). None of the short options
commit e166b64ddb9521826417e94e02a22e4f2fd3ec6a
Author: James Antill <james at and.org>
Date: Mon Sep 21 01:59:32 2009 -0400
Show uid 0xFFFFFFFF as unset, dito. if loginuid wasn't available
diff --git a/output.py b/output.py
index 3857c80..1072111 100755
--- a/output.py
+++ b/output.py
@@ -1196,6 +1196,14 @@ to exit.
return count, "".join(list(actions))
def _pwd_ui_username(self, uid, limit=None):
+ # loginuid is set to -1 on init.
+ if uid is None or uid == 0xFFFFFFFF:
+ loginid = _("<unset>")
+ name = _("System") + " " + loginid
+ if limit is not None and len(name) > limit:
+ name = loginid
+ return name
+
try:
user = pwd.getpwuid(uid)
fullname = user.pw_gecos.split(';', 2)[0]
commit e210a6b1847161d879be0c093f463d9b651714a0
Author: James Antill <james at and.org>
Date: Sat Sep 19 12:51:09 2009 -0400
Do the OUTER JOIN by hand, so we get Aborted transactions.
Fix output code for displaying aborted transactions.
Tweak obsoletes handling in undo/redo.
diff --git a/output.py b/output.py
index 9a993c7..3857c80 100755
--- a/output.py
+++ b/output.py
@@ -1257,6 +1257,8 @@ to exit.
num, uiacts = self._history_uiactions(old.trans_data)
if old.altered_lt_rpmdb and old.altered_gt_rpmdb:
print fmt % (old.tid, name, tm, uiacts, num), "><"
+ elif old.return_code is None:
+ print fmt % (old.tid, name, tm, uiacts, num), "**"
elif old.altered_lt_rpmdb:
print fmt % (old.tid, name, tm, uiacts, num), " <"
elif old.altered_gt_rpmdb:
@@ -1270,7 +1272,7 @@ to exit.
rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
if lastdbv.end_rpmdbversion != rpmdbv:
errstring = _('Warning: RPMDB has been altered since the last yum transaction.')
- base.logger.warning(errstring)
+ self.logger.warning(errstring)
def _history_get_transactions(self, extcmds):
if len(extcmds) < 2:
@@ -1350,27 +1352,30 @@ to exit.
print _("Begin rpmdb :"), old.beg_rpmdbversion, "**"
else:
print _("Begin rpmdb :"), old.beg_rpmdbversion
- endtm = time.ctime(old.end_timestamp)
- endtms = endtm.split()
- if begtm.startswith(endtms[0]): # Chop uninteresting prefix
- begtms = begtm.split()
- sofar = 0
- for i in range(len(endtms)):
- if i > len(begtms):
- break
- if begtms[i] != endtms[i]:
- break
- sofar += len(begtms[i]) + 1
- endtm = (' ' * sofar) + endtm[sofar:]
- diff = _("(%s seconds)") % (old.end_timestamp - old.beg_timestamp)
- print _("End time :"), endtm, diff
+ if old.end_timestamp is not None:
+ endtm = time.ctime(old.end_timestamp)
+ endtms = endtm.split()
+ if begtm.startswith(endtms[0]): # Chop uninteresting prefix
+ begtms = begtm.split()
+ sofar = 0
+ for i in range(len(endtms)):
+ if i > len(begtms):
+ break
+ if begtms[i] != endtms[i]:
+ break
+ sofar += len(begtms[i]) + 1
+ endtm = (' ' * sofar) + endtm[sofar:]
+ diff = _("(%s seconds)") % (old.end_timestamp - old.beg_timestamp)
+ print _("End time :"), endtm, diff
if old.end_rpmdbversion is not None:
if old.altered_gt_rpmdb:
print _("End rpmdb :"), old.end_rpmdbversion, "**"
else:
print _("End rpmdb :"), old.end_rpmdbversion
print _("User :"), name
- if old.return_code:
+ if old.return_code is None:
+ print _("Return-Code :"), "**", _("Aborted"), "**"
+ elif old.return_code:
print _("Return-Code :"), _("Failure:"), old.return_code
else:
print _("Return-Code :"), _("Success")
diff --git a/yum/__init__.py b/yum/__init__.py
index 1bf0797..c1e2c3b 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -3598,7 +3598,9 @@ class YumBase(depsolve.Depsolve):
def history_redo(self, transaction):
""" Given a valid historical transaction object, try and repeat
that transaction. """
- # This is basic atm.
+ # NOTE: This is somewhat basic atm. ... see comment in undo.
+ old_conf_obs = self.conf.obsoletes
+ self.conf.obsoletes = False
done = False
for pkg in transaction.trans_data:
if pkg.state == 'Reinstall':
@@ -3623,12 +3625,19 @@ class YumBase(depsolve.Depsolve):
if pkg.state == 'Erase':
if self.remove(pkgtup=pkg.pkgtup):
done = True
+ self.conf.obsoletes = old_conf_obs
return done
def history_undo(self, transaction):
""" Given a valid historical transaction object, try and undo
that transaction. """
- # FIXME: This is basic atm.
+ # NOTE: This is somewhat basic atm. ... for instance we don't check
+ # that we are going from the old new version. However it's still
+ # better than the RHN rollback code, and people pay for that :).
+ # We turn obsoletes off because we want the specific versions of stuff
+ # from history ... even if they've been obsoleted since then.
+ old_conf_obs = self.conf.obsoletes
+ self.conf.obsoletes = False
done = False
for pkg in transaction.trans_data:
if pkg.state == 'Reinstall':
@@ -3655,15 +3664,13 @@ class YumBase(depsolve.Depsolve):
done = True
for pkg in transaction.trans_data:
if pkg.state == 'Obsoleted':
- old_conf_obs = self.conf.obsoletes
- self.conf.obsoletes = False
if self.install(pkgtup=pkg.pkgtup):
done = True
- self.conf.obsoletes = old_conf_obs
for pkg in transaction.trans_data:
if pkg.state == 'Erase':
if self.install(pkgtup=pkg.pkgtup):
done = True
+ self.conf.obsoletes = old_conf_obs
return done
def _retrievePublicKey(self, keyurl, repo=None):
diff --git a/yum/history.py b/yum/history.py
index 8fa3ae4..9741d36 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -405,7 +405,7 @@ class YumHistory:
ret.append(obj)
return ret
- def old(self, tids=[], limit=None):
+ def old(self, tids=[], limit=None, complete_transactions_only=False):
""" Return a list of the last transactions, note that this includes
partial transactions (ones without an end transaction). """
cur = self._get_cursor()
@@ -416,28 +416,61 @@ class YumHistory:
trans_end.rpmdb_version AS end_rv,
loginuid, return_code
FROM trans_beg JOIN trans_end USING(tid)"""
- # FIXME: Stupid sqlite doesn't do outer joins yet, so if yum dies
- # before creating a trans_end entry we miss it atm.
+ # NOTE: sqlite doesn't do OUTER JOINs ... *sigh*. So we have to do it
+ # ourself.
+ if not complete_transactions_only:
+ sql = """SELECT tid,
+ trans_beg.timestamp AS beg_ts,
+ trans_beg.rpmdb_version AS beg_rv,
+ NULL, NULL,
+ loginuid, NULL
+ FROM trans_beg"""
params = None
- if tids:
- tids = set(tids)
+ if tids and len(tids) <= yum.constants.PATTERNS_INDEXED_MAX:
+ params = tids = list(set(tids))
sql += " WHERE tid IN (%s)" % ", ".join(['?'] * len(tids))
- params = list(tids)
sql += " ORDER BY beg_ts DESC, tid ASC"
if limit is not None:
sql += " LIMIT " + str(limit)
executeSQL(cur, sql, params)
ret = []
+ tid2obj = {}
for row in cur:
- ret.append(YumHistoryTransaction(self, row))
+ if tids and len(tids) > yum.constants.PATTERNS_INDEXED_MAX:
+ if row[0] not in tids:
+ continue
+ obj = YumHistoryTransaction(self, row)
+ tid2obj[row[0]] = obj
+ ret.append(obj)
+
+ sql = """SELECT tid,
+ trans_end.timestamp AS end_ts,
+ trans_end.rpmdb_version AS end_rv,
+ return_code
+ FROM trans_end"""
+ params = tid2obj.keys()
+ if len(params) > yum.constants.PATTERNS_INDEXED_MAX:
+ executeSQL(cur, sql)
+ else:
+ sql += " WHERE tid IN (%s)" % ", ".join(['?'] * len(params))
+ executeSQL(cur, sql, params)
+ for row in cur:
+ if row[0] not in tid2obj:
+ continue
+ tid2obj[row[0]].end_timestamp = row[1]
+ tid2obj[row[0]].end_rpmdbversion = row[2]
+ tid2obj[row[0]].return_code = row[3]
# Go through backwards, and see if the rpmdb versions match
las = None
for obj in reversed(ret):
cur_rv = obj.beg_rpmdbversion
- if las is None or cur_rv is None or (las.tid + 1) != obj.tid:
+ las_rv = None
+ if las is not None:
+ las_rv = las.end_rpmdbversion
+ if las_rv is None or cur_rv is None or (las.tid + 1) != obj.tid:
pass
- elif las.end_rpmdbversion != cur_rv:
+ elif las_rv != cur_rv:
obj.altered_lt_rpmdb = True
las.altered_gt_rpmdb = True
else:
commit 3b2fe0c657fa2ac7df0273931e1b1f62e341ce32
Author: James Antill <james at and.org>
Date: Fri Sep 18 17:21:18 2009 -0400
Add history callbacks to the RPMTransaction:
Log script output (although not per. pkg).
Mark pkgs. as done, as they complete (still need to post process, due
to the crappy callback API).
Change altered_rpmdb to altered_lt_rpmdb and altered_gt_rpmdb.
Tweak output in list/info appropriately.
diff --git a/output.py b/output.py
index ae2d412..9a993c7 100755
--- a/output.py
+++ b/output.py
@@ -1255,8 +1255,12 @@ to exit.
tm = time.strftime("%Y-%m-%d %H:%M",
time.localtime(old.beg_timestamp))
num, uiacts = self._history_uiactions(old.trans_data)
- if old.altered_rpmdb:
- print fmt % (old.tid, name, tm, uiacts, num), "**"
+ if old.altered_lt_rpmdb and old.altered_gt_rpmdb:
+ print fmt % (old.tid, name, tm, uiacts, num), "><"
+ elif old.altered_lt_rpmdb:
+ print fmt % (old.tid, name, tm, uiacts, num), " <"
+ elif old.altered_gt_rpmdb:
+ print fmt % (old.tid, name, tm, uiacts, num), "> "
else:
print fmt % (old.tid, name, tm, uiacts, num)
lastdbv = self.history.last()
@@ -1322,13 +1326,12 @@ to exit.
done = False
for tid in self.history.old(tids):
- if lastdbv is not None and not tid.altered_rpmdb:
+ if lastdbv is not None and tid.tid == lasttid:
# If this is the last transaction, is good and it doesn't
# match the current rpmdb ... then mark it as bad.
- if tid.tid == lasttid:
- rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
- if lastdbv != rpmdbv:
- tid.altered_rpmdb = True
+ rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
+ if lastdbv != rpmdbv:
+ tid.altered_gt_rpmdb = True
lastdbv = None
if done:
@@ -1343,7 +1346,10 @@ to exit.
begtm = time.ctime(old.beg_timestamp)
print _("Begin time :"), begtm
if old.beg_rpmdbversion is not None:
- print _("Begin rpmdb :"), old.beg_rpmdbversion
+ if old.altered_lt_rpmdb:
+ print _("Begin rpmdb :"), old.beg_rpmdbversion, "**"
+ else:
+ print _("Begin rpmdb :"), old.beg_rpmdbversion
endtm = time.ctime(old.end_timestamp)
endtms = endtm.split()
if begtm.startswith(endtms[0]): # Chop uninteresting prefix
@@ -1356,9 +1362,13 @@ to exit.
break
sofar += len(begtms[i]) + 1
endtm = (' ' * sofar) + endtm[sofar:]
- print _("End time :"), endtm
+ diff = _("(%s seconds)") % (old.end_timestamp - old.beg_timestamp)
+ print _("End time :"), endtm, diff
if old.end_rpmdbversion is not None:
- print _("End rpmdb :"), old.end_rpmdbversion
+ if old.altered_gt_rpmdb:
+ print _("End rpmdb :"), old.end_rpmdbversion, "**"
+ else:
+ print _("End rpmdb :"), old.end_rpmdbversion
print _("User :"), name
if old.return_code:
print _("Return-Code :"), _("Failure:"), old.return_code
diff --git a/yum/history.py b/yum/history.py
index 5003bed..8fa3ae4 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -147,6 +147,9 @@ class YumHistoryTransaction:
self._loaded_TW = None
self._loaded_TD = None
+ self.altered_lt_rpmdb = None
+ self.altered_gt_rpmdb = None
+
def __cmp__(self, other):
if other is None:
return 1
@@ -255,13 +258,28 @@ class YumHistory:
if 'checksum_type' in yumdb and 'checksum_type' in yumdb:
csum = "%s:%s" % (yumdb.checksum_type, yumdb.checksum_data)
return self._pkgtup2pid(po.pkgtup, csum)
- def _pkg2pid(self, po):
+ def pkg2pid(self, po):
if isinstance(po, YumInstalledPackage):
return self._ipkg2pid(po)
if isinstance(po, YumAvailablePackage):
return self._apkg2pid(po)
return self._pkgtup2pid(po.pkgtup, None)
+ @staticmethod
+ def txmbr2state(txmbr):
+ state = None
+ if txmbr.output_state in (TS_INSTALL, TS_TRUEINSTALL):
+ if hasattr(txmbr, 'reinstall'):
+ state = 'Reinstall'
+ elif txmbr.downgrades:
+ state = 'Downgrade'
+ if txmbr.output_state == TS_ERASE:
+ if txmbr.downgraded_by:
+ state = 'Downgraded'
+ if state is None:
+ state = _stcode2sttxt[txmbr.output_state]
+ return state
+
def trans_with_pid(self, pid):
cur = self._get_cursor()
res = executeSQL(cur,
@@ -271,6 +289,8 @@ class YumHistory:
return cur.lastrowid
def trans_data_pid_beg(self, pid, state):
+ if not hasattr(self, '_tid'):
+ return # Not configured to run
cur = self._get_cursor()
res = executeSQL(cur,
"""INSERT INTO trans_data_pkgs
@@ -278,11 +298,15 @@ class YumHistory:
VALUES (?, ?, ?)""", (self._tid, pid, state))
return cur.lastrowid
def trans_data_pid_end(self, pid, state):
+ if not hasattr(self, '_tid'):
+ return # Not configured to run
+
cur = self._get_cursor()
res = executeSQL(cur,
"""UPDATE trans_data_pkgs SET done = ?
WHERE tid = ? AND pkgtupid = ? AND state = ?
""", ('TRUE', self._tid, pid, state))
+ self._commit()
return cur.lastrowid
def beg(self, rpmdb_version, using_pkgs, txmbrs):
@@ -300,18 +324,8 @@ class YumHistory:
self.trans_with_pid(pid)
for txmbr in txmbrs:
- pid = self._pkg2pid(txmbr.po)
- state = None
- if txmbr.output_state in (TS_INSTALL, TS_TRUEINSTALL):
- if hasattr(txmbr, 'reinstall'):
- state = 'Reinstall'
- elif txmbr.downgrades:
- state = 'Downgrade'
- if txmbr.output_state == TS_ERASE:
- if txmbr.downgraded_by:
- state = 'Downgraded'
- if state is None:
- state = _stcode2sttxt[txmbr.output_state]
+ pid = self.pkg2pid(txmbr.po)
+ state = self.txmbr2state(txmbr)
self.trans_data_pid_beg(pid, state)
self._commit()
@@ -324,6 +338,18 @@ class YumHistory:
(tid, msg) VALUES (?, ?)""", (self._tid, error))
self._commit()
+ def log_scriptlet_output(self, data, msg):
+ """ Note that data can be either a real pkg. ... or not. """
+ if msg is None or not hasattr(self, '_tid'):
+ return # Not configured to run
+
+ cur = self._get_cursor()
+ for error in msg.split('\n'):
+ executeSQL(cur,
+ """INSERT INTO trans_script_stdout
+ (tid, line) VALUES (?, ?)""", (self._tid, error))
+ self._commit()
+
def end(self, rpmdb_version, return_code, errors=None):
assert return_code or not errors
cur = self._get_cursor()
@@ -335,7 +361,9 @@ class YumHistory:
return_code))
self._commit()
if not return_code:
- # Simple hack, if the transaction finished
+ # Simple hack, if the transaction finished. Note that this
+ # catches the erase cases (as we still don't get pkgtups for them),
+ # Eg. Updated elements.
executeSQL(cur,
"""UPDATE trans_data_pkgs SET done = ?
WHERE tid = ?""", ('TRUE', self._tid,))
@@ -404,18 +432,18 @@ class YumHistory:
ret.append(YumHistoryTransaction(self, row))
# Go through backwards, and see if the rpmdb versions match
- last_rv = None
- last_tid = None
+ las = None
for obj in reversed(ret):
cur_rv = obj.beg_rpmdbversion
- if last_rv is None or cur_rv is None or (last_tid + 1) != obj.tid:
- obj.altered_rpmdb = None
- elif last_rv != cur_rv:
- obj.altered_rpmdb = True
+ if las is None or cur_rv is None or (las.tid + 1) != obj.tid:
+ pass
+ elif las.end_rpmdbversion != cur_rv:
+ obj.altered_lt_rpmdb = True
+ las.altered_gt_rpmdb = True
else:
- obj.altered_rpmdb = False
- last_rv = obj.end_rpmdbversion
- last_tid = obj.tid
+ obj.altered_lt_rpmdb = False
+ las.altered_gt_rpmdb = False
+ las = obj
return ret
diff --git a/yum/rpmtrans.py b/yum/rpmtrans.py
index 4542e03..b1b896f 100644
--- a/yum/rpmtrans.py
+++ b/yum/rpmtrans.py
@@ -230,6 +230,11 @@ class RPMTransaction:
except IOError:
pass
+ def _scriptout(self, data):
+ msgs = self._scriptOutput()
+ self.display.scriptout(data, msgs)
+ self.base.history.log_scriptlet_output(data, msgs)
+
def __del__(self):
self._shutdownOutputLogging()
@@ -439,10 +444,14 @@ class RPMTransaction:
txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
for txmbr in txmbrs:
self.display.filelog(txmbr.po, txmbr.output_state)
- self.display.scriptout(txmbr.po, self._scriptOutput())
+ self._scriptout(txmbr.po)
+ # NOTE: We only do this for install, not erase atm.
+ # because we don't get pkgtup data for erase (this
+ # includes "Updated" pkgs).
+ pid = self.base.history.pkg2pid(txmbr.po)
+ state = self.base.history.txmbr2state(txmbr)
+ self.base.history.trans_data_pid_end(pid, state)
self.ts_done(txmbr.po, txmbr.output_state)
-
-
def _instProgress(self, bytes, total, h):
if h is not None:
@@ -477,7 +486,7 @@ class RPMTransaction:
self.display.event(h, action, 100, 100, self.complete_actions,
self.total_actions)
- self.display.scriptout(h, self._scriptOutput())
+ self._scriptout(h)
if self.test: return # and we're done
self.ts_done(h, action)
commit 89652f1c98c8823e6254dbee32c14e0eddf70c32
Author: James Antill <james at and.org>
Date: Fri Sep 18 13:00:22 2009 -0400
Add installroot to history
diff --git a/yum/__init__.py b/yum/__init__.py
index 86f145d..1bf0797 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -706,7 +706,7 @@ class YumBase(depsolve.Depsolve):
"""auto create the history object that to acess/append the transaction
history information. """
if self._history is None:
- self._history = yum.history.YumHistory(self.rpmdb)
+ self._history = yum.history.YumHistory(root=self.conf.installroot)
return self._history
# properties so they auto-create themselves with defaults
diff --git a/yum/history.py b/yum/history.py
index 8df976a..5003bed 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -172,12 +172,11 @@ class YumHistoryTransaction:
class YumHistory:
""" API for accessing the history sqlite data. """
- def __init__(self, rpmdb, db_path=_history_dir):
- self._rpmdb = weakref(rpmdb)
+ def __init__(self, root='/', db_path=_history_dir):
self._conn = None
self.conf = yum.misc.GenericHolder()
- self.conf.db_path = db_path
+ self.conf.db_path = os.path.normpath(root + '/' + db_path)
self.conf.writable = False
if not os.path.exists(self.conf.db_path):
commit ca871a9525ed2ebdec6fbaae66ea65bd0e1e9c67
Author: James Antill <james at and.org>
Date: Thu Sep 17 19:13:26 2009 -0400
Add history_record config.
Use history_record_packages.
Add API for code/plugins to add their package names:
base.run_with_package_names.add()
conduit.registerPackageName()
Use the config. and API package names in version and history.
diff --git a/cli.py b/cli.py
index 99e0a42..332be99 100644
--- a/cli.py
+++ b/cli.py
@@ -207,8 +207,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
if opts.version:
self.conf.cache = 1
- yum_progs = ['yum', 'yum-metadata-parser', 'rpm',
- 'yum-rhn-plugin']
+ yum_progs = self.run_with_package_names
done = False
def sm_ui_time(x):
return time.strftime("%Y-%m-%d %H:%M", time.gmtime(x))
diff --git a/yum/__init__.py b/yum/__init__.py
index 40a3066..86f145d 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -155,6 +155,7 @@ class YumBase(depsolve.Depsolve):
self.arch = ArchStorage()
self.preconf = _YumPreBaseConf()
+ self.run_with_package_names = set()
def __del__(self):
self.close()
@@ -274,6 +275,10 @@ class YumBase(depsolve.Depsolve):
# worthless. So we delete it, and thus. it'll raise AttributeError
del self.preconf
+ # Packages used to run yum...
+ for pkgname in self.conf.history_record_packages:
+ self.run_with_package_names.add(pkgname)
+
# run the postconfig plugin hook
self.plugins.run('postconfig')
self.yumvar = self.conf.yumvar
@@ -1041,8 +1046,7 @@ class YumBase(depsolve.Depsolve):
self.plugins.run('pretrans')
- using_pkgs_pats = ['yum', 'rpm', 'python', 'yum-metadata-parser',
- 'yum-rhn-plugin']
+ using_pkgs_pats = list(self.run_with_package_names)
using_pkgs = self.rpmdb.returnPackages(patterns=using_pkgs_pats)
rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
lastdbv = self.history.last()
@@ -1051,7 +1055,8 @@ class YumBase(depsolve.Depsolve):
if lastdbv is not None and rpmdbv != lastdbv:
errstring = _('Warning: RPMDB has been altered since the last yum transaction.')
self.logger.warning(errstring)
- self.history.beg(rpmdbv, using_pkgs, list(self.tsInfo))
+ if self.conf.history_record:
+ self.history.beg(rpmdbv, using_pkgs, list(self.tsInfo))
errors = self.ts.run(cb.callback, '')
# ts.run() exit codes are, hmm, "creative": None means all ok, empty
@@ -1068,7 +1073,8 @@ class YumBase(depsolve.Depsolve):
self.verbose_logger.debug(errstring)
resultobject.return_code = 1
else:
- self.history.end(rpmdbv, 2, errors=errors)
+ if self.conf.history_record:
+ self.history.end(rpmdbv, 2, errors=errors)
raise Errors.YumBaseError, errors
if not self.conf.keepcache:
@@ -1156,10 +1162,11 @@ class YumBase(depsolve.Depsolve):
else:
self.verbose_logger.log(logginglevels.DEBUG_2, 'What is this? %s' % txmbr.po)
- ret = -1
- if resultobject is not None:
- ret = resultobject.return_code
- self.history.end(self.rpmdb.simpleVersion(main_only=True)[0], ret)
+ if self.conf.history_record:
+ ret = -1
+ if resultobject is not None:
+ ret = resultobject.return_code
+ self.history.end(self.rpmdb.simpleVersion(main_only=True)[0], ret)
self.rpmdb.dropCachedData()
def costExcludePackages(self):
diff --git a/yum/config.py b/yum/config.py
index bca5591..0d7bc38 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -703,7 +703,7 @@ class YumConf(StartupConf):
sslclientcert = Option()
sslclientkey = Option()
-
+ history_record = BoolOption(True)
history_record_packages = ListOption(['yum', 'rpm', 'yum-metadata-parser'])
_reposlist = []
diff --git a/yum/plugins.py b/yum/plugins.py
index 27b1aa5..02f0d57 100644
--- a/yum/plugins.py
+++ b/yum/plugins.py
@@ -457,6 +457,10 @@ class PluginConduit:
'''
return config.getOption(self._conf, section, opt, config.BoolOption(default))
+ def registerPackageName(self, name):
+ self._base.run_with_package_names.add(name)
+
+
class ConfigPluginConduit(PluginConduit):
def registerOpt(self, name, valuetype, where, default):
commit 526a295001d603eb80856dea78940f08b959a776
Author: James Antill <james at and.org>
Date: Thu Sep 17 17:42:31 2009 -0400
Fix history searching for something with a lot of hits, Eg. "*fc11"
diff --git a/yum/history.py b/yum/history.py
index 5f64541..8df976a 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -24,6 +24,7 @@ from weakref import proxy as weakref
from sqlutils import sqlite, executeSQL
import yum.misc
+import yum.constants
from yum.constants import *
from yum.packages import YumInstalledPackage, YumAvailablePackage, PackageObject
@@ -477,7 +478,16 @@ class YumHistory:
sql += "(%s)" % ",".join(['?'] * len(pkgtupids))
params = list(pkgtupids)
tids = set()
- for row in executeSQL(cur, sql, params):
+ if len(params) > yum.constants.PATTERNS_INDEXED_MAX:
+ executeSQL(cur, """SELECT tid FROM trans_data_pkgs""")
+ for row in cur:
+ if row[0] in params:
+ tids.add(row[0])
+ return tids
+ if not params:
+ return tids
+ executeSQL(cur, sql, params)
+ for row in cur:
tids.add(row[0])
return tids
commit 6616363d9902afc1be94e5f9d39cd29d04bea974
Author: James Antill <james at and.org>
Date: Thu Sep 17 16:24:48 2009 -0400
Remove the outer joins, sqlite doesn't parse them (and also dies in f12).
Also add limit to old and use it in history list.
diff --git a/output.py b/output.py
index f9985c8..ae2d412 100755
--- a/output.py
+++ b/output.py
@@ -1243,8 +1243,11 @@ to exit.
print "-" * 79
fmt = "%6u | %-22.22s | %-16s | %-14s | %4u"
done = 0
- for old in self.history.old(tids):
- if not printall and done > 19:
+ limit = 20
+ if printall:
+ limit = None
+ for old in self.history.old(tids, limit=limit):
+ if not printall and done >= limit:
break
done += 1
diff --git a/yum/history.py b/yum/history.py
index 1731eda..5f64541 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -377,7 +377,9 @@ class YumHistory:
ret.append(obj)
return ret
- def old(self, tids=[]):
+ def old(self, tids=[], limit=None):
+ """ Return a list of the last transactions, note that this includes
+ partial transactions (ones without an end transaction). """
cur = self._get_cursor()
sql = """SELECT tid,
trans_beg.timestamp AS beg_ts,
@@ -385,13 +387,17 @@ class YumHistory:
trans_end.timestamp AS end_ts,
trans_end.rpmdb_version AS end_rv,
loginuid, return_code
- FROM trans_beg OUTER JOIN trans_end USING(tid)"""
+ FROM trans_beg JOIN trans_end USING(tid)"""
+ # FIXME: Stupid sqlite doesn't do outer joins yet, so if yum dies
+ # before creating a trans_end entry we miss it atm.
params = None
if tids:
tids = set(tids)
sql += " WHERE tid IN (%s)" % ", ".join(['?'] * len(tids))
params = list(tids)
sql += " ORDER BY beg_ts DESC, tid ASC"
+ if limit is not None:
+ sql += " LIMIT " + str(limit)
executeSQL(cur, sql, params)
ret = []
for row in cur:
@@ -414,6 +420,8 @@ class YumHistory:
return ret
def last(self):
+ """ This is the last full transaction. So any imcomplete transactions
+ do not count. """
cur = self._get_cursor()
sql = """SELECT tid,
trans_beg.timestamp AS beg_ts,
@@ -421,7 +429,7 @@ class YumHistory:
trans_end.timestamp AS end_ts,
trans_end.rpmdb_version AS end_rv,
loginuid, return_code
- FROM trans_beg OUTER JOIN trans_end USING(tid)
+ FROM trans_beg JOIN trans_end USING(tid)
ORDER BY beg_ts DESC, tid ASC
LIMIT 1"""
executeSQL(cur, sql)
commit bc765f53a18a5f446822753eeb2bd797e39848f5
Author: James Antill <james at and.org>
Date: Mon Sep 14 18:45:11 2009 -0400
Fix history redo/undo to work with obsoletes
diff --git a/yum/__init__.py b/yum/__init__.py
index 5dfe9bf..40a3066 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -3588,7 +3588,7 @@ class YumBase(depsolve.Depsolve):
return returndict
- def history_repeat(self, transaction):
+ def history_redo(self, transaction):
""" Given a valid historical transaction object, try and repeat
that transaction. """
# This is basic atm.
@@ -3609,7 +3609,7 @@ class YumBase(depsolve.Depsolve):
if self.update(pkgtup=pkg.pkgtup):
done = True
for pkg in transaction.trans_data:
- if pkg.state in ('Install', 'True-Install'):
+ if pkg.state in ('Install', 'True-Install', 'Obsoleting'):
if self.install(pkgtup=pkg.pkgtup):
done = True
for pkg in transaction.trans_data:
@@ -3639,10 +3639,21 @@ class YumBase(depsolve.Depsolve):
if self.update(pkgtup=pkg.pkgtup):
done = True
for pkg in transaction.trans_data:
+ if pkg.state == 'Obsoleting':
+ if self.remove(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in transaction.trans_data:
if pkg.state in ('Install', 'True-Install'):
if self.remove(pkgtup=pkg.pkgtup):
done = True
for pkg in transaction.trans_data:
+ if pkg.state == 'Obsoleted':
+ old_conf_obs = self.conf.obsoletes
+ self.conf.obsoletes = False
+ if self.install(pkgtup=pkg.pkgtup):
+ done = True
+ self.conf.obsoletes = old_conf_obs
+ for pkg in transaction.trans_data:
if pkg.state == 'Erase':
if self.install(pkgtup=pkg.pkgtup):
done = True
diff --git a/yumcommands.py b/yumcommands.py
index 9c43af6..f6c4e19 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -1152,19 +1152,19 @@ class HistoryCommand(YumCommand):
return ['history']
def getUsage(self):
- return "[info|list|summary|repeat|undo|new]"
+ return "[info|list|summary|redo|undo|new]"
def getSummary(self):
return _("Display, or use, the transaction history")
- def _hcmd_repeat(self, base, extcmds):
+ def _hcmd_redo(self, base, extcmds):
old = base._history_get_transaction(extcmds)
if old is None:
- return 1, ['Failed history repeat']
+ return 1, ['Failed history redo']
tm = time.ctime(old.beg_timestamp)
print "Repeating transaction %u, from %s" % (old.tid, tm)
base.historyInfoCmdPkgsAltered(old)
- if base.history_repeat(old):
+ if base.history_redo(old):
return 2, ["Repeating transaction %u" % (old.tid,)]
def _hcmd_undo(self, base, extcmds):
@@ -1181,12 +1181,12 @@ class HistoryCommand(YumCommand):
base.history._create_db_file()
def doCheck(self, base, basecmd, extcmds):
- cmds = ('list', 'info', 'summary', 'repeat', 'undo', 'new')
+ cmds = ('list', 'info', 'summary', 'repeat', 'redo', 'undo', 'new')
if extcmds and extcmds[0] not in cmds:
base.logger.critical(_('Invalid history sub-command, use: %s.'),
", ".join(cmds))
raise cli.CliError
- if extcmds and extcmds[0] in ('repeat', 'undo', 'new'):
+ if extcmds and extcmds[0] in ('repeat', 'redo', 'undo', 'new'):
checkRootUID(base)
checkGPGKey(base)
@@ -1204,8 +1204,8 @@ class HistoryCommand(YumCommand):
ret = base.historySummaryCmd(extcmds)
elif vcmd == 'undo':
ret = self._hcmd_undo(base, extcmds)
- elif vcmd == 'repeat':
- ret = self._hcmd_repeat(base, extcmds)
+ elif vcmd in ('redo', 'repeat'):
+ ret = self._hcmd_redo(base, extcmds)
elif vcmd == 'new':
ret = self._hcmd_new(base, extcmds)
@@ -1217,4 +1217,4 @@ class HistoryCommand(YumCommand):
vcmd = 'list'
if extcmds:
vcmd = extcmds[0]
- return vcmd in ('repeat', 'undo')
+ return vcmd in ('repeat', 'redo', 'undo')
commit e9de9c25f1a607adcc3c1eee94e2820aab3e9208
Author: James Antill <james at and.org>
Date: Mon Sep 14 18:41:20 2009 -0400
Minor output UI changes for history
Just display one of Obsoleted/Obsoleting, but count both.
Chop name properly for epoch, display as nevra instead of envra.
Do current rpmdb version check at end of list, not during, as it's IO
bound esp. if rpmdb isn't in RAM.
diff --git a/output.py b/output.py
index d21046e..f9985c8 100755
--- a/output.py
+++ b/output.py
@@ -1181,8 +1181,11 @@ to exit.
st = hpkg.state
if st == 'True-Install':
st = 'Install'
+ if st == 'Obsoleted': # This is just a UI tweak, as we can't have
+ # just one but we need to count them all.
+ st = 'Obsoleting'
if st in ('Install', 'Update', 'Erase', 'Reinstall', 'Downgrade',
- 'Obsoleting', 'Obsoleted'):
+ 'Obsoleting'):
actions.add(st)
count += 1
assert len(actions) <= 6
@@ -1240,24 +1243,11 @@ to exit.
print "-" * 79
fmt = "%6u | %-22.22s | %-16s | %-14s | %4u"
done = 0
- lastdbv = self.history.last()
- if lastdbv is not None:
- lasttid = lastdbv.tid
- lastdbv = lastdbv.end_rpmdbversion
for old in self.history.old(tids):
if not printall and done > 19:
break
done += 1
- if lastdbv is not None and not old.altered_rpmdb:
- # If this is the last transaction, is good and it doesn't
- # match the current rpmdb ... then mark it as bad.
- if old.tid == lasttid:
- rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
- if lastdbv != rpmdbv:
- old.altered_rpmdb = True
- lastdbv = None
-
name = self._pwd_ui_username(old.loginuid, 22)
tm = time.strftime("%Y-%m-%d %H:%M",
time.localtime(old.beg_timestamp))
@@ -1266,6 +1256,14 @@ to exit.
print fmt % (old.tid, name, tm, uiacts, num), "**"
else:
print fmt % (old.tid, name, tm, uiacts, num)
+ lastdbv = self.history.last()
+ if lastdbv is not None:
+ # If this is the last transaction, is good and it doesn't
+ # match the current rpmdb ... then mark it as bad.
+ rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
+ if lastdbv.end_rpmdbversion != rpmdbv:
+ errstring = _('Warning: RPMDB has been altered since the last yum transaction.')
+ base.logger.warning(errstring)
def _history_get_transactions(self, extcmds):
if len(extcmds) < 2:
@@ -1396,19 +1394,27 @@ to exit.
highlight = 'bold'
(hibeg, hiend) = self._highlight(highlight)
+ # To chop the name off we need nevra strings, str(pkg) gives envra
+ # so we have to do it by hand ... *sigh*.
+ if hpkg.epoch == '0':
+ cn = str(hpkg)
+ else:
+ cn = "%s-%s:%s-%s.%s" % (hpkg.name, hpkg.epoch,
+ hpkg.version, hpkg.release, hpkg.arch)
+
if False: pass
elif hpkg.state == 'Update':
ln = len(hpkg.name) + 1
- cn = (" " * ln) + str(hpkg)[ln:]
+ cn = (" " * ln) + cn[ln:]
print "%s%s%-12s%s %s" % (prefix, hibeg, hpkg.state, hiend, cn)
elif hpkg.state == 'Downgraded':
ln = len(hpkg.name) + 1
- cn = (" " * ln) + str(hpkg)[ln:]
+ cn = (" " * ln) + cn[ln:]
print "%s%s%-12s%s %s" % (prefix, hibeg, hpkg.state, hiend, cn)
elif hpkg.state == 'True-Install':
- print "%s%s%-12s%s %s" % (prefix, hibeg, "Install", hiend, hpkg)
+ print "%s%s%-12s%s %s" % (prefix, hibeg, "Install", hiend, cn)
else:
- print "%s%s%-12s%s %s" % (prefix, hibeg, hpkg.state, hiend,hpkg)
+ print "%s%s%-12s%s %s" % (prefix, hibeg, hpkg.state, hiend, cn)
def historySummaryCmd(self, extcmds):
tids, printall = self._history_list_transactions(extcmds)
commit 0bd0bbf9e53e8e304d37bc831d3fd3b652bb8bdb
Author: James Antill <james at and.org>
Date: Mon Sep 14 18:36:05 2009 -0400
Sort reinstalled and downgraded list from makelists
diff --git a/yum/transactioninfo.py b/yum/transactioninfo.py
index 63ad574..bd7bf80 100644
--- a/yum/transactioninfo.py
+++ b/yum/transactioninfo.py
@@ -320,6 +320,8 @@ class TransactionData:
self.depremoved.sort()
self.instgroups.sort()
self.removedgroups.sort()
+ self.reinstalled.sort()
+ self.downgraded.sort()
self.failed.sort()
commit 5210cfb143c815edcecc7ead2d297a0e6053cdf7
Author: James Antill <james at and.org>
Date: Fri Sep 11 15:44:10 2009 -0400
Fix history SQL for py-2.4 and that era. sqlite, maybe generic fixes too
diff --git a/yum/history.py b/yum/history.py
index 03ddff4..1731eda 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -236,7 +236,8 @@ class YumHistory:
res = executeSQL(cur,
"""INSERT INTO pkgtups
(name, arch, epoch, version, release, checksum)
- VALUES (?,?,?,?,?,?)""", (n,a,e,v,r, checksum))
+ VALUES (?, ?, ?, ?, ?, ?)""", (n,a,e,v,r,
+ checksum))
else:
res = executeSQL(cur,
"""INSERT INTO pkgtups
@@ -279,9 +280,9 @@ class YumHistory:
def trans_data_pid_end(self, pid, state):
cur = self._get_cursor()
res = executeSQL(cur,
- """UPDATE trans_data_pkgs SET done = 'TRUE'
+ """UPDATE trans_data_pkgs SET done = ?
WHERE tid = ? AND pkgtupid = ? AND state = ?
- """, (self._tid, pid, state))
+ """, ('TRUE', self._tid, pid, state))
return cur.lastrowid
def beg(self, rpmdb_version, using_pkgs, txmbrs):
@@ -289,9 +290,9 @@ class YumHistory:
res = executeSQL(cur,
"""INSERT INTO trans_beg
(timestamp, rpmdb_version, loginuid)
- VALUES (?,?,?)""", (int(time.time()),
- str(rpmdb_version),
- yum.misc.getloginuid()))
+ VALUES (?, ?, ?)""", (int(time.time()),
+ str(rpmdb_version),
+ yum.misc.getloginuid()))
self._tid = cur.lastrowid
for pkg in using_pkgs:
@@ -329,15 +330,15 @@ class YumHistory:
res = executeSQL(cur,
"""INSERT INTO trans_end
(tid, timestamp, rpmdb_version, return_code)
- VALUES (?,?,?,?)""", (self._tid, int(time.time()),
- str(rpmdb_version),
- return_code))
+ VALUES (?, ?, ?, ?)""", (self._tid,int(time.time()),
+ str(rpmdb_version),
+ return_code))
self._commit()
if not return_code:
# Simple hack, if the transaction finished
executeSQL(cur,
- """UPDATE trans_data_pkgs SET done = 'TRUE'
- WHERE tid = ?""", (self._tid,))
+ """UPDATE trans_data_pkgs SET done = ?
+ WHERE tid = ?""", ('TRUE', self._tid,))
self._commit()
if errors is not None:
self._log_errors(errors)
@@ -345,27 +346,26 @@ class YumHistory:
def _old_with_pkgs(self, tid):
cur = self._get_cursor()
- res = executeSQL(cur,
- """SELECT name, arch, epoch, version, release, checksum
- FROM trans_with_pkgs JOIN pkgtups USING(pkgtupid)
- WHERE tid = ?
- ORDER BY name ASC, epoch ASC""", (tid,))
+ executeSQL(cur,
+ """SELECT name, arch, epoch, version, release, checksum
+ FROM trans_with_pkgs JOIN pkgtups USING(pkgtupid)
+ WHERE tid = ?
+ ORDER BY name ASC, epoch ASC""", (tid,))
ret = []
- for row in res:
+ for row in cur:
obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
ret.append(obj)
return ret
def _old_data_pkgs(self, tid):
cur = self._get_cursor()
- res = executeSQL(cur,
- """SELECT name, arch, epoch, version, release,
- checksum, done, state
- FROM trans_data_pkgs JOIN pkgtups USING(pkgtupid)
- WHERE tid = ?
- ORDER BY name ASC, epoch ASC, state DESC""",
- (tid,))
+ executeSQL(cur,
+ """SELECT name, arch, epoch, version, release,
+ checksum, done, state
+ FROM trans_data_pkgs JOIN pkgtups USING(pkgtupid)
+ WHERE tid = ?
+ ORDER BY name ASC, epoch ASC, state DESC""", (tid,))
ret = []
- for row in res:
+ for row in cur:
obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
obj.done = row[6] == 'TRUE'
obj.state = row[7]
@@ -392,9 +392,9 @@ class YumHistory:
sql += " WHERE tid IN (%s)" % ", ".join(['?'] * len(tids))
params = list(tids)
sql += " ORDER BY beg_ts DESC, tid ASC"
- res = executeSQL(cur, sql, params)
+ executeSQL(cur, sql, params)
ret = []
- for row in res:
+ for row in cur:
ret.append(YumHistoryTransaction(self, row))
# Go through backwards, and see if the rpmdb versions match
@@ -424,10 +424,8 @@ class YumHistory:
FROM trans_beg OUTER JOIN trans_end USING(tid)
ORDER BY beg_ts DESC, tid ASC
LIMIT 1"""
- res = executeSQL(cur, sql)
- if res is None:
- res = []
- for row in res:
+ executeSQL(cur, sql)
+ for row in cur:
return YumHistoryTransaction(self, row)
return None
diff --git a/yum/sqlutils.py b/yum/sqlutils.py
index fda380e..3a93146 100644
--- a/yum/sqlutils.py
+++ b/yum/sqlutils.py
@@ -121,6 +121,11 @@ def QmarkToPyformat(query, params):
if token.endswith("?"):
output.append(token[:-1] + "%%(param%d)s" % count)
count+=1
+ elif token.endswith("?,") or token.endswith("?)"):
+ ntoken = token[:-2] + "%%(param%d)s" % count
+ ntoken += token[-1]
+ output.append(ntoken)
+ count+=1
else:
output.append(token)
commit 5e3e4ec0dfb5700d50b2415f5a31661e4fe14c9f
Author: James Antill <james at and.org>
Date: Mon Aug 31 11:50:35 2009 -0400
Log errors when transaction fails, have return_code == 2.
Also fix rpmdb version check on install, so it works if there are no
historical transactions.
diff --git a/yum/__init__.py b/yum/__init__.py
index 1ad9942..5dfe9bf 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1045,7 +1045,9 @@ class YumBase(depsolve.Depsolve):
'yum-rhn-plugin']
using_pkgs = self.rpmdb.returnPackages(patterns=using_pkgs_pats)
rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
- lastdbv = self.history.last().end_rpmdbversion
+ lastdbv = self.history.last()
+ if lastdbv is not None:
+ lastdbv = lastdbv.end_rpmdbversion
if lastdbv is not None and rpmdbv != lastdbv:
errstring = _('Warning: RPMDB has been altered since the last yum transaction.')
self.logger.warning(errstring)
@@ -1066,6 +1068,7 @@ class YumBase(depsolve.Depsolve):
self.verbose_logger.debug(errstring)
resultobject.return_code = 1
else:
+ self.history.end(rpmdbv, 2, errors=errors)
raise Errors.YumBaseError, errors
if not self.conf.keepcache:
diff --git a/yum/history.py b/yum/history.py
index 066d6cd..03ddff4 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -315,7 +315,16 @@ class YumHistory:
self._commit()
- def end(self, rpmdb_version, return_code):
+ def _log_errors(self, errors):
+ cur = self._get_cursor()
+ for error in errors:
+ executeSQL(cur,
+ """INSERT INTO trans_error
+ (tid, msg) VALUES (?, ?)""", (self._tid, error))
+ self._commit()
+
+ def end(self, rpmdb_version, return_code, errors=None):
+ assert return_code or not errors
cur = self._get_cursor()
res = executeSQL(cur,
"""INSERT INTO trans_end
@@ -330,6 +339,8 @@ class YumHistory:
"""UPDATE trans_data_pkgs SET done = 'TRUE'
WHERE tid = ?""", (self._tid,))
self._commit()
+ if errors is not None:
+ self._log_errors(errors)
del self._tid
def _old_with_pkgs(self, tid):
@@ -414,6 +425,8 @@ class YumHistory:
ORDER BY beg_ts DESC, tid ASC
LIMIT 1"""
res = executeSQL(cur, sql)
+ if res is None:
+ res = []
for row in res:
return YumHistoryTransaction(self, row)
return None
commit cb4cacfd28c44cd70332456374d454c3544a9bb8
Author: James Antill <james at and.org>
Date: Mon Aug 31 11:46:48 2009 -0400
Check rpmdb version against last history transaction, in version command
diff --git a/yumcommands.py b/yumcommands.py
index 1e56e0e..9c43af6 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -1105,6 +1105,12 @@ class VersionCommand(YumCommand):
if base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3):
main_only = False
data = base.rpmdb.simpleVersion(main_only)
+ lastdbv = base.history.last()
+ if lastdbv is not None:
+ lastdbv = lastdbv.end_rpmdbversion
+ if lastdbv is not None and data[0] != lastdbv:
+ errstring = _('Warning: RPMDB has been altered since the last yum transaction.')
+ base.logger.warning(errstring)
cols.append(("%s %s/%s" % (_("Installed:"), rel, ba),
str(data[0])))
_append_repos(cols, data[1])
commit 321fc928dac9799cf6e323a0a97e5ce67b941d6e
Author: James Antill <james at and.org>
Date: Mon Aug 31 11:45:38 2009 -0400
Check rpmdb version against latest history transaction.
Also default to last transaction in history info, if no args. given.
diff --git a/output.py b/output.py
index 350a18b..d21046e 100755
--- a/output.py
+++ b/output.py
@@ -1240,11 +1240,24 @@ to exit.
print "-" * 79
fmt = "%6u | %-22.22s | %-16s | %-14s | %4u"
done = 0
+ lastdbv = self.history.last()
+ if lastdbv is not None:
+ lasttid = lastdbv.tid
+ lastdbv = lastdbv.end_rpmdbversion
for old in self.history.old(tids):
if not printall and done > 19:
break
done += 1
+ if lastdbv is not None and not old.altered_rpmdb:
+ # If this is the last transaction, is good and it doesn't
+ # match the current rpmdb ... then mark it as bad.
+ if old.tid == lasttid:
+ rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
+ if lastdbv != rpmdbv:
+ old.altered_rpmdb = True
+ lastdbv = None
+
name = self._pwd_ui_username(old.loginuid, 22)
tm = time.strftime("%Y-%m-%d %H:%M",
time.localtime(old.beg_timestamp))
@@ -1292,12 +1305,31 @@ to exit.
if pats:
tids.update(self.history.search(pats))
+ if not tids and len(extcmds) < 2:
+ old = self.history.last()
+ if old is not None:
+ tids.add(old.tid)
+
if not tids:
self.logger.critical(_('No transaction ID, or package, given'))
return 1, ['Failed history info']
+ lastdbv = self.history.last()
+ if lastdbv is not None:
+ lasttid = lastdbv.tid
+ lastdbv = lastdbv.end_rpmdbversion
+
done = False
for tid in self.history.old(tids):
+ if lastdbv is not None and not tid.altered_rpmdb:
+ # If this is the last transaction, is good and it doesn't
+ # match the current rpmdb ... then mark it as bad.
+ if tid.tid == lasttid:
+ rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
+ if lastdbv != rpmdbv:
+ tid.altered_rpmdb = True
+ lastdbv = None
+
if done:
print "-" * 79
done = True
commit 8edc7f565ef2110e2c789544b31f660c7d6f2077
Author: James Antill <james at and.org>
Date: Sun Aug 30 15:08:31 2009 -0400
Add speedup to rpmdb version generation.
We only need/use the main version for history collection.
Also speedup version command, in non-verbose case.
diff --git a/yum/__init__.py b/yum/__init__.py
index 48d8810..1ad9942 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1044,7 +1044,7 @@ class YumBase(depsolve.Depsolve):
using_pkgs_pats = ['yum', 'rpm', 'python', 'yum-metadata-parser',
'yum-rhn-plugin']
using_pkgs = self.rpmdb.returnPackages(patterns=using_pkgs_pats)
- rpmdbv = self.rpmdb.simpleVersion()[0]
+ rpmdbv = self.rpmdb.simpleVersion(main_only=True)[0]
lastdbv = self.history.last().end_rpmdbversion
if lastdbv is not None and rpmdbv != lastdbv:
errstring = _('Warning: RPMDB has been altered since the last yum transaction.')
@@ -1156,7 +1156,7 @@ class YumBase(depsolve.Depsolve):
ret = -1
if resultobject is not None:
ret = resultobject.return_code
- self.history.end(self.rpmdb.simpleVersion()[0], ret)
+ self.history.end(self.rpmdb.simpleVersion(main_only=True)[0], ret)
self.rpmdb.dropCachedData()
def costExcludePackages(self):
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index fbeb9b3..8368d52 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -339,7 +339,7 @@ class RPMDBPackageSack(PackageSackBase):
pkgobjlist = pkgobjlist[0] + pkgobjlist[1]
return pkgobjlist
- def simpleVersion(self):
+ def simpleVersion(self, main_only=False):
""" Return a simple version for all installed packages. """
main = PackageSackVersion()
irepos = {}
@@ -350,6 +350,9 @@ class RPMDBPackageSack(PackageSackBase):
csum = (ydbi.checksum_type, ydbi.checksum_data)
main.update(pkg, csum)
+ if main_only:
+ continue
+
repoid = 'installed'
rev = None
if 'from_repo' in pkg.yumdb_info:
diff --git a/yumcommands.py b/yumcommands.py
index c232207..1e56e0e 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -1101,11 +1101,13 @@ class VersionCommand(YumCommand):
cols = []
if vcmd in ('installed', 'all'):
try:
- data = base.rpmdb.simpleVersion()
+ main_only = True
+ if base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3):
+ main_only = False
+ data = base.rpmdb.simpleVersion(main_only)
cols.append(("%s %s/%s" % (_("Installed:"), rel, ba),
str(data[0])))
- if base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3):
- _append_repos(cols, data[1])
+ _append_repos(cols, data[1])
except yum.Errors.YumBaseError, e:
return 1, [str(e)]
if vcmd in ('available', 'all'):
commit 3966ae1395375a6a5bcf5c96930dc1ba79d4d97b
Author: James Antill <james at and.org>
Date: Sun Aug 30 14:54:54 2009 -0400
Add rpmdb version check, and output warning if it doesn't match
Added comparison to PackageSackVersion against str().
diff --git a/yum/__init__.py b/yum/__init__.py
index a6a6502..48d8810 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1044,8 +1044,12 @@ class YumBase(depsolve.Depsolve):
using_pkgs_pats = ['yum', 'rpm', 'python', 'yum-metadata-parser',
'yum-rhn-plugin']
using_pkgs = self.rpmdb.returnPackages(patterns=using_pkgs_pats)
- self.history.beg(self.rpmdb.simpleVersion()[0], using_pkgs,
- list(self.tsInfo))
+ rpmdbv = self.rpmdb.simpleVersion()[0]
+ lastdbv = self.history.last().end_rpmdbversion
+ if lastdbv is not None and rpmdbv != lastdbv:
+ errstring = _('Warning: RPMDB has been altered since the last yum transaction.')
+ self.logger.warning(errstring)
+ self.history.beg(rpmdbv, using_pkgs, list(self.tsInfo))
errors = self.ts.run(cb.callback, '')
# ts.run() exit codes are, hmm, "creative": None means all ok, empty
diff --git a/yum/packageSack.py b/yum/packageSack.py
index 3716410..cc26d03 100644
--- a/yum/packageSack.py
+++ b/yum/packageSack.py
@@ -35,6 +35,8 @@ class PackageSackVersion:
def __eq__(self, other):
if other is None: return False
+ if type(other) in (type(''), type(u'')):
+ return str(self) == other
if self._num != other._num: return False
if self._chksum.digest() != other._chksum.digest(): return False
return True
commit ef19311f17d19b90e947fe76caf3d110170b328c
Author: James Antill <james at and.org>
Date: Sun Aug 30 14:32:36 2009 -0400
Add "last" function to history, to get the last transaction.
Also added real YumHistoryTransaction() class, and change the
trans_with/trans_data members to be dynamic properties.
diff --git a/yum/history.py b/yum/history.py
index e8e7215..066d6cd 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -129,6 +129,45 @@ class YumHistoryPackage(PackageObject):
chk = checksum.split(':')
self._checksums = [(chk[0], chk[1], 0)] # (type, checksum, id(0,1))
+class YumHistoryTransaction:
+ """ Holder for a history transaction. """
+
+ def __init__(self, history, row):
+ self._history = weakref(history)
+
+ self.tid = row[0]
+ self.beg_timestamp = row[1]
+ self.beg_rpmdbversion = row[2]
+ self.end_timestamp = row[3]
+ self.end_rpmdbversion = row[4]
+ self.loginuid = row[5]
+ self.return_code = row[6]
+
+ self._loaded_TW = None
+ self._loaded_TD = None
+
+ def __cmp__(self, other):
+ if other is None:
+ return 1
+ ret = cmp(self.beg_timestamp, other.beg_timestamp)
+ if ret: return -ret
+ ret = cmp(self.end_timestamp, other.end_timestamp)
+ if ret: return ret
+ ret = cmp(self.tid, other.tid)
+ return -ret
+
+ def _getTransWith(self):
+ if self._loaded_TW is None:
+ self._loaded_TW = sorted(self._history._old_with_pkgs(self.tid))
+ return self._loaded_TW
+ def _getTransData(self):
+ if self._loaded_TD is None:
+ self._loaded_TD = sorted(self._history._old_data_pkgs(self.tid))
+ return self._loaded_TD
+
+ trans_with = property(fget=lambda self: self._getTransWith())
+ trans_data = property(fget=lambda self: self._getTransData())
+
class YumHistory:
""" API for accessing the history sqlite data. """
@@ -345,17 +384,7 @@ class YumHistory:
res = executeSQL(cur, sql, params)
ret = []
for row in res:
- obj = yum.misc.GenericHolder()
- obj.tid = row[0]
- obj.beg_timestamp = row[1]
- obj.beg_rpmdbversion = row[2]
- obj.end_timestamp = row[3]
- obj.end_rpmdbversion = row[4]
- obj.loginuid = row[5]
- obj.return_code = row[6]
- obj.trans_with = sorted(self._old_with_pkgs(obj.tid))
- obj.trans_data = sorted(self._old_data_pkgs(obj.tid))
- ret.append(obj)
+ ret.append(YumHistoryTransaction(self, row))
# Go through backwards, and see if the rpmdb versions match
last_rv = None
@@ -373,6 +402,22 @@ class YumHistory:
return ret
+ def last(self):
+ cur = self._get_cursor()
+ sql = """SELECT tid,
+ trans_beg.timestamp AS beg_ts,
+ trans_beg.rpmdb_version AS beg_rv,
+ trans_end.timestamp AS end_ts,
+ trans_end.rpmdb_version AS end_rv,
+ loginuid, return_code
+ FROM trans_beg OUTER JOIN trans_end USING(tid)
+ ORDER BY beg_ts DESC, tid ASC
+ LIMIT 1"""
+ res = executeSQL(cur, sql)
+ for row in res:
+ return YumHistoryTransaction(self, row)
+ return None
+
def _yieldSQLDataList(self, patterns, fields, ignore_case):
"""Yields all the package data for the given params. """
commit 6d00bcea72379e67eb16645d73f99efbfcfd59c8
Author: James Antill <james at and.org>
Date: Thu Aug 27 16:32:40 2009 -0400
Add search() API to history, and a lot of UI improvements for history.
Add .search() API, which works like "yum list" but returns history
transaction IDs that are relevant.
Add ability to search for packages in history list/info/summary. Also
integrate with ability to specify multiple transaction IDs.
Better UI for list/info/summary.
Added "action" column to list/summary.
Added gecos fullname to list/info/summary (if it fits).
Convert "True-Install" => "Install", as that's internal magic.
Change Altered number to be what people expect (ie. an update is 1).
Use ISO date/time in list/summary.
Have Packages Used, print a readable version of what has happend to
the pkg.
Limit list/summary to 19 lines, by default (full screen on 24 row
terminal).
Chop end time in info.
Print "Success" for return code == 0.
Add display of packages when using undo/repeat.
Highlight packages matched in history info.
diff --git a/output.py b/output.py
index 15360fa..350a18b 100755
--- a/output.py
+++ b/output.py
@@ -41,6 +41,7 @@ from yum.constants import *
from yum import logginglevels, _
from yum.rpmtrans import RPMBaseCallback
from yum.packageSack import packagesNewestByNameArch
+import yum.packages
from yum.i18n import utf8_width, utf8_width_fill, utf8_text_fill
@@ -1173,113 +1174,224 @@ to exit.
ui_bs, ui_size, ui_time, ui_end)
self.verbose_logger.log(logginglevels.INFO_2, msg)
+ def _history_uiactions(self, hpkgs):
+ actions = set()
+ count = 0
+ for hpkg in hpkgs:
+ st = hpkg.state
+ if st == 'True-Install':
+ st = 'Install'
+ if st in ('Install', 'Update', 'Erase', 'Reinstall', 'Downgrade',
+ 'Obsoleting', 'Obsoleted'):
+ actions.add(st)
+ count += 1
+ assert len(actions) <= 6
+ if len(actions) > 1:
+ return count, ", ".join([x[0] for x in sorted(actions)])
+
+ # So empty transactions work, although that "shouldn't" really happen
+ return count, "".join(list(actions))
+
+ def _pwd_ui_username(self, uid, limit=None):
+ try:
+ user = pwd.getpwuid(uid)
+ fullname = user.pw_gecos.split(';', 2)[0]
+ name = "%s <%s>" % (fullname, user.pw_name)
+ if limit is not None and len(name) > limit:
+ name = "%s ... <%s>" % (fullname.split()[0], user.pw_name)
+ if len(name) > limit:
+ name = "<%s>" % user.pw_name
+ return name
+ except KeyError:
+ return str(uid)
+
+ def _history_list_transactions(self, extcmds):
+ tids = set()
+ pats = []
+ usertids = extcmds[1:]
+ printall = False
+ if usertids:
+ printall = True
+ if usertids[0] == 'all':
+ usertids.pop(0)
+ for tid in usertids:
+ try:
+ int(tid)
+ tids.add(tid)
+ except ValueError:
+ pats.append(tid)
+ if pats:
+ tids.update(self.history.search(pats))
+
+ if not tids and usertids:
+ self.logger.critical(_('Bad transaction IDs, or package(s), given'))
+ return None, None
+ return tids, printall
+
def historyListCmd(self, extcmds):
""" Shows the user a list of data about the history. """
- tid = None
- try:
- if len(extcmds) > 1:
- tid = int(extcmds[1])
- except ValueError:
- pass
- fmt = "%-8s | %-16s | %-35s | %-11s"
- print fmt % ("ID", "Login user", "Start time", "Altered")
+ tids, printall = self._history_list_transactions(extcmds)
+ if tids is None:
+ return 1, ['Failed history info']
+
+ fmt = "%-6s | %-22s | %-16s | %-14s | %-7s"
+ print fmt % ("ID", "Login user", "Date and time", "Action(s)","Altered")
print "-" * 79
- fmt = "%8u | %-16.16s | %-35.35s | %8u"
- last_rv = None
- for old in self.history.old(tid):
- name = old.loginuid
- try:
- usertup = pwd.getpwuid(old.loginuid)
- name = usertup[0]
- except KeyError:
- pass
- tm = time.ctime(old.beg_timestamp)
+ fmt = "%6u | %-22.22s | %-16s | %-14s | %4u"
+ done = 0
+ for old in self.history.old(tids):
+ if not printall and done > 19:
+ break
+
+ done += 1
+ name = self._pwd_ui_username(old.loginuid, 22)
+ tm = time.strftime("%Y-%m-%d %H:%M",
+ time.localtime(old.beg_timestamp))
+ num, uiacts = self._history_uiactions(old.trans_data)
if old.altered_rpmdb:
- print fmt % (old.tid, name, tm, len(old.trans_data)), "**"
+ print fmt % (old.tid, name, tm, uiacts, num), "**"
else:
- print fmt % (old.tid, name, tm, len(old.trans_data))
- last_rv = old.end_rpmdbversion
+ print fmt % (old.tid, name, tm, uiacts, num)
- def _history_get_transaction(self, extcmds):
+ def _history_get_transactions(self, extcmds):
if len(extcmds) < 2:
self.logger.critical(_('No transaction ID given'))
return None
+ tids = []
try:
- tid = int(extcmds[1])
+ int(extcmds[1])
+ tids.append(extcmds[1])
except ValueError:
self.logger.critical(_('Bad transaction ID given'))
return None
- old = self.history.old(tid)
+ old = self.history.old(tids)
if not old:
self.logger.critical(_('Not found given transaction ID'))
return None
+ return old
+ def _history_get_transaction(self, extcmds):
+ old = self._history_get_transactions(extcmds)
+ if old is None:
+ return None
if len(old) > 1:
self.logger.critical(_('Found more than one transaction ID!'))
return old[0]
def historyInfoCmd(self, extcmds):
- old = self._history_get_transaction(extcmds)
- if old is None:
+ tids = set()
+ pats = []
+ for tid in extcmds[1:]:
+ try:
+ int(tid)
+ tids.add(tid)
+ except ValueError:
+ pats.append(tid)
+ if pats:
+ tids.update(self.history.search(pats))
+
+ if not tids:
+ self.logger.critical(_('No transaction ID, or package, given'))
return 1, ['Failed history info']
- name = old.loginuid
- try:
- usertup = pwd.getpwuid(old.loginuid)
- name = usertup[0]
- except KeyError:
- pass
-
- print _("Transaction ID:"), old.tid
- print _("Begin time :"), time.ctime(old.beg_timestamp)
- print _("Begin rpmdb :"), old.beg_rpmdbversion
- print _("End time :"), time.ctime(old.end_timestamp)
- print _("End rpmdb :"), old.end_rpmdbversion
- print _("User :"), name
- print _("Return-Code :"), old.return_code
- print _("Packages Used :")
+ done = False
+ for tid in self.history.old(tids):
+ if done:
+ print "-" * 79
+ done = True
+ self._historyInfoCmd(tid, pats)
+
+ def _historyInfoCmd(self, old, pats=[]):
+ name = self._pwd_ui_username(old.loginuid)
+
+ print _("Transaction ID :"), old.tid
+ begtm = time.ctime(old.beg_timestamp)
+ print _("Begin time :"), begtm
+ if old.beg_rpmdbversion is not None:
+ print _("Begin rpmdb :"), old.beg_rpmdbversion
+ endtm = time.ctime(old.end_timestamp)
+ endtms = endtm.split()
+ if begtm.startswith(endtms[0]): # Chop uninteresting prefix
+ begtms = begtm.split()
+ sofar = 0
+ for i in range(len(endtms)):
+ if i > len(begtms):
+ break
+ if begtms[i] != endtms[i]:
+ break
+ sofar += len(begtms[i]) + 1
+ endtm = (' ' * sofar) + endtm[sofar:]
+ print _("End time :"), endtm
+ if old.end_rpmdbversion is not None:
+ print _("End rpmdb :"), old.end_rpmdbversion
+ print _("User :"), name
+ if old.return_code:
+ print _("Return-Code :"), _("Failure:"), old.return_code
+ else:
+ print _("Return-Code :"), _("Success")
+ print _("Packages Used :")
for hpkg in old.trans_with:
prefix = " " * 4
- if not self.rpmdb.contains(po=hpkg):
- prefix = " ** "
- if hpkg.epoch == '0':
- print "%s%s" % (prefix, hpkg)
-
+ state = _('Installed')
+ ipkgs = self.rpmdb.searchNames([hpkg.name])
+ ipkgs.sort()
+ if not ipkgs:
+ state = _('Erased')
+ elif hpkg.pkgtup in (ipkg.pkgtup for ipkg in ipkgs):
+ pass
+ elif ipkgs[-1] > hpkg:
+ state = _('Updated')
+ elif ipkgs[0] < hpkg:
+ state = _('Downgraded')
+ else: # multiple versions installed, both older and newer
+ state = _('Weird')
+ print "%s%-12s %s" % (prefix, state, hpkg)
print _("Packages Altered:")
+ self.historyInfoCmdPkgsAltered(old, pats)
+
+ def historyInfoCmdPkgsAltered(self, old, pats=[]):
for hpkg in old.trans_data:
prefix = " " * 4
if not hpkg.done:
prefix = " ** "
+ highlight = 'normal'
+ if pats:
+ x,m,u = yum.packages.parsePackages([hpkg], pats)
+ if x or m:
+ highlight = 'bold'
+ (hibeg, hiend) = self._highlight(highlight)
+
if False: pass
elif hpkg.state == 'Update':
ln = len(hpkg.name) + 1
cn = (" " * ln) + str(hpkg)[ln:]
- print "%s%-12s %s" % (prefix, hpkg.state, cn)
+ print "%s%s%-12s%s %s" % (prefix, hibeg, hpkg.state, hiend, cn)
elif hpkg.state == 'Downgraded':
ln = len(hpkg.name) + 1
cn = (" " * ln) + str(hpkg)[ln:]
- print "%s%-12s %s" % (prefix, hpkg.state, cn)
+ print "%s%s%-12s%s %s" % (prefix, hibeg, hpkg.state, hiend, cn)
+ elif hpkg.state == 'True-Install':
+ print "%s%s%-12s%s %s" % (prefix, hibeg, "Install", hiend, hpkg)
else:
- print "%s%-12s %s" % (prefix, hpkg.state, hpkg)
+ print "%s%s%-12s%s %s" % (prefix, hibeg, hpkg.state, hiend,hpkg)
def historySummaryCmd(self, extcmds):
- fmt = "%-16s | %-38s | %-8s"
- print fmt % ("Login user", "Time (seconds taken)", "Altered")
+ tids, printall = self._history_list_transactions(extcmds)
+ if tids is None:
+ return 1, ['Failed history info']
+
+ fmt = "%-26s | %-19s | %-16s | %-8s"
+ print fmt % ("Login user", "Time", "Action(s)", "Altered")
print "-" * 79
- fmt = "%-16.16s | %-38.38s | %8u"
+ fmt = "%-26.26s | %-19.19s | %-16s | %8u"
data = {'day' : {}, 'week' : {},
'fortnight' : {}, 'quarter' : {}, 'half' : {},
'year' : {}, 'all' : {}}
- for old in self.history.old():
- name = old.loginuid
- try:
- usertup = pwd.getpwuid(old.loginuid)
- name = usertup[0]
- except KeyError:
- pass
+ for old in self.history.old(tids):
+ name = self._pwd_ui_username(old.loginuid, 26)
period = 'all'
now = time.time()
if False: pass
@@ -1296,24 +1408,29 @@ to exit.
elif old.beg_timestamp > (now - (24 * 60 * 60 * 365)):
period = 'year'
data[period].setdefault(name, []).append(old)
- _period2user = {'day' : _("Last day"),
- 'week' : _("Last week"),
+ _period2user = {'day' : _("Last day"),
+ 'week' : _("Last week"),
'fortnight' : _("Last 2 weeks"), # US default :p
- 'quarter' : _("Last 3 months"),
- 'half' : _("Last 6 months"),
- 'year' : _("Last year"),
- 'all' : _("Over a year ago")}
+ 'quarter' : _("Last 3 months"),
+ 'half' : _("Last 6 months"),
+ 'year' : _("Last year"),
+ 'all' : _("Over a year ago")}
+ done = 0
for period in ('day', 'week', 'fortnight', 'quarter', 'half', 'year',
'all'):
if not data[period]:
continue
for name in sorted(data[period]):
- tm = sum(map(lambda x: x.end_timestamp - x.beg_timestamp,
- data[period][name]))
- pkgs = sum(map(lambda x: len(x.trans_data), data[period][name]))
+ if not printall and done > 19:
+ break
+ done += 1
+
+ hpkgs = []
+ for old in data[period][name]:
+ hpkgs.extend(old.trans_data)
+ count, uiacts = self._history_uiactions(hpkgs)
uperiod = _period2user[period]
- uperiod += (" (%u)" % tm)
- print fmt % (name, uperiod, pkgs)
+ print fmt % (name, uperiod, uiacts, count)
class DepSolveProgressCallBack:
diff --git a/yum/history.py b/yum/history.py
index ea87467..e8e7215 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -48,6 +48,71 @@ _sttxt2stcode = {'Update' : TS_UPDATE,
'Obsoleted' : TS_OBSOLETED,
'Obsoleting' : TS_OBSOLETING}
+# ---- horrible Copy and paste from sqlitesack ----
+def _sql_esc(pattern):
+ """ Apply SQLite escaping, if needed. Returns pattern and esc. """
+ esc = ''
+ if "_" in pattern or "%" in pattern:
+ esc = ' ESCAPE "!"'
+ pattern = pattern.replace("!", "!!")
+ pattern = pattern.replace("%", "!%")
+ pattern = pattern.replace("_", "!_")
+ return (pattern, esc)
+
+def _sql_esc_glob(patterns):
+ """ Converts patterns to SQL LIKE format, if required (or gives up if
+ not possible). """
+ ret = []
+ for pattern in patterns:
+ if '[' in pattern: # LIKE only has % and _, so [abc] can't be done.
+ return [] # So Load everything
+
+ # Convert to SQL LIKE format
+ (pattern, esc) = _sql_esc(pattern)
+ pattern = pattern.replace("*", "%")
+ pattern = pattern.replace("?", "_")
+ ret.append((pattern, esc))
+ return ret
+
+def _setupHistorySearchSQL(patterns=None, ignore_case=False):
+ """Setup need_full and patterns for _yieldSQLDataList, also see if
+ we can get away with just using searchNames(). """
+
+ if patterns is None:
+ patterns = []
+
+ fields = ['name', 'sql_nameArch', 'sql_nameVerRelArch',
+ 'sql_nameVer', 'sql_nameVerRel',
+ 'sql_envra', 'sql_nevra']
+ need_full = False
+ for pat in patterns:
+ if yum.misc.re_full_search_needed(pat):
+ need_full = True
+ break
+
+ pat_max = PATTERNS_MAX
+ if not need_full:
+ fields = ['name']
+ pat_max = PATTERNS_INDEXED_MAX
+ if len(patterns) > pat_max:
+ patterns = []
+ if ignore_case:
+ patterns = _sql_esc_glob(patterns)
+ else:
+ tmp = []
+ need_glob = False
+ for pat in patterns:
+ if misc.re_glob(pat):
+ tmp.append((pat, 'glob'))
+ need_glob = True
+ else:
+ tmp.append((pat, '='))
+ if not need_full and not need_glob and patterns:
+ return (need_full, patterns, fields, True)
+ patterns = tmp
+ return (need_full, patterns, fields, False)
+# ---- horrible Copy and paste from sqlitesack ----
+
class YumHistoryPackage(PackageObject):
def __init__(self, name, arch, epoch, version, release, checksum):
@@ -262,7 +327,7 @@ class YumHistory:
ret.append(obj)
return ret
- def old(self, tid=None):
+ def old(self, tids=[]):
cur = self._get_cursor()
sql = """SELECT tid,
trans_beg.timestamp AS beg_ts,
@@ -272,9 +337,10 @@ class YumHistory:
loginuid, return_code
FROM trans_beg OUTER JOIN trans_end USING(tid)"""
params = None
- if tid is not None:
- sql += " WHERE tid = ?"
- params = (tid,)
+ if tids:
+ tids = set(tids)
+ sql += " WHERE tid IN (%s)" % ", ".join(['?'] * len(tids))
+ params = list(tids)
sql += " ORDER BY beg_ts DESC, tid ASC"
res = executeSQL(cur, sql, params)
ret = []
@@ -292,19 +358,65 @@ class YumHistory:
ret.append(obj)
# Go through backwards, and see if the rpmdb versions match
- last_rv = None
+ last_rv = None
+ last_tid = None
for obj in reversed(ret):
cur_rv = obj.beg_rpmdbversion
- if last_rv is None or cur_rv is None:
+ if last_rv is None or cur_rv is None or (last_tid + 1) != obj.tid:
obj.altered_rpmdb = None
elif last_rv != cur_rv:
obj.altered_rpmdb = True
else:
obj.altered_rpmdb = False
- last_rv = obj.end_rpmdbversion
+ last_rv = obj.end_rpmdbversion
+ last_tid = obj.tid
return ret
+ def _yieldSQLDataList(self, patterns, fields, ignore_case):
+ """Yields all the package data for the given params. """
+
+ cur = self._get_cursor()
+ qsql = _FULL_PARSE_QUERY_BEG
+
+ pat_sqls = []
+ pat_data = []
+ for (pattern, rest) in patterns:
+ for field in fields:
+ if ignore_case:
+ pat_sqls.append("%s LIKE ?%s" % (field, rest))
+ else:
+ pat_sqls.append("%s %s ?" % (field, rest))
+ pat_data.append(pattern)
+ assert pat_sqls
+
+ qsql += " OR ".join(pat_sqls)
+ executeSQL(cur, qsql, pat_data)
+ for x in cur:
+ yield x
+
+ def search(self, patterns, ignore_case=True):
+ """ Search for history transactions which contain specified
+ packages al. la. "yum list". Returns transaction ids. """
+ # Search packages ... kind of sucks that it's search not list, pkglist?
+
+ data = _setupHistorySearchSQL(patterns, ignore_case)
+ (need_full, patterns, fields, names) = data
+
+ ret = []
+ pkgtupids = set()
+ for row in self._yieldSQLDataList(patterns, fields, ignore_case):
+ pkgtupids.add(row[0])
+
+ cur = self._get_cursor()
+ sql = """SELECT tid FROM trans_data_pkgs WHERE pkgtupid IN """
+ sql += "(%s)" % ",".join(['?'] * len(pkgtupids))
+ params = list(pkgtupids)
+ tids = set()
+ for row in executeSQL(cur, sql, params):
+ tids.add(row[0])
+ return tids
+
def _create_db_file(self):
""" Create a new history DB file, populating tables etc. """
@@ -361,3 +473,16 @@ class YumHistory:
for op in ops:
cur.execute(op)
self._commit()
+
+# Pasted from sqlitesack
+_FULL_PARSE_QUERY_BEG = """
+SELECT pkgtupid,name,epoch,version,release,arch,
+ name || "." || arch AS sql_nameArch,
+ name || "-" || version || "-" || release || "." || arch AS sql_nameVerRelArch,
+ name || "-" || version AS sql_nameVer,
+ name || "-" || version || "-" || release AS sql_nameVerRel,
+ epoch || ":" || name || "-" || version || "-" || release || "." || arch AS sql_envra,
+ name || "-" || epoch || ":" || version || "-" || release || "." || arch AS sql_nevra
+ FROM pkgtups
+ WHERE
+"""
diff --git a/yumcommands.py b/yumcommands.py
index 5029134..c232207 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -1150,20 +1150,22 @@ class HistoryCommand(YumCommand):
return _("Display, or use, the transaction history")
def _hcmd_repeat(self, base, extcmds):
- old = base._history_get_transaction(base, extcmds)
+ old = base._history_get_transaction(extcmds)
if old is None:
return 1, ['Failed history repeat']
tm = time.ctime(old.beg_timestamp)
- print "Repeating transaction %u, from %s" % (transaction.tid, tm)
+ print "Repeating transaction %u, from %s" % (old.tid, tm)
+ base.historyInfoCmdPkgsAltered(old)
if base.history_repeat(old):
- return 2, ["Repeating transaction %u" % (transaction.tid,)]
+ return 2, ["Repeating transaction %u" % (old.tid,)]
def _hcmd_undo(self, base, extcmds):
- old = base._history_get_transaction(base, extcmds)
+ old = base._history_get_transaction(extcmds)
if old is None:
return 1, ['Failed history undo']
tm = time.ctime(old.beg_timestamp)
print "Undoing transaction %u, from %s" % (old.tid, tm)
+ base.historyInfoCmdPkgsAltered(old)
if base.history_undo(old):
return 2, ["Undoing transaction %u" % (old.tid,)]
commit df3b0bdc57d50017b996c65d1b3112f7fe63e32f
Author: James Antill <james at and.org>
Date: Wed Aug 26 16:52:22 2009 -0400
Show when rpmdb has been altered without a history transaction
Add API to work out when transactions have been altered
=> The beg rpmdb_version doesn't match the previous end
Show it to the user in history list.
diff --git a/output.py b/output.py
index 35b2dc9..15360fa 100755
--- a/output.py
+++ b/output.py
@@ -1182,10 +1182,11 @@ to exit.
except ValueError:
pass
- fmt = "%-8s | %-16s | %-38s | %-8s"
+ fmt = "%-8s | %-16s | %-35s | %-11s"
print fmt % ("ID", "Login user", "Start time", "Altered")
print "-" * 79
- fmt = "%8u | %-16.16s | %-38.38s | %8u"
+ fmt = "%8u | %-16.16s | %-35.35s | %8u"
+ last_rv = None
for old in self.history.old(tid):
name = old.loginuid
try:
@@ -1194,7 +1195,11 @@ to exit.
except KeyError:
pass
tm = time.ctime(old.beg_timestamp)
- print fmt % (old.tid, name, tm, len(old.trans_data))
+ if old.altered_rpmdb:
+ print fmt % (old.tid, name, tm, len(old.trans_data)), "**"
+ else:
+ print fmt % (old.tid, name, tm, len(old.trans_data))
+ last_rv = old.end_rpmdbversion
def _history_get_transaction(self, extcmds):
if len(extcmds) < 2:
diff --git a/yum/history.py b/yum/history.py
index 699b4ec..ea87467 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -290,6 +290,19 @@ class YumHistory:
obj.trans_with = sorted(self._old_with_pkgs(obj.tid))
obj.trans_data = sorted(self._old_data_pkgs(obj.tid))
ret.append(obj)
+
+ # Go through backwards, and see if the rpmdb versions match
+ last_rv = None
+ for obj in reversed(ret):
+ cur_rv = obj.beg_rpmdbversion
+ if last_rv is None or cur_rv is None:
+ obj.altered_rpmdb = None
+ elif last_rv != cur_rv:
+ obj.altered_rpmdb = True
+ else:
+ obj.altered_rpmdb = False
+ last_rv = obj.end_rpmdbversion
+
return ret
def _create_db_file(self):
commit 23c0b759d101b0f814f3bdb119cb182a3b5482b1
Author: James Antill <james at and.org>
Date: Wed Aug 26 16:28:49 2009 -0400
Move history_undo/history_repeat into YumBase, like install/downgrade/etc.
diff --git a/yum/__init__.py b/yum/__init__.py
index 33e143f..a6a6502 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -3581,6 +3581,66 @@ class YumBase(depsolve.Depsolve):
return returndict
+ def history_repeat(self, transaction):
+ """ Given a valid historical transaction object, try and repeat
+ that transaction. """
+ # This is basic atm.
+ done = False
+ for pkg in transaction.trans_data:
+ if pkg.state == 'Reinstall':
+ if self.reinstall(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in transaction.trans_data:
+ if pkg.state == 'Downgrade':
+ try:
+ if self.downgrade(pkgtup=pkg.pkgtup):
+ done = True
+ except yum.Errors.DowngradeError:
+ self.logger.critical(_('Failed to downgrade: %s'), pkg)
+ for pkg in transaction.trans_data:
+ if pkg.state == 'Update':
+ if self.update(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in transaction.trans_data:
+ if pkg.state in ('Install', 'True-Install'):
+ if self.install(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in transaction.trans_data:
+ if pkg.state == 'Erase':
+ if self.remove(pkgtup=pkg.pkgtup):
+ done = True
+ return done
+
+ def history_undo(self, transaction):
+ """ Given a valid historical transaction object, try and undo
+ that transaction. """
+ # FIXME: This is basic atm.
+ done = False
+ for pkg in transaction.trans_data:
+ if pkg.state == 'Reinstall':
+ if self.reinstall(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in transaction.trans_data:
+ if pkg.state == 'Updated':
+ try:
+ if self.downgrade(pkgtup=pkg.pkgtup):
+ done = True
+ except yum.Errors.DowngradeError:
+ self.logger.critical(_('Failed to downgrade: %s'), pkg)
+ for pkg in transaction.trans_data:
+ if pkg.state == 'Downgraded':
+ if self.update(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in transaction.trans_data:
+ if pkg.state in ('Install', 'True-Install'):
+ if self.remove(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in transaction.trans_data:
+ if pkg.state == 'Erase':
+ if self.install(pkgtup=pkg.pkgtup):
+ done = True
+ return done
+
def _retrievePublicKey(self, keyurl, repo=None):
"""
Retrieve a key file
diff --git a/yumcommands.py b/yumcommands.py
index 6bc7a3c..5029134 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -1154,34 +1154,9 @@ class HistoryCommand(YumCommand):
if old is None:
return 1, ['Failed history repeat']
tm = time.ctime(old.beg_timestamp)
- print "Repeating transaction %u, from %s" % (old.tid, tm)
- # This is basic atm.
- done = False
- for pkg in old.trans_data:
- if pkg.state == 'Reinstall':
- if base.reinstall(pkgtup=pkg.pkgtup):
- done = True
- for pkg in old.trans_data:
- if pkg.state == 'Downgrade':
- try:
- if base.downgrade(pkgtup=pkg.pkgtup):
- done = True
- except yum.Errors.DowngradeError:
- base.logger.critical(_('Failed to downgrade: %s'), pkg)
- for pkg in old.trans_data:
- if pkg.state == 'Update':
- if base.update(pkgtup=pkg.pkgtup):
- done = True
- for pkg in old.trans_data:
- if pkg.state in ('Install', 'True-Install'):
- if base.install(pkgtup=pkg.pkgtup):
- done = True
- for pkg in old.trans_data:
- if pkg.state == 'Erase':
- if base.remove(pkgtup=pkg.pkgtup):
- done = True
- if done:
- return 2, ["Repeating transaction %u" % (old.tid,)]
+ print "Repeating transaction %u, from %s" % (transaction.tid, tm)
+ if base.history_repeat(old):
+ return 2, ["Repeating transaction %u" % (transaction.tid,)]
def _hcmd_undo(self, base, extcmds):
old = base._history_get_transaction(base, extcmds)
@@ -1189,32 +1164,7 @@ class HistoryCommand(YumCommand):
return 1, ['Failed history undo']
tm = time.ctime(old.beg_timestamp)
print "Undoing transaction %u, from %s" % (old.tid, tm)
- # FIXME: This is __horribley__ basic atm.
- done = False
- for pkg in old.trans_data:
- if pkg.state == 'Reinstall':
- if base.reinstall(pkgtup=pkg.pkgtup):
- done = True
- for pkg in old.trans_data:
- if pkg.state == 'Updated':
- try:
- if base.downgrade(pkgtup=pkg.pkgtup):
- done = True
- except yum.Errors.DowngradeError:
- base.logger.critical(_('Failed to downgrade: %s'), pkg)
- for pkg in old.trans_data:
- if pkg.state == 'Downgraded':
- if base.update(pkgtup=pkg.pkgtup):
- done = True
- for pkg in old.trans_data:
- if pkg.state in ('Install', 'True-Install'):
- if base.remove(pkgtup=pkg.pkgtup):
- done = True
- for pkg in old.trans_data:
- if pkg.state == 'Erase':
- if base.install(pkgtup=pkg.pkgtup):
- done = True
- if done:
+ if base.history_undo(old):
return 2, ["Undoing transaction %u" % (old.tid,)]
def _hcmd_new(self, base, extcmds):
commit 0d9de029f1ddd09b3cb802fa3011c7b454a10c77
Author: James Antill <james at and.org>
Date: Wed Aug 26 16:22:06 2009 -0400
Move the history output commands to output.py, keeping yumcommands clean
diff --git a/output.py b/output.py
index 2299a9c..35b2dc9 100755
--- a/output.py
+++ b/output.py
@@ -22,6 +22,7 @@ import time
import logging
import types
import gettext
+import pwd
import rpm
import re # For YumTerm
@@ -1172,6 +1173,143 @@ to exit.
ui_bs, ui_size, ui_time, ui_end)
self.verbose_logger.log(logginglevels.INFO_2, msg)
+ def historyListCmd(self, extcmds):
+ """ Shows the user a list of data about the history. """
+ tid = None
+ try:
+ if len(extcmds) > 1:
+ tid = int(extcmds[1])
+ except ValueError:
+ pass
+
+ fmt = "%-8s | %-16s | %-38s | %-8s"
+ print fmt % ("ID", "Login user", "Start time", "Altered")
+ print "-" * 79
+ fmt = "%8u | %-16.16s | %-38.38s | %8u"
+ for old in self.history.old(tid):
+ name = old.loginuid
+ try:
+ usertup = pwd.getpwuid(old.loginuid)
+ name = usertup[0]
+ except KeyError:
+ pass
+ tm = time.ctime(old.beg_timestamp)
+ print fmt % (old.tid, name, tm, len(old.trans_data))
+
+ def _history_get_transaction(self, extcmds):
+ if len(extcmds) < 2:
+ self.logger.critical(_('No transaction ID given'))
+ return None
+
+ try:
+ tid = int(extcmds[1])
+ except ValueError:
+ self.logger.critical(_('Bad transaction ID given'))
+ return None
+
+ old = self.history.old(tid)
+ if not old:
+ self.logger.critical(_('Not found given transaction ID'))
+ return None
+ if len(old) > 1:
+ self.logger.critical(_('Found more than one transaction ID!'))
+ return old[0]
+
+ def historyInfoCmd(self, extcmds):
+ old = self._history_get_transaction(extcmds)
+ if old is None:
+ return 1, ['Failed history info']
+
+ name = old.loginuid
+ try:
+ usertup = pwd.getpwuid(old.loginuid)
+ name = usertup[0]
+ except KeyError:
+ pass
+
+ print _("Transaction ID:"), old.tid
+ print _("Begin time :"), time.ctime(old.beg_timestamp)
+ print _("Begin rpmdb :"), old.beg_rpmdbversion
+ print _("End time :"), time.ctime(old.end_timestamp)
+ print _("End rpmdb :"), old.end_rpmdbversion
+ print _("User :"), name
+ print _("Return-Code :"), old.return_code
+ print _("Packages Used :")
+ for hpkg in old.trans_with:
+ prefix = " " * 4
+ if not self.rpmdb.contains(po=hpkg):
+ prefix = " ** "
+ if hpkg.epoch == '0':
+ print "%s%s" % (prefix, hpkg)
+
+ print _("Packages Altered:")
+ for hpkg in old.trans_data:
+ prefix = " " * 4
+ if not hpkg.done:
+ prefix = " ** "
+
+ if False: pass
+ elif hpkg.state == 'Update':
+ ln = len(hpkg.name) + 1
+ cn = (" " * ln) + str(hpkg)[ln:]
+ print "%s%-12s %s" % (prefix, hpkg.state, cn)
+ elif hpkg.state == 'Downgraded':
+ ln = len(hpkg.name) + 1
+ cn = (" " * ln) + str(hpkg)[ln:]
+ print "%s%-12s %s" % (prefix, hpkg.state, cn)
+ else:
+ print "%s%-12s %s" % (prefix, hpkg.state, hpkg)
+
+ def historySummaryCmd(self, extcmds):
+ fmt = "%-16s | %-38s | %-8s"
+ print fmt % ("Login user", "Time (seconds taken)", "Altered")
+ print "-" * 79
+ fmt = "%-16.16s | %-38.38s | %8u"
+ data = {'day' : {}, 'week' : {},
+ 'fortnight' : {}, 'quarter' : {}, 'half' : {},
+ 'year' : {}, 'all' : {}}
+ for old in self.history.old():
+ name = old.loginuid
+ try:
+ usertup = pwd.getpwuid(old.loginuid)
+ name = usertup[0]
+ except KeyError:
+ pass
+ period = 'all'
+ now = time.time()
+ if False: pass
+ elif old.beg_timestamp > (now - (24 * 60 * 60)):
+ period = 'day'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 7)):
+ period = 'week'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 14)):
+ period = 'fortnight'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 7 * 13)):
+ period = 'quarter'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 7 * 26)):
+ period = 'half'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 365)):
+ period = 'year'
+ data[period].setdefault(name, []).append(old)
+ _period2user = {'day' : _("Last day"),
+ 'week' : _("Last week"),
+ 'fortnight' : _("Last 2 weeks"), # US default :p
+ 'quarter' : _("Last 3 months"),
+ 'half' : _("Last 6 months"),
+ 'year' : _("Last year"),
+ 'all' : _("Over a year ago")}
+ for period in ('day', 'week', 'fortnight', 'quarter', 'half', 'year',
+ 'all'):
+ if not data[period]:
+ continue
+ for name in sorted(data[period]):
+ tm = sum(map(lambda x: x.end_timestamp - x.beg_timestamp,
+ data[period][name]))
+ pkgs = sum(map(lambda x: len(x.trans_data), data[period][name]))
+ uperiod = _period2user[period]
+ uperiod += (" (%u)" % tm)
+ print fmt % (name, uperiod, pkgs)
+
class DepSolveProgressCallBack:
"""provides text output callback functions for Dependency Solver callback"""
diff --git a/yum/config.py b/yum/config.py
index 6b3d3ae..bca5591 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -704,6 +704,8 @@ class YumConf(StartupConf):
sslclientkey = Option()
+ history_record_packages = ListOption(['yum', 'rpm', 'yum-metadata-parser'])
+
_reposlist = []
class RepoConf(BaseConfig):
diff --git a/yumcommands.py b/yumcommands.py
index 50e6eda..6bc7a3c 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -29,7 +29,6 @@ import locale
import fnmatch
import time
from yum.i18n import utf8_width, utf8_width_fill, to_unicode
-import pwd
def checkRootUID(base):
"""
@@ -1150,145 +1149,8 @@ class HistoryCommand(YumCommand):
def getSummary(self):
return _("Display, or use, the transaction history")
- def _hcmd_list(self, base, extcmds):
- tid = None
- try:
- if len(extcmds) > 1:
- tid = int(extcmds[1])
- except ValueError:
- pass
-
- fmt = "%-8s | %-16s | %-38s | %-8s"
- print fmt % ("ID", "Login user", "Start time (seconds taken)","Altered")
- print "-" * 79
- fmt = "%8u | %-16.16s | %-38.38s | %8u"
- for old in base.history.old(tid):
- name = old.loginuid
- try:
- usertup = pwd.getpwuid(old.loginuid)
- name = usertup[0]
- except KeyError:
- pass
- tm = time.ctime(old.beg_timestamp)
- tm += (" (%u)" % (old.end_timestamp - old.beg_timestamp))
- print fmt % (old.tid, name, tm, len(old.trans_data))
-
- def _get_transaction(self, base, extcmds):
- if len(extcmds) < 2:
- base.logger.critical(_('No transaction ID given'))
- return None
-
- try:
- tid = int(extcmds[1])
- except ValueError:
- base.logger.critical(_('Bad transaction ID given'))
- return None
-
- old = base.history.old(tid)
- if not old:
- base.logger.critical(_('Not found given transaction ID'))
- return None
- if len(old) > 1:
- base.logger.critical(_('Found more than one transaction ID!'))
- return old[0]
-
- def _hcmd_info(self, base, extcmds):
- old = self._get_transaction(base, extcmds)
- if old is None:
- return 1, ['Failed history info']
-
- name = old.loginuid
- try:
- usertup = pwd.getpwuid(old.loginuid)
- name = usertup[0]
- except KeyError:
- pass
-
- print "Transaction ID:", old.tid
- print "Begin time:", time.ctime(old.beg_timestamp)
- print "Begin rpmdb:", old.beg_rpmdbversion
- print "End time:", time.ctime(old.end_timestamp)
- print "End rpmdb:", old.end_rpmdbversion
- print "User:", name
- print "Return-Code:", old.return_code
- print "Packages Used:"
- for hpkg in old.trans_with:
- prefix = " " * 4
- if not base.rpmdb.contains(po=hpkg):
- prefix = " ** "
- if hpkg.epoch == '0':
- print "%s%s" % (prefix, hpkg)
-
- print "Packages Altered:"
- for hpkg in old.trans_data:
- prefix = " " * 4
- if not hpkg.done:
- prefix = " ** "
-
- if False: pass
- elif hpkg.state == 'Update':
- ln = len(hpkg.name) + 1
- cn = (" " * ln) + str(hpkg)[ln:]
- print "%s%-12s %s" % (prefix, hpkg.state, cn)
- elif hpkg.state == 'Downgraded':
- ln = len(hpkg.name) + 1
- cn = (" " * ln) + str(hpkg)[ln:]
- print "%s%-12s %s" % (prefix, hpkg.state, cn)
- else:
- print "%s%-12s %s" % (prefix, hpkg.state, hpkg)
-
- def _hcmd_summary(self, base, extcmds):
- fmt = "%-16s | %-38s | %-8s"
- print fmt % ("Login user", "Time (seconds taken)", "Altered")
- print "-" * 79
- fmt = "%-16.16s | %-38.38s | %8u"
- data = {'day' : {}, 'week' : {},
- 'fortnight' : {}, 'quarter' : {}, 'half' : {},
- 'year' : {}, 'all' : {}}
- for old in base.history.old():
- name = old.loginuid
- try:
- usertup = pwd.getpwuid(old.loginuid)
- name = usertup[0]
- except KeyError:
- pass
- period = 'all'
- now = time.time()
- if False: pass
- elif old.beg_timestamp > (now - (24 * 60 * 60)):
- period = 'day'
- elif old.beg_timestamp > (now - (24 * 60 * 60 * 7)):
- period = 'week'
- elif old.beg_timestamp > (now - (24 * 60 * 60 * 14)):
- period = 'fortnight'
- elif old.beg_timestamp > (now - (24 * 60 * 60 * 7 * 13)):
- period = 'quarter'
- elif old.beg_timestamp > (now - (24 * 60 * 60 * 7 * 26)):
- period = 'half'
- elif old.beg_timestamp > (now - (24 * 60 * 60 * 365)):
- period = 'year'
- data[period].setdefault(name, []).append(old)
- _period2user = {'day' : _("Last day"),
- 'week' : _("Last week"),
- 'fortnight' : _("Last 2 weeks"), # US default :p
- 'quarter' : _("Last 3 months"),
- 'half' : _("Last 6 months"),
- 'year' : _("Last year"),
- 'all' : _("Over a year ago")}
- for period in ('day', 'week', 'fortnight', 'quarter', 'half', 'year',
- 'all'):
- if not data[period]:
- continue
- for name in sorted(data[period]):
- tm = sum(map(lambda x: x.end_timestamp - x.beg_timestamp,
- data[period][name]))
- pkgs = sum(map(lambda x: len(x.trans_data), data[period][name]))
- uperiod = _period2user[period]
- uperiod += (" (%u)" % tm)
- print fmt % (name, uperiod, pkgs)
-
def _hcmd_repeat(self, base, extcmds):
- old = self._get_transaction(base, extcmds)
+ old = base._history_get_transaction(base, extcmds)
if old is None:
return 1, ['Failed history repeat']
tm = time.ctime(old.beg_timestamp)
@@ -1322,7 +1184,7 @@ class HistoryCommand(YumCommand):
return 2, ["Repeating transaction %u" % (old.tid,)]
def _hcmd_undo(self, base, extcmds):
- old = self._get_transaction(base, extcmds)
+ old = base._history_get_transaction(base, extcmds)
if old is None:
return 1, ['Failed history undo']
tm = time.ctime(old.beg_timestamp)
@@ -1375,11 +1237,11 @@ class HistoryCommand(YumCommand):
if False: pass
elif vcmd == 'list':
- ret = self._hcmd_list(base, extcmds)
+ ret = base.historyListCmd(extcmds)
elif vcmd == 'info':
- ret = self._hcmd_info(base, extcmds)
+ ret = base.historyInfoCmd(extcmds)
elif vcmd == 'summary':
- ret = self._hcmd_summary(base, extcmds)
+ ret = base.historySummaryCmd(extcmds)
elif vcmd == 'undo':
ret = self._hcmd_undo(base, extcmds)
elif vcmd == 'repeat':
commit 61770cb7639b777b6b73d58c7d6d2caf478fe07b
Author: James Antill <james at and.org>
Date: Wed Aug 26 14:59:00 2009 -0400
Remove dump command.
Fix trans_data_pid_end().
Complete all txmbrs when the transaction finished happily.
diff --git a/yum/history.py b/yum/history.py
index 50cb876..699b4ec 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -175,7 +175,7 @@ class YumHistory:
def trans_data_pid_end(self, pid, state):
cur = self._get_cursor()
res = executeSQL(cur,
- """UPDATE trans_data_pkgs SET done = TRUE
+ """UPDATE trans_data_pkgs SET done = 'TRUE'
WHERE tid = ? AND pkgtupid = ? AND state = ?
""", (self._tid, pid, state))
return cur.lastrowid
@@ -220,6 +220,12 @@ class YumHistory:
str(rpmdb_version),
return_code))
self._commit()
+ if not return_code:
+ # Simple hack, if the transaction finished
+ executeSQL(cur,
+ """UPDATE trans_data_pkgs SET done = 'TRUE'
+ WHERE tid = ?""", (self._tid,))
+ self._commit()
del self._tid
def _old_with_pkgs(self, tid):
diff --git a/yumcommands.py b/yumcommands.py
index 49d51a6..50e6eda 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -1358,18 +1358,13 @@ class HistoryCommand(YumCommand):
def _hcmd_new(self, base, extcmds):
base.history._create_db_file()
- def _hcmd_dump(self, base, extcmds):
- rpmdb_ver = base.rpmdb.simpleVersion()[0]
- base.history.beg(rpmdb_ver, base.rpmdb.returnPackages(), [])
- base.history.end(rpmdb_ver, 0)
-
def doCheck(self, base, basecmd, extcmds):
- cmds = ('list', 'info', 'summary', 'repeat', 'undo', 'dump', 'new')
+ cmds = ('list', 'info', 'summary', 'repeat', 'undo', 'new')
if extcmds and extcmds[0] not in cmds:
base.logger.critical(_('Invalid history sub-command, use: %s.'),
", ".join(cmds))
raise cli.CliError
- if extcmds and extcmds[0] in ('repeat', 'undo', 'dump', 'new'):
+ if extcmds and extcmds[0] in ('repeat', 'undo', 'new'):
checkRootUID(base)
checkGPGKey(base)
@@ -1389,8 +1384,6 @@ class HistoryCommand(YumCommand):
ret = self._hcmd_undo(base, extcmds)
elif vcmd == 'repeat':
ret = self._hcmd_repeat(base, extcmds)
- elif vcmd == 'dump':
- ret = self._hcmd_dump(base, extcmds)
elif vcmd == 'new':
ret = self._hcmd_new(base, extcmds)
commit 1dc7f899ddcd0eb42c9d33856d6a5587ba8a0694
Author: James Antill <james at and.org>
Date: Tue Aug 25 19:49:43 2009 -0400
Mark txmbr's as reinstall ... so history can use it
diff --git a/yum/transactioninfo.py b/yum/transactioninfo.py
index be772e5..63ad574 100644
--- a/yum/transactioninfo.py
+++ b/yum/transactioninfo.py
@@ -271,6 +271,7 @@ class TransactionData:
elif txmbr.output_state in (TS_INSTALL, TS_TRUEINSTALL):
if include_reinstall and self.rpmdb.contains(po=txmbr.po):
+ txmbr.reinstall = True
self.reinstalled.append(txmbr)
continue
commit d486f35e621a68e09df156e8e140fe9c37a852ec
Author: James Antill <james at and.org>
Date: Tue Aug 25 18:02:22 2009 -0400
Register the history command
diff --git a/cli.py b/cli.py
index 615a1f6..99e0a42 100644
--- a/cli.py
+++ b/cli.py
@@ -100,6 +100,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
self.registerCommand(yumcommands.ReInstallCommand())
self.registerCommand(yumcommands.DowngradeCommand())
self.registerCommand(yumcommands.VersionCommand())
+ self.registerCommand(yumcommands.HistoryCommand())
def registerCommand(self, command):
for name in command.getNames():
commit 28b2487630b35e7394f0dd803cb1454626751baf
Author: James Antill <james at and.org>
Date: Tue Aug 25 18:02:08 2009 -0400
Add the history command, so the user can see/use the history API
diff --git a/yumcommands.py b/yumcommands.py
index 3d88ee4..49d51a6 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -29,6 +29,7 @@ import locale
import fnmatch
import time
from yum.i18n import utf8_width, utf8_width_fill, to_unicode
+import pwd
def checkRootUID(base):
"""
@@ -1065,7 +1066,7 @@ class DowngradeCommand(YumCommand):
def needTs(self, base, basecmd, extcmds):
return False
-
+
class VersionCommand(YumCommand):
def getNames(self):
@@ -1130,10 +1131,275 @@ class VersionCommand(YumCommand):
for line in cols:
print base.fmtColumns(zip(line, columns))
- return 0, []
+ return 0, ['version']
def needTs(self, base, basecmd, extcmds):
vcmd = 'installed'
if extcmds:
vcmd = extcmds[0]
return vcmd in ('available', 'all')
+
+
+class HistoryCommand(YumCommand):
+ def getNames(self):
+ return ['history']
+
+ def getUsage(self):
+ return "[info|list|summary|repeat|undo|new]"
+
+ def getSummary(self):
+ return _("Display, or use, the transaction history")
+
+ def _hcmd_list(self, base, extcmds):
+ tid = None
+ try:
+ if len(extcmds) > 1:
+ tid = int(extcmds[1])
+ except ValueError:
+ pass
+
+ fmt = "%-8s | %-16s | %-38s | %-8s"
+ print fmt % ("ID", "Login user", "Start time (seconds taken)","Altered")
+ print "-" * 79
+ fmt = "%8u | %-16.16s | %-38.38s | %8u"
+ for old in base.history.old(tid):
+ name = old.loginuid
+ try:
+ usertup = pwd.getpwuid(old.loginuid)
+ name = usertup[0]
+ except KeyError:
+ pass
+ tm = time.ctime(old.beg_timestamp)
+ tm += (" (%u)" % (old.end_timestamp - old.beg_timestamp))
+ print fmt % (old.tid, name, tm, len(old.trans_data))
+
+ def _get_transaction(self, base, extcmds):
+ if len(extcmds) < 2:
+ base.logger.critical(_('No transaction ID given'))
+ return None
+
+ try:
+ tid = int(extcmds[1])
+ except ValueError:
+ base.logger.critical(_('Bad transaction ID given'))
+ return None
+
+ old = base.history.old(tid)
+ if not old:
+ base.logger.critical(_('Not found given transaction ID'))
+ return None
+ if len(old) > 1:
+ base.logger.critical(_('Found more than one transaction ID!'))
+ return old[0]
+
+ def _hcmd_info(self, base, extcmds):
+ old = self._get_transaction(base, extcmds)
+ if old is None:
+ return 1, ['Failed history info']
+
+ name = old.loginuid
+ try:
+ usertup = pwd.getpwuid(old.loginuid)
+ name = usertup[0]
+ except KeyError:
+ pass
+
+ print "Transaction ID:", old.tid
+ print "Begin time:", time.ctime(old.beg_timestamp)
+ print "Begin rpmdb:", old.beg_rpmdbversion
+ print "End time:", time.ctime(old.end_timestamp)
+ print "End rpmdb:", old.end_rpmdbversion
+ print "User:", name
+ print "Return-Code:", old.return_code
+ print "Packages Used:"
+ for hpkg in old.trans_with:
+ prefix = " " * 4
+ if not base.rpmdb.contains(po=hpkg):
+ prefix = " ** "
+ if hpkg.epoch == '0':
+ print "%s%s" % (prefix, hpkg)
+
+ print "Packages Altered:"
+ for hpkg in old.trans_data:
+ prefix = " " * 4
+ if not hpkg.done:
+ prefix = " ** "
+
+ if False: pass
+ elif hpkg.state == 'Update':
+ ln = len(hpkg.name) + 1
+ cn = (" " * ln) + str(hpkg)[ln:]
+ print "%s%-12s %s" % (prefix, hpkg.state, cn)
+ elif hpkg.state == 'Downgraded':
+ ln = len(hpkg.name) + 1
+ cn = (" " * ln) + str(hpkg)[ln:]
+ print "%s%-12s %s" % (prefix, hpkg.state, cn)
+ else:
+ print "%s%-12s %s" % (prefix, hpkg.state, hpkg)
+
+ def _hcmd_summary(self, base, extcmds):
+ fmt = "%-16s | %-38s | %-8s"
+ print fmt % ("Login user", "Time (seconds taken)", "Altered")
+ print "-" * 79
+ fmt = "%-16.16s | %-38.38s | %8u"
+ data = {'day' : {}, 'week' : {},
+ 'fortnight' : {}, 'quarter' : {}, 'half' : {},
+ 'year' : {}, 'all' : {}}
+ for old in base.history.old():
+ name = old.loginuid
+ try:
+ usertup = pwd.getpwuid(old.loginuid)
+ name = usertup[0]
+ except KeyError:
+ pass
+ period = 'all'
+ now = time.time()
+ if False: pass
+ elif old.beg_timestamp > (now - (24 * 60 * 60)):
+ period = 'day'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 7)):
+ period = 'week'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 14)):
+ period = 'fortnight'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 7 * 13)):
+ period = 'quarter'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 7 * 26)):
+ period = 'half'
+ elif old.beg_timestamp > (now - (24 * 60 * 60 * 365)):
+ period = 'year'
+ data[period].setdefault(name, []).append(old)
+ _period2user = {'day' : _("Last day"),
+ 'week' : _("Last week"),
+ 'fortnight' : _("Last 2 weeks"), # US default :p
+ 'quarter' : _("Last 3 months"),
+ 'half' : _("Last 6 months"),
+ 'year' : _("Last year"),
+ 'all' : _("Over a year ago")}
+ for period in ('day', 'week', 'fortnight', 'quarter', 'half', 'year',
+ 'all'):
+ if not data[period]:
+ continue
+ for name in sorted(data[period]):
+ tm = sum(map(lambda x: x.end_timestamp - x.beg_timestamp,
+ data[period][name]))
+ pkgs = sum(map(lambda x: len(x.trans_data), data[period][name]))
+ uperiod = _period2user[period]
+ uperiod += (" (%u)" % tm)
+ print fmt % (name, uperiod, pkgs)
+
+ def _hcmd_repeat(self, base, extcmds):
+ old = self._get_transaction(base, extcmds)
+ if old is None:
+ return 1, ['Failed history repeat']
+ tm = time.ctime(old.beg_timestamp)
+ print "Repeating transaction %u, from %s" % (old.tid, tm)
+ # This is basic atm.
+ done = False
+ for pkg in old.trans_data:
+ if pkg.state == 'Reinstall':
+ if base.reinstall(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in old.trans_data:
+ if pkg.state == 'Downgrade':
+ try:
+ if base.downgrade(pkgtup=pkg.pkgtup):
+ done = True
+ except yum.Errors.DowngradeError:
+ base.logger.critical(_('Failed to downgrade: %s'), pkg)
+ for pkg in old.trans_data:
+ if pkg.state == 'Update':
+ if base.update(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in old.trans_data:
+ if pkg.state in ('Install', 'True-Install'):
+ if base.install(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in old.trans_data:
+ if pkg.state == 'Erase':
+ if base.remove(pkgtup=pkg.pkgtup):
+ done = True
+ if done:
+ return 2, ["Repeating transaction %u" % (old.tid,)]
+
+ def _hcmd_undo(self, base, extcmds):
+ old = self._get_transaction(base, extcmds)
+ if old is None:
+ return 1, ['Failed history undo']
+ tm = time.ctime(old.beg_timestamp)
+ print "Undoing transaction %u, from %s" % (old.tid, tm)
+ # FIXME: This is __horribley__ basic atm.
+ done = False
+ for pkg in old.trans_data:
+ if pkg.state == 'Reinstall':
+ if base.reinstall(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in old.trans_data:
+ if pkg.state == 'Updated':
+ try:
+ if base.downgrade(pkgtup=pkg.pkgtup):
+ done = True
+ except yum.Errors.DowngradeError:
+ base.logger.critical(_('Failed to downgrade: %s'), pkg)
+ for pkg in old.trans_data:
+ if pkg.state == 'Downgraded':
+ if base.update(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in old.trans_data:
+ if pkg.state in ('Install', 'True-Install'):
+ if base.remove(pkgtup=pkg.pkgtup):
+ done = True
+ for pkg in old.trans_data:
+ if pkg.state == 'Erase':
+ if base.install(pkgtup=pkg.pkgtup):
+ done = True
+ if done:
+ return 2, ["Undoing transaction %u" % (old.tid,)]
+
+ def _hcmd_new(self, base, extcmds):
+ base.history._create_db_file()
+
+ def _hcmd_dump(self, base, extcmds):
+ rpmdb_ver = base.rpmdb.simpleVersion()[0]
+ base.history.beg(rpmdb_ver, base.rpmdb.returnPackages(), [])
+ base.history.end(rpmdb_ver, 0)
+
+ def doCheck(self, base, basecmd, extcmds):
+ cmds = ('list', 'info', 'summary', 'repeat', 'undo', 'dump', 'new')
+ if extcmds and extcmds[0] not in cmds:
+ base.logger.critical(_('Invalid history sub-command, use: %s.'),
+ ", ".join(cmds))
+ raise cli.CliError
+ if extcmds and extcmds[0] in ('repeat', 'undo', 'dump', 'new'):
+ checkRootUID(base)
+ checkGPGKey(base)
+
+ def doCommand(self, base, basecmd, extcmds):
+ vcmd = 'list'
+ if extcmds:
+ vcmd = extcmds[0]
+
+ if False: pass
+ elif vcmd == 'list':
+ ret = self._hcmd_list(base, extcmds)
+ elif vcmd == 'info':
+ ret = self._hcmd_info(base, extcmds)
+ elif vcmd == 'summary':
+ ret = self._hcmd_summary(base, extcmds)
+ elif vcmd == 'undo':
+ ret = self._hcmd_undo(base, extcmds)
+ elif vcmd == 'repeat':
+ ret = self._hcmd_repeat(base, extcmds)
+ elif vcmd == 'dump':
+ ret = self._hcmd_dump(base, extcmds)
+ elif vcmd == 'new':
+ ret = self._hcmd_new(base, extcmds)
+
+ if ret is None:
+ return 0, ['history %s' % (vcmd,)]
+ return ret
+
+ def needTs(self, base, basecmd, extcmds):
+ vcmd = 'list'
+ if extcmds:
+ vcmd = extcmds[0]
+ return vcmd in ('repeat', 'undo')
commit 68549a20af01535d5d64c6dabbce5418b2b76487
Author: James Antill <james at and.org>
Date: Tue Aug 25 18:01:33 2009 -0400
Add history to the YumBase object
diff --git a/yum/__init__.py b/yum/__init__.py
index eca0bfb..33e143f 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -57,6 +57,7 @@ import plugins
import logginglevels
import yumRepo
import callbacks
+import yum.history
import warnings
warnings.simplefilter("ignore", Errors.YumFutureDeprecationWarning)
@@ -137,6 +138,7 @@ class YumBase(depsolve.Depsolve):
self._rpmdb = None
self._up = None
self._comps = None
+ self._history = None
self._pkgSack = None
self._lockfile = None
self.skipped_packages = [] # packages skip by the skip-broken code
@@ -694,6 +696,13 @@ class YumBase(depsolve.Depsolve):
self._comps.compile(self.rpmdb.simplePkgList())
self.verbose_logger.debug('group time: %0.3f' % (time.time() - group_st))
return self._comps
+
+ def _getHistory(self):
+ """auto create the history object that to acess/append the transaction
+ history information. """
+ if self._history is None:
+ self._history = yum.history.YumHistory(self.rpmdb)
+ return self._history
# properties so they auto-create themselves with defaults
repos = property(fget=lambda self: self._getRepos(),
@@ -718,6 +727,9 @@ class YumBase(depsolve.Depsolve):
comps = property(fget=lambda self: self._getGroups(),
fset=lambda self, value: self._setGroups(value),
fdel=lambda self: setattr(self, "_comps", None))
+ history = property(fget=lambda self: self._getHistory(),
+ fset=lambda self, value: setattr(self, "_history",value),
+ fdel=lambda self: setattr(self, "_history", None))
def doSackFilelistPopulate(self):
@@ -1029,6 +1041,12 @@ class YumBase(depsolve.Depsolve):
self.plugins.run('pretrans')
+ using_pkgs_pats = ['yum', 'rpm', 'python', 'yum-metadata-parser',
+ 'yum-rhn-plugin']
+ using_pkgs = self.rpmdb.returnPackages(patterns=using_pkgs_pats)
+ self.history.beg(self.rpmdb.simpleVersion()[0], using_pkgs,
+ list(self.tsInfo))
+
errors = self.ts.run(cb.callback, '')
# ts.run() exit codes are, hmm, "creative": None means all ok, empty
# list means some errors happened in the transaction and non-empty
@@ -1060,10 +1078,10 @@ class YumBase(depsolve.Depsolve):
self.rpmdb.dropCachedData() # drop out the rpm cache so we don't step on bad hdr indexes
self.plugins.run('posttrans')
# sync up what just happened versus what is in the rpmdb
- self.verifyTransaction()
+ self.verifyTransaction(resultobject)
return resultobject
- def verifyTransaction(self):
+ def verifyTransaction(self, resultobject=None):
"""checks that the transaction did what we expected it to do. Also
propagates our external yumdb info"""
@@ -1131,6 +1149,10 @@ class YumBase(depsolve.Depsolve):
else:
self.verbose_logger.log(logginglevels.DEBUG_2, 'What is this? %s' % txmbr.po)
+ ret = -1
+ if resultobject is not None:
+ ret = resultobject.return_code
+ self.history.end(self.rpmdb.simpleVersion()[0], ret)
self.rpmdb.dropCachedData()
def costExcludePackages(self):
@@ -3450,6 +3472,7 @@ class YumBase(depsolve.Depsolve):
if not apkgs:
# Do we still want to return errors here?
# We don't in the cases below, so I didn't here...
+ pkgs = []
if 'pattern' in kwargs:
pkgs = self.rpmdb.returnPackages(patterns=[kwargs['pattern']],
ignore_case=False)
commit 1bed84997a01de26ec36ecac8bb4e1d0ed1ca956
Author: James Antill <james at and.org>
Date: Tue Aug 25 17:58:55 2009 -0400
Add initial version of history APIs.
This records what transactions happened, and by whom etc.
Mostly for auditing in Fedora, but also allows us to do "clever"
yum-debug-dump/restore type stuff. Ie. O(1) "supportable" rollback ops.
Note that there is no DB versioning atm. ... and no way to turn this
off, so don't put it in production atm. :)
diff --git a/yum/history.py b/yum/history.py
new file mode 100644
index 0000000..50cb876
--- /dev/null
+++ b/yum/history.py
@@ -0,0 +1,344 @@
+#!/usr/bin/python -t
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2009 Red Hat
+#
+# James Antill <james at fedoraproject.org>
+
+import time
+import os, os.path
+import glob
+from weakref import proxy as weakref
+
+from sqlutils import sqlite, executeSQL
+import yum.misc
+from yum.constants import *
+from yum.packages import YumInstalledPackage, YumAvailablePackage, PackageObject
+
+_history_dir = '/var/lib/yum/history'
+
+_stcode2sttxt = {TS_UPDATE : 'Update',
+ TS_UPDATED : 'Updated',
+ TS_ERASE: 'Erase',
+ TS_INSTALL: 'Install',
+ TS_TRUEINSTALL : 'True-Install',
+ TS_OBSOLETED: 'Obsoleted',
+ TS_OBSOLETING: 'Obsoleting'}
+
+_sttxt2stcode = {'Update' : TS_UPDATE,
+ 'Updated' : TS_UPDATED,
+ 'Erase' : TS_ERASE,
+ 'Install' : TS_INSTALL,
+ 'True-Install' : TS_TRUEINSTALL,
+ 'Reinstall' : TS_INSTALL, # Broken
+ 'Downgrade' : TS_INSTALL, # Broken
+ 'Downgraded' : TS_INSTALL, # Broken
+ 'Obsoleted' : TS_OBSOLETED,
+ 'Obsoleting' : TS_OBSOLETING}
+
+class YumHistoryPackage(PackageObject):
+
+ def __init__(self, name, arch, epoch, version, release, checksum):
+ self.name = name
+ self.version = version
+ self.release = release
+ self.epoch = epoch
+ self.arch = arch
+ self.pkgtup = (self.name, self.arch,
+ self.epoch, self.version, self.release)
+ if checksum is None:
+ self._checksums = [] # (type, checksum, id(0,1)
+ else:
+ chk = checksum.split(':')
+ self._checksums = [(chk[0], chk[1], 0)] # (type, checksum, id(0,1))
+
+class YumHistory:
+ """ API for accessing the history sqlite data. """
+
+ def __init__(self, rpmdb, db_path=_history_dir):
+ self._rpmdb = weakref(rpmdb)
+ self._conn = None
+
+ self.conf = yum.misc.GenericHolder()
+ self.conf.db_path = db_path
+ self.conf.writable = False
+
+ if not os.path.exists(self.conf.db_path):
+ try:
+ os.makedirs(self.conf.db_path)
+ except (IOError, OSError), e:
+ # some sort of useful thing here? A warning?
+ return
+ self.conf.writable = True
+ else:
+ if os.access(self.conf.db_path, os.W_OK):
+ self.conf.writable = True
+
+ DBs = glob.glob('%s/history-*-*-*.sqlite' % self.conf.db_path)
+ self._db_file = None
+ for d in reversed(sorted(DBs)):
+ fname = os.path.basename(d)
+ fname = fname[len("history-"):-len(".sqlite")]
+ pieces = fname.split('-', 4)
+ if len(pieces) != 3:
+ continue
+ try:
+ map(int, pieces)
+ except ValueError:
+ continue
+
+ self._db_file = d
+ break
+
+ if self._db_file is None:
+ self._create_db_file()
+
+ def _get_cursor(self):
+ if self._conn is None:
+ self._conn = sqlite.connect(self._db_file)
+ return self._conn.cursor()
+ def _commit(self):
+ return self._conn.commit()
+
+ def _pkgtup2pid(self, pkgtup, checksum=None):
+ cur = self._get_cursor()
+ executeSQL(cur, """SELECT pkgtupid, checksum FROM pkgtups
+ WHERE name=? AND arch=? AND
+ epoch=? AND version=? AND release=?""", pkgtup)
+ for sql_pkgtupid, sql_checksum in cur:
+ if checksum is None and sql_checksum is None:
+ return sql_pkgtupid
+ if checksum is None:
+ continue
+ if sql_checksum is None:
+ continue
+ if checksum == sql_checksum:
+ return sql_pkgtupid
+
+ if checksum is not None:
+ (n,a,e,v,r) = pkgtup
+ res = executeSQL(cur,
+ """INSERT INTO pkgtups
+ (name, arch, epoch, version, release, checksum)
+ VALUES (?,?,?,?,?,?)""", (n,a,e,v,r, checksum))
+ else:
+ res = executeSQL(cur,
+ """INSERT INTO pkgtups
+ (name, arch, epoch, version, release)
+ VALUES (?, ?, ?, ?, ?)""", pkgtup)
+ return cur.lastrowid
+ def _apkg2pid(self, po):
+ csum = po.returnIdSum()
+ if csum is not None:
+ csum = "%s:%s" % (str(csum[0]), str(csum[1]))
+ return self._pkgtup2pid(po.pkgtup, csum)
+ def _ipkg2pid(self, po):
+ csum = None
+ yumdb = po.yumdb_info
+ if 'checksum_type' in yumdb and 'checksum_type' in yumdb:
+ csum = "%s:%s" % (yumdb.checksum_type, yumdb.checksum_data)
+ return self._pkgtup2pid(po.pkgtup, csum)
+ def _pkg2pid(self, po):
+ if isinstance(po, YumInstalledPackage):
+ return self._ipkg2pid(po)
+ if isinstance(po, YumAvailablePackage):
+ return self._apkg2pid(po)
+ return self._pkgtup2pid(po.pkgtup, None)
+
+ def trans_with_pid(self, pid):
+ cur = self._get_cursor()
+ res = executeSQL(cur,
+ """INSERT INTO trans_with_pkgs
+ (tid, pkgtupid)
+ VALUES (?, ?)""", (self._tid, pid))
+ return cur.lastrowid
+
+ def trans_data_pid_beg(self, pid, state):
+ cur = self._get_cursor()
+ res = executeSQL(cur,
+ """INSERT INTO trans_data_pkgs
+ (tid, pkgtupid, state)
+ VALUES (?, ?, ?)""", (self._tid, pid, state))
+ return cur.lastrowid
+ def trans_data_pid_end(self, pid, state):
+ cur = self._get_cursor()
+ res = executeSQL(cur,
+ """UPDATE trans_data_pkgs SET done = TRUE
+ WHERE tid = ? AND pkgtupid = ? AND state = ?
+ """, (self._tid, pid, state))
+ return cur.lastrowid
+
+ def beg(self, rpmdb_version, using_pkgs, txmbrs):
+ cur = self._get_cursor()
+ res = executeSQL(cur,
+ """INSERT INTO trans_beg
+ (timestamp, rpmdb_version, loginuid)
+ VALUES (?,?,?)""", (int(time.time()),
+ str(rpmdb_version),
+ yum.misc.getloginuid()))
+ self._tid = cur.lastrowid
+
+ for pkg in using_pkgs:
+ pid = self._ipkg2pid(pkg)
+ self.trans_with_pid(pid)
+
+ for txmbr in txmbrs:
+ pid = self._pkg2pid(txmbr.po)
+ state = None
+ if txmbr.output_state in (TS_INSTALL, TS_TRUEINSTALL):
+ if hasattr(txmbr, 'reinstall'):
+ state = 'Reinstall'
+ elif txmbr.downgrades:
+ state = 'Downgrade'
+ if txmbr.output_state == TS_ERASE:
+ if txmbr.downgraded_by:
+ state = 'Downgraded'
+ if state is None:
+ state = _stcode2sttxt[txmbr.output_state]
+ self.trans_data_pid_beg(pid, state)
+
+ self._commit()
+
+ def end(self, rpmdb_version, return_code):
+ cur = self._get_cursor()
+ res = executeSQL(cur,
+ """INSERT INTO trans_end
+ (tid, timestamp, rpmdb_version, return_code)
+ VALUES (?,?,?,?)""", (self._tid, int(time.time()),
+ str(rpmdb_version),
+ return_code))
+ self._commit()
+ del self._tid
+
+ def _old_with_pkgs(self, tid):
+ cur = self._get_cursor()
+ res = executeSQL(cur,
+ """SELECT name, arch, epoch, version, release, checksum
+ FROM trans_with_pkgs JOIN pkgtups USING(pkgtupid)
+ WHERE tid = ?
+ ORDER BY name ASC, epoch ASC""", (tid,))
+ ret = []
+ for row in res:
+ obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
+ ret.append(obj)
+ return ret
+ def _old_data_pkgs(self, tid):
+ cur = self._get_cursor()
+ res = executeSQL(cur,
+ """SELECT name, arch, epoch, version, release,
+ checksum, done, state
+ FROM trans_data_pkgs JOIN pkgtups USING(pkgtupid)
+ WHERE tid = ?
+ ORDER BY name ASC, epoch ASC, state DESC""",
+ (tid,))
+ ret = []
+ for row in res:
+ obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
+ obj.done = row[6] == 'TRUE'
+ obj.state = row[7]
+ obj.state_installed = None
+ if _sttxt2stcode[obj.state] in TS_INSTALL_STATES:
+ obj.state_installed = True
+ if _sttxt2stcode[obj.state] in TS_REMOVE_STATES:
+ obj.state_installed = False
+ ret.append(obj)
+ return ret
+
+ def old(self, tid=None):
+ cur = self._get_cursor()
+ sql = """SELECT tid,
+ trans_beg.timestamp AS beg_ts,
+ trans_beg.rpmdb_version AS beg_rv,
+ trans_end.timestamp AS end_ts,
+ trans_end.rpmdb_version AS end_rv,
+ loginuid, return_code
+ FROM trans_beg OUTER JOIN trans_end USING(tid)"""
+ params = None
+ if tid is not None:
+ sql += " WHERE tid = ?"
+ params = (tid,)
+ sql += " ORDER BY beg_ts DESC, tid ASC"
+ res = executeSQL(cur, sql, params)
+ ret = []
+ for row in res:
+ obj = yum.misc.GenericHolder()
+ obj.tid = row[0]
+ obj.beg_timestamp = row[1]
+ obj.beg_rpmdbversion = row[2]
+ obj.end_timestamp = row[3]
+ obj.end_rpmdbversion = row[4]
+ obj.loginuid = row[5]
+ obj.return_code = row[6]
+ obj.trans_with = sorted(self._old_with_pkgs(obj.tid))
+ obj.trans_data = sorted(self._old_data_pkgs(obj.tid))
+ ret.append(obj)
+ return ret
+
+ def _create_db_file(self):
+ """ Create a new history DB file, populating tables etc. """
+
+ _db_file = '%s/%s-%s.%s' % (self.conf.db_path,
+ 'history',
+ time.strftime('%Y-%m-%d'),
+ 'sqlite')
+ if self._db_file == _db_file:
+ os.rename(_db_file, _db_file + '.old')
+ self._db_file = _db_file
+
+ cur = self._get_cursor()
+ ops = ['''\
+ CREATE TABLE trans_beg (
+ tid INTEGER PRIMARY KEY,
+ timestamp INTEGER NOT NULL, rpmdb_version TEXT NOT NULL,
+ loginuid INTEGER);
+''', '''\
+ CREATE TABLE trans_end (
+ tid INTEGER PRIMARY KEY REFERENCES trans_beg,
+ timestamp INTEGER NOT NULL, rpmdb_version TEXT NOT NULL,
+ return_code INTEGER NOT NULL);
+''', '''\
+\
+ CREATE TABLE trans_with_pkgs (
+ tid INTEGER NOT NULL REFERENCES trans_beg,
+ pkgtupid INTEGER NOT NULL REFERENCES pkgtups);
+''', '''\
+\
+ CREATE TABLE trans_error (
+ mid INTEGER PRIMARY KEY,
+ tid INTEGER NOT NULL REFERENCES trans_beg,
+ msg TEXT NOT NULL);
+''', '''\
+ CREATE TABLE trans_script_stdout (
+ lid INTEGER PRIMARY KEY,
+ tid INTEGER NOT NULL REFERENCES trans_beg,
+ line TEXT NOT NULL);
+''', '''\
+\
+ CREATE TABLE trans_data_pkgs (
+ tid INTEGER NOT NULL REFERENCES trans_beg,
+ pkgtupid INTEGER NOT NULL REFERENCES pkgtups,
+ done BOOL NOT NULL DEFAULT FALSE, state TEXT NOT NULL);
+''', '''\
+\
+ CREATE TABLE pkgtups (
+ pkgtupid INTEGER PRIMARY KEY, name TEXT NOT NULL, arch TEXT NOT NULL,
+ epoch TEXT NOT NULL, version TEXT NOT NULL, release TEXT NOT NULL,
+ checksum TEXT);
+''', '''\
+ CREATE INDEX i_pkgtup_naevr ON pkgtups (name, arch, epoch, version, release);
+''']
+ for op in ops:
+ cur.execute(op)
+ self._commit()
commit c2ace8a90c0360d18081efb3821ba91cb1a226b7
Author: James Antill <james at and.org>
Date: Tue Aug 25 17:58:08 2009 -0400
Add getloginuid(), so we can see who called "sudo yum"
diff --git a/yum/misc.py b/yum/misc.py
index a8f7954..a092b65 100644
--- a/yum/misc.py
+++ b/yum/misc.py
@@ -797,6 +797,21 @@ def unlink_f(filename):
if e.errno != errno.ENOENT:
raise
+def getloginuid():
+ """ Get the audit-uid/login-uid, if available. None is returned if there
+ was a problem. Note that no caching is done here. """
+ # We might normally call audit.audit_getloginuid(), except that requires
+ # importing all of the audit module. And it doesn't work anyway: BZ 518721
+ try:
+ fo = open("/proc/self/loginuid")
+ except IOError:
+ return None
+ data = fo.read()
+ try:
+ return int(data)
+ except ValueError:
+ return None
+
# ---------- i18n ----------
import locale
import sys
More information about the Yum-commits
mailing list