~straemer/ubuntu/quantal/update-manager/fix-for-1058070

« back to all changes in this revision

Viewing changes to DistUpgrade/DistUpgradeQuirks.py

  • Committer: Package Import Robot
  • Author(s): Michael Terry
  • Date: 2012-06-29 10:59:30 UTC
  • mfrom: (389.1.2 precise-security)
  • Revision ID: package-import@ubuntu.com-20120629105930-0oaj9vdvykmvkjum
Tags: 1:0.165
* Implementation of "update on start" feature from spec
  https://wiki.ubuntu.com/SoftwareUpdates
* Use a single main window that changes instead of having modal dialogs
* Implement several special-purpose dialogs like "No updates" or
  "Dist upgrade needed" accordingn to the above spec
* Split out release upgrader code and DistUpgrade module into a separate
  source package
* Drop python-update-manager, as it is unused
* debian/tests:
  - Add dep8 tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# DistUpgradeQuirks.py 
2
 
#  
3
 
#  Copyright (c) 2004-2010 Canonical
4
 
#  
5
 
#  Author: Michael Vogt <michael.vogt@ubuntu.com>
6
 
7
 
#  This program is free software; you can redistribute it and/or 
8
 
#  modify it under the terms of the GNU General Public License as 
9
 
#  published by the Free Software Foundation; either version 2 of the
10
 
#  License, or (at your option) any later version.
11
 
12
 
#  This program is distributed in the hope that it will be useful,
13
 
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
#  GNU General Public License for more details.
16
 
17
 
#  You should have received a copy of the GNU General Public License
18
 
#  along with this program; if not, write to the Free Software
19
 
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20
 
#  USA
21
 
 
22
 
from __future__ import absolute_import
23
 
 
24
 
import apt
25
 
import atexit
26
 
import glob
27
 
import logging
28
 
import os
29
 
import re
30
 
import hashlib
31
 
import shutil
32
 
import sys
33
 
import subprocess
34
 
from subprocess import PIPE, Popen
35
 
from hashlib import md5
36
 
 
37
 
from .utils import lsmod, get_arch
38
 
 
39
 
from .DistUpgradeGettext import gettext as _
40
 
from janitor.plugincore.manager import PluginManager
41
 
 
42
 
class DistUpgradeQuirks(object):
43
 
    """
44
 
    This class collects the various quirks handlers that can
45
 
    be hooked into to fix/work around issues that the individual
46
 
    releases have
47
 
    """
48
 
    
49
 
    def __init__(self, controller, config):
50
 
        self.controller = controller
51
 
        self._view = controller._view
52
 
        self.config = config
53
 
        self.uname = Popen(["uname","-r"], stdout=PIPE,
54
 
                           universal_newlines=True).communicate()[0].strip()
55
 
        self.arch = get_arch()
56
 
        self.plugin_manager = PluginManager(self.controller, ["./plugins"])
57
 
 
58
 
    # the quirk function have the name:
59
 
    #  $Name (e.g. PostUpgrade)
60
 
    #  $todist$Name (e.g. intrepidPostUpgrade)
61
 
    #  $from_$fromdist$Name (e.g. from_dapperPostUpgrade)
62
 
    def run(self, quirksName):
63
 
        """
64
 
        Run the specific quirks handler, the follow handlers are supported:
65
 
        - PreCacheOpen: run *before* the apt cache is opened the first time
66
 
                        to set options that affect the cache
67
 
        - PostInitialUpdate: run *before* the sources.list is rewritten but
68
 
                             after a initial apt-get update
69
 
        - PostDistUpgradeCache: run *after* the dist-upgrade was calculated
70
 
                                in the cache
71
 
        - StartUpgrade: before the first package gets installed (but the
72
 
                        download is finished)
73
 
        - PostUpgrade: run *after* the upgrade is finished successfully and 
74
 
                       packages got installed
75
 
        - PostCleanup: run *after* the cleanup (orphaned etc) is finished
76
 
        """
77
 
        # we do not run any quirks in partialUpgrade mode
78
 
        if self.controller._partialUpgrade:
79
 
            logging.info("not running quirks in partialUpgrade mode")
80
 
            return
81
 
        # first check for matching plugins
82
 
        for condition in [
83
 
            quirksName,
84
 
            "%s%s" %  (self.config.get("Sources","To"), quirksName),
85
 
            "from_%s%s" % (self.config.get("Sources","From"), quirksName)
86
 
            ]:
87
 
            for plugin in self.plugin_manager.get_plugins(condition):
88
 
                logging.debug("running quirks plugin %s" % plugin)
89
 
                plugin.do_cleanup_cruft()
90
 
        
91
 
        # run the handler that is common to all dists
92
 
        funcname = "%s" % quirksName
93
 
        func = getattr(self, funcname, None)
94
 
        if func is not None:
95
 
            logging.debug("quirks: running %s" % funcname)
96
 
            func()
97
 
 
98
 
        # run the quirksHandler to-dist
99
 
        funcname = "%s%s" % (self.config.get("Sources","To"), quirksName)
100
 
        func = getattr(self, funcname, None)
101
 
        if func is not None:
102
 
            logging.debug("quirks: running %s" % funcname)
103
 
            func()
104
 
 
105
 
        # now run the quirksHandler from_${FROM-DIST}Quirks
106
 
        funcname = "from_%s%s" % (self.config.get("Sources","From"), quirksName)
107
 
        func = getattr(self, funcname, None)
108
 
        if func is not None:
109
 
            logging.debug("quirks: running %s" % funcname)
110
 
            func()
111
 
 
112
 
    # individual quirks handler that run *before* the cache is opened
113
 
    def PreCacheOpen(self):
114
 
        """ run before the apt cache is opened the first time """
115
 
        logging.debug("running Quirks.PreCacheOpen")
116
 
 
117
 
    def oneiricPreCacheOpen(self):
118
 
        logging.debug("running Quirks.oneiricPreCacheOpen")
119
 
        # enable i386 multiach temporarely during the upgrade if on amd64
120
 
        # this needs to be done very early as libapt caches the result
121
 
        # of the "getArchitectures()" call in aptconfig and its not possible
122
 
        # currently to invalidate this cache
123
 
        if apt.apt_pkg.config.find("Apt::Architecture") == "amd64":
124
 
            logging.debug("multiarch: enabling i386 as a additional architecture")
125
 
            apt.apt_pkg.config.set("Apt::Architectures::", "i386")
126
 
            # increase case size to workaround bug in natty apt that
127
 
            # may cause segfault on cache grow
128
 
            apt.apt_pkg.config.set("APT::Cache-Start", str(48*1024*1024))
129
 
 
130
 
 
131
 
    # individual quirks handler when the dpkg run is finished ---------
132
 
    def PostCleanup(self):
133
 
        " run after cleanup " 
134
 
        logging.debug("running Quirks.PostCleanup")
135
 
 
136
 
    def from_dapperPostUpgrade(self):
137
 
        " this works around quirks for dapper->hardy upgrades "
138
 
        logging.debug("running Controller.from_dapperQuirks handler")
139
 
        self._rewriteFstab()
140
 
        self._checkAdminGroup()
141
 
        
142
 
    def intrepidPostUpgrade(self):
143
 
        " this applies rules for the hardy->intrepid upgrade "
144
 
        logging.debug("running Controller.intrepidQuirks handler")
145
 
        self._addRelatimeToFstab()
146
 
 
147
 
    def gutsyPostUpgrade(self):
148
 
        """ this function works around quirks in the feisty->gutsy upgrade """
149
 
        logging.debug("running Controller.gutsyQuirks handler")
150
 
 
151
 
    def feistyPostUpgrade(self):
152
 
        """ this function works around quirks in the edgy->feisty upgrade """
153
 
        logging.debug("running Controller.feistyQuirks handler")
154
 
        self._rewriteFstab()
155
 
        self._checkAdminGroup()
156
 
 
157
 
    def karmicPostUpgrade(self):
158
 
        """ this function works around quirks in the jaunty->karmic upgrade """
159
 
        logging.debug("running Controller.karmicPostUpgrade handler")
