* one-shot transaction protection for install and remove atomicops
This commit is contained in:
+36
-25
@@ -9,26 +9,19 @@
|
|||||||
#
|
#
|
||||||
# Please read the COPYING file.
|
# Please read the COPYING file.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Package install operation
|
|
||||||
|
|
||||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||||
|
|
||||||
|
"Atomic package operations such as install/remove/upgrade"
|
||||||
# for python 2.3 compatibility
|
|
||||||
import sys
|
|
||||||
ver = sys.version_info
|
|
||||||
if ver[0] <= 2 and ver[1] < 4:
|
|
||||||
from sets import Set as set
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
__trans = gettext.translation('pisi', fallback=True)
|
__trans = gettext.translation('pisi', fallback=True)
|
||||||
_ = __trans.ugettext
|
_ = __trans.ugettext
|
||||||
|
|
||||||
import pisi
|
import sys
|
||||||
|
import os
|
||||||
|
import bsddb3.db as db
|
||||||
|
|
||||||
|
import pisi
|
||||||
import pisi.context as ctx
|
import pisi.context as ctx
|
||||||
import pisi.packagedb as packagedb
|
import pisi.packagedb as packagedb
|
||||||
import pisi.dependency as dependency
|
import pisi.dependency as dependency
|
||||||
@@ -78,6 +71,7 @@ class Install(AtomicOperation):
|
|||||||
(self.pkginfo.name, self.pkginfo.version,
|
(self.pkginfo.name, self.pkginfo.version,
|
||||||
self.pkginfo.release, self.pkginfo.build))
|
self.pkginfo.release, self.pkginfo.build))
|
||||||
ctx.ui.notify(pisi.ui.installing, package = self.pkginfo, files = self.files)
|
ctx.ui.notify(pisi.ui.installing, package = self.pkginfo, files = self.files)
|
||||||
|
|
||||||
self.ask_reinstall = ask_reinstall
|
self.ask_reinstall = ask_reinstall
|
||||||
self.check_requirements()
|
self.check_requirements()
|
||||||
self.check_relations()
|
self.check_relations()
|
||||||
@@ -90,7 +84,15 @@ class Install(AtomicOperation):
|
|||||||
ctx.ui.notify(pisi.ui.configuring, package = self.pkginfo, files = self.files)
|
ctx.ui.notify(pisi.ui.configuring, package = self.pkginfo, files = self.files)
|
||||||
comariface.run_postinstall(self.pkginfo.name)
|
comariface.run_postinstall(self.pkginfo.name)
|
||||||
ctx.ui.notify(pisi.ui.configured, package = self.pkginfo, files = self.files)
|
ctx.ui.notify(pisi.ui.configured, package = self.pkginfo, files = self.files)
|
||||||
self.update_databases()
|
|
||||||
|
txn = ctx.dbenv.txn_begin()
|
||||||
|
try:
|
||||||
|
self.update_databases(txn)
|
||||||
|
txn.commit()
|
||||||
|
except db.DBError, e:
|
||||||
|
txn.abort()
|
||||||
|
raise e
|
||||||
|
|
||||||
self.update_environment()
|
self.update_environment()
|
||||||
ctx.ui.status()
|
ctx.ui.status()
|
||||||
if self.upgrade:
|
if self.upgrade:
|
||||||
@@ -228,11 +230,10 @@ class Install(AtomicOperation):
|
|||||||
pisi.comariface.register(pcomar, self.metadata.package.name,
|
pisi.comariface.register(pcomar, self.metadata.package.name,
|
||||||
scriptPath)
|
scriptPath)
|
||||||
|
|
||||||
def update_databases(self):
|
def update_databases(self, txn):
|
||||||
"update databases"
|
"update databases"
|
||||||
|
|
||||||
if self.reinstall:
|
if self.reinstall:
|
||||||
Remove(self.metadata.package.name).remove_db()
|
Remove(self.metadata.package.name).remove_db(txn)
|
||||||
if not self.same_ver:
|
if not self.same_ver:
|
||||||
Remove.remove_pisi_files(self.old_path)
|
Remove.remove_pisi_files(self.old_path)
|
||||||
|
|
||||||
@@ -241,13 +242,14 @@ class Install(AtomicOperation):
|
|||||||
self.metadata.package.version,
|
self.metadata.package.version,
|
||||||
self.metadata.package.release,
|
self.metadata.package.release,
|
||||||
self.metadata.package.build,
|
self.metadata.package.build,
|
||||||
self.metadata.package.distribution)
|
self.metadata.package.distribution,
|
||||||
|
txn)
|
||||||
|
|
||||||
# filesdb
|
# filesdb
|
||||||
ctx.filesdb.add_files(self.metadata.package.name, self.files)
|
ctx.filesdb.add_files(self.metadata.package.name, self.files, txn)
|
||||||
|
|
||||||
# installed packages
|
# installed packages
|
||||||
packagedb.inst_packagedb.add_package(self.pkginfo)
|
packagedb.inst_packagedb.add_package(self.pkginfo, txn)
|
||||||
|
|
||||||
def update_environment(self):
|
def update_environment(self):
|
||||||
# check if we have any shared objects or anything under
|
# check if we have any shared objects or anything under
|
||||||
@@ -328,7 +330,14 @@ class Remove(AtomicOperation):
|
|||||||
for fileinfo in self.files.list:
|
for fileinfo in self.files.list:
|
||||||
self.remove_file(fileinfo)
|
self.remove_file(fileinfo)
|
||||||
|
|
||||||
self.remove_db()
|
txn = ctx.dbenv.txn_begin()
|
||||||
|
try:
|
||||||
|
self.remove_db(txn)
|
||||||
|
txn.commit()
|
||||||
|
except db.DBError, e:
|
||||||
|
txn.abort()
|
||||||
|
raise e
|
||||||
|
|
||||||
self.remove_pisi_files(self.package.pkg_dir())
|
self.remove_pisi_files(self.package.pkg_dir())
|
||||||
ctx.ui.status()
|
ctx.ui.status()
|
||||||
ctx.ui.notify(pisi.ui.removed, package = self.package, files = self.files)
|
ctx.ui.notify(pisi.ui.removed, package = self.package, files = self.files)
|
||||||
@@ -372,11 +381,13 @@ class Remove(AtomicOperation):
|
|||||||
|
|
||||||
remove_pisi_files = staticmethod(remove_pisi_files)
|
remove_pisi_files = staticmethod(remove_pisi_files)
|
||||||
|
|
||||||
def remove_db(self):
|
def remove_db(self, txn):
|
||||||
ctx.installdb.remove(self.package_name)
|
ctx.installdb.remove(self.package_name, txn)
|
||||||
ctx.filesdb.remove_files(ctx.installdb.files(self.package_name))
|
ctx.filesdb.remove_files(ctx.installdb.files(self.package_name), txn)
|
||||||
#FIXME: this looks like a mistake!
|
if packagedb.thirdparty_packagedb.has_package(self.package_name, txn):
|
||||||
packagedb.remove_package(self.package_name)
|
packagedb.thirdparty_packagedb.remove_package(self.package_name, txn)
|
||||||
|
if packagedb.inst_packagedb.has_package(self.package_name, txn):
|
||||||
|
packagedb.inst_packagedb.remove_package(self.package_name, txn)
|
||||||
|
|
||||||
|
|
||||||
def remove_single(package_name):
|
def remove_single(package_name):
|
||||||
|
|||||||
@@ -41,3 +41,19 @@ initialized = False
|
|||||||
#def register(_impl):
|
#def register(_impl):
|
||||||
# """ Register a UI implementation"""
|
# """ Register a UI implementation"""
|
||||||
# ui = _impl
|
# ui = _impl
|
||||||
|
|
||||||
|
|
||||||
|
def txn_proc(proc, txn):
|
||||||
|
# can be used to txn protect a method automatically
|
||||||
|
assert dbenv
|
||||||
|
if not txn:
|
||||||
|
autotxn = dbenv.txn_begin()
|
||||||
|
try:
|
||||||
|
retval = proc(autotxn)
|
||||||
|
except db.DBError, e:
|
||||||
|
autotxn.abort()
|
||||||
|
raise e
|
||||||
|
autotxn.commit()
|
||||||
|
return retval
|
||||||
|
else:
|
||||||
|
return proc(txn)
|
||||||
|
|||||||
+17
-12
@@ -161,26 +161,31 @@ class InstallDB:
|
|||||||
|
|
||||||
self.d.txn_proc(proc,txn)
|
self.d.txn_proc(proc,txn)
|
||||||
|
|
||||||
def clear_pending(self, pkg):
|
def clear_pending(self, pkg, txn = None):
|
||||||
pkg = str(pkg)
|
pkg = str(pkg)
|
||||||
info = self.d[pkg]
|
def proc(txn):
|
||||||
if self.is_installed(pkg):
|
info = self.d.get(pkg, txn)
|
||||||
|
if self.is_installed(pkg, txn):
|
||||||
assert info.state == 'ip'
|
assert info.state == 'ip'
|
||||||
info.state = 'i'
|
info.state = 'i'
|
||||||
self.d[pkg] = info
|
self.d.put(pkg, info, txn)
|
||||||
del self.dp[pkg]
|
self.dp.delete(pkg, txn)
|
||||||
|
self.d.txn_proc(proc,txn)
|
||||||
|
|
||||||
def remove(self, pkg):
|
def remove(self, pkg, txn = None):
|
||||||
pkg = str(pkg)
|
pkg = str(pkg)
|
||||||
info = self.d[pkg]
|
def proc(txn):
|
||||||
|
info = self.d.get(pkg, txn)
|
||||||
info.state = 'r'
|
info.state = 'r'
|
||||||
self.d[pkg] = info
|
self.d.put(pkg, info, txn)
|
||||||
|
self.d.txn_proc(proc, txn)
|
||||||
|
|
||||||
def purge(self, pkg):
|
def purge(self, pkg, txn = None):
|
||||||
pkg = str(pkg)
|
pkg = str(pkg)
|
||||||
if self.d.has_key(pkg):
|
def proc(txn):
|
||||||
del self.d[pkg]
|
if self.d.has_key(pkg, txn):
|
||||||
|
self.d.delete(pkg, txn)
|
||||||
|
self.d.txn_proc(proc, txn)
|
||||||
|
|
||||||
db = None
|
db = None
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ class Error(pisi.Error):
|
|||||||
def init_dbenv():
|
def init_dbenv():
|
||||||
ctx.dbenv = dbobj.DBEnv()
|
ctx.dbenv = dbobj.DBEnv()
|
||||||
ctx.dbenv.open(pisi.context.config.db_dir(),
|
ctx.dbenv.open(pisi.context.config.db_dir(),
|
||||||
db.DB_CREATE | db.DB_INIT_MPOOL | db.DB_INIT_TXN | db.DB_INIT_LOG )
|
db.DB_CREATE | # allow db to create files
|
||||||
|
db.DB_INIT_MPOOL | # cache
|
||||||
|
db.DB_INIT_TXN | # transaction subsystem
|
||||||
|
db.DB_INIT_LOG | # logging subsystem
|
||||||
|
db.DB_RECOVER) # run normal recovery
|
||||||
|
|
||||||
#def open(filename, flags='r', mode = 0644, filetype = db.DB_BTREE):
|
#def open(filename, flags='r', mode = 0644, filetype = db.DB_BTREE):
|
||||||
# db = LockedDBShelf(None, mode, filetype, None, True)
|
# db = LockedDBShelf(None, mode, filetype, None, True)
|
||||||
|
|||||||
+16
-10
@@ -44,9 +44,9 @@ class PackageDB(object):
|
|||||||
self.d.close()
|
self.d.close()
|
||||||
self.dr.close()
|
self.dr.close()
|
||||||
|
|
||||||
def has_package(self, name):
|
def has_package(self, name, txn = None):
|
||||||
name = str(name)
|
name = str(name)
|
||||||
return self.d.has_key(name)
|
return self.d.has_key(name, txn)
|
||||||
|
|
||||||
def get_package(self, name):
|
def get_package(self, name):
|
||||||
name = str(name)
|
name = str(name)
|
||||||
@@ -95,12 +95,14 @@ class PackageDB(object):
|
|||||||
def clear(self):
|
def clear(self):
|
||||||
self.d.clear()
|
self.d.clear()
|
||||||
|
|
||||||
def remove_package(self, name):
|
def remove_package(self, name, txn = None):
|
||||||
name = str(name)
|
name = str(name)
|
||||||
package_info = self.d[name]
|
def proc(txn):
|
||||||
del self.d[name]
|
package_info = self.d.get(name, txn)
|
||||||
|
self.d.delete(name, txn)
|
||||||
#FIXME: what's happening to dr?
|
#FIXME: what's happening to dr?
|
||||||
ctx.componentdb.remove_package(package_info.partOf, package_info.name)
|
ctx.componentdb.remove_package(package_info.partOf, package_info.name, txn)
|
||||||
|
self.d.txn_proc(proc, txn)
|
||||||
|
|
||||||
packagedbs = {}
|
packagedbs = {}
|
||||||
|
|
||||||
@@ -114,18 +116,22 @@ def remove_db(name):
|
|||||||
del pisi.packagedb.packagedbs[name]
|
del pisi.packagedb.packagedbs[name]
|
||||||
#FIXME: erase database file?
|
#FIXME: erase database file?
|
||||||
|
|
||||||
def has_package(name):
|
def has_package(name, txn = None):
|
||||||
|
def proc(txn):
|
||||||
repo = which_repo(name)
|
repo = which_repo(name)
|
||||||
if repo or thirdparty_packagedb.has_package(name) or inst_packagedb.has_package(name):
|
if repo or thirdparty_packagedb.has_package(name, txn) or inst_packagedb.has_package(name, txn):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
return ctx.txn_proc(proc, txn)
|
||||||
|
|
||||||
def which_repo(name):
|
def which_repo(name, txn = None):
|
||||||
import pisi.repodb
|
import pisi.repodb
|
||||||
|
def proc(txn):
|
||||||
for repo in pisi.repodb.db.list():
|
for repo in pisi.repodb.db.list():
|
||||||
if get_db(repo).has_package(name):
|
if get_db(repo).has_package(name, txn):
|
||||||
return repo
|
return repo
|
||||||
return None
|
return None
|
||||||
|
return ctx.txn_proc(proc, txn)
|
||||||
|
|
||||||
def get_package(name):
|
def get_package(name):
|
||||||
repo = which_repo(name)
|
repo = which_repo(name)
|
||||||
|
|||||||
Reference in New Issue
Block a user