Expérience de migration de mailman2 à mailman3

#1

Bonjour,

Je rend compte ici de mon expérience de migration de mailman2 à mailman3. En partant d’une nouvelle installation de mailman2 (en trichant avec un container docker) avec quelques données pour faire semblant.

En supposant un reverse proxy SSL vers le port :8080 sur chat.the.re, on peut faire:

  • mkdir -p archives/private
  • sudo chown -R list:list archives
  • docker run --rm -e URL_PATTERN=https -v $(pwd)/lists:/var/lib/mailman/lists -v $(pwd)/archives:/var/lib/mailman/archives -p 8080:80 --name mailman --add-host chat.the.re:127.0.0.1 --hostname chat.the.re -e URL_HOST=chat.the.re -e EMAIL_HOST=chat.the.re -e LIST_ADMIN=loic@dachary.org
    -e MASTER_PASSWORD=“example” d3fk/mailman2

Puis créer une liste list1 via l’interface web et y envoyer un courriel avec:

  • docker exec -ti mailman bash
  • ( echo ‘Subject: test’ ; echo ; echo ‘content’ ) | sendmail -v -F ‘Loic Dachary’ -f loic@dachary.org list1@chat.the.re

Et ensuite modérer le courriel puis aller voir sur https://chat.the.re/lists/private/list1 après quelques minutes pour vérifier qu’il est bien arrivé et archivé.

Pour installer mailman3 j’installe les paquets debian. J’ai été voir les instructions du wiki qui ne contiennent pas d’information utile.

  • Ne pas suivre les instructions de /usr/share/doc/mailman3/README.Debian pour postfix (ça casse postfix parce que /var/lib/mailman3/data/postfix_lmtp n’existe pas).
  • Suivre les instructions de /usr/share/doc/mailman3-web/README.Debian.gz
    • sudo mkdir /var/log/nginx/mailman3 ; sudo chown www-data /var/log/nginx/mailman3
    • sudo django-admin createsuperuser --pythonpath /usr/share/mailman3-web --settings settings --username admin --email loic@dachary.org # password 5SwycsEucmiac
    • /etc/mailman3/nginx.conf pour changer :80 en :8000
    • ln -s /etc/mailman3/nginx.conf /etc/nginx/sites-enabled
  • Si l’installation ne se fait pas bien (pourquoi je ne sais pas mais c’est arrivé une fois) Il faut copier admin_pass de /etc/mailman3/mailman.cfg dans MAILMAN_REST_API_PASS (vide de base) /etc/mailman3/mailman-web.py pour que https://example.com/postorius/lists/ fonctionne sinon c’est erreur 500
  • Changer la valeur de EMAILNAME dans /etc/mailman3/mailman-web.py
  • sudo systemctl restart mailman3-web

A suivre

2 Likes
Expériences vécues de migration de mailman vers discourse?
#2

Je tente l’import d’une liste qui vient de mailman 2.1, après l’avoir créée comme recommandée (via l’interface web).

# mailman import21 spip-dev@chat.the.re /tmp/config.pck 
Traceback (most recent call last):
  File "/usr/bin/mailman", line 11, in <module>
    load_entry_point('mailman==3.2.1', 'console_scripts', 'mailman')()
  File "/usr/lib/python3/dist-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python3/dist-packages/mailman/bin/mailman.py", line 69, in invoke
    return super().invoke(ctx)
  File "/usr/lib/python3/dist-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python3/dist-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python3/dist-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/usr/lib/python3/dist-packages/mailman/commands/cli_import.py", line 64, in import21
    pickle_file, encoding='utf-8', errors='ignore')
ModuleNotFoundError: No module named 'Mailman'

On dirait bien que ça correspond à un problème discuté en juillet 2019 sur la liste mailman et corrigé depuis. Mais apparament pas dans la version 3.2.1 qui est installée par défaut dans buster. Il n’y a pas un mouvement important sur la page du package et il a été retiré de testing en mai 2020, donc installer un package depuis testing ne va pas aider. La version 3.2.2 est packagée dans sid mais … le patch n’est disponible qu’à partir de la 3.3.0, c’est ballot. Du coup je vais installer mailman dans un venv pour avoir les versions les plus récentes du script d’import.