160
 
        self._ntfsFstabFixup()
161
 
        self._checkLanguageSupport()
162
 
 
163
 
    # quirks when run when the initial apt-get update was run ----------------
164
 
    def from_lucidPostInitialUpdate(self):
165
 
        """ Quirks that are run before the sources.list is updated to the
166
 
            new distribution when upgrading from a lucid system (either
167
 
            to maverick or the new LTS)
168
 
        """
169
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
170
 
        # systems < i686 will not upgrade
171
 
        self._test_and_fail_on_non_i686()
172
 
        self._test_and_warn_on_i8xx()
173
 
 
174
 
    def oneiricPostInitialUpdate(self):
175
 
        self._test_and_warn_on_i8xx()
176
 
 
177
 
    def lucidPostInitialUpdate(self):
178
 
        """ quirks that are run before the sources.list is updated to lucid """
179
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
180
 
        # upgrades on systems with < arvm6 CPUs will break
181
 
        self._test_and_fail_on_non_arm_v6()
182
 
        # vserver+upstart are problematic
183
 
        self._test_and_warn_if_vserver()
184
 
        # fglrx dropped support for some cards
185
 
        self._test_and_warn_on_dropped_fglrx_support()
186
 
 
187
 
    # quirks when the cache upgrade calculation is finished -------------------
188
 
    def from_dapperPostDistUpgradeCache(self):
189
 
        self.hardyPostDistUpgradeCache()
190
 
        self.gutsyPostDistUpgradeCache()
191
 
        self.feistyPostDistUpgradeCache()
192
 
        self.edgyPostDistUpgradeCache()
193
 
 
194
 
    def from_hardyPostDistUpgradeCache(self):
195
 
        """ this function works around quirks in upgrades from hardy """
196
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
197
 
        # ensure 386 -> generic transition happens
198
 
        self._kernel386TransitionCheck()
199
 
        # ensure kubuntu-kde4-desktop transition
200
 
        self._kubuntuDesktopTransition()
201
 
        # evms got removed after hardy, warn and abort
202
 
        if self._usesEvmsInMounts():
203
 
            logging.error("evms in use in /etc/fstab")
204
 
            self._view.error(_("evms in use"),
205
 
                             _("Your system uses the 'evms' volume manager "
206
 
                               "in /proc/mounts. "
207
 
                               "The 'evms' software is no longer supported, "
208
 
                               "please switch it off and run the upgrade "
209
 
                               "again when this is done."))
210
 
            self.controller.abort()
211
 
        # check if "wl" module is loaded and if so, install bcmwl-kernel-source
212
 
        self._checkAndInstallBroadcom()
213
 
        # langpacks got re-organized in 9.10
214
 
        self._dealWithLanguageSupportTransition()
215
 
        # nvidia-71, nvidia-96 got dropped
216
 
        self._test_and_warn_on_old_nvidia()
217
 
        # new nvidia needs a CPU with sse support
218
 
        self._test_and_warn_on_nvidia_and_no_sse()
219
 
 
220
 
    def nattyPostDistUpgradeCache(self):
221
 
        """
222
 
        this function works around quirks in the 
223
 
        maverick -> natty cache upgrade calculation
224
 
        """
225
 
        self._add_kdegames_card_extra_if_installed()
226
 
 
227
 
    def maverickPostDistUpgradeCache(self):
228
 
        """
229
 
        this function works around quirks in the 
230
 
        lucid->maverick upgrade calculation
231
 
        """
232
 
        self._add_extras_repository()
233
 
        self._gutenprint_fixup()
234
 
 
235
 
    def karmicPostDistUpgradeCache(self):
236
 
        """ 
237
 
        this function works around quirks in the 
238
 
        jaunty->karmic upgrade calculation
239
 
        """
240
 
        # check if "wl" module is loaded and if so, install
241
 
        # bcmwl-kernel-source (this is needed for lts->lts as well)
242
 
        self._checkAndInstallBroadcom()
243
 
        self._dealWithLanguageSupportTransition()
244
 
        self._kernel386TransitionCheck()
245
 
        self._mysqlClusterCheck()
246
 
 
247
 
    def jauntyPostDistUpgradeCache(self):
248
 
        """ 
249
 
        this function works around quirks in the 
250
 
        intrepid->jaunty upgrade calculation
251
 
        """
252
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
253
 
        # bug 332328 - make sure pidgin-libnotify is upgraded
254
 
        for pkg in ["pidgin-libnotify"]:
255
 
            if (pkg in self.controller.cache and
256
 
                self.controller.cache[pkg].is_installed and
257
 
                not self.controller.cache[pkg].marked_upgrade):
258
 
                logging.debug("forcing '%s' upgrade" % pkg)
259
 
                self.controller.cache[pkg].mark_upgrade()
260
 
        # deal with kipi/gwenview/kphotoalbum
261
 
        for pkg in ["gwenview","digikam"]:
262
 
            if (pkg in self.controller.cache and
263
 
                self.controller.cache[pkg].is_installed and
264
 
                not self.controller.cache[pkg].marked_upgrade):
265
 
                logging.debug("forcing libkipi '%s' upgrade" % pkg)
266
 
                if "libkipi0" in self.controller.cache:
267
 
                    logging.debug("removing  libkipi0)")
268
 
                    self.controller.cache["libkipi0"].mark_delete()
269
 
                self.controller.cache[pkg].mark_upgrade()
270
 
        
271
 
    def intrepidPostDistUpgradeCache(self):
272
 
        """ 
273
 
        this function works around quirks in the 
274
 
        hardy->intrepid upgrade 
275
 
        """
276
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
277
 
        # kdelibs4-dev is unhappy (#279621)
278
 
        fromp = "kdelibs4-dev"
279
 
        to = "kdelibs5-dev"
280
 
        if (fromp in self.controller.cache and
281
 
            self.controller.cache[fromp].is_installed and
282
 
            to in self.controller.cache):
283
 
            self.controller.cache.mark_install(to, "kdelibs4-dev -> kdelibs5-dev transition")
284
 
 
285
 
    def hardyPostDistUpgradeCache(self):
286
 
        """ 
287
 
        this function works around quirks in the 
288
 
        {dapper,gutsy}->hardy upgrade 
289
 
        """
290
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
291
 
        # deal with gnome-translator and help apt with the breaks
292
 
        if ("nautilus" in self.controller.cache and
293
 
            self.controller.cache["nautilus"].is_installed and
294
 
            not self.controller.cache["nautilus"].marked_upgrade):
295
 
            # uninstallable and gutsy apt is unhappy about this
296
 
            # breaks because it wants to upgrade it and gives up
297
 
            # if it can't
298
 
            for broken in ("link-monitor-applet"):
299
 
                if broken in self.controller.cache and self.controller.cache[broken].is_installed:
300
 
                    self.controller.cache[broken].mark_delete()
301
 
            self.controller.cache["nautilus"].mark_install()
302
 
        # evms gives problems, remove it if it is not in use
303
 
        self._checkAndRemoveEvms()
304
 
        # give the language-support-* packages a extra kick
305
 
        # (if we have network, otherwise this will not work)
306
 
        if self.config.get("Options","withNetwork") == "True":
307
 
            for pkg in self.controller.cache:
308
 
                if (pkg.name.startswith("language-support-") and
309
 
                    pkg.is_installed and
310
 
                    not pkg.marked_upgrade):
311
 
                    self.controller.cache.mark_install(pkg.name,"extra language-support- kick")
312
 
 
313
 
    def gutsyPostDistUpgradeCache(self):
314
 
        """ this function works around quirks in the feisty->gutsy upgrade """
315
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
316
 
        # lowlatency kernel flavour vanished from feisty->gutsy
317
 
        try:
318
 
            (version, build, flavour) = self.uname.split("-")
319
 
            if (flavour == 'lowlatency' or 
320
 
                flavour == '686' or
321
 
                flavour == 'k7'):
322
 
                kernel = "linux-image-generic"
323
 
                if not (self.controller.cache[kernel].is_installed or self.controller.cache[kernel].marked_install):
