This does a few things, the only really user visible part though is
that "yum history info" will now get the from_repo data from the history
DB ... if available. And the history DB will get bigger :).
The history package class is also tweaked so that YumHistoryPackage objects
now act a lot more like YumHeaderPackage objects (using rpmdb/yumdb
data).
We also change pkg2pid() so that you can "lookup" a pkg. to see if it's
in the history, without changing the DB.
---
output.py | 3 +
yum/__init__.py | 6 +-
yum/history.py | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++----
3 files changed, 272 insertions(+), 23 deletions(-)
diff --git a/output.py b/output.py
index 9610232..00a938d 100755
--- a/output.py
+++ b/output.py
@@ -2002,6 +2002,9 @@ to exit.
def _hpkg2from_repo(self, hpkg):
""" Given a pkg, find the ipkg.ui_from_repo ... if none, then
get an apkg. ... and put a ? in there. """
+ if 'from_repo' in hpkg.yumdb_info:
+ return hpkg.ui_from_repo
+
ipkgs = self.rpmdb.searchPkgTuple(hpkg.pkgtup)
if not ipkgs:
apkgs = self.pkgSack.searchPkgTuple(hpkg.pkgtup)
diff --git a/yum/__init__.py b/yum/__init__.py
index b29dc80..fe312ce 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -881,7 +881,8 @@ class YumBase(depsolve.Depsolve):
if self._history is None:
pdb_path = self.conf.persistdir + "/history"
self._history = yum.history.YumHistory(root=self.conf.installroot,
- db_path=pdb_path)
+ db_path=pdb_path,
+
releasever=self.conf.yumvar['releasever'])
return self._history
# properties so they auto-create themselves with defaults
@@ -1647,6 +1648,9 @@ class YumBase(depsolve.Depsolve):
elif loginuid is not None:
po.yumdb_info.installed_by = str(loginuid)
+ if self.conf.history_record:
+ self.history.sync_alldb(po)
+
# Remove old ones after installing new ones, so we can copy values.
for txmbr in self.tsInfo:
if txmbr.output_state in TS_INSTALL_STATES:
diff --git a/yum/history.py b/yum/history.py
index 5385bd1..609394f 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -97,9 +97,58 @@ def _setupHistorySearchSQL(patterns=None, ignore_case=False):
return (need_full, patterns, fields, False)
# ---- horrible Copy and paste from sqlitesack ----
+class _YumHistPackageYumDB:
+ """ Class to pretend to be yumdb_info for history packages. """
+
+ def __init__(self, pkg):
+ self._pkg = pkg
+
+ _valid_yumdb_keys = set(["command_line",
+ "from_repo", "from_repo_revision",
+ "from_repo_timestamp",
+ "installed_by", "changed_by",
+ "reason", "releasever"])
+ def __getattr__(self, attr):
+ """ Load yumdb attributes from the history sqlite. """
+ pkg = self._pkg
+ if attr.startswith('_'):
+ raise AttributeError, "%s has no yum attribute %s" % (pkg, attr)
+
+ if attr not in self._valid_yumdb_keys:
+ raise AttributeError, "%s has no yum attribute %s" % (pkg, attr)
+
+ val = pkg._history._load_yumdb_key(pkg, attr)
+ if False and val is None:
+ raise AttributeError, "%s has no yum attribute %s" % (pkg, attr)
+
+ if val is None:
+ return None
+
+ val = str(val) or ""
+ setattr(self, attr, val)
+
+ return val
+
+ def __contains__(self, attr):
+ # This is faster than __iter__ and it makes things fail in a much more
+ # obvious way in weird FS corruption cases like: BZ 593436
+ x = self.get(attr)
+ return x is not None
+
+ def get(self, attr, default=None):
+ """retrieve an add'l data obj"""
+
+ try:
+ res = getattr(self, attr)
+ except AttributeError:
+ return default
+ return res
+
+
class YumHistoryPackage(PackageObject):
- def __init__(self, name, arch, epoch, version, release, checksum=None):
+ def __init__(self, name, arch, epoch, version, release, checksum=None,
+ history=None):
self.name = name
self.version = version
self.release = release
@@ -111,21 +160,69 @@ class YumHistoryPackage(PackageObject):
self._checksums = [] # (type, checksum, id(0,1)
else:
chk = checksum.split(':')
- self._checksums = [(chk[0], chk[1], 0)] # (type, checksum, id(0,1))
+ self._checksums = [(chk[0], chk[1], 1)] # (type, checksum, id(0,1))
# Needed for equality comparisons in PackageObject
self.repoid = "<history>"
+ self._history = history
+ self.yumdb_info = _YumHistPackageYumDB(self)
+
+ _valid_rpmdb_keys = set(["buildtime", "buildhost",
+ "license", "packager",
+ "size", "sourcerpm", "url", "vendor",
+ # ?
+ "committer", "committime"])
+ def __getattr__(self, attr):
+ """ Load rpmdb attributes from the history sqlite. """
+ if attr.startswith('_'):
+ raise AttributeError, "%s has no attribute %s" % (self, attr)
+
+ if attr not in self._valid_rpmdb_keys:
+ raise AttributeError, "%s has no attribute %s" % (self, attr)
+
+ val = self._history._load_rpmdb_key(self, attr)
+ if False and val is None:
+ raise AttributeError, "%s has no attribute %s" % (self, attr)
+
+ if val is None:
+ return None
+
+ val = str(val) or ""
+ setattr(self, attr, val)
+
+ return val
+
+ def _ui_from_repo(self):
+ """ This reports the repo the package is from, we integrate YUMDB info.
+ for RPM packages so a package from "fedora" that is installed has a
+ ui_from_repo of "@fedora". Note that, esp. with the --releasever
+ option, "fedora" or "rawhide" isn't authoritive.
+ So we also check against the current releasever and if it is
+ different we also print the YUMDB releasever. This means that
+ installing from F12 fedora, while running F12, would report as
+ "@fedora/13". """
+ if 'from_repo' in self.yumdb_info:
+ self._history.releasever
+ end = ''
+ if (self._history.releasever is not None and
+ 'releasever' in self.yumdb_info and
+ self.yumdb_info.releasever != self._history.releasever):
+ end = '/' + self.yumdb_info.releasever
+ return '@' + self.yumdb_info.from_repo + end
+ return self.repoid
+ ui_from_repo = property(fget=lambda self: self._ui_from_repo())
+
+
class YumHistoryPackageState(YumHistoryPackage):
- def __init__(self, name,arch, epoch,version,release, state, checksum=None):
+ def __init__(self, name,arch, epoch,version,release, state, checksum=None,
+ history=None):
YumHistoryPackage.__init__(self, name,arch, epoch,version,release,
- checksum)
+ checksum, history)
self.done = None
self.state = state
- self.repoid = '<history>'
-
-class YumHistoryRpmdbProblem(PackageObject):
+class YumHistoryRpmdbProblem:
""" Class representing an rpmdb problem that existed at the time of the
transaction. """
@@ -328,7 +425,8 @@ class YumMergedHistoryTransaction(YumHistoryTransaction):
@staticmethod
def _conv_pkg_state(pkg, state):
npkg = YumHistoryPackageState(pkg.name, pkg.arch,
- pkg.epoch,pkg.version,pkg.release, state)
+ pkg.epoch,pkg.version,pkg.release, state,
+ pkg._history)
npkg._checksums = pkg._checksums
npkg.done = pkg.done
if _sttxt2stcode[npkg.state] in TS_INSTALL_STATES:
@@ -557,7 +655,7 @@ class YumMergedHistoryTransaction(YumHistoryTransaction):
class YumHistory:
""" API for accessing the history sqlite data. """
- def __init__(self, root='/', db_path=_history_dir):
+ def __init__(self, root='/', db_path=_history_dir, releasever=None):
self._conn = None
self.conf = yum.misc.GenericHolder()
@@ -568,6 +666,8 @@ class YumHistory:
self.conf.writable = False
self.conf.readable = True
+ self.releasever = releasever
+
if not os.path.exists(self.conf.db_path):
try:
os.makedirs(self.conf.db_path)
@@ -644,7 +744,7 @@ class YumHistory:
self._conn.close()
self._conn = None
- def _pkgtup2pid(self, pkgtup, checksum=None):
+ def _pkgtup2pid(self, pkgtup, checksum=None, create=True):
cur = self._get_cursor()
executeSQL(cur, """SELECT pkgtupid, checksum FROM pkgtups
WHERE name=? AND arch=? AND
@@ -659,6 +759,9 @@ class YumHistory:
if checksum == sql_checksum:
return sql_pkgtupid
+ if not create:
+ return None
+
(n,a,e,v,r) = pkgtup
(n,a,e,v,r) = (to_unicode(n),to_unicode(a),
to_unicode(e),to_unicode(v),to_unicode(r))
@@ -674,23 +777,28 @@ class YumHistory:
(name, arch, epoch, version, release)
VALUES (?, ?, ?, ?, ?)""", (n,a,e,v,r))
return cur.lastrowid
- def _apkg2pid(self, po):
+ def _apkg2pid(self, po, create=True):
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):
+ return self._pkgtup2pid(po.pkgtup, csum, create)
+ def _ipkg2pid(self, po, create=True):
csum = None
yumdb = po.yumdb_info
if 'checksum_type' in yumdb and 'checksum_data' in yumdb:
csum = "%s:%s" % (yumdb.checksum_type, yumdb.checksum_data)
- return self._pkgtup2pid(po.pkgtup, csum)
- def pkg2pid(self, po):
+ return self._pkgtup2pid(po.pkgtup, csum, create)
+ def _hpkg2pid(self, po, create=False):
+ return self._apkg2pid(po, create)
+
+ def pkg2pid(self, po, create=True):
if isinstance(po, YumInstalledPackage):
- return self._ipkg2pid(po)
+ return self._ipkg2pid(po, create)
if isinstance(po, YumAvailablePackage):
- return self._apkg2pid(po)
- return self._pkgtup2pid(po.pkgtup, None)
+ return self._apkg2pid(po, create)
+ if isinstance(po, YumHistoryPackage):
+ return self._hpkg2pid(po, create)
+ return self._pkgtup2pid(po.pkgtup, None, create)
@staticmethod
def txmbr2state(txmbr):
@@ -984,7 +1092,8 @@ class YumHistory:
ORDER BY name ASC, epoch ASC""", (tid,))
ret = []
for row in cur:
- obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
+ obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5],
+ history=self)
ret.append(obj)
return ret
def _old_data_pkgs(self, tid):
@@ -998,7 +1107,7 @@ class YumHistory:
ret = []
for row in cur:
obj = YumHistoryPackageState(row[0],row[1],row[2],row[3],row[4],
- row[7], row[5])
+ row[7], row[5], history=self)
obj.done = row[6] == 'TRUE'
obj.state_installed = None
if _sttxt2stcode[obj.state] in TS_INSTALL_STATES:
@@ -1018,7 +1127,8 @@ class YumHistory:
ORDER BY name ASC, epoch ASC""", (tid,))
ret = []
for row in cur:
- obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
+ obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5],
+ history=self)
ret.append(obj)
return ret
def _old_prob_pkgs(self, rpid):
@@ -1032,7 +1142,8 @@ class YumHistory:
ORDER BY name ASC, epoch ASC""", (rpid,))
ret = []
for row in cur:
- obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
+ obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5],
+ history=self)
obj.main = row[6] == 'TRUE'
ret.append(obj)
return ret
@@ -1151,6 +1262,94 @@ class YumHistory:
assert len(ret) == 1
return ret[0]
+ def _load_anydb_key(self, pkg, db, attr):
+ cur = self._get_cursor()
+ if cur is None or not self._update_db_file_3():
+ return None
+
+ pid = self.pkg2pid(pkg, create=False)
+ if pid is None:
+ return None
+
+ sql = """SELECT %(db)sdb_val FROM pkg_%(db)sdb
+ WHERE pkgtupid=? and %(db)sdb_key=? """ % {'db' : db}
+ executeSQL(cur, sql, (pid, attr))
+ for row in cur:
+ return row[0]
+
+ return None
+
+ def _load_rpmdb_key(self, pkg, attr):
+ return self._load_anydb_key(pkg, "rpm", attr)
+ def _load_yumdb_key(self, pkg, attr):
+ return self._load_anydb_key(pkg, "yum", attr)
+
+ def _save_anydb_key(self, pkg, db, attr, val):
+ cur = self._get_cursor()
+ if cur is None or not self._update_db_file_3():
+ return None
+
+ pid = self.pkg2pid(pkg, create=False)
+ if pid is None:
+ return None
+
+ sql = """INSERT INTO pkg_%(db)sdb (pkgtupid, %(db)sdb_key,
%(db)sdb_val)
+ VALUES (?, ?, ?)""" % {'db' : db}
+ executeSQL(cur, sql, (pid, attr, val))
+ for row in cur:
+ return row[0]
+
+ return None
+
+ def _save_rpmdb_key(self, pkg, attr, val):
+ return self._save_anydb_key(pkg, "rpm", attr, val)
+ def _save_yumdb_key(self, pkg, attr, val):
+ return self._save_anydb_key(pkg, "yum", attr, val)
+
+ def _save_rpmdb(self, ipkg):
+ """ Save all the data for rpmdb for this installed pkg, assumes
+ there is no data currently. """
+ for attr in YumHistoryPackage._valid_rpmdb_keys:
+ val = getattr(ipkg, attr, None)
+ if val is None:
+ continue
+ self._save_anydb_key(ipkg, "rpm", attr, val)
+
+ def _save_yumdb(self, ipkg):
+ """ Save all the data for yumdb for this installed pkg, assumes
+ there is no data currently. """
+ for attr in _YumHistPackageYumDB._valid_yumdb_keys:
+ val = ipkg.yumdb_info.get(attr)
+ if val is None:
+ continue
+ self._save_anydb_key(ipkg, "yum", attr, val)
+
+ def _wipe_anydb(self, pkg, db):
+ """ Delete all the data for rpmdb/yumdb for this installed pkg. """
+ cur = self._get_cursor()
+ if cur is None or not self._update_db_file_3():
+ return False
+
+ pid = self.pkg2pid(pkg, create=False)
+ if pid is None:
+ return False
+
+ sql = """DELETE FROM pkg_%(db)sdb WHERE pkgtupid=?""" % {'db' : db}
+ executeSQL(cur, sql, (pid,))
+
+ return True
+
+ def sync_alldb(self, ipkg):
+ """ Sync. all the data for rpmdb/yumdb for this installed pkg. """
+ if not self._wipe_anydb(ipkg, "rpm"):
+ return False
+ self._wipe_anydb(ipkg, "yum")
+ if not self._save_rpmdb(ipkg):
+ return False
+ self._save_yumdb(ipkg)
+ self._commit()
+ return True
+
def _yieldSQLDataList(self, patterns, fields, ignore_case):
"""Yields all the package data for the given params. """
@@ -1220,6 +1419,47 @@ class YumHistory:
tids.add(row[0])
return tids
+ _update_ops_3 = ['''\
+\
+ CREATE TABLE pkg_rpmdb (
+ pkgtupid INTEGER NOT NULL REFERENCES pkgtups,
+ rpmdb_key TEXT NOT NULL,
+ rpmdb_val TEXT NOT NULL);
+''', '''\
+ CREATE INDEX i_pkgkey_rpmdb ON pkg_rpmdb (pkgtupid, rpmdb_key);
+''', '''\
+ CREATE TABLE pkg_yumdb (
+ pkgtupid INTEGER NOT NULL REFERENCES pkgtups,
+ yumdb_key TEXT NOT NULL,
+ yumdb_val TEXT NOT NULL);
+''', '''\
+ CREATE INDEX i_pkgkey_yumdb ON pkg_yumdb (pkgtupid, yumdb_key);
+''']
+
+ def _update_db_file_3(self):
+ """ Update to version 3 of history, rpmdb/yumdb data. """
+ if not self._update_db_file_2():
+ return False
+
+ if hasattr(self, '_cached_updated_3'):
+ return self._cached_updated_3
+
+ cur = self._get_cursor()
+ if cur is None:
+ return False
+
+ executeSQL(cur, "PRAGMA table_info(pkg_yumdb)")
+ # If we get anything, we're fine. There might be a better way of
+ # saying "anything" but this works.
+ for ob in cur:
+ break
+ else:
+ for op in self._update_ops_3:
+ cur.execute(op)
+ self._commit()
+ self._cached_updated_3 = True
+ return True
+
_update_ops_2 = ['''\
\
CREATE TABLE trans_skip_pkgs (
@@ -1374,6 +1614,8 @@ class YumHistory:
cur.execute(op)
for op in self._update_ops_2:
cur.execute(op)
+ for op in self._update_ops_3:
+ cur.execute(op)
self._commit()
# Pasted from sqlitesack
--
1.7.6
_______________________________________________
Yum-devel mailing list
[email protected]
http://lists.baseurl.org/mailman/listinfo/yum-devel