Impossible de faire la mise à jour de la version sur Ubuntu 14.04

27

J'essaie actuellement de mettre à niveau une boîte ubuntu 14.04 vers xenial. J'essaie de faire une mise à jour de version, et son échec avec des erreurs comme UnicodeDecodeError: le codec 'utf-8' ne peut pas décoder l'octet 0x96 en position 382: octet de démarrage invalide

Cela ressemble à un bogue connu - j'ai essayé et je n'ai pas eu de chance de trouver le paquet incriminé, et j'ai désactivé / supprimé mes 2 fichiers package.lst non standard pour les référentiels nodeource et veeam.

Le traceback lit quelque chose comme ça

Traceback (most recent call last):
  File "/tmp/ubuntu-release-upgrader-woadaq_z/xenial", line 8, in <module>
    sys.exit(main())
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeMain.py", line 242, in main
    if app.run():
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeController.py", line 1876, in run
    return self.fullUpgrade()
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeController.py", line 1757, in fullUpgrade
    if not self.doPostInitialUpdate():
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeController.py", line 943, in doPostInitialUpdate
    self.tasks = self.cache.installedTasks
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeCache.py", line 806, in installedTasks
    for line in pkg._pcache._records.record.split("\n"):
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x96 in position 382: invalid start byte
Error in sys.excepthook:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/problem_report.py", line 416, in add_to_existing
    self.write(f)
  File "/usr/lib/python3/dist-packages/problem_report.py", line 369, in write
    block = f.read(1048576)
  File "/usr/lib/python3.4/codecs.py", line 319, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte

Original exception was:
Traceback (most recent call last):
  File "/tmp/ubuntu-release-upgrader-woadaq_z/xenial", line 8, in <module>
    sys.exit(main())
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeMain.py", line 242, in main
    if app.run():
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeController.py", line 1876, in run
    return self.fullUpgrade()
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeController.py", line 1757, in fullUpgrade
    if not self.doPostInitialUpdate():
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeController.py", line 943, in doPostInitialUpdate
    self.tasks = self.cache.installedTasks
  File "/tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeCache.py", line 806, in installedTasks
    for line in pkg._pcache._records.record.split("\n"):
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x96 in position 382: invalid start byte
=== Command terminated with exit status 1 (Mon Apr  3 09:31:21 2017) ===

Et il n'y a rien de vraiment utile dans les journaux. Comment puis-je faire fonctionner la mise à jour do-release?

Compagnon Geek
la source

Réponses:

44

Ce que vous avez là-bas est le script de mise à niveau lui-même qui se déclenche quelque part sur les données invalides. Vous devez rechercher et supprimer les données non valides.

Dans ce cas, c'était le paquet veeamsnap. La suppression de ce package devrait le corriger. Mais comme cela est différent pour chaque cas, je décrirai les mesures prises pour parvenir à cette conclusion. C'est un processus assez compliqué.

C'est amusant, car les chaînes python3 devraient toutes être en UTF-8. Ce que vous avez ici (découvert après coup) est un module C ( apt_pkg) insérant en quelque sorte des données non UTF-8 dans une chaîne python3, interrompant ainsi chaque tentative de lecture de la chaîne - remarquez comment le gestionnaire d'erreur lui-même a également levé une exception?

Dans le débogueur inconnu, nous allons!