324
 
                    logging.debug("Selecting new kernel '%s'" % kernel)
325
 
                    self.controller.cache[kernel].mark_install()
326
 
        except Exception as e:
327
 
            logging.warning("problem while transitioning lowlatency kernel (%s)" % e)
328
 
        # fix feisty->gutsy utils-linux -> nfs-common transition (LP: #141559)
329
 
        try:
330
 
            for line in open("/proc/mounts"):
331
 
                line = line.strip()
332
 
                if line == '' or line.startswith("#"):
333
 
                    continue
334
 
                try:
335
 
                    (device, mount_point, fstype, options, a, b) = line.split()
336
 
                except Exception as e:
337
 
                    logging.error("can't parse line '%s'" % line)
338
 
                    continue
339
 
                if "nfs" in fstype:
340
 
                    logging.debug("found nfs mount in line '%s', marking nfs-common for install " % line)
341
 
                    self.controller.cache["nfs-common"].mark_install()
342
 
                    break
343
 
        except Exception as e:
344
 
            logging.warning("problem while transitioning util-linux -> nfs-common (%s)" % e)
345
 
 
346
 
    def feistyPostDistUpgradeCache(self):
347
 
        """ this function works around quirks in the edgy->feisty upgrade """
348
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
349
 
        # ndiswrapper changed again *sigh*
350
 
        for (fr, to) in [("ndiswrapper-utils-1.8","ndiswrapper-utils-1.9")]:
351
 
            if fr in self.controller.cache and to in self.controller.cache:
352
 
                if self.controller.cache[fr].is_installed and not self.controller.cache[to].marked_install:
353
 
                    try:
354
 
                        self.controller.cache.mark_install(to,"%s->%s quirk upgrade rule" % (fr, to))
355
 
                    except SystemError as e:
356
 
                        logging.warning("Failed to apply %s->%s install (%s)" % (fr, to, e))
357
 
            
358
 
 
359
 
    def edgyPostDistUpgradeCache(self):
360
 
        """ this function works around quirks in the dapper->edgy upgrade """
361
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
362
 
        for pkg in self.controller.cache:
363
 
            # deal with the python2.4-$foo -> python-$foo transition
364
 
            if (pkg.name.startswith("python2.4-") and
365
 
                pkg.is_installed and
366
 
                not pkg.marked_upgrade):
367
 
                basepkg = "python-"+pkg.name[len("python2.4-"):]
368
 
                if (basepkg in self.controller.cache and
369
 
                    self.controller.cache[basepkg].candidate and
370
 
                    self.controller.cache[basepkg].candidate.downloadable and
371
 
                    not self.controller.cache[basepkg].marked_install):
372
 
                    try:
373
 
                        self.controller.cache.mark_install(basepkg,
374
 
                                         "python2.4->python upgrade rule")
375
 
                    except SystemError as e:
376
 
                        logging.debug("Failed to apply python2.4->python install: %s (%s)" % (basepkg, e))
377
 
            # xserver-xorg-input-$foo gives us trouble during the upgrade too
378
 
            if (pkg.name.startswith("xserver-xorg-input-") and
379
 
                pkg.is_installed and
380
 
                not pkg.marked_upgrade):
381
 
                try:
382
 
                    self.controller.cache.mark_install(pkg.name, "xserver-xorg-input fixup rule")
383
 
                except SystemError as e:
384
 
                    logging.debug("Failed to apply fixup: %s (%s)" % (pkg.name, e))
385
 
            
386
 
        # deal with held-backs that are unneeded
387
 
        for pkgname in ["hpijs", "bzr", "tomboy"]:
388
 
            if (pkgname in self.controller.cache and self.controller.cache[pkgname].is_installed and
389
 
                self.controller.cache[pkgname].is_upgradable and not self.controller.cache[pkgname].marked_upgrade):
390
 
                try:
391
 
                    self.controller.cache.mark_install(pkgname,"%s quirk upgrade rule" % pkgname)
392
 
                except SystemError as e:
393
 
                    logging.debug("Failed to apply %s install (%s)" % (pkgname,e))
394
 
        # libgl1-mesa-dri from xgl.compiz.info (and friends) breaks the
395
 
        # upgrade, work around this here by downgrading the package
396
 
        if "libgl1-mesa-dri" in self.controller.cache:
397
 
            pkg = self.controller.cache["libgl1-mesa-dri"]
398
 
            # the version from the compiz repo has a "6.5.1+cvs20060824" ver
399
 
            if (pkg.candidate is not None and pkg.installed is not None and
400
 
                pkg.candidate.version == pkg.installed.version and
401
 
                "+cvs2006" in pkg.candidate.version):
402
 
                for ver in pkg._pkg.version_list:
403
 
                    # the "official" edgy version has "6.5.1~20060817-0ubuntu3"
404
 
                    if "~2006" in ver.ver_str:
405
 
                        # ensure that it is from a trusted repo
406
 
                        for (VerFileIter, index) in ver.file_list:
407
 
                            indexfile = self.controller.cache._list.find_index(VerFileIter)
408
 
                            if indexfile and indexfile.is_trusted:
409
 
                                logging.info("Forcing downgrade of libgl1-mesa-dri for xgl.compz.info installs")
410
 
                                self.controller.cache._depcache.set_candidate_ver(pkg._pkg, ver)
411
 
                                break
412
 
                                    
413
 
        # deal with general if $foo is installed, install $bar
414
 
        for (fr, to) in [("xserver-xorg-driver-all","xserver-xorg-video-all")]:
415
 
            if fr in self.controller.cache and to in self.controller.cache:
416
 
                if self.controller.cache[fr].is_installed and not self.controller.cache[to].marked_install:
417
 
                    try:
418
 
                        self.controller.cache.mark_install(to,"%s->%s quirk upgrade rule" % (fr, to))
419
 
                    except SystemError as e:
420
 
                        logging.debug("Failed to apply %s->%s install (%s)" % (fr, to, e))
421
 
                    
422
 
    def dapperPostDistUpgradeCache(self):
423
 
        """ this function works around quirks in the breezy->dapper upgrade """
424
 
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
425
 
        if ("nvidia-glx" in self.controller.cache and self.controller.cache["nvidia-glx"].is_installed and
426
 
            "nvidia-settings" in self.controller.cache and self.controller.cache["nvidia-settings"].is_installed):
427
 
            logging.debug("nvidia-settings and nvidia-glx is installed")
428
 
            self.controller.cache.mark_remove("nvidia-settings")
429
 
            self.controller.cache.mark_install("nvidia-glx")
430
 
 
431
 
    # run right before the first packages get installed
432
 
    def StartUpgrade(self):
433
 
        self._applyPatches()
434
 
        self._removeOldApportCrashes()
435
 
        self._removeBadMaintainerScripts()
436
 
        self._killUpdateNotifier()
437
 
        self._killKBluetooth()
438
 
        self._killScreensaver()
439
 
        self._pokeScreensaver()
440
 
        self._stopDocvertConverter()
441
 
    def oneiricStartUpgrade(self):
442
 
        logging.debug("oneiric StartUpgrade quirks")
443
 
        # fix grub issue
444
 
        if (os.path.exists("/usr/sbin/update-grub") and
445
 
            not os.path.exists("/etc/kernel/postinst.d/zz-update-grub")):
446
 
            # create a version of zz-update-grub to avoid depending on
447
 
            # the upgrade order. if that file is missing, we may end
448
 
            # up generating a broken grub.cfg
449
 
            targetdir = "/etc/kernel/postinst.d"
450
 
            if not os.path.exists(targetdir):
451
 
                os.makedirs(targetdir)
452
 
            logging.debug("copying zz-update-grub into %s" % targetdir)
453
 
            shutil.copy("zz-update-grub", targetdir)
454
 
            os.chmod(os.path.join(targetdir, "zz-update-grub"), 0o755)
455
 
        # enable multiarch permanently
456
 
        if apt.apt_pkg.config.find("Apt::Architecture") == "amd64":
457
 
            self._enable_multiarch(foreign_arch="i386")
458
 
            
459
 
    def from_hardyStartUpgrade(self):