#3

Installation de mailman3 dans un virtualenv à partir des sources

# git clone https://gitlab.com/mailman/mailman
# apt-get install python3-dev python3-venv
# python3 -m venv venv
# cd mailman
# source ../venv/bin/activate
# pip install wheel
# pip install -e .

Mais ça révèle une autre erreur:

# mailman import21 spip-dev@chat.the.re /tmp/config.pck
Usage: mailman import21 [OPTIONS] LISTSPEC PICKLE_FILE
Try 'mailman import21 -h' for help.

Error: Not a Mailman 2.1 configuration file: <_io.BufferedReader name='/tmp/config.pck'>

En extrayant le message d’erreur

# git diff
diff --git a/src/mailman/commands/cli_import.py b/src/mailman/commands/cli_import.py
index 2d694a325..f82c9272f 100644
--- a/src/mailman/commands/cli_import.py
+++ b/src/mailman/commands/cli_import.py
@@ -71,9 +71,9 @@ def import21(ctx, listspec, pickle_file):
                     pickle_file, encoding='utf-8', errors='ignore')
             except EOFError:
                 break
-            except pickle.UnpicklingError:
+            except pickle.UnpicklingError as e:
                 ctx.fail(
-                    _('Not a Mailman 2.1 configuration file: $pickle_file'))
+                    _(f'Not a Mailman 2.1 configuration file: $pickle_file {e}'))
             else:
                 if not isinstance(config_dict, dict):
                     print(_('Ignoring non-dictionary: {0!r}').format(

on obtient

#  mailman import21 spip-dev@chat.the.re /tmp/config.pck 
Usage: mailman import21 [OPTIONS] LISTSPEC PICKLE_FILE
Try 'mailman import21 -h' for help.

Error: Not a Mailman 2.1 configuration file: <_io.BufferedReader name='/tmp/config.pck'> pickle data was truncated

Et en effet… :man_facepalming: je n’avais pas copié le bon fichier. Du coup ça marche mieux avec le bon fichier.

# mailman import21 spip-dev@chat.the.re /tmp/config.pck                      
Importing members     [####################################]  100%                                                           
Importing defers      [####################################]  100%
Importing rejects     [####################################]  100%
Importing discards    [####################################]  100%
#4

Pour l’import des mbox il y a des instructions qui ne fonctionnent pas:

# python manage.py hyperkitty_import -l foo-list@example.com $var_prefix/archives/private/foo-list.mbox/foo-list.mbox
python: can't open file 'manage.py': [Errno 2] No such file or directory

et les instructions de archlinux non plus parce que les choses ne sont pas installées au même endroit sur debian.

# django-admin hyperkitty_import --pythonpath /usr/share/webapps/hyperkitty --settings settings -l ADDRESS mbox_file 
Traceback (most recent call last):
  File "/usr/bin/django-admin", line 21, in <module>
    management.execute_from_command_line()
  File "/usr/lib/python3/dist-packages/django/core/management/__init__.py", line 364, in execute_from_command_line
    utility.execute()
  File "/usr/lib/python3/dist-packages/django/core/management/__init__.py", line 308, in execute
    settings.INSTALLED_APPS
  File "/usr/lib/python3/dist-packages/django/conf/__init__.py", line 56, in __getattr__
    self._setup(name)
  File "/usr/lib/python3/dist-packages/django/conf/__init__.py", line 41, in _setup
    self._wrapped = Settings(settings_module)
  File "/usr/lib/python3/dist-packages/django/conf/__init__.py", line 110, in __init__
    mod = importlib.import_module(self.SETTINGS_MODULE)
  File "/usr/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 965, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'settings'

Une personne qui connait bien django va s’en sortir parce que le module est bien la:

# dpkg -L python3-django-hyperkitty | grep import
/usr/lib/python3/dist-packages/hyperkitty/management/commands/hyperkitty_import.py
/usr/lib/python3/dist-packages/hyperkitty/tests/commands/test_import.py

et qu’il faut juste savoir comment appeler django pour qu’il le trouve.

#5

Après avoir cherché en vain sur le net, je me suis décidé à regarder comment mailman3-web est lancé. Parce qu’il doit bien avoir ce qu’il faut pour importer les modules, vu qu’il marche. Et en regardant dans /etc/mailman3/uwsgi.ini je vois qu’il tourne dans le dossier /usr/share/mailman3-web et que si on se déplace dedans, soudain c’est possible de faire python manage.py hyperkitty_import

#  python3 manage.py hyperkitty_import --help
usage: manage.py hyperkitty_import [-h] [--version] [-v {0,1,2,3}]
                                   [--settings SETTINGS]
                                   [--pythonpath PYTHONPATH] [--traceback]
                                   [--no-color] [--delete] [-l LIST_ADDRESS]
                                   [--no-sync-mailman] [--since SINCE]
                                   [--ignore-mtime]
                                   mbox [mbox ...]

Imports the specified mailbox archive

positional arguments:
  mbox

optional arguments:
  -h, --help            show this help message and exit
  --version             show program's version number and exit
  -v {0,1,2,3}, --verbosity {0,1,2,3}
                        Verbosity level; 0=minimal output, 1=normal output,
                        2=verbose output, 3=very verbose output
  --settings SETTINGS   The Python path to a settings module, e.g.
                        "myproject.settings.main". If this isn't provided, the
                        DJANGO_SETTINGS_MODULE environment variable will be
                        used.
  --pythonpath PYTHONPATH
                        A directory to add to the Python path, e.g.
                        "/home/djangoprojects/myproject".
  --traceback           Raise on CommandError exceptions
  --no-color            Don't colorize the command output.
  --delete              Delete poll instead of closing it
  -l LIST_ADDRESS, --list-address LIST_ADDRESS
                        the full list address the mailbox will be imported to
  --no-sync-mailman     do not sync properties with Mailman (faster, useful
                        for batch imports)
  --since SINCE         only import emails later than this date. Defaults to
                        the date of the newest message in the existing archive
                        if any.
  --ignore-mtime        do not check mbox mtimes (slower)
#6
# cd /usr/share/mailman3-web
# time python3 manage.py hyperkitty_import --list-address spip-dev@chat.the.re --since 2000-01-01 --no-sync-mailman /home/debian/spip-dev.mbox/*.mbox
...
Importing from mbox file /home/debian/spip-dev.mbox/200002.mbox to spip-dev@chat.the.re
...
Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/lib/python3/dist-packages/django/core/management/__init__.py", line 364, in execute_from_command_line
    utility.execute()
  File "/usr/lib/python3/dist-packages/django/core/management/__init__.py", line 356, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/lib/python3/dist-packages/django/core/management/base.py", line 283, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/lib/python3/dist-packages/django/core/management/base.py", line 330, in execute
    output = self.handle(*args, **options)
  File "/usr/lib/python3/dist-packages/hyperkitty/management/commands/hyperkitty_import.py", line 305, in handle
    importer.from_mbox(mbfile)
  File "/usr/lib/python3/dist-packages/hyperkitty/management/commands/hyperkitty_import.py", line 169, in from_mbox
    "subject", TEXTWRAP_RE.sub(" ", message["subject"]))
  File "/usr/lib/python3.7/email/message.py", line 555, in replace_header
    self._headers[i] = self.policy.header_store_parse(k, _value)
  File "/usr/lib/python3.7/email/policy.py", line 145, in header_store_parse
    raise ValueError("Header values may not contain linefeed "
ValueError: Header values may not contain linefeed or carriage return characters

Et c’est la que le vrai travail manuel commence. Mais on peut déjà voir que ça marche sur l’interface web et c’est réconfortant.

Ca vaut peut-être le coup de tenter l’import en utilisant une version plus récente de hyperkitty ?

1 Like
#7

En installant (via les sources avec pip install -e . dans un venv) la version 1.3.3 de hyperkitty, j’arrive effectivement plus loin:

# python3 manage.py hyperkitty_import --list-address spip-dev@mailman.the.re --since 2000-01-01 --no-sync-mailman /home/debian/spip-dev.mbox/*.mbox
...
Importing from mbox file /home/debian/spip-dev.mbox/200211.mbox to spip-dev@mailman.the.re                                      
Importing from mbox file /home/debian/spip-dev.mbox/200212.mbox to spip-dev@mailman.the.re                                      
/Failed adding message <3DF814AB.4040802@free.fr>: unknown encoding: #charset                                                   
Importing from mbox file /home/debian/spip-dev.mbox/200301.mbox to spip-dev@mailman.the.re                                      
\Traceback (most recent call last):                                                                                             
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 2398, in parse_mime_parameters                                  
    token, value = get_parameter(value)                                                                                         
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 2255, in get_parameter                                          
    token, value = get_attribute(value)                                                                                         
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 2143, in get_attribute                                          
    "expected token but found '{}'".format(value))                                                                              
email.errors.HeaderParseError: expected token but found '=?ISO-8859-1?Q?coordonn=E9_pou?==?ISO-8859-1?Q?r_le_contr=F4le_officiel
_des_denr=E9es_alimen?==?ISO-8859-1?Q?taires_pour_2003?="'                                                                      
                                                                                                                                
During handling of the above exception, another exception occurred:                                                             
                                                                                                                                
Traceback (most recent call last):                                                                                              
  File "manage.py", line 10, in <module>                                                                                        
    execute_from_command_line(sys.argv)                                                                                         
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/django/core/management/__init__.py", line 401, in execute_from_
command_line                                                                                                                    
    utility.execute()                                                                                                           
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/django/core/management/__init__.py", line 395, in execute      
    self.fetch_command(subcommand).run_from_argv(self.argv)                                                                     
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/django/core/management/base.py", line 328, in run_from_argv    
    self.execute(*args, **cmd_options)                                                                                          
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/django/core/management/base.py", line 369, in execute          
    output = self.handle(*args, **options)                                                                                      
  File "/home/debian/hyperkitty/hyperkitty/management/commands/hyperkitty_import.py", line 327, in handle                       
    importer.from_mbox(mbfile)                                                                                                  
  File "/home/debian/hyperkitty/hyperkitty/management/commands/hyperkitty_import.py", line 158, in from_mbox                    
    message = message_from_bytes(msg_raw, policy=policy.default)                                                                
  File "/usr/lib/python3.7/email/__init__.py", line 46, in message_from_bytes                                                   
    return BytesParser(*args, **kws).parsebytes(s)                                                                              
  File "/usr/lib/python3.7/email/parser.py", line 124, in parsebytes                                                            
    return self.parser.parsestr(text, headersonly)                                                                              
  File "/usr/lib/python3.7/email/parser.py", line 68, in parsestr                                 
  File "/usr/lib/python3.7/email/parser.py", line 68, in parsestr                                                              
    return self.parse(StringIO(text), headersonly=headersonly)
  File "/usr/lib/python3.7/email/parser.py", line 57, in parse
    feedparser.feed(data)
  File "/usr/lib/python3.7/email/feedparser.py", line 176, in feed                                                             
    self._call_parse()
  File "/usr/lib/python3.7/email/feedparser.py", line 180, in _call_parse                                                      
    self._parse()
  File "/usr/lib/python3.7/email/feedparser.py", line 385, in _parsegen                                                        
    for retval in self._parsegen():
  File "/usr/lib/python3.7/email/feedparser.py", line 256, in _parsegen                                                        
    if self._cur.get_content_type() == 'message/delivery-status':                                                              
  File "/usr/lib/python3.7/email/message.py", line 578, in get_content_type                                                    
    value = self.get('content-type', missing)
  File "/usr/lib/python3.7/email/message.py", line 471, in get
    return self.policy.header_fetch_parse(k, v)
  File "/usr/lib/python3.7/email/policy.py", line 162, in header_fetch_parse                                                   
    return self.header_factory(name, value)
  File "/usr/lib/python3.7/email/headerregistry.py", line 589, in __call__                                                     
    return self[name](name, value)
  File "/usr/lib/python3.7/email/headerregistry.py", line 197, in __new__                                                      
    cls.parse(value, kwds)
  File "/usr/lib/python3.7/email/headerregistry.py", line 446, in parse                                                        
    kwds['parse_tree'] = parse_tree = cls.value_parser(value)
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 2504, in parse_content_type_header                             
    ctype.append(parse_mime_parameters(value[1:]))
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 2413, in parse_mime_parameters                                 
    token, value = get_invalid_parameter(value)
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 2063, in get_invalid_parameter                                 
    token, value = get_phrase(value)
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 1377, in get_phrase                                            
    token, value = get_word(value)
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 1340, in get_word                                              
    token, value = get_quoted_string(value)
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 1241, in get_quoted_string                                     
    token, value = get_bare_quoted_string(value)
  File "/usr/lib/python3.7/email/_header_value_parser.py", line 1170, in get_bare_quoted_string                                
    if value[0] == '"':
IndexError: string index out of range

Idéalement toutes les erreurs de l’import seraient traitées de cette façon:

  • Affichage de l’erreur avec l’id du message
  • Ajout du message dans une mbox d’erreur

De façon à importer tout ce qui est possible au lieu d’échouer, tout en permettant le traitement manuel des messages en échec. Il n’est pas possible d’utiliser la branche master et elle ne semble pas contenir un correctif sur l’import:

# git log --no-merges --oneline 1.3.3..master
5fbf98d Use Angle brackets for In-reply-to header since it is stripped.
538beee Add Python 3.9 for testing.
e8410d2 Replaced deprecated ugettext functions with gettext.
3a004aa Fix typo in example_project/settings.py.
0c73ab3 Warn about mailman_hyperkitty module in Mailman integration
418fe46 Add the ability to disable gravatars.
8dd1a2e Fix a bug where the reply buttons were missing from the replies.
da9c8e6 Fix wrong padding around the navigation buttons in overview
8feb164 Translated using Weblate (Swedish)
5985628 Translated using Weblate (Portuguese)
d04ceed Translated using Weblate (German)
f4f96a3 Add configuration to disable web posting.
e112877 overview.html: fix superuser typo
6791aff Removed isort workaround. isort 5.0.6 is fixed.
68f9325 Extend lock life for update_and_clean_index job and add some doc.
7e9cdbd Translated using Weblate (Russian)
d978014 Translated using Weblate (Portuguese (Brazil))
a3de739 Translated using Weblate (Russian)
a93f5f0 Sync owners and moderators from Mailman Core for MailingList model.
18f9722 Version bump post release.

En cherchant dans les issues contenant import ou bien celles qui ont le tag import, l’idée ne semble pas avoir été proposée. On dirait bien qu’un patch serait pertinent.

En attendant on peut déjà faire les mbox les unes après les autres pour éviter qu’une erreur crash l’ensemble:

# for mbox in /home/debian/spip-dev.mbox/*.mbox ; do echo $mbox ; echo -------------------------------------------- ; python3 manage.py hyperkitty_import --list-address spip-dev@mailman.the.re --since 2000-01-01 --no-sync-mailman $mbox ; done |& tee /var/log/hyperkitty_import.log
#8

L’indexation prend un certain temps et consomme ~1GB de RAM (RES):

time python manage.py update_index_one_list spip-dev@mailman.
the.re
Indexing 70099 emails

real    175m45.389s
user    85m41.816s
sys     52m12.642s
#9

Je découvre avec embarras que le serveur mailman3 est cassé. Je ne suis pas certain que ce soit arrivé après l’import des mbox ou après l’indexation. En tout cas ça ressemble à ce bug et pourrait bien venir du fait que j’ai fait des trucs dans la base avec une version de mailman3 qui ne correspond pas à la version qui ne correspond pas à la version qui fait tourner le serveur (cf plus haut, pour contourner une certaine fragilité de l’import de config.pck).

# journalctl --unit mailman3
-- Logs begin at Mon 2020-09-21 06:56:06 UTC, end at Mon 2020-09-21 11:14:12 UTC. --
Sep 21 06:56:12 mailman-host.the.re systemd[1]: Starting Mailman3 server...
Sep 21 06:56:18 mailman-host.the.re mailman3[449]: Traceback (most recent call last):
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/alembic/script/base.py", line 143, in _catch_revision_errors
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     yield
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/alembic/script/base.py", line 336, in _upgrade_revs
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     revs = list(revs)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/alembic/script/revision.py", line 645, in _iterate_revisions
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     requested_lowers = self.get_revisions(lower)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/alembic/script/revision.py", line 299, in get_revisions
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     return sum([self.get_revisions(id_elem) for id_elem in id_], ())
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/alembic/script/revision.py", line 299, in <listcomp>
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     return sum([self.get_revisions(id_elem) for id_elem in id_], ())
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/alembic/script/revision.py", line 304, in get_revisions
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     for rev_id in resolved_id)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/alembic/script/revision.py", line 304, in <genexpr>
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     for rev_id in resolved_id)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/alembic/script/revision.py", line 362, in _revision_for_ident
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     resolved_id)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]: alembic.script.revision.ResolutionError: No such revision or branch '9735f5e5dbdb'
Sep 21 06:56:18 mailman-host.the.re mailman3[449]: The above exception was the direct cause of the following exception:
Sep 21 06:56:18 mailman-host.the.re mailman3[449]: Traceback (most recent call last):
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/bin/mailman", line 11, in <module>
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     load_entry_point('mailman==3.2.1', 'console_scripts', 'mailman')()
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/click/core.py", line 764, in __call__
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     return self.main(*args, **kwargs)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/click/core.py", line 716, in main
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     with self.make_context(prog_name, args, **extra) as ctx:
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/click/core.py", line 641, in make_context
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     self.parse_args(ctx, args)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/click/core.py", line 1089, in parse_args
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     rest = Command.parse_args(self, ctx, args)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/click/core.py", line 940, in parse_args
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     value, args = param.handle_parse_result(ctx, opts, args)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/click/core.py", line 1477, in handle_parse_result
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     self.callback, ctx, self, value)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/click/core.py", line 96, in invoke_param_callback
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     return callback(ctx, param, value)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/mailman/bin/mailman.py", line 94, in initialize_config
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     initialize(value)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/mailman/core/initialize.py", line 218, in initialize
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     initialize_2(propagate_logs=propagate_logs)
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/mailman/core/initialize.py", line 177, in initialize_2
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     config.db = getUtility(IDatabaseFactory, utility_name).create()
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/mailman/database/factory.py", line 55, in create
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     SchemaManager(database).setup_database()
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/mailman/database/factory.py", line 106, in setup_database
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     alembic.command.upgrade(alembic_cfg, 'head')
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:   File "/usr/lib/python3/dist-packages/alembic/command.py", line 254, in upgrade
Sep 21 06:56:18 mailman-host.the.re mailman3[449]:     script.run_env()
#10

