home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / DistUpgrade / DistUpgradeController.py < prev    next >
Encoding:
Python Source  |  2009-04-27  |  75.3 KB  |  1,631 lines

  1. # DistUpgradeController.py 
  2. #  
  3. #  Copyright (c) 2004-2008 Canonical
  4. #  
  5. #  Author: Michael Vogt <michael.vogt@ubuntu.com>
  6. #  This program is free software; you can redistribute it and/or 
  7. #  modify it under the terms of the GNU General Public License as 
  8. #  published by the Free Software Foundation; either version 2 of the
  9. #  License, or (at your option) any later version.
  10. #  This program is distributed in the hope that it will be useful,
  11. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #  GNU General Public License for more details.
  14. #  You should have received a copy of the GNU General Public License
  15. #  along with this program; if not, write to the Free Software
  16. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  17. #  USA
  18.  
  19.  
  20. import warnings
  21. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  22. import apt
  23. import apt_pkg
  24. import sys
  25. import os
  26. import subprocess
  27. import logging
  28. import re
  29. import statvfs
  30. import shutil
  31. import glob
  32. import time
  33. import copy
  34. import ConfigParser
  35. from stat import *
  36. from utils import country_mirror, url_downloadable
  37. from string import Template
  38.  
  39.  
  40. import DistUpgradeView
  41. from DistUpgradeConfigParser import DistUpgradeConfig
  42. from DistUpgradeFetcherCore import country_mirror
  43. from DistUpgradeQuirks import DistUpgradeQuirks
  44. from DistUpgradeAptCdrom import AptCdrom
  45. from DistUpgradeAufs import setupAufs, aufsOptionsAndEnvironmentSetup
  46.  
  47. from sourceslist import SourcesList, SourceEntry, is_mirror
  48. from distro import Distribution, get_distro, NoDistroTemplateException
  49.  
  50. from DistUpgradeGettext import gettext as _
  51. from DistUpgradeGettext import ngettext
  52. import gettext
  53.  
  54. from DistUpgradeCache import *
  55. from DistUpgradeApport import *
  56.  
  57.  
  58. class NoBackportsFoundException(Exception):
  59.     pass
  60.  
  61.  
  62. class DistUpgradeController(object):
  63.     """ this is the controller that does most of the work """
  64.     
  65.     def __init__(self, distUpgradeView, options=None, datadir=None):
  66.         # setup the paths
  67.         localedir = "/usr/share/locale/update-manager/"
  68.         if datadir == None:
  69.             datadir = os.getcwd()
  70.             localedir = os.path.join(datadir,"mo")
  71.             gladedir = datadir
  72.         self.datadir = datadir
  73.         self.options = options
  74.  
  75.         # init gettext
  76.         gettext.bindtextdomain("update-manager",localedir)
  77.         gettext.textdomain("update-manager")
  78.  
  79.         # setup the view
  80.         logging.debug("Using '%s' view" % distUpgradeView.__class__.__name__)
  81.         self._view = distUpgradeView
  82.         self._view.updateStatus(_("Reading cache"))
  83.         self.cache = None
  84.  
  85.         if not self.options or self.options.withNetwork == None:
  86.             self.useNetwork = True
  87.         else:
  88.             self.useNetwork = self.options.withNetwork
  89.         if options:
  90.             cdrompath = options.cdromPath
  91.         else:
  92.             cdrompath = None
  93.         self.aptcdrom = AptCdrom(distUpgradeView, cdrompath)
  94.  
  95.         # the configuration
  96.         self.config = DistUpgradeConfig(datadir)
  97.         self.sources_backup_ext = "."+self.config.get("Files","BackupExt")
  98.  
  99.         # move some of the options stuff into the self.config, 
  100.         # ConfigParser deals only with strings it seems *sigh*
  101.         self.config.add_section("Options")
  102.         self.config.set("Options","withNetwork", str(self.useNetwork))
  103.  
  104.         # aufs stuff
  105.         aufsOptionsAndEnvironmentSetup(self.options, self.config)
  106.  
  107.         # some constants here
  108.         self.fromDist = self.config.get("Sources","From")
  109.         self.toDist = self.config.get("Sources","To")
  110.         self.origin = self.config.get("Sources","ValidOrigin")
  111.         self.arch = apt_pkg.Config.Find("APT::Architecture")
  112.  
  113.         # we run with --force-overwrite by default
  114.         if not os.environ.has_key("RELEASE_UPGRADE_NO_FORCE_OVERWRITE"):
  115.             logging.debug("enable dpkg --force-overwrite")
  116.             apt_pkg.Config.Set("DPkg::Options::","--force-overwrite")
  117.  
  118.         # we run in full upgrade mode by default
  119.         self._partialUpgrade = False
  120.         
  121.         # install the quirks handler
  122.         self.quirks = DistUpgradeQuirks(self, self.config)
  123.  
  124.         # setup env var 
  125.         os.environ["RELEASE_UPGRADE_IN_PROGRESS"] = "1"
  126.         os.environ["PYCENTRAL_NO_DPKG_QUERY"] = "1"
  127.         os.environ["PYCENTRAL_FORCE_OVERWRITE"] = "1"
  128.         os.environ["PATH"] = "%s:%s" % (os.getcwd()+"/imported",
  129.                                         os.environ["PATH"])
  130.  
  131.         # set max retries
  132.         maxRetries = self.config.getint("Network","MaxRetries")
  133.         apt_pkg.Config.Set("Acquire::Retries", str(maxRetries))
  134.         # max sizes for dpkgpm for large installs (see linux/limits.h and 
  135.         #                                          linux/binfmts.h)
  136.         apt_pkg.Config.Set("Dpkg::MaxArgs", str(64*1024))
  137.         apt_pkg.Config.Set("Dpkg::MaxArgBytes", str(128*1024))
  138.  
  139.         # smaller to avoid hangs
  140.         apt_pkg.Config.Set("Acquire::http::Timeout","20")
  141.         apt_pkg.Config.Set("Acquire::ftp::Timeout","20")
  142.  
  143.         # forced obsoletes
  144.         self.forced_obsoletes = self.config.getlist("Distro","ForcedObsoletes")
  145.         # list of valid mirrors that we can add
  146.         self.valid_mirrors = self.config.getListFromFile("Sources","ValidMirrors")
  147.         # debugging
  148.         #apt_pkg.Config.Set("DPkg::Options::","--debug=0077")
  149.  
  150.  
  151.     def openCache(self, lock=True):
  152.         logging.debug("openCache()")
  153.         if self.cache is not None:
  154.             self.cache.releaseLock()
  155.             self.cache.unlockListsDir()
  156.         try:
  157.             self.cache = MyCache(self.config,
  158.                                  self._view,
  159.                                  self.quirks,
  160.                                  self._view.getOpCacheProgress(),
  161.                                  lock)
  162.             # alias name for the plugin interface code
  163.             self.apt_cache = self.cache
  164.         # if we get a dpkg error that it was interrupted, just
  165.         # run dpkg --configure -a
  166.         except CacheExceptionDpkgInterrupted, e:
  167.             logging.warning("dpkg interrupted, calling dpkg --configure -a")
  168.             self._view.getTerminal().call(["dpkg","--configure","-a"])
  169.             self.cache = MyCache(self.config,
  170.                                  self._view,
  171.                                  self.quirks,
  172.                                  self._view.getOpCacheProgress())
  173.         except CacheExceptionLockingFailed, e:
  174.             logging.error("Cache can not be locked (%s)" % e)
  175.             self._view.error(_("Unable to get exclusive lock"),
  176.                              _("This usually means that another "
  177.                                "package management application "
  178.                                "(like apt-get or aptitude) "
  179.                                "already running. Please close that "
  180.                                "application first."));
  181.             sys.exit(1)
  182.         self.cache.partialUpgrade = self._partialUpgrade
  183.         logging.debug("/openCache()")
  184.  
  185.     def _isRemoteLogin(self):
  186.         " check if we are running form a remote login "
  187.         # easy
  188.         if (os.environ.has_key("SSH_CONNECTION") or
  189.             os.environ.has_key("SSH_TTY")):
  190.             return True
  191.         # sudo cleans out SSH_ environment
  192.         out = subprocess.Popen(["who","-m","--ips"],stdout=subprocess.PIPE).communicate()[0]
  193.         logging.debug("who -m --ips: '%s'" % out)
  194.         # the host is in ()
  195.         if not "(" in out:
  196.             return False
  197.         # if we have a () parse it
  198.         ip = out.strip().rsplit('(')[1]
  199.         ip = ip.strip(')')
  200.         # if we have a ip here and it does not start with a
  201.         # ":" we have a remote login
  202.         # FIXME: what about IPv6 ?
  203.         if not ip.startswith(":"):
  204.             return True
  205.         return False
  206.  
  207.     def _viewSupportsSSH(self):
  208.       """
  209.       Returns True if this view support upgrades over ssh.
  210.       In theory all views should support it, but for savety
  211.       we do only allow text ssh upgrades (see LP: #322482)
  212.       """
  213.       supported = self.config.getlist("View","SupportSSH")
  214.       if self._view.__class__.__name__ in supported:
  215.           return True
  216.       return False
  217.  
  218.     def _sshMagic(self):
  219.         """ this will check for server mode and if we run over ssh.
  220.             if this is the case, we will ask and spawn a additional
  221.             daemon (to be sure we have a spare one around in case
  222.             of trouble)
  223.         """
  224.         pidfile = os.path.join("/var/run/release-upgrader-sshd.pid")
  225.         if (not os.path.exists(pidfile) and self._isRemoteLogin()):
  226.             # check if the frontend supports ssh ugprades (see lp: #322482)
  227.             if not self._viewSupportsSSH():
  228.                 logging.error("upgrade over ssh not alllowed")
  229.                 self._view.error(_("Upgrading over remote connection not supported"),
  230.                                  _("You are running the upgrade over a "
  231.                                    "remote ssh connection with a frontend "
  232.                                    "that does "
  233.                                    "not support this. The upgrade will "
  234.                                    "abort now. Please try without ssh.")
  235.                                  )
  236.                 sys.exit(1)
  237.                 return False
  238.             # ask for a spare one to start
  239.             port = 9004
  240.             res = self._view.askYesNoQuestion(
  241.                 _("Continue running under SSH?"),
  242.                 _("This session appears to be running under ssh. "
  243.                   "It is not recommended to perform a upgrade "
  244.                   "over ssh currently because in case of failure "
  245.                 "it is harder to recover.\n\n"
  246.                   "If you continue, a additional ssh daemon will be "
  247.                   "started at port '%s'.\n"
  248.                   "Do you want to continue?") % port)
  249.             # abort
  250.             if res == False:
  251.                 sys.exit(1)
  252.             res = subprocess.call(["/usr/sbin/sshd",
  253.                                    "-o", "PidFile=%s" % pidfile,
  254.                                    "-p",str(port)])
  255.             if res == 0:
  256.                 self._view.information(
  257.                     _("Starting additional sshd"),
  258.                     _("To make recovery in case of failure easier, an "
  259.                       "additional sshd will be started on port '%s'. "
  260.                       "If anything goes wrong with the running ssh "
  261.                       "you can still connect to the additional one.\n"
  262.                       ) % port)
  263.         return True
  264.  
  265.     def _tryUpdateSelf(self):
  266.         """ this is a helper that is run if we are started from a CD
  267.             and we have network - we will then try to fetch a update
  268.             of ourself
  269.         """  
  270.         from MetaRelease import MetaReleaseCore
  271.         from DistUpgradeFetcherSelf import DistUpgradeFetcherSelf
  272.         # check if we run from a LTS 
  273.         forceLTS=False
  274.         if (self.release == "dapper" or
  275.             self.release == "hardy"):
  276.             forceLTS=True
  277.         m = MetaReleaseCore(useDevelopmentRelease=False,
  278.                             forceLTS=forceLTS)
  279.         # this will timeout eventually
  280.         while m.downloading:
  281.             self._view.processEvents()
  282.             time.sleep(0.1)
  283.         if m.new_dist is None:
  284.             logging.error("No new dist found")
  285.             return False
  286.         # we have a new dist
  287.         progress = self._view.getFetchProgress()
  288.         fetcher = DistUpgradeFetcherSelf(new_dist=m.new_dist,
  289.                                          progress=progress,
  290.                                          options=self.options,
  291.                                          view=self._view)
  292.         fetcher.run()
  293.  
  294.     def _pythonSymlinkCheck(self):
  295.         """ sanity check that /usr/bin/python points to the default
  296.             python version. Users tend to modify this symlink, which
  297.             breaks stuff in obscure ways (Ubuntu #75557).
  298.         """
  299.         logging.debug("_pythonSymlinkCheck run")
  300.         from ConfigParser import SafeConfigParser, NoOptionError
  301.         if os.path.exists('/usr/share/python/debian_defaults'):
  302.             config = SafeConfigParser()
  303.             config.readfp(file('/usr/share/python/debian_defaults'))
  304.             try:
  305.                 expected_default = config.get('DEFAULT', 'default-version')
  306.             except NoOptionError:
  307.                 logging.debug("no default version for python found in '%s'" % config)
  308.                 return False
  309.             try:
  310.                 fs_default_version = os.readlink('/usr/bin/python')
  311.             except OSError, e:
  312.                 logging.error("os.readlink failed (%s)" % e)
  313.                 return False
  314.             if not fs_default_version in (expected_default, os.path.join('/usr/bin', expected_default)):
  315.                 logging.debug("python symlink points to: '%s', but expected is '%s' or '%s'" % (fs_default_version, expected_default, os.path.join('/usr/bin', expected_default)))
  316.                 return False
  317.         return True
  318.  
  319.  
  320.     def prepare(self):
  321.         """ initial cache opening, sanity checking, network checking """
  322.         # first check if that is a good upgrade
  323.         self.release = release = subprocess.Popen(["lsb_release","-c","-s"],
  324.                                    stdout=subprocess.PIPE).communicate()[0].strip()
  325.         logging.debug("lsb-release: '%s'" % release)
  326.         if not (release == self.fromDist or release == self.toDist):
  327.             logging.error("Bad upgrade: '%s' != '%s' " % (release, self.fromDist))
  328.             self._view.error(_("Can not upgrade"),
  329.                              _("An upgrade from '%s' to '%s' is not "
  330.                                "supported with this tool." % (release, self.toDist)))
  331.             sys.exit(1)
  332.  
  333.         # setup aufs
  334.         if self.config.getWithDefault("Aufs", "EnableFullOverlay", False):
  335.             aufs_rw_dir = self.config.get("Aufs","RWDir")
  336.             if not setupAufs(aufs_rw_dir):
  337.                 logging.error("aufs setup failed")
  338.                 self._view.error(_("Sandbox setup failed"),
  339.                                  _("It was not possible to create the sandbox "
  340.                                    "environment."))
  341.                 return False
  342.  
  343.             # all good, tell the user about the sandbox mode
  344.             logging.info("running in aufs overlay mode")
  345.             self._view.information(_("Sandbox mode"),
  346.                                    _("This upgrade is running in sandbox "
  347.                                      "(test) mode. All changes are written "
  348.                                      "to '%s' and will be lost on the next "
  349.                                      "reboot.\n\n"
  350.                                      "*No* changes written to a systemdir "
  351.                                      "from now until the next reboot are "
  352.                                      "permanent.") % aufs_rw_dir)
  353.  
  354.         # setup backports (if we have them)
  355.         if self.options and self.options.havePrerequists:
  356.             backportsdir = os.getcwd()+"/backports"
  357.             logging.info("using backports in '%s' " % backportsdir)
  358.             logging.debug("have: %s" % glob.glob(backportsdir+"/*.udeb"))
  359.             if os.path.exists(backportsdir+"/usr/bin/dpkg"):
  360.                 apt_pkg.Config.Set("Dir::Bin::dpkg",backportsdir+"/usr/bin/dpkg");
  361.             if os.path.exists(backportsdir+"/usr/lib/apt/methods"):
  362.                 apt_pkg.Config.Set("Dir::Bin::methods",backportsdir+"/usr/lib/apt/methods")
  363.             conf = backportsdir+"/etc/apt/apt.conf.d/01ubuntu"
  364.             if os.path.exists(conf):
  365.                 logging.debug("adding config '%s'" % conf)
  366.                 apt_pkg.ReadConfigFile(apt_pkg.Config, conf)
  367.  
  368.         # do the ssh check and warn if we run under ssh
  369.         self._sshMagic()
  370.         # check python version
  371.         if not self._pythonSymlinkCheck():
  372.             logging.error("pythonSymlinkCheck() failed, aborting")
  373.             self._view.error(_("Can not upgrade"),
  374.                              _("Your python install is corrupted. "
  375.                                "Please fix the '/usr/bin/python' symlink."))
  376.             sys.exit(1)
  377.         # open cache
  378.         try:
  379.             self.openCache()
  380.         except SystemError, e:
  381.             logging.error("openCache() failed: '%s'" % e)
  382.             return False
  383.         if not self.cache.sanityCheck(self._view):
  384.             return False
  385.  
  386.         # now figure out if we need to go into desktop or 
  387.         # server mode - we use a heuristic for this
  388.         self.serverMode = self.cache.needServerMode()
  389.         if self.serverMode:
  390.             os.environ["RELEASE_UPGRADE_MODE"] = "server"
  391.         else:
  392.             os.environ["RELEASE_UPGRADE_MODE"] = "desktop"
  393.  
  394.         if not self.checkViewDepends():
  395.             logging.error("checkViewDepends() failed")
  396.             return False
  397.  
  398.         if os.path.exists("/usr/bin/debsig-verify"):
  399.             logging.error("debsig-verify is installed")
  400.             self._view.error(_("Package 'debsig-verify' is installed"),
  401.                              _("The upgrade can not continue with that "
  402.                                "package installed.\n"
  403.                                "Please remove it with synaptic "
  404.                                "or 'apt-get remove debsig-verify' first "
  405.                                "and run the upgrade again."))
  406.             self.abort()
  407.  
  408.         # FIXME: we may try to find out a bit more about the network
  409.         # connection here and ask more  intelligent questions
  410.         if self.aptcdrom and self.options and self.options.withNetwork == None:
  411.             res = self._view.askYesNoQuestion(_("Include latest updates from the Internet?"),
  412.                                               _("The upgrade system can use the internet to "
  413.                                                 "automatically download "
  414.                                                 "the latest updates and install them during the "
  415.                                                 "upgrade.  If you have a network connection this is "
  416.                                                 "highly recommended.\n\n"
  417.                                                 "The upgrade will take longer, but when "
  418.                                                 "it is complete, your system will be fully up to "
  419.                                                 "date.  You can choose not to do this, but you "
  420.                                                 "should install the latest updates soon after "
  421.                                                 "upgrading.\n"
  422.                                                 "If you answer 'no' here, the network is not "
  423.                                                 "used at all."),
  424.                                               'Yes')
  425.             self.useNetwork = res
  426.             self.config.set("Options","withNetwork", str(self.useNetwork))
  427.             logging.debug("useNetwork: '%s' (selected by user)" % res)
  428.             if res:
  429.                 self._tryUpdateSelf()
  430.         return True
  431.  
  432.     def _sourcesListEntryDownloadable(self, entry):
  433.         """
  434.         helper that checks if a sources.list entry points to 
  435.         something downloadable
  436.         """
  437.         logging.debug("verifySourcesListEntry: %s" % entry)
  438.         # no way to verify without network
  439.         if not self.useNetwork:
  440.             logging.debug("skiping downloadable check (no network)")
  441.             return True
  442.         # check if the entry points to something we can download
  443.         uri = "%s/dists/%s/Release" % (entry.uri, entry.dist)
  444.         return url_downloadable(uri, logging.debug)
  445.  
  446.     def rewriteSourcesList(self, mirror_check=True):
  447.         logging.debug("rewriteSourcesList()")
  448.  
  449.         sync_components = self.config.getlist("Sources","Components")
  450.  
  451.         # skip mirror check if special environment is set
  452.         # (useful for server admins with internal repos)
  453.         if (self.config.getWithDefault("Sources","AllowThirdParty",False) or
  454.             "RELEASE_UPRADER_ALLOW_THIRD_PARTY" in os.environ):
  455.             logging.warning("mirror check skipped, *overriden* via config")
  456.             mirror_check=False
  457.  
  458.         # check if we need to enable main
  459.         if mirror_check == True and self.useNetwork:
  460.             # now check if the base-meta pkgs are available in
  461.             # the archive or only available as "now"
  462.             # -> if not that means that "main" is missing and we
  463.             #    need to  enable it
  464.             for pkgname in self.config.getlist("Distro","BaseMetaPkgs"):
  465.                 if ((not self.cache.has_key(pkgname)
  466.                      or
  467.                      len(self.cache[pkgname].candidateOrigin) == 0)
  468.                     or
  469.                     (len(self.cache[pkgname].candidateOrigin) == 1 and
  470.                      self.cache[pkgname].candidateOrigin[0].archive == "now")
  471.                    ):
  472.                     logging.debug("BaseMetaPkg '%s' has no candidateOrigin" % pkgname)
  473.                     try:
  474.                         distro = get_distro()
  475.                         distro.get_sources(self.sources)
  476.                         distro.enable_component("main")
  477.                     except NoDistroTemplateException,e :
  478.                         # fallback if everything else does not work,
  479.                         # we replace the sources.list with a single
  480.                         # line to ubuntu-main
  481.                         logging.warning('get_distro().enable_component("man") failed, overwriting sources.list instead as last resort')
  482.                         s =  "# auto generated by update-manager"
  483.                         s += "deb http://archive.ubuntu.com/ubuntu %s main restricted" % self.toDist
  484.                         s += "deb http://archive.ubuntu.com/ubuntu %s-updates main restricted" % self.toDist
  485.                         s += "deb http://security.ubuntu.com/ubuntu %s-security main restricted" % self.toDist
  486.                         open("/etc/apt/sources.list","w").write(s)
  487.                     break
  488.             
  489.         # this must map, i.e. second in "from" must be the second in "to"
  490.         # (but they can be different, so in theory we could exchange
  491.         #  component names here)
  492.         fromDists = [self.fromDist,
  493.                      self.fromDist+"-security",
  494.                      self.fromDist+"-updates",
  495.                      self.fromDist+"-proposed",
  496.                      self.fromDist+"-backports"
  497.                     ]
  498.         toDists = [self.toDist,
  499.                    self.toDist+"-security",
  500.                    self.toDist+"-updates",
  501.                    self.toDist+"-proposed",
  502.                    self.toDist+"-backports"
  503.                    ]
  504.  
  505.         self.sources_disabled = False
  506.  
  507.         # look over the stuff we have
  508.         foundToDist = False
  509.         # collect information on what components (main,universe) are enabled for what distro (sub)version
  510.         # e.g. found_components = { 'hardy':set("main","restricted"), 'hardy-updates':set("main") }
  511.         found_components = {}
  512.         for entry in self.sources.list[:]:
  513.  
  514.             # ignore invalid records or disabled ones
  515.             if entry.invalid or entry.disabled:
  516.                 continue
  517.             
  518.             # we disable breezy cdrom sources to make sure that demoted
  519.             # packages are removed
  520.             if entry.uri.startswith("cdrom:") and entry.dist == self.fromDist:
  521.                 entry.disabled = True
  522.                 continue
  523.             # ignore cdrom sources otherwise
  524.             elif entry.uri.startswith("cdrom:"):
  525.                 continue
  526.  
  527.             # special case for archive.canonical.com that needs to
  528.             # be rewritten (for pre-gutsy upgrades)
  529.             cdist = "%s-commercial" % self.fromDist
  530.             if (not entry.disabled and
  531.                 entry.uri.startswith("http://archive.canonical.com") and
  532.                 entry.dist == cdist):
  533.                 entry.dist = self.toDist
  534.                 entry.comps = ["partner"]
  535.                 logging.debug("transitioned commercial to '%s' " % entry)
  536.                 continue
  537.  
  538.             # special case for landscape.canonical.com because they
  539.             # don't use a standard archive layout (gutsy->hardy)
  540.             if (not entry.disabled and
  541.                 entry.uri.startswith("http://landscape.canonical.com/packages/%s" % self.fromDist)):
  542.                 logging.debug("commenting landscape.canonical.com out")
  543.                 entry.disabled = True
  544.                 continue
  545.             
  546.             # handle upgrades from a EOL release and check if there
  547.             # is a supported release available
  548.             if (not entry.disabled and
  549.                 "old-releases.ubuntu.com/" in entry.uri):
  550.                 logging.debug("upgrade from old-releases.ubuntu.com detected")
  551.                 # test country mirror first, then archive.u.c
  552.                 for uri in ["http://%sarchive.ubuntu.com/ubuntu" % country_mirror(),
  553.                             "http://archive.ubuntu.com/ubuntu"]:
  554.                     test_entry = copy.copy(entry)
  555.                     test_entry.uri = uri
  556.                     test_entry.dist = self.toDist
  557.                     if self._sourcesListEntryDownloadable(test_entry):
  558.                         logging.info("transition from old-release.u.c to %s" % uri)
  559.                         entry.uri = uri
  560.                         break
  561.  
  562.             logging.debug("examining: '%s'" % entry)
  563.             # check if it's a mirror (or official site)
  564.             validMirror = self.isMirror(entry.uri)
  565.             if validMirror or not mirror_check:
  566.                 validMirror = True
  567.                 # disabled/security/commercial are special cases
  568.                 # we use validTo/foundToDist to figure out if we have a 
  569.                 # main archive mirror in the sources.list or if we 
  570.                 # need to add one
  571.                 validTo = True
  572.                 if (entry.disabled or
  573.                     entry.type == "deb-src" or
  574.                     entry.uri.startswith("http://security.ubuntu.com") or
  575.                     entry.uri.startswith("http://archive.canonical.com")):
  576.                     validTo = False
  577.                 if entry.dist in toDists:
  578.                     # so the self.sources.list is already set to the new
  579.                     # distro
  580.                     logging.debug("entry '%s' is already set to new dist" % entry)
  581.                     foundToDist |= validTo
  582.                 elif entry.dist in fromDists:
  583.                     foundToDist |= validTo
  584.                     entry.dist = toDists[fromDists.index(entry.dist)]
  585.                     logging.debug("entry '%s' updated to new dist" % entry)
  586.                 else:
  587.                     # disable all entries that are official but don't
  588.                     # point to either "to" or "from" dist
  589.                     entry.disabled = True
  590.                     self.sources_disabled = True
  591.                     logging.debug("entry '%s' was disabled (unknown dist)" % entry)
  592.  
  593.                 # if we make it to this point, we have a official mirror
  594.  
  595.                 # check if the arch is powerpc or sparc and if so, transition
  596.                 # to ports.ubuntu.com (powerpc got demoted in gutsy, sparc
  597.                 # in hardy)
  598.                 if (entry.type == "deb" and
  599.                     not "ports.ubuntu.com" in entry.uri and
  600.                     (self.arch == "powerpc" or self.arch == "sparc")):
  601.                     logging.debug("moving %s source entry to 'ports.ubuntu.com' " % self.arch)
  602.                     entry.uri = "http://ports.ubuntu.com/ubuntu-ports/"
  603.  
  604.                 # gather what components are enabled and are inconsitent
  605.                 for d in ["%s" % self.toDist,
  606.                           "%s-updates" % self.toDist,
  607.                           "%s-security" % self.toDist]:
  608.                     # create entry if needed, ignore disabled
  609.                     # entries and deb-src
  610.                     found_components.setdefault(d,set())
  611.                     if (not entry.disabled and entry.dist == d and
  612.                         entry.type == "deb"):
  613.                         for comp in entry.comps:
  614.                             # only sync components we know about
  615.                             if not comp in sync_components:
  616.                                 continue
  617.                             found_components[d].add(comp)
  618.                     
  619.             # disable anything that is not from a official mirror
  620.             if not validMirror:
  621.                 if entry.dist == self.fromDist:
  622.                     entry.dist = self.toDist
  623.                 entry.comment += " " + _("disabled on upgrade to %s") % self.toDist
  624.                 entry.disabled = True
  625.                 self.sources_disabled = True
  626.                 logging.debug("entry '%s' was disabled (unknown mirror)" % entry)
  627.  
  628.         # now go over the list again and check for missing components 
  629.         # in $dist-updates and $dist-security and add them
  630.         for entry in self.sources.list[:]:
  631.             # skip all comps that are not relevant (including e.g. "hardy")
  632.             if (entry.invalid or entry.disabled or entry.type == "deb-src" or 
  633.                 entry.uri.startswith("cdrom:") or entry.dist == self.toDist):
  634.                 continue
  635.             # now check for "$dist-updates" and "$dist-security" and add any inconsistencies
  636.             if found_components.has_key(entry.dist):
  637.                 # add the delta between "hardy" comps and "hardy-updates" comps once
  638.                 logging.info("fixing components inconsistency from '%s'" % entry)
  639.                 entry.comps.extend(list(found_components[self.toDist]-found_components[entry.dist]))
  640.                 logging.info("to new entry '%s'" % entry)
  641.                 del found_components[entry.dist]
  642.         return foundToDist
  643.  
  644.     def updateSourcesList(self):
  645.         logging.debug("updateSourcesList()")
  646.         self.sources = SourcesList(matcherPath=".")
  647.         if not self.rewriteSourcesList(mirror_check=True):
  648.             logging.error("No valid mirror found")
  649.             res = self._view.askYesNoQuestion(_("No valid mirror found"),
  650.                              _("While scanning your repository "
  651.                                "information no mirror entry for "
  652.                                "the upgrade was found."
  653.                                "This can happen if you run a internal "
  654.                                "mirror or if the mirror information is "
  655.                                "out of date.\n\n"
  656.                                "Do you want to rewrite your "
  657.                                "'sources.list' file anyway? If you choose "
  658.                                "'Yes' here it will update all '%s' to '%s' "
  659.                                "entries.\n"
  660.                                "If you select 'no' the update will cancel."
  661.                                ) % (self.fromDist, self.toDist))
  662.             if res:
  663.                 # re-init the sources and try again
  664.                 self.sources = SourcesList(matcherPath=".")
  665.                 # its ok if rewriteSourcesList fails here if
  666.                 # we do not use a network, the sources.list may be empty
  667.                 if (not self.rewriteSourcesList(mirror_check=False)
  668.                     and self.useNetwork):
  669.                     #hm, still nothing useful ...
  670.                     prim = _("Generate default sources?")
  671.                     secon = _("After scanning your 'sources.list' no "
  672.                               "valid entry for '%s' was found.\n\n"
  673.                               "Should default entries for '%s' be "
  674.                               "added? If you select 'No', the update "
  675.                               "will cancel.") % (self.fromDist, self.toDist)
  676.                     if not self._view.askYesNoQuestion(prim, secon):
  677.                         self.abort()
  678.  
  679.                     # add some defaults here
  680.                     # FIXME: find mirror here
  681.                     logging.info("generate new default sources.list")
  682.                     uri = "http://archive.ubuntu.com/ubuntu"
  683.                     comps = ["main","restricted"]
  684.                     self.sources.add("deb", uri, self.toDist, comps)
  685.                     self.sources.add("deb", uri, self.toDist+"-updates", comps)
  686.                     self.sources.add("deb",
  687.                                      "http://security.ubuntu.com/ubuntu/",
  688.                                      self.toDist+"-security", comps)
  689.             else:
  690.                 self.abort()
  691.  
  692.         # write (well, backup first ;) !
  693.         self.sources.backup(self.sources_backup_ext)
  694.         self.sources.save()
  695.  
  696.         # re-check if the written self.sources are valid, if not revert and
  697.         # bail out
  698.         # TODO: check if some main packages are still available or if we
  699.         #       accidentally shot them, if not, maybe offer to write a standard
  700.         #       sources.list?
  701.         try:
  702.             sourceslist = apt_pkg.GetPkgSourceList()
  703.             sourceslist.ReadMainList()
  704.         except SystemError:
  705.             logging.error("Repository information invalid after updating (we broke it!)")
  706.             self._view.error(_("Repository information invalid"),
  707.                              _("Upgrading the repository information "
  708.                                "resulted in a invalid file. Please "
  709.                                "report this as a bug."))
  710.             return False
  711.  
  712.         if self.sources_disabled:
  713.             self._view.information(_("Third party sources disabled"),
  714.                              _("Some third party entries in your sources.list "
  715.                                "were disabled. You can re-enable them "
  716.                                "after the upgrade with the "
  717.                                "'software-properties' tool or "
  718.                                "your package manager."
  719.                                ))
  720.         return True
  721.  
  722.     def _logChanges(self):
  723.         # debugging output
  724.         logging.debug("About to apply the following changes")
  725.         inst = []
  726.         up = []
  727.         rm = []
  728.         held = []
  729.         keep = []
  730.         for pkg in self.cache:
  731.             if pkg.markedInstall: inst.append(pkg.name)
  732.             elif pkg.markedUpgrade: up.append(pkg.name)
  733.             elif pkg.markedDelete: rm.append(pkg.name)
  734.             elif (pkg.isInstalled and pkg.isUpgradable): held.append(pkg.name)
  735.             elif pkg.isInstalled and pkg.markedKeep: keep.append(pkg.name)
  736.         logging.debug("Keep at same version: %s" % " ".join(keep))
  737.         logging.debug("Upgradable, but held- back: %s" % " ".join(held))
  738.         logging.debug("Remove: %s" % " ".join(rm))
  739.         logging.debug("Install: %s" % " ".join(inst))
  740.         logging.debug("Upgrade: %s" % " ".join(up))
  741.         
  742.  
  743.     def doPostInitialUpdate(self):
  744.         # check if we have packages in ReqReinst state that are not
  745.         # downloadable
  746.         logging.debug("doPostInitialUpdate")
  747.         if not self._partialUpgrade:
  748.             self.quirks.run("PostInitialUpdate")
  749.         if len(self.cache.reqReinstallPkgs) > 0:
  750.             logging.warning("packages in reqReinstall state, trying to fix")
  751.             self.cache.fixReqReinst(self._view)
  752.             self.openCache()
  753.         if len(self.cache.reqReinstallPkgs) > 0:
  754.             reqreinst = self.cache.reqReinstallPkgs
  755.             header = ngettext("Package in inconsistent state",
  756.                               "Packages in inconsistent state",
  757.                               len(reqreinst))
  758.             summary = ngettext("The package '%s' is in an inconsistent "
  759.                                "state and needs to be reinstalled, but "
  760.                                "no archive can be found for it. "
  761.                                "Please reinstall the package manually "
  762.                                "or remove it from the system.",
  763.                                "The packages '%s' are in an inconsistent "
  764.                                "state and need to be reinstalled, but "
  765.                                "no archive can be found for them. "
  766.                                "Please reinstall the packages manually "
  767.                                "or remove them from the system.",
  768.                                len(reqreinst)) % ", ".join(reqreinst)
  769.             self._view.error(header, summary)
  770.             return False
  771.         # FIXME: check out what packages are downloadable etc to
  772.         # compare the list after the update again
  773.         self.obsolete_pkgs = self.cache._getObsoletesPkgs()
  774.         self.foreign_pkgs = self.cache._getForeignPkgs(self.origin, self.fromDist, self.toDist)
  775.         if self.serverMode:
  776.             self.tasks = self.cache.installedTasks
  777.         logging.debug("Foreign: %s" % " ".join(self.foreign_pkgs))
  778.         logging.debug("Obsolete: %s" % " ".join(self.obsolete_pkgs))
  779.         return True
  780.  
  781.     def doUpdate(self, showErrors=True, forceRetries=None):
  782.         logging.debug("running doUpdate() (showErrors=%s)" % showErrors)
  783.         if not self.useNetwork:
  784.             logging.debug("doUpdate() will not use the network because self.useNetwork==false")
  785.             return True
  786.         self.cache._list.ReadMainList()
  787.         progress = self._view.getFetchProgress()
  788.         # FIXME: also remove all files from the lists partial dir!
  789.         currentRetry = 0
  790.         if forceRetries is not None:
  791.             maxRetries=forceRetries
  792.         else:
  793.             maxRetries = self.config.getint("Network","MaxRetries")
  794.         while currentRetry < maxRetries:
  795.             try:
  796.                 res = self.cache.update(progress)
  797.             except (SystemError, IOError), e:
  798.                 logging.error("IOError/SystemError in cache.update(): '%s'. Retrying (currentRetry: %s)" % (e,currentRetry))
  799.                 currentRetry += 1
  800.                 continue
  801.             # no exception, so all was fine, we are done
  802.             return True
  803.  
  804.         logging.error("doUpdate() failed completely")
  805.         if showErrors:
  806.             self._view.error(_("Error during update"),
  807.                              _("A problem occurred during the update. "
  808.                                "This is usually some sort of network "
  809.                                "problem, please check your network "
  810.                                "connection and retry."), "%s" % e)
  811.         return False
  812.  
  813.  
  814.     def _checkFreeSpace(self):
  815.         " this checks if we have enough free space on /var and /usr"
  816.         err_sum = _("Not enough free disk space")
  817.         err_long= _("The upgrade is now aborted. "
  818.                     "The upgrade needs a total of %s free space on disk '%s'. "
  819.                     "Please free at least an additional %s of disk "
  820.                     "space on '%s'. "
  821.                     "Empty your trash and remove temporary "
  822.                     "packages of former installations using "
  823.                     "'sudo apt-get clean'.")
  824.         # allow override
  825.         if self.config.getWithDefault("FreeSpace","SkipCheck",False):
  826.             logging.warning("free space check skipped via config override")
  827.             return True
  828.         # do the check
  829.         try:
  830.             self.cache.checkFreeSpace()
  831.         except NotEnoughFreeSpaceError, e:
  832.             # ok, showing multiple error dialog sucks from the UI
  833.             # perspective, but it means we do not need to break the
  834.             # string freeze
  835.             for required in e.free_space_required_list:
  836.                 self._view.error(err_sum, err_long % (required.size_total,
  837.                                                       required.dir,
  838.                                                       required.size_needed,
  839.                                                       required.dir))
  840.             return False
  841.         return True
  842.  
  843.  
  844.     def askDistUpgrade(self):
  845.         # check what packages got demoted and ask the user
  846.         # if those shall be removed
  847.         demotions = set()
  848.         demotions_file = self.config.get("Distro","Demotions")
  849.         if os.path.exists(demotions_file):
  850.             map(lambda pkgname: demotions.add(pkgname.strip()),
  851.                 filter(lambda line: not line.startswith("#"),
  852.                        open(demotions_file).readlines()))
  853.         self.installed_demotions = [pkg.name for pkg in self.cache if pkg.isInstalled and pkg.name in demotions]
  854.         if len(self.installed_demotions) > 0:
  855.         self.installed_demotions.sort()
  856.             logging.debug("demoted: '%s'" % " ".join(self.installed_demotions))
  857.             self._view.showDemotions(_("Support for some applications ended"),
  858.                                    _("Canonical Ltd. no longer provides "
  859.                                      "support for the following software "
  860.                                      "packages. You can still get support "
  861.                                      "from the community.\n\n"
  862.                                      "If you have not enabled community "
  863.                                      "maintained software (universe), "
  864.                                      "these packages will be suggested for "
  865.                                      "removal at the end of the upgrade."),
  866.                                      self.installed_demotions)
  867.             self._view.updateStatus(_("Calculating the changes"))
  868.         # FIXME: integrate this into main upgrade dialog!?!
  869.         if not self.cache.distUpgrade(self._view, self.serverMode, self._partialUpgrade):
  870.             return False
  871.  
  872.         if self.serverMode:
  873.             if not self.cache.installTasks(self.tasks):
  874.                 return False
  875.         changes = self.cache.getChanges()
  876.         # log the changes for debugging
  877.         self._logChanges()
  878.         # check if we have enough free space 
  879.         if not self._checkFreeSpace():
  880.             return False
  881.         # ask the user if he wants to do the changes
  882.         res = self._view.confirmChanges(_("Do you want to start the upgrade?"),
  883.                                         changes,
  884.                                         self.cache.requiredDownload)
  885.         return res
  886.  
  887.     def _disableAptCronJob(self):
  888.         self._aptCronJobPerms = 0755
  889.         if os.path.exists("/etc/cron.daily/apt"):
  890.             self._aptCronJobPerms = os.stat("/etc/cron.daily/apt")[ST_MODE]
  891.             logging.debug("disabling apt cron job (%s)" % oct(self._aptCronJobPerms))
  892.             os.chmod("/etc/cron.daily/apt",0644)
  893.     def _enableAptCronJob(self):
  894.         if os.path.exists("/etc/cron.daily/apt"):
  895.             logging.debug("enabling apt cron job")
  896.             os.chmod("/etc/cron.daily/apt", self._aptCronJobPerms)
  897.  
  898.     def doDistUpgradeFetching(self):
  899.         # ensure that no apt cleanup is run during the download/install
  900.         self._disableAptCronJob()
  901.         # get the upgrade
  902.         currentRetry = 0
  903.         fprogress = self._view.getFetchProgress()
  904.         iprogress = self._view.getInstallProgress(self.cache)
  905.         # retry the fetching in case of errors
  906.         maxRetries = self.config.getint("Network","MaxRetries")
  907.         # FIXME: we get errors like 
  908.         #   "I wasn't able to locate file for the %s package" 
  909.         #  here sometimes. its unclear why and not reproducible, the 
  910.         #  current theory is that for some reason the file is not
  911.         #  considered trusted at the moment 
  912.         #  pkgAcquireArchive::QueueNext() runs debReleaseIndex::IsTrused()
  913.         #  (the later just checks for the existence of the .gpg file)
  914.         #  OR 
  915.         #  the fact that we get a pm and fetcher here confuses something
  916.         #  in libapt?
  917.         # POSSIBLE workaround: keep the list-dir locked so that 
  918.         #          no apt-get update can run outside from the release
  919.         #          upgrader 
  920.         while currentRetry < maxRetries:
  921.             try:
  922.                 pm = apt_pkg.GetPackageManager(self.cache._depcache)
  923.                 fetcher = apt_pkg.GetAcquire(fprogress)
  924.                 res = self.cache._fetchArchives(fetcher, pm)
  925.             except IOError, e:
  926.                 # fetch failed, will be retried
  927.                 logging.error("IOError in cache.commit(): '%s'. Retrying (currentTry: %s)" % (e,currentRetry))
  928.                 currentRetry += 1
  929.                 continue
  930.             return True
  931.         
  932.         # maximum fetch-retries reached without a successful commit
  933.         logging.error("giving up on fetching after maximum retries")
  934.         self._view.error(_("Could not download the upgrades"),
  935.                          _("The upgrade is now aborted. Please check your "
  936.                            "Internet connection or "
  937.                            "installation media and try again. All files "
  938.                            "downloaded so far are kept."),
  939.                            "%s" % e)
  940.         # abort here because we want our sources.list back
  941.         self._enableAptCronJob()
  942.         self.abort()
  943.  
  944.     def enableApport(self, fname="/etc/default/apport"):
  945.         " enable apoprt "
  946.         # for jaunty and later we could use this instead:
  947.         #  env = copy.copy(os.environ)
  948.         #  env["force_start"] = "1"
  949.         #  subprocess.call(["/etc/init.d/apport","start"], env=env)
  950.         # but hardy and intrepid do not have the force_start yet
  951.         if not os.path.exists(fname):
  952.             return
  953.         # copy the jaunty version of the conf file in place
  954.         # (this avoids a conffile prompt later)
  955.         logging.debug("enabling apport")
  956.         shutil.copy("etc-default-apport","/etc/default/apport")
  957.         subprocess.call(["/etc/init.d/apport","start"])
  958.         
  959.     def doDistUpgrade(self):
  960.         # check if we want apport running during the upgrade
  961.         if self.config.getWithDefault("Distro","EnableApport", False):
  962.             self.enableApport()
  963.         # get the upgrade
  964.         currentRetry = 0
  965.         fprogress = self._view.getFetchProgress()
  966.         iprogress = self._view.getInstallProgress(self.cache)
  967.         # retry the fetching in case of errors
  968.         maxRetries = self.config.getint("Network","MaxRetries")
  969.         if not self._partialUpgrade:
  970.             self.quirks.run("StartUpgrade")
  971.         while currentRetry < maxRetries:
  972.             try:
  973.                 res = self.cache.commit(fprogress,iprogress)
  974.             except SystemError, e:
  975.                 logging.error("SystemError from cache.commit(): %s" % e)
  976.                 # if its a ordering bug we can cleanly revert to
  977.                 # the previous release, no packages have been installed
  978.                 # yet (LP: #328655, #356781)
  979.                 pre_configure_errors = [
  980.                   "E:Internal Error, Could not perform immediate configuration",
  981.                   "E:Couldn't configure pre-depend "]
  982.                 for preconf_error in pre_configure_errors:
  983.                     if str(e).startswith(preconf_error):
  984.                         logging.debug("detected preconfigure error, restorting state")
  985.                         self._enableAptCronJob()
  986.                         # FIXME: strings are not good, but we are in string freeze
  987.                         # currently
  988.                         msg = _("Error during commit")
  989.                         msg += "\n'%s'\n" % str(e)
  990.                         msg += _("Restoring original system state")
  991.                         self._view.error(_("Could not install the upgrades"), msg)
  992.                         # abort() exits cleanly
  993.                         self.abort()
  994.                 
  995.                 # invoke the frontend now and show a error message
  996.                 msg = _("The upgrade is now aborted. Your system "
  997.                         "could be in an unusable state. A recovery "
  998.                         "will run now (dpkg --configure -a).")
  999.                 if not self._partialUpgrade:
  1000.                     if not run_apport():
  1001.                         msg += _("\n\nPlease report this bug against the 'update-manager' "
  1002.                                  "package and include the files in /var/log/dist-upgrade/ "
  1003.                                  "in the bug report.\n"
  1004.                                  "%s" % e)
  1005.                 self._view.error(_("Could not install the upgrades"), msg)
  1006.                 # installing the packages failed, can't be retried
  1007.                 self._view.getTerminal().call(["dpkg","--configure","-a"])
  1008.                 self._enableAptCronJob()
  1009.                 return False
  1010.             except IOError, e:
  1011.                 # fetch failed, will be retried
  1012.                 logging.error("IOError in cache.commit(): '%s'. Retrying (currentTry: %s)" % (e,currentRetry))
  1013.                 currentRetry += 1
  1014.                 continue
  1015.             # no exception, so all was fine, we are done
  1016.             self._enableAptCronJob()
  1017.             return True
  1018.         
  1019.         # maximum fetch-retries reached without a successful commit
  1020.         logging.error("giving up on fetching after maximum retries")
  1021.         self._view.error(_("Could not download the upgrades"),
  1022.                          _("The upgrade is now aborted. Please check your "\
  1023.                            "Internet connection or "\
  1024.                            "installation media and try again. "),
  1025.                            "%s" % e)
  1026.         # abort here because we want our sources.list back
  1027.         self.abort()
  1028.  
  1029.     def doPostUpgrade(self):
  1030.         # reopen cache
  1031.         self.openCache()
  1032.         # run the quirks handler that does does like things adding
  1033.         # missing groups or similar work arounds, only do it on real
  1034.         # upgrades
  1035.         if not self._partialUpgrade:
  1036.             self.quirks.run("PostUpgrade")
  1037.         # check out what packages are cruft now
  1038.         # use self.{foreign,obsolete}_pkgs here and see what changed
  1039.         now_obsolete = self.cache._getObsoletesPkgs()
  1040.         now_foreign = self.cache._getForeignPkgs(self.origin, self.fromDist, self.toDist)
  1041.         logging.debug("Obsolete: %s" % " ".join(now_obsolete))
  1042.         logging.debug("Foreign: %s" % " ".join(now_foreign))
  1043.         # now sanity check - if a base meta package is in the obsolete list now, that means
  1044.         # that something went wrong (see #335154) badly with the network. this should never happen, but it did happen
  1045.         # at least once so we add extra paranoia here
  1046.         for pkg in self.config.getlist("Distro","BaseMetaPkgs"):
  1047.             if pkg in now_obsolete:
  1048.                 logging.error("the BaseMetaPkg '%s' is in the obsolete list, something is wrong, ignoring the obsoletes" % pkg)
  1049.                 now_obsolete = set()
  1050.                 break
  1051.         # check if we actually want obsolete removal
  1052.         if not self.config.getWithDefault("Distro","RemoveObsoletes", True):
  1053.             logging.debug("Skipping obsolete Removal")
  1054.             return True
  1055.  
  1056.         # now get the meta-pkg specific obsoletes and purges
  1057.         for pkg in self.config.getlist("Distro","MetaPkgs"):
  1058.             if self.cache.has_key(pkg) and self.cache[pkg].isInstalled:
  1059.                 self.forced_obsoletes.extend(self.config.getlist(pkg,"ForcedObsoletes"))
  1060.         # now add the obsolete kernels to the forced obsoletes
  1061.         self.forced_obsoletes.extend(self.cache.identifyObsoleteKernels())
  1062.         logging.debug("forced_obsoletes: %s", self.forced_obsoletes)
  1063.  
  1064.         # mark packages that are now obsolete (and where not obsolete
  1065.         # before) to be deleted. make sure to not delete any foreign
  1066.         # (that is, not from ubuntu) packages
  1067.         if self.useNetwork:
  1068.             # we can only do the obsoletes calculation here if we use a
  1069.             # network. otherwise after rewriting the sources.list everything
  1070.             # that is not on the CD becomes obsolete (not-downloadable)
  1071.             remove_candidates = now_obsolete - self.obsolete_pkgs
  1072.         else:
  1073.             # initial remove candidates when no network is used should
  1074.             # be the demotions to make sure we don't leave potential
  1075.             # unsupported software
  1076.             remove_candidates = set(self.installed_demotions)
  1077.         remove_candidates |= set(self.forced_obsoletes)
  1078.  
  1079.         # no go for the unused dependencies
  1080.         unused_dependencies = self.cache._getUnusedDependencies()
  1081.         logging.debug("Unused dependencies: %s" %" ".join(unused_dependencies))
  1082.         remove_candidates |= set(unused_dependencies)
  1083.  
  1084.         # see if we actually have to do anything here
  1085.         if not self.config.getWithDefault("Distro","RemoveObsoletes", True):
  1086.             logging.debug("Skipping RemoveObsoletes as stated in the config")
  1087.             remove_candidates = set()
  1088.         logging.debug("remove_candidates: '%s'" % remove_candidates)
  1089.         logging.debug("Start checking for obsolete pkgs")
  1090.         for pkgname in remove_candidates:
  1091.             if pkgname not in self.foreign_pkgs:
  1092.                 self._view.processEvents()
  1093.                 if not self.cache.tryMarkObsoleteForRemoval(pkgname, remove_candidates, self.foreign_pkgs):
  1094.                     logging.debug("'%s' scheduled for remove but not safe to remove, skipping", pkgname)
  1095.         logging.debug("Finish checking for obsolete pkgs")
  1096.  
  1097.         # get changes
  1098.         changes = self.cache.getChanges()
  1099.         logging.debug("The following packages are remove candidates: %s" % " ".join([pkg.name for pkg in changes]))
  1100.         summary = _("Remove obsolete packages?")
  1101.         actions = [_("_Keep"), _("_Remove")]
  1102.         # FIXME Add an explanation about what obsolete packages are
  1103.         #explanation = _("")
  1104.         if len(changes) > 0 and \
  1105.                self._view.confirmChanges(summary, changes, 0, actions, False):
  1106.             fprogress = self._view.getFetchProgress()
  1107.             iprogress = self._view.getInstallProgress(self.cache)
  1108.             try:
  1109.                 res = self.cache.commit(fprogress,iprogress)
  1110.             except (SystemError, IOError), e:
  1111.                 logging.error("cache.commit() in doPostUpgrade() failed: %s" % e)
  1112.                 self._view.error(_("Error during commit"),
  1113.                                  _("A problem occurred during the clean-up. "
  1114.                                    "Please see the below message for more "
  1115.                                    "information. "),
  1116.                                    "%s" % e)
  1117.         # run stuff after cleanup
  1118.         if not self._partialUpgrade:
  1119.             self.quirks.run("PostCleanup")
  1120.         # run the post upgrade scripts that can do fixup like xorg.conf
  1121.         # fixes etc - only do on real upgrades
  1122.         if not self._partialUpgrade:
  1123.             self.runPostInstallScripts()
  1124.         return True
  1125.  
  1126.     def runPostInstallScripts(self):
  1127.         """ 
  1128.         scripts that are run in any case after the distupgrade finished 
  1129.         whether or not it was successfull
  1130.         """
  1131.         # now run the post-upgrade fixup scripts (if any)
  1132.         for script in self.config.getlist("Distro","PostInstallScripts"):
  1133.             if not os.path.exists(script):
  1134.                 logging.warning("PostInstallScript: '%s' not found" % script)
  1135.                 continue
  1136.             logging.debug("Running PostInstallScript: '%s'" % script)
  1137.             try:
  1138.                 # work around kde tmpfile problem where it eats permissions
  1139.                 os.chmod(script, 0755)
  1140.                 self._view.getTerminal().call([script], hidden=True)
  1141.             except Exception, e:
  1142.                 logging.error("got error from PostInstallScript %s (%s)" % (script, e))
  1143.         
  1144.     def abort(self):
  1145.         """ abort the upgrade, cleanup (as much as possible) """
  1146.         if hasattr(self, "sources"):
  1147.             self.sources.restoreBackup(self.sources_backup_ext)
  1148.         if hasattr(self, "aptcdrom"):
  1149.             self.aptcdrom.restoreBackup(self.sources_backup_ext)
  1150.         # generate a new cache
  1151.         self._view.updateStatus(_("Restoring original system state"))
  1152.         self._view.abort()
  1153.         self.openCache()
  1154.         sys.exit(1)
  1155.  
  1156.     def _checkDep(self, depstr):
  1157.         " check if a given depends can be satisfied "
  1158.         for or_group in apt_pkg.ParseDepends(depstr):
  1159.             logging.debug("checking: '%s' " % or_group)
  1160.             for dep in or_group:
  1161.                 depname = dep[0]
  1162.                 ver = dep[1]
  1163.                 oper = dep[2]
  1164.                 if not self.cache.has_key(depname):
  1165.                     logging.error("_checkDep: '%s' not in cache" % depname)
  1166.                     return False
  1167.                 inst = self.cache[depname]
  1168.                 instver = inst.installedVersion
  1169.                 if (instver != None and
  1170.                     apt_pkg.CheckDep(instver,oper,ver) == True):
  1171.                     return True
  1172.         logging.error("depends '%s' is not satisfied" % depstr)
  1173.         return False
  1174.                 
  1175.     def checkViewDepends(self):
  1176.         " check if depends are satisfied "
  1177.         logging.debug("checkViewDepends()")
  1178.         res = True
  1179.         # now check if anything from $foo-updates is required
  1180.         depends = self.config.getlist("View","Depends")
  1181.         depends.extend(self.config.getlist(self._view.__class__.__name__,
  1182.                                            "Depends"))
  1183.         for dep in depends:
  1184.             logging.debug("depends: '%s'", dep)
  1185.             res &= self._checkDep(dep)
  1186.             if not res:
  1187.                 # FIXME: instead of error out, fetch and install it
  1188.                 #        here
  1189.                 self._view.error(_("Required depends is not installed"),
  1190.                                  _("The required dependency '%s' is not "
  1191.                                    "installed. " % dep))
  1192.                 sys.exit(1)
  1193.         return res 
  1194.  
  1195.     def _verifyBackports(self):
  1196.         # run update (but ignore errors in case the countrymirror
  1197.         # substitution goes wrong, real errors will be caught later
  1198.         # when the cache is searched for the backport packages)
  1199.         backportslist = self.config.getlist("PreRequists","Packages")
  1200.         i=0
  1201.         noCache = apt_pkg.Config.Find("Acquire::http::No-Cache","false")
  1202.         maxRetries = self.config.getint("Network","MaxRetries")
  1203.         while i < maxRetries:
  1204.             self.doUpdate(showErrors=False)
  1205.             self.openCache()
  1206.             for pkgname in backportslist:
  1207.                 if not self.cache.has_key(pkgname):
  1208.                     logging.error("Can not find backport '%s'" % pkgname)
  1209.                     raise NoBackportsFoundException, pkgname
  1210.             if self._allBackportsAuthenticated(backportslist):
  1211.                 break
  1212.             # FIXME: move this to some more generic place
  1213.             logging.debug("setting a cache control header to turn off caching temporarily")
  1214.             apt_pkg.Config.Set("Acquire::http::No-Cache","true")
  1215.             i += 1
  1216.         if i == maxRetries:
  1217.             logging.error("pre-requists item is NOT trusted, giving up")
  1218.             return False
  1219.         apt_pkg.Config.Set("Acquire::http::No-Cache",noCache)
  1220.         return True
  1221.  
  1222.     def _allBackportsAuthenticated(self, backportslist):
  1223.         # check if the user overwrote the check
  1224.         if apt_pkg.Config.FindB("APT::Get::AllowUnauthenticated",False) == True:
  1225.             logging.warning("skip authentication check because of APT::Get::AllowUnauthenticated==true")
  1226.             return True
  1227.         try:
  1228.             b = self.config.getboolean("Distro","AllowUnauthenticated")
  1229.             if b:
  1230.                 return True
  1231.         except ConfigParser.NoOptionError, e:
  1232.             pass
  1233.         for pkgname in backportslist:
  1234.             pkg = self.cache[pkgname]                
  1235.             for cand in pkg.candidateOrigin:
  1236.                 if cand.trusted:
  1237.                     break
  1238.             else:
  1239.                 return False
  1240.         return True
  1241.  
  1242.     def isMirror(self, uri):
  1243.         " check if uri is a known mirror "
  1244.         for mirror in self.valid_mirrors:
  1245.             if is_mirror(mirror, uri):
  1246.                 return True
  1247.         return False
  1248.  
  1249.     def _getPreReqMirrorLines(self, dumb=False):
  1250.         " get sources.list snippet lines for the current mirror "
  1251.         lines = ""
  1252.         sources = SourcesList(matcherPath=".")
  1253.         for entry in sources.list:
  1254.             if entry.invalid or entry.disabled:
  1255.                 continue
  1256.             if (entry.type == "deb" and 
  1257.                 self.isMirror(entry.uri) and
  1258.                 not entry.uri.startswith("http://security.ubuntu.com") and
  1259.                 not entry.uri.startswith("http://archive.ubuntu.com") ):
  1260.                 new_line = "deb %s %s-backports main/debian-installer\n" % (entry.uri, self.fromDist)
  1261.                 if not new_line in lines:
  1262.                     lines += new_line
  1263.             if (dumb and entry.type == "deb" and
  1264.                 "main" in entry.comps):
  1265.                 lines += "deb %s %s-backports main/debian-installer\n" % (entry.uri, self.fromDist)
  1266.         return lines
  1267.  
  1268.     def _addPreRequistsSourcesList(self, template, out, dumb=False):
  1269.         " add prerequists based on template into the path outfile "
  1270.         # go over the sources.list and try to find a valid mirror
  1271.         # that we can use to add the backports dir
  1272.         logging.debug("writing prerequists sources.list at: '%s' " % out)
  1273.         outfile = open(out, "w")
  1274.         mirrorlines = self._getPreReqMirrorLines(dumb)
  1275.         for line in open(template):
  1276.             template = Template(line)
  1277.             outline = template.safe_substitute(mirror=mirrorlines)
  1278.             outfile.write(outline)
  1279.             logging.debug("adding '%s' prerequists" % outline)
  1280.         outfile.close()
  1281.         return True
  1282.  
  1283.     def getRequiredBackports(self):
  1284.         " download the backports specified in DistUpgrade.cfg "
  1285.         logging.debug("getRequiredBackports()")
  1286.         res = True
  1287.         backportsdir = os.path.join(os.getcwd(),"backports")
  1288.         if not os.path.exists(backportsdir):
  1289.             os.mkdir(backportsdir)
  1290.         backportslist = self.config.getlist("PreRequists","Packages")
  1291.  
  1292.         # if we have them on the CD we are fine
  1293.         if self.aptcdrom and not self.useNetwork:
  1294.             logging.debug("Searching for pre-requists on CDROM")
  1295.             p = os.path.join(self.aptcdrom.cdrompath,
  1296.                              "dists/stable/main/dist-upgrader/binary-%s/" % apt_pkg.Config.Find("APT::Architecture"))
  1297.             found_pkgs = set()
  1298.             for udeb in glob.glob(p+"*_*.udeb"):
  1299.                 logging.debug("copying pre-req '%s' to '%s'" % (udeb, backportsdir))
  1300.                 found_pkgs.add(os.path.basename(udeb).split("_")[0])
  1301.                 shutil.copy(udeb, backportsdir)
  1302.             # now check if we got all backports on the CD
  1303.             if not set(backportslist) == found_pkgs:
  1304.                 logging.error("Expected backports: '%s' but got '%s'" % (set(backportslist), found_pkgs))
  1305.                 return False
  1306.             return self.setupRequiredBackports(backportsdir)
  1307.  
  1308.         # we support PreRequists/SourcesList-$arch sections here too
  1309.         # 
  1310.         # logic for mirror finding works list this:     
  1311.         # - use the mirror template from the config, then: [done]
  1312.         # 
  1313.         #  - try to find known mirror (isMirror) and prepend it [done]
  1314.         #  - archive.ubuntu.com is always a fallback at the end [done]
  1315.         # 
  1316.         # see if we find backports with that
  1317.         # - if not, try guessing based on URI, Trust and Dist   [done]
  1318.         #   in existing sources.list (internal mirror with no
  1319.         #   outside connection maybe)
  1320.         # 
  1321.         # make sure to remove file on cancel
  1322.         
  1323.         # FIXME: use the DistUpgradeFetcherCore logic
  1324.         #        in mirror_from_sources_list() here
  1325.         #        (and factor that code out into a helper)
  1326.  
  1327.         conf_option = "SourcesList"
  1328.         if self.config.has_option("PreRequists",conf_option+"-%s" % self.arch):
  1329.             conf_option = conf_option + "-%s" % self.arch
  1330.         prereq_template = self.config.get("PreRequists",conf_option)
  1331.         if not os.path.exists(prereq_template):
  1332.             logging.error("sourceslist not found '%s'" % prereq_template)
  1333.             return False
  1334.         outpath = os.path.join(apt_pkg.Config.FindDir("Dir::Etc::sourceparts"), prereq_template)
  1335.         outfile = os.path.join(apt_pkg.Config.FindDir("Dir::Etc::sourceparts"), prereq_template)
  1336.         self._addPreRequistsSourcesList(prereq_template, outfile) 
  1337.         try:
  1338.             self._verifyBackports()
  1339.         except NoBackportsFoundException, e:
  1340.             self._addPreRequistsSourcesList(prereq_template, outfile, dumb=True) 
  1341.             try:
  1342.                 self._verifyBackports()
  1343.             except NoBackportsFoundException, e:
  1344.                 logging.warning("no backport for '%s' found" % e)
  1345.             return False
  1346.         
  1347.         # save cachedir and setup new one
  1348.         cachedir = apt_pkg.Config.Find("Dir::Cache::archives")
  1349.         cwd = os.getcwd()
  1350.         if not os.path.exists(os.path.join(backportsdir,"partial")):
  1351.             os.mkdir(os.path.join(backportsdir,"partial"))
  1352.         os.chdir(backportsdir)
  1353.         apt_pkg.Config.Set("Dir::Cache::archives",backportsdir)
  1354.  
  1355.         # FIXME: sanity check the origin (just for safety)
  1356.         for pkgname in backportslist:
  1357.             pkg = self.cache[pkgname]
  1358.             # look for the right version (backport)
  1359.             ver = self.cache._depcache.GetCandidateVer(pkg._pkg)
  1360.             if not ver:
  1361.                 logging.error("No candidate for '%s'" % pkgname)
  1362.                 os.unlink(outpath)
  1363.                 return False
  1364.             if ver.FileList == None:
  1365.                 logging.error("No ver.FileList for '%s'" % pkgname)
  1366.                 os.unlink(outpath)
  1367.                 return False
  1368.             logging.debug("marking '%s' for install" % pkgname)
  1369.             # mvo: autoInst is not available on dapper
  1370.             #pkg.markInstall(autoInst=False, autoFix=False)
  1371.             pkg.markInstall(autoFix=False)
  1372.  
  1373.         # mark the backports for upgrade and get them
  1374.         fetcher = apt_pkg.GetAcquire(self._view.getFetchProgress())
  1375.         pm = apt_pkg.GetPackageManager(self.cache._depcache)
  1376.  
  1377.         # now get it
  1378.         try:
  1379.             res = True
  1380.             self.cache._fetchArchives(fetcher, pm)
  1381.         except IOError, e:
  1382.             logging.error("_fetchArchives returned '%s'" % e)
  1383.             res = False
  1384.  
  1385.         if res == False:
  1386.             logging.warning("_fetchArchives for backports returned False")
  1387.  
  1388.         # reset the cache dir
  1389.         os.unlink(outpath)
  1390.         apt_pkg.Config.Set("Dir::Cache::archives",cachedir)
  1391.         os.chdir(cwd)
  1392.         return self.setupRequiredBackports(backportsdir)
  1393.  
  1394.     def setupRequiredBackports(self, backportsdir):
  1395.         " setup the required backports in a evil way "
  1396.         if not glob.glob(backportsdir+"/*.udeb"):
  1397.             logging.error("no backports found in setupRequiredBackports()")
  1398.             return False
  1399.         # unpack the backports first
  1400.         for deb in glob.glob(backportsdir+"/*.udeb"):
  1401.             logging.debug("extracting udeb '%s' " % deb)
  1402.             if os.system("dpkg-deb -x %s %s" % (deb, backportsdir)) != 0:
  1403.                 return False
  1404.         # setup some paths to make sure the new stuff is used
  1405.         os.environ["LD_LIBRARY_PATH"] = backportsdir+"/usr/lib"
  1406.         os.environ["PYTHONPATH"] = backportsdir+"/usr/lib/python%s.%s/site-packages/" % (sys.version_info[0], sys.version_info[1])
  1407.         os.environ["PATH"] = "%s:%s" % (backportsdir+"/usr/bin",
  1408.                                         os.environ["PATH"])
  1409.         # copy log so that it gets not overwritten
  1410.         logging.shutdown()
  1411.         shutil.copy("/var/log/dist-upgrade/main.log",
  1412.                     "/var/log/dist-upgrade/main_pre_req.log")
  1413.         # now exec self again
  1414.         args = sys.argv + ["--have-prerequists"]
  1415.         if self.useNetwork:
  1416.             args.append("--with-network")
  1417.         else:
  1418.             args.append("--without-network")
  1419.         # work around kde being clever and removing the x bit
  1420.         if not ((S_IMODE(os.stat(sys.argv[0])[ST_MODE]) & S_IXUSR) == S_IXUSR):
  1421.             os.chmod(sys.argv[0], 0755)
  1422.         os.execve(sys.argv[0],args, os.environ)
  1423.  
  1424.     def preDoDistUpgrade(self):
  1425.         " this runs right before apt calls out to dpkg "
  1426.         # kill update-notifier now to suppress reboot required
  1427.         if os.path.exists("/usr/bin/killall"):
  1428.             subprocess.call(["killall","-q","update-notifier"])
  1429.         # check theme, crux is known to fail badly when upgraded 
  1430.         # from dapper
  1431.         if (self.fromDist == "dapper" and 
  1432.             "DISPLAY" in os.environ and "SUDO_USER" in os.environ):
  1433.             out = subprocess.Popen(["sudo","-u", os.environ["SUDO_USER"],
  1434.                                     "./theme-switch-helper.py", "-g"],
  1435.                                     stdout=subprocess.PIPE).communicate()[0]
  1436.             if "Crux" in out:
  1437.                 subprocess.call(["sudo","-u", os.environ["SUDO_USER"],
  1438.                                     "./theme-switch-helper.py", "--defaults"])
  1439.         return True
  1440.  
  1441.     # this is the core
  1442.     def fullUpgrade(self):
  1443.         # sanity check (check for ubuntu-desktop, brokenCache etc)
  1444.         self._view.updateStatus(_("Checking package manager"))
  1445.         self._view.setStep(DistUpgradeView.STEP_PREPARE)
  1446.  
  1447.         if not self.prepare():
  1448.             logging.error("self.prepared() failed")
  1449.             self._view.error(_("Preparing the upgrade failed"),
  1450.                              _("Preparing the system for the upgrade "
  1451.                                "failed. Please report this as a bug "
  1452.                                "against the 'update-manager' "
  1453.                                "package and include the files in "
  1454.                                "/var/log/dist-upgrade/ "
  1455.                                "in the bug report." ))
  1456.             sys.exit(1)
  1457.  
  1458.         # mvo: commented out for now, see #54234, this needs to be
  1459.         #      refactored to use a arch=any tarball
  1460.         if (self.config.has_section("PreRequists") and
  1461.             self.options and
  1462.             self.options.havePrerequists == False):
  1463.             logging.debug("need backports")
  1464.             # get backported packages (if needed)
  1465.             if not self.getRequiredBackports():
  1466.                 self._view.error(_("Getting upgrade prerequisites failed"),
  1467.                                  _("The system was unable to get the "
  1468.                                    "prerequisites for the upgrade. "
  1469.                                    "The upgrade will abort now and restore "
  1470.                                    "the original system state.\n"
  1471.                                    "\n"
  1472.                                    "Please report this as a bug "
  1473.                                    "against the 'update-manager' "
  1474.                                    "package and include the files in "
  1475.                                    "/var/log/dist-upgrade/ "
  1476.                                    "in the bug report." ))
  1477.                 self.abort()
  1478.  
  1479.         # run a "apt-get update" now, its ok to ignore errors, 
  1480.         # because 
  1481.         # a) we disable any third party sources later
  1482.         # b) we check if we have valid ubuntu sources later
  1483.         #    after we rewrite the sources.list and do a 
  1484.         #    apt-get update there too
  1485.         # because the (unmodified) sources.list of the user
  1486.         # may contain bad/unreachable entries we run only
  1487.         # with a single retry
  1488.         self.doUpdate(showErrors=False, forceRetries=1)
  1489.         self.openCache()
  1490.  
  1491.         # do pre-upgrade stuff (calc list of obsolete pkgs etc)
  1492.         if not self.doPostInitialUpdate():
  1493.             self.abort()
  1494.  
  1495.         # update sources.list
  1496.         self._view.setStep(DistUpgradeView.STEP_MODIFY_SOURCES)
  1497.         self._view.updateStatus(_("Updating repository information"))
  1498.         if not self.updateSourcesList():
  1499.             self.abort()
  1500.  
  1501.         # add cdrom (if we have one)
  1502.         if (self.aptcdrom and
  1503.             not self.aptcdrom.add(self.sources_backup_ext)):
  1504.             sys.exit(1)
  1505.  
  1506.         # then update the package index files
  1507.         if not self.doUpdate():
  1508.             self.abort()
  1509.  
  1510.         # then open the cache (again)
  1511.         self._view.updateStatus(_("Checking package manager"))
  1512.         self.openCache()
  1513.         # now check if we still have some key packages after the update
  1514.         # if not something went seriously wrong
  1515.         for pkg in self.config.getlist("Distro","BaseMetaPkgs"):
  1516.             if not self.cache.has_key(pkg):
  1517.                 # FIXME: we could offer to add default source entries here,
  1518.                 #        but we need to be careful to not duplicate them
  1519.                 #        (i.e. the error here could be something else than
  1520.                 #        missing sources entries but network errors etc)
  1521.                 logging.error("No '%s' after sources.list rewrite+update" % pkg) 
  1522.                 self._view.error(_("Invalid package information"),
  1523.                                  _("After your package information was "
  1524.                                    "updated the essential package '%s' can "
  1525.                                    "not be found anymore.\n"
  1526.                                    "This indicates a serious error, please "
  1527.                                    "report this bug against the 'update-manager' "
  1528.                                    "package and include the files in /var/log/dist-upgrade/ "
  1529.                                    "in the bug report.") % pkg)
  1530.                 self.abort()
  1531.  
  1532.         # calc the dist-upgrade and see if the removals are ok/expected
  1533.         # do the dist-upgrade
  1534.         self._view.updateStatus(_("Calculating the changes"))
  1535.         if not self.askDistUpgrade():
  1536.             self.abort()
  1537.  
  1538.         # fetch the stuff
  1539.         self._view.setStep(DistUpgradeView.STEP_FETCH)
  1540.         self._view.updateStatus(_("Fetching"))
  1541.         if not self.doDistUpgradeFetching():
  1542.             self.abort()
  1543.  
  1544.         # now do the upgrade
  1545.         self.preDoDistUpgrade()
  1546.         self._view.setStep(DistUpgradeView.STEP_INSTALL)
  1547.         self._view.updateStatus(_("Upgrading"))
  1548.         if not self.doDistUpgrade():
  1549.             # run the post install scripts (for stuff like UUID conversion)
  1550.             self.runPostInstallScripts()
  1551.             # don't abort here, because it would restore the sources.list
  1552.             self._view.information(_("Upgrade complete"),
  1553.                                    _("The upgrade is completed but there "
  1554.                                      "were errors during the upgrade "
  1555.                                      "process."))
  1556.             sys.exit(1) 
  1557.             
  1558.         # do post-upgrade stuff
  1559.         self._view.setStep(DistUpgradeView.STEP_CLEANUP)
  1560.         self._view.updateStatus(_("Searching for obsolete software"))
  1561.         self.doPostUpgrade()
  1562.  
  1563.         # done, ask for reboot
  1564.         self._view.setStep(DistUpgradeView.STEP_REBOOT)
  1565.         self._view.updateStatus(_("System upgrade is complete."))            
  1566.         # FIXME should we look into /var/run/reboot-required here?
  1567.         if self._view.confirmRestart():
  1568.             p = subprocess.Popen("/sbin/reboot")
  1569.             sys.exit(0)
  1570.         
  1571.     def run(self):
  1572.         self._view.processEvents()
  1573.         self.fullUpgrade()
  1574.     
  1575.     def doPartialUpgrade(self):
  1576.         " partial upgrade mode, useful for repairing "
  1577.         from DistUpgrade.DistUpgradeView import STEP_PREPARE, STEP_MODIFY_SOURCES, STEP_FETCH, STEP_INSTALL, STEP_CLEANUP, STEP_REBOOT
  1578.         self._view.setStep(STEP_PREPARE)
  1579.         self._view.hideStep(STEP_MODIFY_SOURCES)
  1580.         self._view.hideStep(STEP_REBOOT)
  1581.         self._partialUpgrade = True
  1582.         self.prepare()
  1583.         if not self.doPostInitialUpdate():
  1584.             return False
  1585.         if not self.askDistUpgrade():
  1586.             return False
  1587.         self._view.setStep(STEP_FETCH)
  1588.         self._view.updateStatus(_("Fetching"))
  1589.         if not self.doDistUpgradeFetching():
  1590.             return False
  1591.         self._view.setStep(STEP_INSTALL)
  1592.         self._view.updateStatus(_("Upgrading"))
  1593.         if not self.doDistUpgrade():
  1594.             self._view.information(_("Upgrade complete"),
  1595.                                    _("The upgrade is completed but there "
  1596.                                      "were errors during the upgrade "
  1597.                                      "process."))
  1598.             return False
  1599.         self._view.setStep(STEP_CLEANUP)
  1600.         if not self.doPostUpgrade():
  1601.             self._view.information(_("Upgrade complete"),
  1602.                                    _("The upgrade is completed but there "
  1603.                                      "were errors during the upgrade "
  1604.                                      "process."))
  1605.             return False
  1606.         self._view.information(_("Upgrade complete"),
  1607.                                _("The partial upgrade was completed."))
  1608.         return True
  1609.  
  1610.  
  1611. if __name__ == "__main__":
  1612.     from DistUpgradeView import DistUpgradeView
  1613.     from DistUpgradeViewText import DistUpgradeViewText
  1614.     from DistUpgradeCache import MyCache
  1615.     logging.basicConfig(level=logging.DEBUG)
  1616.     v = DistUpgradeViewText()
  1617.     dc = DistUpgradeController(v)
  1618.     #dc.openCache()
  1619.     dc._disableAptCronJob()
  1620.     dc._enableAptCronJob()
  1621.     #dc._addRelatimeToFstab()
  1622.     #dc.prepare()
  1623.     #dc.askDistUpgrade()
  1624.     #dc._checkFreeSpace()
  1625.     #dc._rewriteFstab()
  1626.     #dc._checkAdminGroup()
  1627.     #dc._rewriteAptPeriodic(2)
  1628.