460
 
        logging.debug("from_hardyStartUpgrade quirks")
461
 
        self._stopApparmor()
462
 
    def jauntyStartUpgrade(self):
463
 
        self._createPycentralPkgRemove()
464
 
        # hal/NM triggers problem, if the old (intrepid) hal gets
465
 
        # triggered for a restart this causes NM to drop all connections
466
 
        # because (old) hal thinks it has no devices anymore (LP: #327053)
467
 
        ap = "/var/lib/dpkg/info/hal.postinst"
468
 
        if os.path.exists(ap):
469
 
            # intrepid md5 of hal.postinst (jaunty one is different)
470
 
            # md5 jaunty 22c146857d751181cfe299a171fc11c9
471
 
            md5sum = "146145275900af343d990a4dea968d7c"
472
 
            if md5(open(ap).read()).hexdigest() == md5sum:
473
 
                logging.debug("removing bad script '%s'" % ap)
474
 
                os.unlink(ap)
475
 
 
476
 
    # helpers
477
 
    def _get_pci_ids(self):
478
 
        """ return a set of pci ids of the system (using lspci -n) """
479
 
        lspci = set()
480
 
        try:
481
 
            p = subprocess.Popen(["lspci","-n"], stdout=subprocess.PIPE,
482
 
                                 universal_newlines=True)
483
 
        except OSError:
484
 
            return lspci
485
 
        for line in p.communicate()[0].split("\n"):
486
 
            if line:
487
 
                lspci.add(line.split()[2])
488
 
        return lspci
489
 
 
490
 
    def _test_and_warn_on_i8xx(self):
491
 
        I8XX_PCI_IDS = ["8086:7121", # i810
492
 
                        "8086:7125", # i810e
493
 
                        "8086:1132", # i815
494
 
                        "8086:3577", # i830
495
 
                        "8086:2562", # i845
496
 
                        "8086:3582", # i855
497
 
                        "8086:2572", # i865
498
 
                        ]
499
 
        lspci = self._get_pci_ids()
500
 
        if set(I8XX_PCI_IDS).intersection(lspci):
501
 
            res = self._view.askYesNoQuestion(
502
 
                _("Your graphics hardware may not be fully supported in "
503
 
                  "Ubuntu 12.04 LTS."),
504
 
                _("The support in Ubuntu 12.04 LTS for your Intel "
505
 
                  "graphics hardware is limited "
506
 
                  "and you may encounter problems after the upgrade. "
507
 
                  "For more information see "
508
 
                  "https://wiki.ubuntu.com/X/Bugs/UpdateManagerWarningForI8xx "
509
 
                  "Do you want to continue with the upgrade?")
510
 
                )
511
 
            if res == False:
512
 
                self.controller.abort()
513
 
 
514
 
    def _test_and_warn_on_nvidia_and_no_sse(self):
515
 
        """ The current 
516
 
        """
517
 
        # check if we have sse
518
 
        cache = self.controller.cache
519
 
        for pkgname in ["nvidia-glx-180", "nvidia-glx-185", "nvidia-glx-195"]:
520
 
            if (pkgname in cache and
521
 
                cache[pkgname].marked_install and
522
 
                self._checkVideoDriver("nvidia")):
523
 
                logging.debug("found %s video driver" % pkgname)
524
 
                if not self._cpuHasSSESupport():
525
 
                    logging.warning("nvidia driver that needs SSE but cpu has no SSE support")
526
 
                    res = self._view.askYesNoQuestion(_("Upgrading may reduce desktop "
527
 
                                        "effects, and performance in games "
528
 
                                        "and other graphically intensive "
529
 
                                        "programs."),
530
 
                                      _("This computer is currently using "
531
 
                                        "the NVIDIA 'nvidia' "
532
 
                                        "graphics driver. "
533
 
                                        "No version of this driver is "
534
 
                                        "available that works with your "
535
 
                                        "video card in Ubuntu "
536
 
                                        "10.04 LTS.\n\nDo you want to continue?"))
537
 
                    if res == False:
538
 
                        self.controller.abort()
539
 
                    # if the user continue, do not install the broken driver
540
 
                    # so that we can transiton him to the free "nv" one after
541
 
                    # the upgrade
542
 
                    self.controller.cache[pkgname].mark_keep()
543
 
        
544
 
 
545
 
    def _test_and_warn_on_old_nvidia(self):
546
 
        """ nvidia-glx-71 and -96 are no longer in the archive since 8.10 """
547
 
        # now check for nvidia and show a warning if needed
548
 
        cache = self.controller.cache
549
 
        for pkgname in ["nvidia-glx-71","nvidia-glx-96"]:
550
 
            if (pkgname in cache and
551
 
                cache[pkgname].marked_install and
552
 
                self._checkVideoDriver("nvidia")):
553
 
                logging.debug("found %s video driver" % pkgname)
554
 
                res = self._view.askYesNoQuestion(_("Upgrading may reduce desktop "
555
 
                                        "effects, and performance in games "
556
 
                                        "and other graphically intensive "
557
 
                                        "programs."),
558
 
                                      _("This computer is currently using "
559
 
                                        "the NVIDIA 'nvidia' "
560
 
                                        "graphics driver. "
561
 
                                        "No version of this driver is "
562
 
                                        "available that works with your "
563
 
                                        "video card in Ubuntu "
564
 
                                        "10.04 LTS.\n\nDo you want to continue?"))
565
 
                if res == False:
566
 
                    self.controller.abort()
567
 
                # if the user continue, do not install the broken driver
568
 
                # so that we can transiton him to the free "nv" one after
569
 
                # the upgrade
570
 
                self.controller.cache[pkgname].mark_keep()
571
 
 
572
 
    def _test_and_warn_on_dropped_fglrx_support(self):
573
 
        """
574
 
        Some cards are no longer supported by fglrx. Check if that
575
 
        is the case and warn
576
 
        """
577
 
        # this is to deal with the fact that support for some of the cards
578
 
        # that fglrx used to support got dropped
579
 
        if (self._checkVideoDriver("fglrx") and 
580
 
            not self._supportInModaliases("fglrx")):
581
 
             res = self._view.askYesNoQuestion(_("Upgrading may reduce desktop "
582
 
                                         "effects, and performance in games "
583
 
                                         "and other graphically intensive "
584
 
                                         "programs."),
585
 
                                       _("This computer is currently using "
586
 
                                         "the AMD 'fglrx' graphics driver. "
587
 
                                         "No version of this driver is "
588
 
                                         "available that works with your "
589
 
                                         "hardware in Ubuntu "
590
 
                                         "10.04 LTS.\n\nDo you want to continue?"))
591
 
             if res == False:
592
 
                 self.controller.abort()
593
 
             # if the user wants to continue we remove the fglrx driver
594
 
             # here because its no use (no support for this card)
595
 
             logging.debug("remove xorg-driver-fglrx,xorg-driver-fglrx-envy,fglrx-kernel-source")
596
 
             l=self.controller.config.getlist("Distro","PostUpgradePurge")
597
 
             l.append("xorg-driver-fglrx")
598
 
             l.append("xorg-driver-fglrx-envy")
599
 
             l.append("fglrx-kernel-source")
600
 
             l.append("fglrx-amdcccle")
601
 
             l.append("xorg-driver-fglrx-dev")
602
 
             l.append("libamdxvba1")
603
 
             self.controller.config.set("Distro","PostUpgradePurge",",".join(l))
604
 
 
605
 
    def _test_and_fail_on_non_i686(self):
606
 
        """
607
 
        Test and fail if the cpu is not i686 or more or if its a newer
608
 
        CPU but does not have the cmov feature (LP: #587186)
609
 
        """
610
 
        # check on i386 only
611
 
        if self.arch == "i386":
612
 
            logging.debug("checking for i586 CPU")
613
 
            if not self._cpu_is_i686_and_has_cmov():
614
 
                logging.error("not a i686 or no cmov")
615
 
                summary = _("No i686 CPU")