La meilleure façon de diagnostiquer des problèmes comme celui-ci est de provoquer une pause du débogueur avant la ligne qui échoue. Avec Python, lorsque vous avez une série d'appels imbriqués comme celui-ci, la façon la plus simple d'ajouter une pause de débogage est de modifier le fichier lui-même.

  1. En utilisant votre exemple, nous pouvons voir que l'échec en question se trouve dans la /tmp/ubuntu-release-upgrader-woadaq_z/DistUpgrade/DistUpgradeCache.pyligne de fichier 806, alors lançons un éditeur de texte et allons sur cette ligne. Le chemin temporaire sera différent pour chaque exécution, alors assurez-vous d'utiliser celui de votre sortie d'erreur!

    capture d'écran de l'éditeur

  2. À partir d'ici, nous pouvons d'abord ajouter une pause simple dans le débogueur , en insérant import pdb; pdb.set_trace();à la ligne 806 juste avant l'erreur. Parce que c'est Python, l'indentation est importante!

    capture d'écran de la déclaration de débogage

  3. Nous devons maintenant exécuter le programme modifié. Ne courez do-release-upgradeplus; qui va probablement en télécharger un nouveau. Voir dans les journaux d'erreurs, la première ligne après "l'exception d'origine était"? Celui avec qui /tmp/ubuntu-release-upgrader-woadaq_z/xenial? C'est celui que vous souhaitez exécuter. Exécutez donc ce fichier en tant que root (ou sudo).

    L'exécution qui devrait vous mettre dans le débogueur (pdb):

    capture d'écran du débogueur

  4. À partir d'ici, nous déterminons le nombre total de packages. Le moyen le plus simple est de courir sum(1 for _ in self). Attendez un peu (cela peut prendre un certain temps) et il imprimera un numéro. Dans ce cas, ça l'était 76028.

    Maintenant, puisque l'erreur ne se produit probablement pas dans les premiers, et nous ne voulons pas parcourir manuellement> 75000 packages, et nous ne pouvons pas ajouter de gestionnaire d'exceptions (car l'erreur est si grave qu'elle casse Python lui-même) , nous avons besoin d'une alternative.

  5. Supprimez la ligne ajoutée à l'étape 4. Modifiez le code pour imprimer un nombre incrémentiel pour chaque package. Par exemple, ajoutez foo = 0au-dessus de la boucle sur la ligne 802 et foo += 1; print(foo)sur la ligne 807 (juste avant la ligne en erreur).

    capture d'écran du code d'impression du numéro

  6. Exécutez à nouveau le code en utilisant la même commande qu'à l'étape 3. Il imprimera une grande liste de nombres. Laissez-le fonctionner jusqu'à ce qu'il réimprime l'erreur. Vous devrez peut-être agrandir votre fenêtre:

    capture d'écran de la sortie du numéro

    Ce dernier numéro devrait être le paquet sur lequel il s'est écrasé. Notez ce numéro.

  7. Maintenant que vous savez quel package / numéro provoque le crash, il est temps d'ajouter la pause du débogueur avec une condition à exécuter uniquement sur ce package. Par exemple, si vous plantez sur le package 72285, ajoutez if foo == 72285: import pdb; pdb.set_trace()juste après la ligne qui s'imprime foo:

    capture d'écran de la nouvelle pause pdb

  8. Exécutez à nouveau le code. Maintenant, quand vous y entrez, pdbil devrait être sur le package qui provoque le crash. Vous pouvez taper le nom de la variable pkgpour imprimer sa valeur, qui vous indiquera le nom du package actuel:

    capture d'écran du nom du package

    Plus généralement, la saisie du nom d'une variable imprimera sa sortie.

  9. Supprimez le package incriminé et réessayez la mise à niveau (à partir d'une nouvelle version de do-release-upgrade).

Bob
la source
7
Il s'agit d'une introduction très agréable et très douce à gdb, qui peut être utilisée avec différents niveaux de compétence par n'importe quel utilisateur. +1 de moi et bravo. Et, BTW, vous pouvez simplement ajouter que taper pkg dans le débogueur affichera la valeur de la variable du même nom, comme défini sur la ligne 803. En d'autres termes, pkg n'est pas une instruction de débogueur. À votre santé.
MariusMatutiae
@MariusMatutiae Edited. Et c'est pdb;) (En fait, c'était plus destiné à résoudre cette classe de problèmes, mais c'est bien que vous trouviez facile à suivre comme introduction générale.)
Bob
Pour résoudre ce problème en particulier, ne serait-il pas plus facile d'ajouter simplement une ligne au script qui imprime tout ce que le message de débogage veut imprimer pour un enregistrement de package qui n'existe pas? (Il y a ce message logging.debug juste au-dessus) Ou cela suppose-t-il que la variable pkg pourrait ne pas être imprimée du tout en raison du bogue, et que le débogueur python peut imprimer quoi que ce soit?
CausingUnderflowsEverywhere
Si nous avons toujours le blog Super User, ce serait un excellent ajout!
Canadian Luke REINSTATE MONICA du
@CausingUnderflowsEverywhere En théorie, oui. En pratique, une suggestion similaire du rapport de bogue lié n'a apparemment pas fonctionné (je ne sais pas pourquoi, juste d'après ce que OP m'a dit) et j'ai fini par le faire de manière interactive au cas où quelque chose d'autre aurait déclenché le crash - par exemple, n'a pas sachez que dans ce cas, c'était la recordpropriété elle-même qui ne pouvait pas être lue.
Bob