Après enquête, l’erreur ci dessus est introduite par l’import de config.pck en utilisant une version de mailman venant des sources. Je ne m’en suis pas rendu compte tout de suite parce que ça se produit quand le serveur re-démarre.

(eǝ) debian@mailman-host:~$ dpkg -l python3-alembic
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name            Version      Architecture Description
+++-===============-============-============-===============================================================
ii  python3-alembic 1.0.0-3      all          lightweight database migration tool for SQLAlchemy - Python 3.x
(venv)  (eǝ) root@mailman-host:/home/debian/mailman# pip show alembic
Name: alembic
Version: 1.4.3

Il y a en effet un leger décalage de version :sweat_smile: Le problème suggère aussi que l’import écrit directement dans la base qu’il serait donc plus sage de tenter une approche un peu différente, à savoir copier le script d’import depuis une version récente mais ne pas l’executer dans un venv installé depuis les sources de façon à minimiser l’impact des changement de version des dépendances.

#11

Pour éviter de corrompre la base de donnée irrémédiablement tout en utilisant la derniere version du script, l’import d’une liste peut se faire comme ça:

# mailman create -N spip-dev@mailman.the.re
# wget -O /usr/lib/python3/dist-packages/mailman/commands/cli_import.py https://gitlab.com/mailman/mailman/-/raw/master/src/mailman/commands/cli_import.py
# wget -O /usr/lib/python3/dist-packages/mailman/utilities/importer.py https://gitlab.com/mailman/mailman/-/raw/master/src/mailman/utilities/importer.py
# mailman import21 spip-dev@mailman.the.re /tmp/config.pck
#12