616
 
                msg = _("Your system uses an i586 CPU or a CPU that does "
617
 
                        "not have the 'cmov' extension. "
618
 
                        "All packages were built with "
619
 
                        "optimizations requiring i686 as the "
620
 
                        "minimal architecture. It is not possible to "
621
 
                        "upgrade your system to a new Ubuntu release "
622
 
                        "with this hardware.")
623
 
                self._view.error(summary, msg)
624
 
                self.controller.abort()
625
 
 
626
 
    def _cpu_is_i686_and_has_cmov(self, cpuinfo_path="/proc/cpuinfo"):
627
 
        if not os.path.exists(cpuinfo_path):
628
 
            logging.error("cannot open %s ?!?" % cpuinfo_path)
629
 
            return True
630
 
        cpuinfo = open(cpuinfo_path).read()
631
 
        # check family
632
 
        if re.search("^cpu family\s*:\s*[345]\s*", cpuinfo, re.MULTILINE):
633
 
            logging.debug("found cpu family [345], no i686+")
634
 
            return False
635
 
        # check flags for cmov
636
 
        match = re.search("^flags\s*:\s*(.*)", cpuinfo, re.MULTILINE)
637
 
        if match:
638
 
            if not "cmov" in match.group(1).split():
639
 
                logging.debug("found flags '%s'" % match.group(1))
640
 
                logging.debug("can not find cmov in flags")
641
 
                return False
642
 
        return True
643
 
 
644
 
 
645
 
    def _test_and_fail_on_non_arm_v6(self):
646
 
        """ 
647
 
        Test and fail if the cpu is not a arm v6 or greater,
648
 
        from 9.10 on we do no longer support those CPUs
649
 
        """
650
 
        if self.arch == "armel":
651
 
            if not self._checkArmCPU():
652
 
                self._view.error(_("No ARMv6 CPU"),
653
 
                    _("Your system uses an ARM CPU that is older "
654
 
                      "than the ARMv6 architecture. "
655
 
                      "All packages in karmic were built with "
656
 
                      "optimizations requiring ARMv6 as the "
657
 
                      "minimal architecture. It is not possible to "
658
 
                      "upgrade your system to a new Ubuntu release "
659
 
                      "with this hardware."))
660
 
                self.controller.abort()
661
 
 
662
 
    def _test_and_warn_if_vserver(self):
663
 
        """
664
 
        upstart and vserver environments are not a good match, warn
665
 
        if we find one
666
 
        """
667
 
        # verver test (LP: #454783), see if there is a init around
668
 
        try:
669
 
            os.kill(1, 0)
670
 
        except:
671
 
            logging.warn("no init found")
672
 
            res = self._view.askYesNoQuestion(
673
 
                _("No init available"),
674
 
                _("Your system appears to be a virtualised environment "
675
 
                  "without an init daemon, e.g. Linux-VServer. "
676
 
                  "Ubuntu 10.04 LTS cannot function within this type of "
677
 
                  "environment, requiring an update to your virtual "
678
 
                  "machine configuration first.\n\n"
679
 
                  "Are you sure you want to continue?"))
680
 
            if res == False:
681
 
                self.controller.abort()
682
 
            self._view.processEvents()
683
 
 
684
 
    def _kubuntuDesktopTransition(self):
685
 
        """
686
 
        check if a key depends of kubuntu-kde4-desktop is installed
687
 
        and transition in this case as well
688
 
        """
689
 
        deps_found = False
690
 
        frompkg = "kubuntu-kde4-desktop"
691
 
        topkg = "kubuntu-desktop"
692
 
        if self.config.getlist(frompkg,"KeyDependencies"):
693
 
            deps_found = True
694
 
            for pkg in self.config.getlist(frompkg,"KeyDependencies"):
695
 
                deps_found &= (pkg in self.controller.cache and
696
 
                               self.controller.cache[pkg].is_installed)
697
 
        if deps_found:
698
 
            logging.debug("transitioning %s to %s (via key depends)" % (frompkg, topkg))
699
 
            self.controller.cache[topkg].mark_install()
700
 
 
701
 
    def _mysqlClusterCheck(self):
702
 
        """
703
 
        check if ndb clustering is used and do not upgrade mysql
704
 
        if it is (LP: #450837)
705
 
        """
706
 
        logging.debug("_mysqlClusterCheck")
707
 
        if ("mysql-server" in self.controller.cache and
708
 
            self.controller.cache["mysql-server"].is_installed):
709
 
            # taken from the mysql-server-5.1.preinst
710
 
            ret = subprocess.call([
711
 
                    "egrep", "-q", "-i", "-r",
712
 
                    "^[^#]*ndb.connectstring|^[:space:]*\[[:space:]*ndb_mgmd", 
713
 
                    "/etc/mysql/"])
714
 
            logging.debug("egrep returned %s" % ret)
715
 
            # if clustering is used, do not upgrade to 5.1 and 
716
 
            # remove mysql-{server,client}
717
 
            # metapackage and upgrade the 5.0 packages
718
 
            if ret == 0:
719
 
                logging.debug("mysql clustering in use, do not upgrade to 5.1")
720
 
                for pkg in ("mysql-server", "mysql-client"):
721
 
                    self.controller.cache.mark_remove(pkg, "clustering in use")
722
 
                    # mark mysql-{server,client}-5.0 as manual install (#453513)
723
 
                    depcache = self.controller.cache._depcache
724
 
                    for pkg in ["mysql-server-5.0", "mysql-client-5.0"]:
725
 
                        if pkg.is_installed and depcache.is_auto_installed(pkg._pkg):
726
 
                            logging.debug("marking '%s' manual installed" % pkg.name)
727
 
                            autoInstDeps = False
728
 
                            fromUser = True
729
 
                            depcache.Mark_install(pkg._pkg, autoInstDeps, fromUser)
730
 
            else:
731
 
                self.controller.cache.mark_upgrade("mysql-server", "no clustering in use")
732
 
 
733
 
    def _checkArmCPU(self):
734
 
        """
735
 
        parse /proc/cpuinfo and search for ARMv6 or greater
736
 
        """
737
 
        logging.debug("checking for ARM CPU version")
738
 
        if not os.path.exists("/proc/cpuinfo"):
739
 
            logging.error("cannot open /proc/cpuinfo ?!?")
740
 
            return False
741
 
        cpuinfo = open("/proc/cpuinfo")
742
 
        if re.search("^Processor\s*:\s*ARMv[45]", cpuinfo.read(), re.MULTILINE):
743
 
            return False
744
 
        return True
745
 
 
746
 
    def _dealWithLanguageSupportTransition(self):
747
 
        """
748
 
        In karmic the language-support-translations-* metapackages
749
 
        are gone and the direct dependencies will get marked for
750
 
        auto removal - mark them as manual instead
751
 
        """
752
 
        logging.debug("language-support-translations-* transition")
753
 
        for pkg in self.controller.cache:
754
 
            depcache = self.controller.cache._depcache
755
 
            if (pkg.name.startswith("language-support-translations") and
756
 
                pkg.is_installed):
757
 
                for dp_or in pkg.installed.dependencies:
758
 
                    for dpname in dp_or.or_dependencies:
759
 
                        dp = self.controller.cache[dpname.name]
760
 
                        if dp.is_installed and depcache.is_auto_installed(dp._pkg):
761
 
                            logging.debug("marking '%s' manual installed" % dp.name)
762
 
                            autoInstDeps = False
763
 
                            fromUser = True
764
 
                            depcache.mark_install(dp._pkg, autoInstDeps, fromUser)
765
 
                            
766
 
    def _checkLanguageSupport(self):
767
 
        """
768
 
        check if the language support is fully installed and if
769
 
        not generate a update-notifier note on next login
770
 
        """
771
 
        if not os.path.exists("/usr/bin/check-language-support"):
772
 
            logging.debug("no check-language-support available")
773
 
            return
774
 
        p = subprocess.Popen(["check-language-support"],
775
 
                             stdout=subprocess.PIPE, universal_newlines=True)
776
 
        for pkgname in p.communicate()[0].split():
777
 
            if (pkgname in self.controller.cache and
778
 
                not self.controller.cache[pkgname].is_installed):
