summaryrefslogtreecommitdiff
path: root/DistUpgrade/DistUpgradeControler.py
diff options
context:
space:
mode:
Diffstat (limited to 'DistUpgrade/DistUpgradeControler.py')
-rw-r--r--DistUpgrade/DistUpgradeControler.py196
1 files changed, 166 insertions, 30 deletions
diff --git a/DistUpgrade/DistUpgradeControler.py b/DistUpgrade/DistUpgradeControler.py
index 02d25121..4e76a65d 100644
--- a/DistUpgrade/DistUpgradeControler.py
+++ b/DistUpgrade/DistUpgradeControler.py
@@ -31,6 +31,7 @@ import logging
import re
import statvfs
import shutil
+import glob
from DistUpgradeConfigParser import DistUpgradeConfig
from aptsources import SourcesList, SourceEntry, Distribution, is_mirror
@@ -59,7 +60,8 @@ class AptCdrom(object):
if backup_ext:
cdromstate = os.path.join(apt_pkg.Config.FindDir("Dir::State"),
apt_pkg.Config.Find("Dir::State::cdroms"))
- shutil.copy(cdromstate, cdromstate+backup_ext)
+ if os.path.exists(cdromstate):
+ shutil.copy(cdromstate, cdromstate+backup_ext)
# do the actual work
apt_pkg.Config.Set("Acquire::cdrom::mount",self.cdrompath)
apt_pkg.Config.Set("APT::CDROM::NoMount","true")
@@ -74,7 +76,7 @@ class AptCdrom(object):
_("There was a error adding the CD, the "
"upgrade will abort. Please report this as "
"a bug if this is a valid Ubuntu CD.\n\n"
- "The error message was:\n'%s'" % e))
+ "The error message was:\n'%s'") % e)
return False
logging.debug("AptCdrom.add() returned: %s" % res)
return res
@@ -86,7 +88,7 @@ class AptCdrom(object):
class DistUpgradeControler(object):
""" this is the controler that does most of the work """
- def __init__(self, distUpgradeView, cdromPath=None, datadir=None):
+ def __init__(self, distUpgradeView, options=None, datadir=None):
# setup the pathes
localedir = "/usr/share/locale/update-manager/"
if datadir == None:
@@ -95,6 +97,8 @@ class DistUpgradeControler(object):
gladedir = datadir
self.datadir = datadir
+ self.options = options
+
# init gettext
gettext.bindtextdomain("update-manager",localedir)
gettext.textdomain("update-manager")
@@ -104,9 +108,15 @@ class DistUpgradeControler(object):
self._view.updateStatus(_("Reading cache"))
self.cache = None
- # specific for the CDROM based upgrade
- self.aptcdrom = AptCdrom(distUpgradeView, cdromPath)
- self.useNetwork = True
+ if not self.options or self.options.withNetwork == None:
+ self.useNetwork = True
+ else:
+ self.useNetwork = self.options.withNetwork
+ if options:
+ cdrompath = options.cdromPath
+ else:
+ cdrompath = None
+ self.aptcdrom = AptCdrom(distUpgradeView, cdrompath)
# the configuration
self.config = DistUpgradeConfig(datadir)
@@ -123,9 +133,8 @@ class DistUpgradeControler(object):
# turn on debuging in the cache
apt_pkg.Config.Set("Debug::pkgProblemResolver","true")
apt_pkg.Config.Set("Debug::pkgDepCache::AutoInstall","true")
- # FIXME: make this "append"?
fd = os.open("/var/log/dist-upgrade/apt.log",
- os.O_RDWR|os.O_CREAT|os.O_TRUNC, 0644)
+ os.O_RDWR|os.O_CREAT|os.O_APPEND, 0644)
os.dup2(fd,1)
os.dup2(fd,2)
@@ -134,12 +143,16 @@ class DistUpgradeControler(object):
def prepare(self):
""" initial cache opening, sanity checking, network checking """
- self.openCache()
+ try:
+ self.openCache()
+ except SystemError, e:
+ logging.error("openCache() failed: '%s'" % e)
+ return False
if not self.cache.sanityCheck(self._view):
return False
- # FIXME: we may try to find out a bit more about the network connection here and ask more
- # inteligent questions
- if self.aptcdrom:
+ # FIXME: we may try to find out a bit more about the network
+ # connection here and ask more inteligent questions
+ if self.aptcdrom and self.options and self.options.withNetwork == None:
res = self._view.askYesNoQuestion(_("Fetch data from the network for the upgrade?"),
_("The upgrade can use the network to check "
"the latest updates and to fetch packages that are not on the "
@@ -157,6 +170,7 @@ class DistUpgradeControler(object):
# enable main (we always need this!)
distro = Distribution()
distro.get_sources(self.sources)
+ # make sure that main is enabled
distro.enable_component(self.sources, "main")
# this must map, i.e. second in "from" must be the second in "to"
@@ -188,7 +202,7 @@ class DistUpgradeControler(object):
# we disable breezy cdrom sources to make sure that demoted
# packages are removed
- if entry.uri.startswith("cdrom:") and entry.dist == "breezy":
+ if entry.uri.startswith("cdrom:") and entry.dist == self.fromDist:
entry.disabled = True
continue
# ignore cdrom sources otherwise
@@ -270,7 +284,7 @@ class DistUpgradeControler(object):
self.toDist+"-security", comps)
else:
self.abort()
-
+
# write (well, backup first ;) !
self.sources.backup(self.sources_backup_ext)
self.sources.save()
@@ -293,7 +307,7 @@ class DistUpgradeControler(object):
if self.sources_disabled:
self._view.information(_("Third party sources disabled"),
- _("Some third party entries in your souces.list "
+ _("Some third party entries in your sources.list "
"were disabled. You can re-enable them "
"after the upgrade with the "
"'software-properties' tool or with synaptic."
@@ -306,13 +320,17 @@ class DistUpgradeControler(object):
inst = []
up = []
rm = []
+ held = []
for pkg in self.cache:
if pkg.markedInstall: inst.append(pkg.name)
elif pkg.markedUpgrade: up.append(pkg.name)
elif pkg.markedDelete: rm.append(pkg.name)
+ elif (pkg.isInstalled and pkg.isUpgradable): held.append(pkg.name)
+ logging.debug("Held-back: %s" % " ".join(held))
logging.debug("Remove: %s" % " ".join(rm))
logging.debug("Install: %s" % " ".join(inst))
logging.debug("Upgrade: %s" % " ".join(up))
+
def doPreUpgrade(self):
# FIXME: check out what packages are downloadable etc to
@@ -341,7 +359,8 @@ class DistUpgradeControler(object):
continue
# no exception, so all was fine, we are done
return True
-
+
+ logging.error("doUpdate() failed complettely")
self._view.error(_("Error during update"),
_("A problem occured during the update. "
"This is usually some sort of network "
@@ -359,6 +378,18 @@ class DistUpgradeControler(object):
"packages of former installations using "
"'sudo apt-get clean'.")
+ # gather/log some staticts
+ mnt_map = {}
+ for d in ["/","/usr","/var","/boot"]:
+ st = os.statvfs(d)
+ free = st[statvfs.F_BAVAIL]*st[statvfs.F_FRSIZE]
+ if st in mnt_map:
+ logging.debug("Dir %s mounted on %s" % (d,mnt_map[st]))
+ else:
+ logging.debug("Free space on %s: %s" % (d,free))
+ mnt_map[st] = d
+ del mnt_map
+
# first check for /var (or where the archives are downloaded too)
archivedir = apt_pkg.Config.FindDir("Dir::Cache::archives")
st_archivedir = os.statvfs(archivedir)
@@ -367,6 +398,7 @@ class DistUpgradeControler(object):
logging.debug("free on %s: %s " % (archivedir, free))
if self.cache.requiredDownload > free:
free_at_least = apt_pkg.SizeToStr(self.cache.requiredDownload-free)
+ logging.error("not enough free space (missing %s)" % free_at_least)
self._view.error(err_sum, err_long % (free_at_least,archivedir))
return False
@@ -418,6 +450,9 @@ class DistUpgradeControler(object):
return res
def doDistUpgrade(self):
+ if self.options and self.options.haveBackports:
+ backportsdir = os.getcwd()+"/backports"
+ apt_pkg.Config.Set("Dir::Bin::dpkg",backportsdir+"/usr/bin/dpkg");
currentRetry = 0
fprogress = self._view.getFetchProgress()
iprogress = self._view.getInstallProgress(self.cache)
@@ -428,6 +463,7 @@ class DistUpgradeControler(object):
res = self.cache.commit(fprogress,iprogress)
except SystemError, e:
# installing the packages failed, can't be retried
+ logging.error("SystemError from cache.commit(): %s" % e)
self._view.getTerminal().call(["dpkg","--configure","-a"])
self._view.error(_("Could not install the upgrades"),
_("The upgrade aborts now. Your system "
@@ -447,7 +483,7 @@ class DistUpgradeControler(object):
return True
# maximum fetch-retries reached without a successful commit
- logging.debug("giving up on fetching after maximum retries")
+ logging.error("giving up on fetching after maximum retries")
self._view.error(_("Could not download the upgrades"),
_("The upgrade aborts now. Please check your "\
"internet connection or "\
@@ -485,15 +521,15 @@ class DistUpgradeControler(object):
demoted = [pkg.name for pkg in installed_demotions]
demoted.sort()
logging.debug("demoted: '%s'" % " ".join(demoted))
- self._view.information(_("Some software no longer officially "
- "supported"),
- _("These installed packages are "
- "no longer officially supported, "
- "and are now only "
- "community-supported ('universe').\n\n"
- "If you don't have 'universe' enabled "
+ self._view.information(_("Support for some applications ended"),
+ _("Canonical Ltd. no longer provides "
+ "support for the following software "
+ "packages. You can still get support "
+ "from the community.\n\n"
+ "If you have not enabled community "
+ "maintained software (universe), "
"these packages will be suggested for "
- "removal in the next step. "),
+ "removal in the next step."),
"\n".join(demoted))
# mark packages that are now obsolete (and where not obsolete
@@ -532,6 +568,7 @@ class DistUpgradeControler(object):
try:
res = self.cache.commit(fprogress,iprogress)
except (SystemError, IOError), e:
+ logging.error("cache.commit() in doPostUpgrade() failed: %s" % e)
self._view.error(_("Error during commit"),
_("Some problem occured during the clean-up. "
"Please see the below message for more "
@@ -540,22 +577,121 @@ class DistUpgradeControler(object):
def abort(self):
""" abort the upgrade, cleanup (as much as possible) """
- self.sources.restoreBackup(self.sources_backup_ext)
- self.aptcdrom.restoreBackup(self.sources_backup_ext)
+ if hasattr(self, "sources"):
+ self.sources.restoreBackup(self.sources_backup_ext)
+ if hasattr(self, "aptcdrom"):
+ self.aptcdrom.restoreBackup(self.sources_backup_ext)
# generate a new cache
self._view.updateStatus(_("Restoring original system state"))
+ self._view.abort()
self.openCache()
sys.exit(1)
-
+ def getRequiredBackports(self):
+ " download the backports specified in DistUpgrade.cfg "
+ # add the backports sources.list fragment
+ shutil.copy(self.config.get("Backports","SourcesList"),
+ apt_pkg.Config.FindDir("Dir::Etc::sourceparts"))
+ # run update
+ self.doUpdate()
+ self.openCache()
+
+ # save cachedir and setup new one
+ cachedir = apt_pkg.Config.Find("Dir::Cache::archives")
+ cwd = os.getcwd()
+ backportsdir = os.path.join(os.getcwd(),"backports")
+ if not os.path.exists(backportsdir):
+ os.mkdir(backportsdir)
+ if not os.path.exists(os.path.join(backportsdir,"partial")):
+ os.mkdir(os.path.join(backportsdir,"partial"))
+ os.chdir(backportsdir)
+ apt_pkg.Config.Set("Dir::Cache::archives",backportsdir)
+
+ # mark the backports for upgrade and get them
+ fetcher = apt_pkg.GetAcquire(self._view.getFetchProgress())
+ # FIXME: add a version line to the cfg file to make sure
+ # we get the right version file! and add sanity checking
+ # that we don't get (accidently) the edgy version
+ for pkgname in self.config.getlist("Backports","Packages"):
+ pkg = self.cache[pkgname]
+ # look for the right version (backport)
+ for ver in pkg._pkg.VersionList:
+ print ver.VerStr
+ if self.config.get("Backports","VersionIdent") in ver.VerStr:
+ break
+ else:
+ # FIXME: be more clever here (exception)
+ raise Exception, "No backport found!?!"
+ return False
+ if ver.FileList == None:
+ print "No FileList for: %s " % self._pkg.Name()
+ return False
+ f, index = ver.FileList.pop(0)
+ pkg._records.Lookup((f,index))
+ path = apt_pkg.ParseSection(pkg._records.Record)["Filename"]
+ for (packagefile,i) in ver.FileList:
+ indexfile = self.cache._list.FindIndex(packagefile)
+ if indexfile:
+ match = re.match(r"<.*ArchiveURI='(.*)'>$",
+ str(indexfile))
+ if match:
+ uri = match.group(1) + path
+ apt_pkg.GetPkgAcqFile(fetcher, uri=uri,
+ size=ver.Size,
+ descr=_("Fetching backport of '%s'") % pkgname)
+ res = fetcher.Run()
+ if res != fetcher.ResultContinue:
+ # ick! error ...
+ return False
+
+ # reset the cache dir
+ os.unlink(apt_pkg.Config.FindDir("Dir::Etc::sourceparts")+"/backport-source.list")
+ apt_pkg.Config.Set("Dir::Cache::archives",cachedir)
+ os.chdir(cwd)
+ # unpack it
+ for deb in glob.glob(backportsdir+"/*.deb"):
+ ret = os.system("dpkg-deb -x %s %s" % (deb, backportsdir))
+ # FIXME: do error checking
+ return self.setupRequiredBackports(backportsdir)
+
+ def setupRequiredBackports(self, backportsdir):
+ " setup the required backports in a evil way "
+ # setup some pathes to make sure the new stuff is used
+ os.environ["LD_LIBRARY_PATH"] = backportsdir+"/usr/lib"
+ os.environ["PYTHONPATH"] = backportsdir+"/usr/lib/python2.4/site-packages/"
+ os.environ["PATH"] = "%s:%s" % (backportsdir+"/usr/bin",
+ os.getenv("PATH"))
+
+ # now exec self again
+ args = sys.argv+["--have-backports"]
+ if self.useNetwork:
+ args.append("--with-network")
+ else:
+ args.append("--without-network")
+ os.execve(sys.argv[0],args, os.environ)
+
# this is the core
def edgyUpgrade(self):
# sanity check (check for ubuntu-desktop, brokenCache etc)
self._view.updateStatus(_("Checking package manager"))
self._view.setStep(1)
-
+
if not self.prepare():
- self.abort(1)
+ logging.error("self.prepared() failed")
+ self._view.error(_("Preparing the upgrade failed"),
+ _("Preparing the system for the upgrade "
+ "failed. Please report this as a bug "
+ "against the 'update-manager' "
+ "package and include the files in "
+ "/var/log/dist-upgrade/ "
+ "in the bugreport." ))
+ sys.exit(1)
+
+ # mvo: commented out for now, see #54234, this needs to be
+ # refactored to use a arch=any tarball
+ #if self.options and self.options.haveBackports == False:
+ # # get backported packages (if needed)
+ # self.getRequiredBackports()
# run a "apt-get update" now
if not self.doUpdate():