L’encodage des listes mailman2 est iso8859-1. Cependant le script d’import suppose que c’est utf-8.

Traceback (most recent call last):
  File "/usr/bin/mailman", line 11, in <module>
    load_entry_point('mailman==3.2.1', 'console_scripts', 'mailman')()
  File "/usr/lib/python3/dist-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python3/dist-packages/mailman/bin/mailman.py", line 69, in invoke
    return super().invoke(ctx)
  File "/usr/lib/python3/dist-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python3/dist-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python3/dist-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/usr/lib/python3/dist-packages/mailman/commands/cli_import.py", line 71, in import21                                                                                    
    pickle_file, encoding='utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 55: invalid continuation byte                                                                           `

et ignore les erreurs d’encodage: les accents disparaissent donc. Ca mérite un hack:

diff --git a/cli_import.py.orig b/cli_import.py
index 2d694a3..154bd98 100644
--- a/cli_import.py.orig
+++ b/cli_import.py
@@ -68,7 +68,7 @@ def import21(ctx, listspec, pickle_file):
         while True:
             try:
                 config_dict = pickle.load(
-                    pickle_file, encoding='utf-8', errors='ignore')
+                    pickle_file, encoding='iso8859-1')
             except EOFError:
                 break
             except pickle.UnpicklingError:

et un bug report.

#13

merci @dachary parce que ça me pend aussi au nez cette migration, je suis donc ton post avec attention

1 Like
#14

Après l’import réussit d’une liste dont les archives (fichiers .mbox) font environ 200Mo, essentiellement du texte (environ 70,000 messages), les archives occupent environ 600Mo

# du -sh /var/lib/mailman3/web/mailman3web.db /var/lib/mailman3/web/fulltext_index/
173M    /var/lib/mailman3/web/mailman3web.db
470M    /var/lib/mailman3/web/fulltext_index/

Il semble qu’il faille compter au pire quatre fois la taille d’une mbox après injection dans mailman3, ce qui semble un peu beaucoup. Par ailleurs je ne vois pas ou il stocke les attachements. Dans le cas de ce test c’est sqlite qui est utilisé mais je doute que la taille des données soit significativement réduite si c’est MySQL ou postgresQL.

La base de donnée de mailman dans laquelle ont été insérées ~400 listes de diffusion fait ~50Mo, c’est négligeable (liste des abonnés, privé-public etc.).

# ls -lh /var/lib/mailman3/data/mailman.db 
-rw-rw---- 1 list list 53M Sep 21 22:38 /var/lib/mailman3/data/mailman.db
#15

Une option a été ajoutée aujourd’hui et on peut maintenant écrire:

mailman import21 --charset iso8859-1 spip-dev@mailman.the.re /tmp/config.pck
#16

L’indexation texte intégral d’une liste a consommé trop de mémoire, probablement plus de 2GB sur une machine qui en a 4 au total.

# time python3 manage.py update_index_one_list ?????@mailman.the.re                                                                                                                                         
Indexing 32224 emails                                                                                                                     
MemoryError while preparing object for update                                                                                             
Traceback (most recent call last):                                                                                                        
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/haystack/backends/whoosh_backend.py", line 283, in update                
    writer.update_document(**doc)                                                                                                         
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/writing.py", line 1024, in update_document                        
    self._record("update_document", args, kwargs)                                                                                         
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/writing.py", line 1001, in _record                                
    getattr(self.writer, method)(*args, **kwargs)                                                                                         
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/writing.py", line 483, in update_document                         
    with self.searcher() as s:                                                                                                            
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/writing.py", line 297, in searcher                                
    return Searcher(self.reader(), **kwargs)                                                                                              
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/writing.py", line 639, in reader                                  
    self.generation, reuse=reuse)                                                                                                         
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/index.py", line 535, in _reader                                   
    readers = [segreader(segment) for segment in segments]                                                                                
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/index.py", line 535, in <listcomp>                                
    readers = [segreader(segment) for segment in segments]                                                                                
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/index.py", line 524, in segreader                                 
    generation=generation)                                                                                                                
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/reading.py", line 620, in __init__                                
    self._terms = self._codec.terms_reader(self._storage, segment)                                                                        
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/codec/whoosh3.py", line 120, in terms_reader                      
    tifile = storage.open_file(tiname)                                                                                                    
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/filedb/filestore.py", line 333, in open_file                      
    return self.a.open_file(name, *args, **kwargs)                                                                                        
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/filedb/compound.py", line 121, in open_file                       
    f = BufferFile(buf, name=name)                                                                                                        
  File "/home/debian/venv-hyperkitty/lib/python3.7/site-packages/whoosh/filedb/structfile.py", line 357, in __init__                      
    self.file = BytesIO(buf)                                                                                                              