779
 
                logging.debug("language support package '%s' missing" % pkgname)
780
 
                # check if kde/gnome and copy language-selector note
781
 
                base = "/usr/share/language-support/"
782
 
                target = "/var/lib/update-notifier/user.d"
783
 
                for p in ("incomplete-language-support-gnome.note",
784
 
                          "incomplete-language-support-qt.note"):
785
 
                    if os.path.exists(os.path.join(base,p)):
786
 
                        shutil.copy(os.path.join(base,p), target)
787
 
                        return
788
 
 
789
 
    def _checkAndInstallBroadcom(self):
790
 
        """
791
 
        check for the 'wl' kernel module and install bcmwl-kernel-source
792
 
        if the module is loaded
793
 
        """
794
 
        logging.debug("checking for 'wl' module")
795
 
        if "wl" in lsmod():
796
 
            self.controller.cache.mark_install("bcmwl-kernel-source",
797
 
                                              "'wl' module found in lsmod")
798
 
 
799
 
    def _stopApparmor(self):
800
 
        """ /etc/init.d/apparmor stop (see bug #559433)"""
801
 
        if os.path.exists("/etc/init.d/apparmor"):
802
 
            logging.debug("/etc/init.d/apparmor stop")
803
 
            subprocess.call(["/etc/init.d/apparmor","stop"])
804
 
    def _stopDocvertConverter(self):
805
 
        " /etc/init.d/docvert-converter stop (see bug #450569)"
806
 
        if os.path.exists("/etc/init.d/docvert-converter"):
807
 
            logging.debug("/etc/init.d/docvert-converter stop")
808
 
            subprocess.call(["/etc/init.d/docvert-converter","stop"])
809
 
    def _killUpdateNotifier(self):
810
 
        "kill update-notifier"
811
 
        # kill update-notifier now to suppress reboot required
812
 
        if os.path.exists("/usr/bin/killall"):
813
 
            logging.debug("killing update-notifier")
814
 
            subprocess.call(["killall","-q","update-notifier"])
815
 
    def _killKBluetooth(self):
816
 
        """killall kblueplugd kbluetooth (riddel requested it)"""
817
 
        if os.path.exists("/usr/bin/killall"):
818
 
            logging.debug("killing kblueplugd kbluetooth4")
819
 
            subprocess.call(["killall", "-q", "kblueplugd", "kbluetooth4"])
820
 
    def _killScreensaver(self):
821
 
        """killall gnome-screensaver """
822
 
        if os.path.exists("/usr/bin/killall"):
823
 
            logging.debug("killing gnome-screensaver")
824
 
            subprocess.call(["killall", "-q", "gnome-screensaver"])
825
 
    def _pokeScreensaver(self):
826
 
        if os.path.exists("/usr/bin/xdg-screensaver") and os.environ.get('DISPLAY') :
827
 
            logging.debug("setup poke timer for the scrensaver")
828
 
            try:
829
 
                self._poke = subprocess.Popen(
830
 
                    "while true; do /usr/bin/xdg-screensaver reset >/dev/null 2>&1; sleep 30; done",
831
 
                    shell=True)
832
 
                atexit.register(self._stopPokeScreensaver)
833
 
            except:
834
 
                logging.exception("failed to setup screensaver poke")
835
 
    def _stopPokeScreensaver(self):
836
 
        if self._poke:
837
 
            res = False
838
 
            try:
839
 
                self._poke.terminate()
840
 
                res = self._poke.wait()
841
 
            except:
842
 
                logging.exception("failed to stop screensaver poke")
843
 
            self._poke = None
844
 
            return res
845
 
    def _removeBadMaintainerScripts(self):
846
 
        " remove bad/broken maintainer scripts (last resort) "
847
 
        # apache: workaround #95325 (edgy->feisty)
848
 
        # pango-libthai #103384 (edgy->feisty)
849
 
        bad_scripts = ["/var/lib/dpkg/info/apache2-common.prerm",
850
 
                       "/var/lib/dpkg/info/pango-libthai.postrm",
851
 
                       ]
852
 
        for ap in bad_scripts:
853
 
            if os.path.exists(ap):
854
 
                logging.debug("removing bad script '%s'" % ap)
855
 
                os.unlink(ap)
856
 
 
857
 
    def _createPycentralPkgRemove(self):
858
 
        """
859
 
        intrepid->jaunty, create /var/lib/pycentral/pkgremove flag file
860
 
        to help python-central so that it removes all preinst links
861
 
        on upgrade
862
 
        """
863
 
        logging.debug("adding pkgremove file")
864
 
        if not os.path.exists("/var/lib/pycentral/"):
865
 
            os.makedirs("/var/lib/pycentral")
866
 
        open("/var/lib/pycentral/pkgremove","w")
867
 
 
868
 
    def _removeOldApportCrashes(self):
869
 
        " remove old apport crash files "
870
 
        try:
871
 
            for f in glob.glob("/var/crash/*.crash"):
872
 
                logging.debug("removing old crash file '%s'" % f)
873
 
                os.unlink(f)
874
 
        except Exception as e:
875
 
            logging.warning("error during unlink of old crash files (%s)" % e)
876
 
 
877
 
    def _cpuHasSSESupport(self, cpuinfo="/proc/cpuinfo"):
878
 
        " helper that checks if the given cpu has sse support "
879
 
        if not os.path.exists(cpuinfo):
880
 
            return False
881
 
        for line in open(cpuinfo):
882
 
            if line.startswith("flags") and not " sse" in line:
883
 
                return False
884
 
        return True
885
 
 
886
 
    def _usesEvmsInMounts(self):
887
 
        " check if evms is used in /proc/mounts "
888
 
        logging.debug("running _usesEvmsInMounts")
889
 
        for line in open("/proc/mounts"):
890
 
            line = line.strip()
891
 
            if line == '' or line.startswith("#"):
892
 
                continue
893
 
            try:
894
 
                (device, mount_point, fstype, options, a, b) = line.split()
895
 
            except Exception:
896
 
                logging.error("can't parse line '%s'" % line)
897
 
                continue
898
 
            if "evms" in device:
899
 
                logging.debug("found evms device in line '%s', skipping " % line)
900
 
                return True
901
 
        return False
902
 
 
903
 
    def _checkAndRemoveEvms(self):
904
 
        " check if evms is in use and if not, remove it "
905
 
        logging.debug("running _checkAndRemoveEvms")
906
 
        if self._usesEvmsInMounts():
907
 
            return False
908
 
        # if not in use, nuke it
909
 
        for pkg in ["evms","libevms-2.5","libevms-dev",
910
 
                    "evms-ncurses", "evms-ha",
911
 
                    "evms-bootdebug",
912
 
                    "evms-gui", "evms-cli",
913
 
                    "linux-patch-evms"]:
914
 
            if pkg in self.controller.cache and self.controller.cache[pkg].is_installed:
915
 
                self.controller.cache[pkg].mark_delete()
916
 
        return True
917
 
 
918
 
    def _addRelatimeToFstab(self):
919
 
        " add the relatime option to ext2/ext3 filesystems on upgrade "
920
 
        logging.debug("_addRelatime")
921
 
        replaced = False
922
 
        lines = []
923
 
        for line in open("/etc/fstab"):
924
 
            line = line.strip()
925
 
            if line == '' or line.startswith("#"):
926
 
                lines.append(line)
927
 
                continue
928
 
            try:
929
 
                (device, mount_point, fstype, options, a, b) = line.split()
930
 
            except Exception:
931
 
                logging.error("can't parse line '%s'" % line)
932
 
                lines.append(line)
933
 
                continue
934
 
            if (("ext2" in fstype or
935
 
                 "ext3" in fstype) and 
936
 
                (not "noatime" in options) and
937
 
                (not "relatime" in options) ):
938
 
                logging.debug("adding 'relatime' to line '%s' " % line)
939
 
                line = line.replace(options,"%s,relatime" % options)
940
 
                logging.debug("replaced line is '%s' " % line)
941
 
                replaced=True
942
 
            lines.append(line)
943
 
        # we have converted a line
944
 
        if replaced:
945
 
            logging.debug("writing new /etc/fstab")
946
 
            f=open("/etc/fstab.intrepid","w")
947
 
            f.write("\n".join(lines))
948
 
            # add final newline (see LP: #279093)
949
 
            f.write("\n")
950
 
            f.close()
951
 
            os.rename("/etc/fstab.intrepid","/etc/fstab")
952
 
        return True
953
 
        
954
 
    def _ntfsFstabFixup(self, fstab="/etc/fstab"):
955
 
        """change PASS 1 to 0 for ntfs entries (#441242)"""
956
 
        logging.debug("_ntfsFstabFixup")
957
 
        replaced = False
958
 
        lines = []
959
 
        for line in open(fstab):
960
 
            line = line.strip()
961
 
            if line == '' or line.startswith("#"):
962
 
                lines.append(line)
963
 
                continue
964
 
            try:
965
 
                (device, mount_point, fstype, options, fdump, fpass) = line.split()
966
 
            except Exception:
967
 
                logging.error("can't parse line '%s'" % line)
968
 
                lines.append(line)
969
 
                continue
970
 
            if ("ntfs" in fstype and fpass == "1"):
971
 
                logging.debug("changing PASS for ntfs to 0 for '%s' " % line)
972
 
                if line[-1] == "1":
973
 
                    line = line[:-1] + "0"
974
 
                else:
975
 
                    logging.error("unexpected value in line")
976
 
                logging.debug("replaced line is '%s' " % line)
977
 
                replaced=True
978
 
            lines.append(line)
979
 
        # we have converted a line
980
 
        if replaced:
981
 
            suffix = ".jaunty"
982
 
            logging.debug("writing new /etc/fstab")
983
 
            f=open(fstab + suffix, "w")
984
 
            f.write("\n".join(lines))
985
 
            # add final newline (see LP: #279093)
986
 
            f.write("\n")
987
 
            f.close()
988
 
            os.rename(fstab+suffix, fstab)
989
 
        return True
990
 
        
991
 
 
992
 
    def _rewriteFstab(self):
993
 
        " convert /dev/{hd?,scd0} to /dev/cdrom for the feisty upgrade "
994
 
        logging.debug("_rewriteFstab()")
995
 
        replaced = 0
996
 
        lines = []
997
 
        # we have one cdrom to convert
998
 
        for line in open("/etc/fstab"):
999
 
            line = line.strip()
1000
 
            if line == '' or line.startswith("#"):
1001
 
                lines.append(line)
1002
 
                continue
1003
 
            try:
1004
 
                (device, mount_point, fstype, options, a, b) = line.split()
1005
 
            except Exception:
1006
 
                logging.error("can't parse line '%s'" % line)
1007
 
                lines.append(line)
1008
 
                continue
1009
 
            # edgy kernel has /dev/cdrom -> /dev/hd?
1010
 
            # feisty kernel (for a lot of chipsets) /dev/cdrom -> /dev/scd0
1011
 
            # this breaks static mounting (LP#86424)
1012
 
            #
1013
 
            # we convert here to /dev/cdrom only if current /dev/cdrom
1014
 
            # points to the device in /etc/fstab already. this ensures
1015
 
            # that we don't break anything or that we get it wrong
1016
 
            # for systems with two (or more) cdroms. this is ok, because
1017
 
            # we convert under the old kernel
1018
 
            if ("iso9660" in fstype and
1019
 
                device != "/dev/cdrom" and
1020
 
                os.path.exists("/dev/cdrom") and
1021
 
                os.path.realpath("/dev/cdrom") == device
1022
 
                ):
1023
 
                logging.debug("replacing '%s' " % line)
1024
 
                line = line.replace(device,"/dev/cdrom")
1025
 
                logging.debug("replaced line is '%s' " % line)
1026
 
                replaced += 1
1027
 
            lines.append(line)
1028
 
        # we have converted a line (otherwise we would have exited already)
1029
 
        if replaced > 0:
1030
 
            logging.debug("writing new /etc/fstab")
1031
 
            shutil.copy("/etc/fstab","/etc/fstab.edgy")
1032
 
            f=open("/etc/fstab","w")
1033
 
            f.write("\n".join(lines))
1034
 
            # add final newline (see LP: #279093)
1035
 
            f.write("\n")
1036
 
            f.close()
1037
 
        return True
1038
 
 
1039
 
    def _checkAdminGroup(self):
1040
 
        " check if the current sudo user is in the admin group "
1041
 
        logging.debug("_checkAdminGroup")
1042
 
        import grp
1043
 
        try:
1044
 
            admin_group = grp.getgrnam("admin").gr_mem
1045
 
        except KeyError as e:
1046
 
            logging.warning("System has no admin group (%s)" % e)
1047
 
            subprocess.call(["addgroup","--system","admin"])
1048
 
        # double paranoia
1049
 
        try:
1050
 
            admin_group = grp.getgrnam("admin").gr_mem
1051
 
        except KeyError as e:
1052
 
            logging.warning("adding the admin group failed (%s)" % e)
1053
 
            return
1054
 
        # if the current SUDO_USER is not in the admin group
1055
 
        # we add him - this is no security issue because
1056
 
        # the user is already root so adding him to the admin group
1057
 
        # does not change anything
1058
 
        if ("SUDO_USER" in os.environ and
1059
 
            not os.environ["SUDO_USER"] in admin_group):
1060
 
            admin_user = os.environ["SUDO_USER"]
1061
 
            logging.info("SUDO_USER=%s is not in admin group" % admin_user)
1062
 
            cmd = ["usermod","-a","-G","admin",admin_user]
1063
 
            res = subprocess.call(cmd)
1064
 
            logging.debug("cmd: %s returned %i" % (cmd, res))
1065
 
 
1066
 
    def _checkVideoDriver(self, name):
1067
 
        " check if the given driver is in use in xorg.conf "
1068
 
        XORG="/etc/X11/xorg.conf"
1069
 
        if not os.path.exists(XORG):
1070
 
            return False
1071
 
        for line in open(XORG):
1072
 
            s=line.split("#")[0].strip()
1073
 
            # check for fglrx driver entry
1074
 
            if (s.lower().startswith("driver") and
1075
 
                s.endswith('"%s"' % name)):
1076
 
                return True
1077
 
        return False
1078
 
 
1079
 
    def _applyPatches(self, patchdir="./patches"):
1080
 
        """
1081
 
        helper that applies the patches in patchdir. the format is
1082
 
        _path_to_file.md5sum
1083
 
        
1084
 
        and it will apply the diff to that file if the md5sum
1085
 
        matches
1086
 
        """
1087
 
        if not os.path.exists(patchdir):
1088
 
            logging.debug("no patchdir")
1089
 
            return
1090
 
        for f in os.listdir(patchdir):
1091
 
            # skip, not a patch file, they all end with .$md5sum
1092
 
            if not "." in f:
1093
 
                logging.debug("skipping '%s' (no '.')" % f)
1094
 
                continue
1095
 
            logging.debug("check if patch '%s' needs to be applied" % f)
1096
 
            (encoded_path, md5sum, result_md5sum) = f.rsplit(".", 2)
1097
 
            # FIXME: this is not clever and needs quoting support for
1098
 
            #        filenames with "_" in the name
1099
 
            path = encoded_path.replace("_","/")
1100
 
            logging.debug("target for '%s' is '%s' -> '%s'" % (
1101
 
                    f, encoded_path, path))
1102
 
            # target does not exist
1103
 
            if not os.path.exists(path):
1104
 
                logging.debug("target '%s' does not exist" % path)
1105
 
                continue
1106
 
            # check the input md5sum, this is not strictly needed as patch()
1107
 
            # will verify the result md5sum and discard the result if that
1108
 
            # does not match but this will remove a misleading error in the 
1109
 
            # logs
1110
 
            md5 = hashlib.md5()
1111
 
            with open(path, "rb") as fd:
1112
 
                md5.update(fd.read())
1113
 
            if md5.hexdigest() == result_md5sum:
1114
 
                logging.debug("already at target hash, skipping '%s'" % path)