MemoryError                                                                                          

J’augmente la mémoire pour avoir 8Go et on va voir si ça passe :crossed_fingers:

#17

Et en effet, après avoir relancé le process d’indexation sur une machine avec plus de mémoire, on voit rapidement que ça monte à 3.1Go.

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                              
1246 www-data  20   0 3965716   3,1g   1,5g R  97,0  41,0   1:07.00 python3 /usr/share/mailman3-web/manage.py runjobs hourly     

et ça fluctue, je suppose que ça dépend de la taille du message indexé à un moment. S’il contient de gros attachements, ą peut monter fort. Quelques minutes plus tard le process retombe à 1.3Go.

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                              
216 root      20   0 1686936   1,3g 606016 R  99,0  17,4   8:48.91 python3                                                              
#18

La mise à jour de l’index s’est terminée sans erreur.

(venv-hyperkitty)  (eǝ) root@mailman-host:/usr/share/mailman3-web# time python3 manage.py update_index_one_list ????@mailman.the.re
Indexing 32224 emails

real    260m40,567s
user    134m33,045s
sys     120m35,858s

L’index occupe environ 1.5Go ce qui semble cohérent avec le fait que la liste contient des attachements volumineux qui ne sont pas indexés.

Je remarque que la taille de /var/lib/mailman3/web/mailmanweb.db (9Go) est inférieure à celle des mbox décompressées de la liste qui vient d’être importée (11Go). Ce qui veut dire qu’il y a de la compression quelque part ou bien que certains messages et/ou attachements ont été ignorés.

#19

Quand on fait une recherche dans les archives (un mot) dont le volume est

(venv-hyperkitty)  (eǝ) root@mailman-host:/usr/share/mailman3-web# du -sh /var/lib/mailman3/web/*
1,5G    /var/lib/mailman3/web/fulltext_index
8,2G    /var/lib/mailman3/web/mailman3web.db
7,5M    /var/lib/mailman3/web/static

L’occupation mémoire du process qui fait la recherche est importante (~4.5Go).

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                              
1020 www-data  20   0 5697352   4,4g   2,0g R  75,7  57,9   0:48.92 /usr/bin/uwsgi --plugin python3 --ini /etc/mailman3/uwsgi.ini        

Aucune idée si c’est proportionel à la taile de l’index text intégral (1.5Go) ou si c’est aussi fonction du nombre de messages que la liste contient. Ca n’augmente pas linéairement avec le nombre de résultats.

#20

Les attachements sont décodés par hyperkitty via django-mailman3 et stockés dans des blobs binaires et prennent donc moins de place que leur équivalent encodé.