1115
 
                continue
1116
 
            elif md5.hexdigest() != md5sum:
1117
 
                logging.warn("unexpected target md5sum, skipping: '%s'" % path)
1118
 
                continue
1119
 
            # patchable, do it
1120
 
            from .DistUpgradePatcher import patch
1121
 
            try:
1122
 
                patch(path, os.path.join(patchdir, f), result_md5sum)
1123
 
                logging.info("applied '%s' successfully" % f)
1124
 
            except Exception:
1125
 
                logging.exception("ed failed for '%s'" % f)
1126
 
                    
1127
 
    def _supportInModaliases(self, pkgname, lspci=None):
1128
 
        """ 
1129
 
        Check if pkgname will work on this hardware
1130
 
 
1131
 
        This helper will check with the modaliasesdir if the given 
1132
 
        pkg will work on this hardware (or the hardware given
1133
 
        via the lspci argument)
1134
 
        """
1135
 
        # get lspci info (if needed)
1136
 
        if not lspci:
1137
 
            lspci = self._get_pci_ids()
1138
 
        # get pkg
1139
 
        if (not pkgname in self.controller.cache or
1140
 
            not self.controller.cache[pkgname].candidate):
1141
 
            logging.warn("can not find '%s' in cache")
1142
 
            return False
1143
 
        pkg = self.controller.cache[pkgname]
1144
 
        for (module, pciid_list) in self._parse_modaliases_from_pkg_header(pkg.candidate.record):
1145
 
            for pciid in pciid_list:
1146
 
                m = re.match("pci:v0000(.+)d0000(.+)sv.*", pciid)
1147
 
                if m:
1148
 
                    matchid = "%s:%s" % (m.group(1), m.group(2))
1149
 
                    if matchid.lower() in lspci:
1150
 
                        logging.debug("found system pciid '%s' in modaliases" % matchid)
1151
 
                        return True
1152
 
        logging.debug("checking for %s support in modaliases but none found" % pkgname)
1153
 
        return False
1154
 
                    
1155
 
    def _parse_modaliases_from_pkg_header(self, pkgrecord):
1156
 
        """ return a list of (module1, (pciid, ...), (module2, (pciid, ...)))"""
1157
 
        if not "Modaliases" in pkgrecord:
1158
 
            return []
1159
 
        # split the string
1160
 
        modules = []
1161
 
        for m in pkgrecord["Modaliases"].split(")"):
1162
 
            m = m.strip(", ")
1163
 
            if not m:
1164
 
                continue
1165
 
            (module, pciids) = m.split("(")
1166
 
            modules.append ((module, [x.strip() for x in pciids.split(",")]))
1167
 
        return modules
1168
 
 
1169
 
    def _kernel386TransitionCheck(self):
1170
 
        """ test if the current kernel is 386 and if the system is 
1171
 
            capable of using a generic one instead (#353534)
1172
 
        """
1173
 
        logging.debug("_kernel386TransitionCheck")
1174
 
        # we test first if one of 386 is installed
1175
 
        # if so, check if the system could also work with -generic
1176
 
        # (we get that from base-installer) and try to installed
1177
 
        #  that)
1178
 
        for pkgname in ["linux-386", "linux-image-386"]:
1179
 
            if (pkgname in self.controller.cache and
1180
 
                self.controller.cache[pkgname].is_installed):
1181
 
                working_kernels = self.controller.cache.getKernelsFromBaseInstaller()
1182
 
                upgrade_to = ["linux-generic", "linux-image-generic"]
1183
 
                for pkgname in upgrade_to:
1184
 
                    if pkgname in working_kernels:
1185
 
                        logging.debug("386 kernel installed, but generic kernel  will work on this machine")
1186
 
                        if self.controller.cache.mark_install(pkgname, "386 -> generic transition"):
1187
 
                            return
1188
 
        
1189
 
 
1190
 
    def _add_extras_repository(self):
1191
 
        logging.debug("_add_extras_repository")
1192
 
        cache = self.controller.cache
1193
 
        if not "ubuntu-extras-keyring" in cache:
1194
 
            logging.debug("no ubuntu-extras-keyring, no need to add repo")
1195
 
            return
1196
 
        if not (cache["ubuntu-extras-keyring"].marked_install or
1197
 
                cache["ubuntu-extras-keyring"].installed):
1198
 
            logging.debug("ubuntu-extras-keyring not installed/marked_install")
1199
 
            return
1200
 
        try:
1201
 
            import aptsources.sourceslist
1202
 
            sources = aptsources.sourceslist.SourcesList()
1203
 
            for entry in sources:
1204
 
                if "extras.ubuntu.com" in entry.uri:
1205
 
                    logging.debug("found extras.ubuntu.com, no need to add it")
1206
 
                    break
1207
 
            else:
1208
 
                logging.info("no extras.ubuntu.com, adding it to sources.list")
1209
 
                sources.add("deb","http://extras.ubuntu.com/ubuntu",
1210
 
                            self.controller.toDist, ["main"],
1211
 
                            "Third party developers repository")
1212
 
                sources.save()
1213
 
        except:
1214
 
            logging.exception("error adding extras.ubuntu.com")
1215
 
 
1216
 
    def _gutenprint_fixup(self):
1217
 
        """ foomatic-db-gutenprint get removed during the upgrade,
1218
 
            replace it with the compressed ijsgutenprint-ppds
1219
 
            (context is foomatic-db vs foomatic-db-compressed-ppds)
1220
 
        """
1221
 
        try:
1222
 
            cache = self.controller.cache
1223
 
            if ("foomatic-db-gutenprint" in cache and
1224
 
                cache["foomatic-db-gutenprint"].marked_delete and
1225
 
                "ijsgutenprint-ppds" in cache):
1226
 
                logging.info("installing ijsgutenprint-ppds")
1227
 
                cache.mark_install(
1228
 
                    "ijsgutenprint-ppds",
1229
 
                    "foomatic-db-gutenprint -> ijsgutenprint-ppds rule")
1230
 
        except:
1231
 
            logging.exception("_gutenprint_fixup failed")
1232
 
 
1233
 
    def _enable_multiarch(self, foreign_arch="i386"):
1234
 
        """ enable multiarch via /etc/dpkg/dpkg.cfg.d/multiarch """
1235
 
        cfg = "/etc/dpkg/dpkg.cfg.d/multiarch"
1236
 
        if not os.path.exists(cfg):
1237
 
            try:
1238
 
                os.makedirs("/etc/dpkg/dpkg.cfg.d/")
1239
 
            except OSError:
1240
 
                pass
1241
 
            open(cfg, "w").write("foreign-architecture %s\n" % foreign_arch)
1242
 
 
1243
 
    def _add_kdegames_card_extra_if_installed(self):
1244
 
        """ test if kdegames-card-data is installed and if so,
1245
 
            add kdegames-card-data-extra so that users do not 
1246
 
            loose functionality (LP: #745396)
1247
 
        """
1248
 
        try:
1249
 
            cache = self.controller.cache
1250
 
            if not ("kdegames-card-data" in cache or
1251
 
                    "kdegames-card-data-extra" in cache):
1252
 
                return
1253
 
            if (cache["kdegames-card-data"].is_installed or
1254
 
                cache["kdegames-card-data"].marked_install):
1255
 
                cache.mark_install(
1256
 
                    "kdegames-card-data-extra",
1257
 
                    "kdegames-card-data -> k-c-d-extra transition")
1258
 
        except:
1259
 
            logging.exception("_add_kdegames_card_extra_if_installed failed")
1260
 
        
1261
 
    def ensure_recommends_are_installed_on_desktops(self):
1262
 
        """ ensure that on a desktop install recommends are installed 
1263
 
            (LP: #759262)
1264
 
        """
1265
 
        import apt
1266
 
        if not self.controller.serverMode:
1267
 
            if not apt.apt_pkg.config.find_b("Apt::Install-Recommends"):
1268
 
                logging.warn("Apt::Install-Recommends was disabled, enabling it just for the upgrade")
1269
 
                apt.apt_pkg.config.set("Apt::Install-Recommends", "1")
1270