code. grind. sleep.

vendredi 23 août 2013

modular-xorg, radeon et pas de KMS

Il y avait un moment que je n'avais pas touché à NetBSD et donc mis à jour mon lappy avec du pkg frais. Entre temps, la version de X.org modular, donc issue de pkgsrc, est revenue en 2012, avec son lot de drivers mis à jour. Le drivers xf86-video-ati, est passé en version 7.1.0, sauf qu'il fonctionne uniquement en KMS (Kernel Mode Setting), chose qu'on n'a pas encore dans notre kernel.

Il faut donc la dernière version de la branche 6 du driver, qui contient encore le support UMS, disponible dans le paquet x11/xf86-video-ati6, qui porte le même nom de package, bizarrement. Tout ça est suivi dans le PR 47935.

Pour que le serveur X trouve le device avec ce driver, j'ai du virer la ligne BusID dans la section Device du xorg.conf.

mercredi 5 juin 2013

pitrery : Faciliter la restauration PITR de PostgreSQL

pitrery est un ensemble de scripts pour gérer plus facilement la sauvegarde à chaud et la restauration PITR dans PostgreSQL.

Il s'agit de simples scripts bash, qui n'imposent pas autant de dépendances qu'un pgbarman et se concentre uniquement sur le PITR, là où des outils tels que OmniPITR, PITRTools semblent vouloir gérer également la réplication. L'objectif de pitrery est de pouvoir facilement restaurer.

La version 1.3 apporte des nouveauté intéressantes :

  • Support de PostgreSQL 9.2, les changements de définition du catalogue en 9.2 sont pris en compte pour les tablespaces
  • Simplification du script d'archivage
  • Possibilité de configurer un utilisateur de connexion différent pour les accès SSH
  • Amélioration de l'affichage de la liste des sauvegardes
  • Ajout d'un résumé des informations essentielles pour la restauration
  • Possibilité de modifier l'emplacement de chaque tablespace à la restauration
  • Mode sans exécution de la restauration avec affichage des informations de restauration seulement
  • Beaucoup de corrections de bug

La documentation est à jour sur https://github.com/dalibo/pitrery/w...

Le code est disponible sur https://github.com/dalibo/pitrery/

Les versions sont disponinbles sur https://dl.dalibo.com/public/pitrer...

Les idées pour la prochaine version sont, en vrac :

  • Faire le base backup avec rsync, pour pallier la production de tarball énormes
  • Paralléliser le tar des tablespaces
  • Ajouter l'exécution de commandes pré/post backup
  • Améliorer l'algo de rétention des backups pour gérer age et nombre de backup en même temps

vendredi 10 mai 2013

Un bulk build par jour dans un screen

Mes packages NetBSD sont préparés par pbulk, qui tourne en continu grâce au script pbulk-builder.

J'avais prévu avec l'option -1 de lui éviter d'entrer dans une boucle infinie, et j'ai pas eu tort. En effet, la majorité des tours ne fait que mettre à jour l'arbre pkgsrc, résoudre les dépendances, sans rien compiler de nouveau. La première solution que j'ai trouvé a été de créer une règle sieve pour ne pas recevoir des dizaines de mails de rapport de bulk inutiles, en les plaçant dans un répertoire séparé...

N'ayant jamais pris le temps d'utiliser cette fameuse option one-shot, j'ai décidé de mettre le lancement du bulk dans la crontab, sauf que c'est long et qu'il vaut mieux suivre ça avec screen. C'est possible grâce aux options -d et -m (avec -S pour mettre un titre) :

0 23 * * * /usr/pkg/bin/screen -dmS bulk -c /root/.screenrc /usr/pkg/bin/bash ~orgrim/nb-utils/pkgsrc/pbulk-builder -c -1 /data/pbulk

On peut alors attacher le screen quand le bulk tourne.

PS: merci au fans de tmux de passer sur le chan #netbsdfr sur Freenode pour me dire comment faire pareil avec tmux :)

samedi 6 avril 2013

Oups : des tablespaces imbriqués

Imbriquer des tablespaces n'a pas vraiment de sens dans PostgreSQL surtout si on veut se prendre la tête avec des montages dans tous les sens... Mais bon c'est permis, car PostgreSQL utilise uniquement les liens symboliques dans $PGDATA/pg_tblspc pour accéder au contenu des tablespaces.

Si on veut savoir ce qu'il en est voici une requête qui montre ça.

Pour PostgreSQL <= 9.1 :

SELECT f.oid, f.spcname AS name, f.spclocation AS location,
  f.spclocation ~ ('^' || (SELECT setting FROM pg_settings
                           WHERE name = 'data_directory')) AS inside_pgdata,
  (SELECT o.spcname
   FROM pg_tablespace o
   WHERE f.spclocation ~ ('^' || o.spclocation || '/')
     AND o.spcname NOT IN ('pg_default', 'pg_global')
   ORDER BY o.spclocation DESC LIMIT 1) AS parent,
  (SELECT o.spclocation
   FROM pg_tablespace o
   WHERE f.spclocation ~ ('^' || o.spclocation || '/')
     AND o.spcname NOT IN ('pg_default', 'pg_global')
   ORDER BY o.spclocation DESC LIMIT 1) AS parent_location
FROM pg_tablespace f
WHERE f.spcname NOT IN ('pg_default', 'pg_global');

Pour PostgreSQL >= 9.2, la colonne spcname de pg_tablespace a été remplacée par la fonction pg_tablespace_location() :

SELECT f.oid, f.spcname AS name, pg_tablespace_location(f.oid) AS location,
  pg_tablespace_location(f.oid) ~ ('^' || (SELECT setting FROM pg_settings
                                    WHERE name = 'data_directory')) AS inside_pgdata,
  (SELECT o.spcname
   FROM pg_tablespace o
   WHERE pg_tablespace_location(f.oid) ~ ('^' || pg_tablespace_location(o.oid) || '/')
     AND o.spcname NOT IN ('pg_default', 'pg_global')
   ORDER BY pg_tablespace_location(o.oid) DESC LIMIT 1) AS parent,
  (SELECT pg_tablespace_location(o.oid)
   FROM pg_tablespace o
   WHERE pg_tablespace_location(f.oid) ~ ('^' || pg_tablespace_location(o.oid) || '/')
     AND o.spcname NOT IN ('pg_default', 'pg_global')
   ORDER BY pg_tablespace_location(o.oid) DESC LIMIT 1) AS parent_location
FROM pg_tablespace f
WHERE f.spcname NOT IN ('pg_default', 'pg_global');

On obtient par exemple :

  oid  |   name   |                       location                       | inside_pgdata | parent  |                 parent_location                 
-------+----------+------------------------------------------------------+---------------+---------+-------------------------------------------------
 16399 | inside   | /home/pgsql/postgresql-9.0.10/data/inside            | t             |         | 
 16400 | outside  | /home/pgsql/postgresql-9.0.10/outside                | f             |         | 
 16414 | imbrique | /home/pgsql/postgresql-9.0.10/outside/imbrique       | f             | outside | /home/pgsql/postgresql-9.0.10/outside
 16415 | rimb     | /home/pgsql/postgresql-9.0.10/outside/rimb           | f             | outside | /home/pgsql/postgresql-9.0.10/outside
 16416 | rimbimb  | /home/pgsql/postgresql-9.0.10/outside/rimb/imb       | f             | rimb    | /home/pgsql/postgresql-9.0.10/outside/rimb
 16417 | deuimb   | /home/pgsql/postgresql-9.0.10/outside/rimb/truc/2imb | f             | truc    | /home/pgsql/postgresql-9.0.10/outside/rimb/truc
 16418 | truc     | /home/pgsql/postgresql-9.0.10/outside/rimb/truc      | f             | rimb    | /home/pgsql/postgresql-9.0.10/outside/rimb
(7 rows)

Pour bien gérer ses tablespaces, l'idéal est de faire un répertoire dédié au même niveau que $PGDATA, et de créer les tablespaces dedans, avec un tablespace par répertoire sur un seul niveau :

 /home/pgsql/postgresql-9.2.4
├── data
└── tblspc
    ├── tbs_1
    ├── tbs_2
    ├── ...
    └── tbs_n

C'est plus propre et on voit tout de suite l'utilisation disque avec df : parce qu'on met des disques différents derrière, bien entendu... :)

samedi 26 janvier 2013

pbulk-builder et pkgtools/mksandbox

Après la réinstall d'une de mes machines de build en 6.0.1, j'ai eu la bonne surprise de voir que le script de build pbulk-builder (https://github.com/orgrim/nb-utils) ne trouvait plus mksandbox dans l'arbre de pkgsrc. Il est désormais dans son paquet : pkgtools/mksandbox

Le script est à jour. le script de montage de la sandbox se prend un sed dans la foulée pour éviter qu'il force le montage de l'arbre pkgsrc dans /tree/pkgsrc...

lundi 30 janvier 2012

Got GUC?

Les paramètres de configuration de PostgreSQL sont appelés GUC ce qui signifie Grand Unified Configuration, c'est le nom de la partie du code qui gère les paramètres de configuration. En gros, ce sont tous les paramètres du fichier postgresql.conf.

Ce qui est moins connu et utilisé, c'est la possibilité de configurer ces paramètres à différents niveaux :

  1. Fichier postgresql.conf
  2. Ligne de commande du postmaster, le processus principal du serveur
  3. Base de données
  4. Rôle
  5. Session
  6. Transaction

La précédence des valeurs va en descendant dans la liste, par exemple la valeur d'un paramètre au niveau d'un rôle écrase celle positionnée au niveau de la base de donnée ou la ligne de commande. Ce comportement est très intéressant pour définir une valeur d'un paramètre dépendante du contexte d'exécution d'un traitement. Par exemple on peut placer un timeout des requêtes au niveau de la base pour éviter qu'une application ne jette l'éponge avant PostgreSQL, et configurer l'absence de timeout pour un rôle dédié aux opérations de VACUUM et ANALYSE, on limite ainsi l'effet de bord du timeout :

-- timeout à 30 secondes sur la base de données
ALTER DATABASE mabase SET statement_timeout TO 30000;

-- pas de timeout pour le role maintenance chargé du vacuum
ALTER ROLE maintenance SET statement_timeout TO 0;

Selon l'endroit où doit être positionné la valeur on utilise :

  • postgresql.conf : directement dans le fichier
  • ligne de commande : dans le script d'init avec l'option -c et à l'exécution de pg_ctl avec l'option -o
  • base de données : ALTER DATABASE nom_base SET param TO valeur;
  • rôle : ALTER ROLE nom_role SET param TO valeur;
  • session : SET [ SESSION ] param TO valeur;
  • transaction : SET LOCAL param TO valeur;

Pour le passage des valeurs au niveau SQL, on peut utiliser ... RESET param à la place de SET param TO pour réinitialiser la valeur à son défaut pour le contexte choisi.

On peut également définir des paramètres personnalisés, comme le font certaines extensions. Pour cela il faut définir une classe de variables personnalisée, en déclarant un préfixe (on en sépare plusieurs par des virgules) dans le paramètre de configuration custom_variable_classes :

custom_variable_classes = 'nico'

Ensuite, on peut directement ajouter nos variables personnalisées en les préfixant par nico. :

nico.test_guc = 1000

On peut alors manipuler ces variables comme ceci :

mydb=# SHOW nico.test_guc;
 nico.test_guc 
---------------
 1000
(1 row)

mydb=# SHOW nico.test_guc;
 nico.test_guc 
---------------
 1000
(1 row)

mydb=# SET nico.test_guc = 3;
SET
mydb=# SHOW nico.test_guc;
 nico.test_guc 
---------------
 3
(1 row)

mydb=# SET nico.reguc = on;
SET
mydb=# SHOW nico.reguc;
 nico.reguc 
------------
 on
(1 row)

Enfin, on peut utiliser les fonctions current_setting() et set_config() pour manipuler ces variables dans des fonctions :

mydb=# SELECT set_config('nico.test_guc', '100', false);
 set_config 
------------
 100
(1 row)

mydb=# SELECT current_setting('nico.test_guc');
 current_setting 
-----------------
 100
(1 row)

PS: merci à ce post pour l'idée de creuser le sujet.

mardi 24 janvier 2012

Compiler dans l'arbre des sources

Je m'occupe actuellement de préparer ma box pour le FOSDEM, et il s'avère qu'il manque le support du DRM (Direct Rendering Manager, le truc pour avoir de l'accélération graphique dans le kernel pour X.org) pour ma carte vidéo. Il s'agit de NetBSD 5.1.1, la version 6 n'aura pas ce manque.

Il faut donc recompiler un noyau pour ajouter cette fonctionnalité, pour faire vite on ne passe pas par build.sh, les tools etc, on compile directement dans l'arbre des sources, montrer comment faire ça est le but de post.

On commence par récupérer les sources sur un serveur CVS près de chez soi :

# export CVS_RSH=ssh
# export CVSROOT="anoncvs@anoncvs.NetBSD.org:/cvsroot"
# cd /usr
# cvs -q -z3 co -P -rnetbsd-5-1-1-RELEASE src

Ensuite, on n'a pas besoin d'avoir une conf particulière dans son /etc/mk.conf, on n'a juste à ajouter les options dans le fichier de conf du kernel et compiler directement par l'intermédiaire de make.

On édite le fichier /usr/src/sys/arch/i386/conf/GENERIC.local, pour y ajouter les lignes suivantes, il est inclus par le fichier GENERIC :

# DRI driver    
i915drm*        at vga?         # Intel i915, i945 DRM driver
mach64drm*      at vga?         # mach64 (3D Rage Pro, Rage) DRM driver
mgadrm*         at vga?         # Matrox G[24]00, G[45]50 DRM driver
r128drm*        at vga?         # ATI Rage 128 DRM driver
radeondrm*      at vga?         # ATI Radeon DRM driver
savagedrm*      at vga?         # S3 Savage DRM driver
sisdrm*         at vga?         # SiS DRM driver
tdfxdrm*        at vga?         # 3dfx (voodoo) DRM driver

On compile le kernel à la main :

# config GENERIC
# cd ../compile/GENERIC
# make depend
# make

On installe le kernel à la main :

# mv /netbsd /netbsd.old
# cp netbsd /
# chmod 444 /netbsd

Pour revenir facilement en arrière en cas de souci, on peut ajouter la ligne suivante dans /boot.cfg, en deuxième position :

menu=Boot old kernel:boot netbsd.old

Il ne reste plus qu'à rebooter sur le nouveau kernel.

Référence : Le guide

On peut faire la même manip pour mettre à jour une partie du système seulement, par exemple lorsqu'une faille de sécurité doit être corrigée. La méthode de compilation dans l'arbre des sources est indiquée dans l'avis.

Plus généralement, la méthode est la suivante, avec l'exemple de ls :

# cd /usr/src/bin/ls
# make USETOOLS=no cleandir
# make USETOOLS=no dependall

Le binaire résultant et sa doc sont prêts dans le répertoire courant, il ne reste qu'à installer :

# make USETOOLS=no install

vendredi 21 octobre 2011

X11 forwarding request failed on channel 0

Quand j'essaye de me logguer sur ma box NetBSD fraichement passé en X.org modular, j'ai ça :

orgrim@serfouette ~ $ ssh rateau 
X11 forwarding request failed on channel 0
Last login: Fri Oct 21 12:20:24 2011 from serfouette.home.orgrim.net

WTF? ça tombe en marche SSH normalement.

Et bien le souci vient de l'échange des magic cookies pour l'authentification entre serveurs X à travers SSH, c'est utilisé par le X11 forwarding et on a besoin de plusieurs choses pour ça :

  • le chemin complet vers xauth sur le client
  • le chemin complet vers xauth sur le serveur
  • Avoir X11Forward yes sur le serveur quand on le demande sur le client (il vaut yes dans le /etc/ssh/ssh_config du client par flemme de taper -X sur la ligne de commande)

Le client est sous Linux, donc pas de soucis le chemin en dur /usr/bin/xauth dans le binaire sshd marche. Par contre pour NetBSD avec du X.org modular, il faut décommenter l'option XAuthLocation dans /etc/ssh/sshd_config et /etc/ssh/ssh_config pour donner le bon chemin vers xauth, comme indiqué en commentaire dans ces deux fichiers :

# If you use xorg from pkgsrc then uncomment the following line.
#  XAuthLocation /usr/pkg/bin/xauth

Enfin, comme le X11Forward est à no par défaut sur NetBSD, je l'ai activé. La vrai solution est de faire ça côté client en utilisant explicitement l'option -X quand on veut ouvrir des fenêtres sur le serveur.

Le pire c'est que Google ne sort rien sur le message "X11 forwarding request failed on channel 0", à part une question sans réponse sur un stackoverflow-like en Russe !

mardi 18 octobre 2011

Quand PostgreSQL n'a plus d'espace disque à manger

Voilà donc une question intéressante, comment se comporte PostgreSQL face à un système de fichier plein ? Un peu d'expérimentation est nécessaire pour se rassurer...

On crée deux systèmes de fichiers de faible taille pour les tests. Le premier stockera PGDATA, ainsi qu'une base de données nommée db_data. Le second sera le tablesapce ts1, dans lequel on créera une base de données db_ts1.

L'objectif est de montrer que seules les transactions modifiant des objets stockés sur des systèmes de fichier plein sont affectées, c'est pourquoi on a besoin de plusieurs tablespaces

Les binaires se trouvent dans /home/pgsql/postgresql-9.0.4, PGDATA dans /home/pgsql/postgresql-9.0.4/data, et le tablespace dans /home/pgsql/postgresql-9.0.4/ts1. On a donc monté et donné la propriété des deux filesystèmes à orgrim, que fera tourner PostgreSQL :

# df -h
...
/dev/mapper/sys-pg1   504M   54M  425M  12% /home/pgsql/postgresql-9.0.4/data
/dev/mapper/sys-pg2   504M   17M  462M   4% /home/pgsql/postgresql-9.0.4/ts1
...

# cd /home/pgsql/postgresql-9.0.4/
# chown orgrim: data ts1
# chmod 700 data ts1

Le cluster est préparé avec l'environnement suivant :

$ env | grep PG
PGPORT=5904
PGDATABASE=postgres
PGDATA=/home/pgsql/postgresql-9.0.4/data
PATH=/home/pgsql/postgresql-9.0.4/bin:$PATH

On lance donc initdb, puis on crée les bases avec le tablespace :

$ initdb
$ psql
psql (9.0.4)
Type "help" for help.

postgres=# CREATE DATABASE db_data;
CREATE DATABASE
postgres=# CREATE TABLESPACE ts1 LOCATION '/home/pgsql/postgresql-9.0.4/ts1';
CREATE TABLESPACE
postgres=# CREATE DATABASE db_ts1 TABLESPACE ts1;
CREATE DATABASE

Ensuite, on se connecte à la base de données db_ts1 et on y crée une base qui permettra de remplir le tablespace ts1 :

$ while ((1)); do psql -c "INSERT INTO t SELECT generate_series(1, 1000) AS i;" db_ts1; done

Au bout, d'un moment le système de fichier du tablespace est plein et tout requête générant des écritures sort en erreur avec un message de cette forme :

ERROR:  could not extend file "pg_tblspc/16385/PG_9.0_201008051/16386/16390": wrote only 4096 of 8192 bytes at block 58438
HINT:  Check free disk space.

Ensuite, on essaye avec le répertoire PGDATA, on crée donc une table dans la base db_data :

$ psql db_data
psql (9.0.4)
Type "help" for help.

db_data=# CREATE TABLE t (i int);
CREATE TABLE

De la même façon, on remplit cette table jusqu'à épuisement de l'espace libre :

$ while ((1)); do psql -c "INSERT INTO t SELECT generate_series(1, 1000) AS i;" db_data; done

Résultat, les requêtes sortent en erreur de la même façon. On a peut-être de la chance ici, le filesystem contenant pg_xlog, les problèmes pourraient être pis.

Il est également possible de remplir le système de fichiers de journaux de transactions, ce qui est problématique du fait que chaque transaction est écrite ici tout tablespace confondu. On vide donc les deux bases :

$ psql db_ts1
psql (9.0.4)
Type "help" for help.

db_ts1=# TRUNCATE t;
TRUNCATE TABLE

$ psql db_data
psql (9.0.4)
Type "help" for help.

db_data=# TRUNCATE t;
TRUNCATE TABLE

Et on place le paramètre checkpoint_segments à 300, valeur démesurée par rapport à la place disponible dans PGDATA.

Après un reload, on refait alors le test de remplissage de la base db_ts1, qui assure que les journaux de transactions seuls remplissent PGDATA.

Le serveur PostgreSQL s'arrête parce qu'il ne peut écrire le journal de transaction :

PANIC:  could not write to file "pg_xlog/xlogtemp.8795": No space left on device
STATEMENT:  INSERT INTO t SELECT generate_series(1, 1000) AS i;
LOG:  server process (PID 8795) was terminated by signal 6: Aborted
LOG:  terminating any other active server processes
WARNING:  terminating connection because of crash of another server process
DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.
HINT:  In a moment you should be able to reconnect to the database and repeat your command.
LOG:  all server processes terminated; reinitializing

Il redémarre illico, mais le problème persiste :

FATAL:  the database system is in recovery mode
LOG:  database system was interrupted; last known up at 2011-10-12 00:01:40 CEST
LOG:  database system was not properly shut down; automatic recovery in progress
LOG:  consistent recovery state reached at 0/5FDB7480
LOG:  redo starts at 0/5AA57D68
LOG:  could not open file "pg_xlog/000000010000000000000074" (log file 0, segment 116): No such file or directory
LOG:  redo done at 0/73FFFF90
LOG:  last completed transaction was at log time 2011-10-12 00:05:11.710639+02
PANIC:  could not write to file "pg_xlog/xlogtemp.8797": No space left on device
LOG:  startup process (PID 8797) was terminated by signal 6: Aborted
LOG:  aborting startup due to startup process failure

Le seul moyen de se sortir de cette situation est d'utiliser les 5% d'espace libre du filesystème réservés au super utilisateur, de baisser la valeur de checkpoint_segments à une valeur évitant le filesystem full, les checkpoints successif supprimeront les fichiers en trop.

Lorsqu'on a plus besoin de 5% réservés, il ne faut pas oublier de remettre la réservation.

Enfin, ce cas le plus critique peut arriver assez facilement lorsqu'on a de l'archivage, si le serveur ne peut plus archiver les fichiers WAL, alors il les conserve, un filesystem full sur un espace d'archivage peut donc être transmis au serveur...

mardi 4 octobre 2011

Passer de X.org natif à modular

X.org est fourni dans le basesys et dans pkgsrc, on appelle le premier « native » et le second « modular » selon la valeur de la variable X11_TYPE que l'on positionne dans son /etc/mk.conf pour signifier à pkgsrc sur lequel linker.

Il s'agit des mêmes versions à peu de choses prêt, et X.org native n'est pas vieux ou non maintenu comme la rumeur voudrait le faire croire. Il est juste en retard parce qu'il suit le cycle de release du basesys alors que modular suit celui de pkgsrc et est tiré vers l'avant par les packages qui en dépendent. Cela peut poser problème lorsqu'on suit la cible mouvante qu'est pkgsrc-current.

La première chose à faire pour passer de native à modular est d'éditer /etc/mk.conf pour changer X11_TYPE, on en profite pour ne plus compiler le native :

MKX11=no 
X11_TYPE=modular

Ensuite, il faut modifier la liste de packages à compiler pour y ajouter soit tout modular en installant meta-pkgs/modular-xorg, soit en n'installant que le nécessaire, ça fait plus cool, dans /usr/pkgsrc/pkgchk.conf (si vous avez suivi les docs ici ou dans le wiki) :

meta-pkgs/modular-xorg-apps
meta-pkgs/modular-xorg-libs
meta-pkgs/modular-xorg-fonts
x11/xf86-input-keyboard
x11/xf86-input-mouse
x11/xf86-input-void
x11/xf86-video-nv
x11/xf86-video-vesa
x11/xf86-video-vga
x11/xf86-video-wsfb
x11/modular-xorg-server

Ensuite on donne ça à manger à pkg_comp ou à son bulk. L'important ici est de tout recompiler pour bien transférer les dépendances de native vers modular : en gros on pète la sandbox, que ça soit de mk/bulk ou pkg_comp et on recommence. Etant passé en mode bulk partiel comme indiqué dans un précédent post, voici comment faire :

1. On vérifie avec mount que la standbox n'est pas montée ni qu'un build tourne (dans ce cas faut le killer), sinon :

# /usr/sandbox/sandbox umount

2. On vérifie que le contenu des mk.conf du système et de la sandbox sont en phase, c'est le seul fichier de la sandbox à sauver

3. On recrée la sandbox :

# rm -rf /usr/sandbox
# cd /usr/pkgsrc/mk/bulk
# sh mksandbox --without-x /usr/sandbox

4. On nettoie les packages déjà compilés, pour forcer leur recompilation, et les fichiers cachés du bulk build :

# cd /usr/pkgsrc/packages/
# mv CVS .CVS
# rm -rf *
# mv .CVS CVS
# cd /usr/pkgsrc
# rm .broken.html .bulk_build_id .bulk_db .bulklock .depends .dependstree .index  .order .start .supports
# pkgclean

5. On lance le bulk build et on attend, il faut prévoir entre 150 et 200 packages supplémentaires à compiler.

Les étapes suivantes sont simples, on sauvegarde tout ce qu'il faut dans /usr/pkg, puis on supprime tous les packages installés :

# cd /usr
# mv pkg pkg.old
# cd /var/db
# mkdir old_pkgdb
# mv pkg pkg.refcount old_pkgdb
# rm -rf pkgin

A partir d'ici, on n'a plus de programmes issus de pkgsrc, en gros par de sudo, vim et autres...

Puis, il faut supprimer les fichiers des sets de X.org, on se base pour cela sur le contenu de /etc/mtree/set.x*. On en arrive donc à un stade où on est dans la même situation qu'après une installation du système sans les sets de X.org natif.

Enfin, on réinstalle tout les packages avec pkgin, qui dans sa version 0.5.0 (du CVS) peut importer une liste de packages au format de pkg_chk, qui se trouve être le même utilisé par le bulk-build, comme de par hasard :

# pkg_add http://pkgsrc.orgrim.net/NetBSD/5.1/amd64/All/pkg_install-20110805.tgz
# mkdir -p /usr/pkg/etc
# cp -r /usr/pkg.old/etc/pkgin /usr/pkg/etc
# ./pkgin up
# ./pkgin im pkgchk.conf

Note : si le pkg_add de pkg_install ne passe pas, essayer avec -f.

et hop, reste plus qu'à reconfigurer les chemins dans /etc/X11/xorg.conf et c'est bon.

vendredi 19 août 2011

Bulk build partiel de pkgsrc

En suivant l'excellent tip de Mr GuiGui2, j'ai pu monter ma petite archi de bulk build personnelle pour fournir du package tout frais à pkgin.

J'ai donc ajouté le bloc magique suivant à mon /etc/mk.conf, qui permet de gérer la présence de commentaires dans pkgchk.conf :

# bulk build config
DEPENDS_TARGET= bulk-install
BATCH=          yes

BULK_PREREQ+=   pkgtools/lintpkgsrc
.if defined(SPECIFIC_PKGS)
PKGLIST!=               awk '$$1 !~ /^\#/ {print $$1}' ${PKGCHK_CONF}
.       for _pkg_ in ${PKGLIST}
HOST_SPECIFIC_PKGS+=    ${_pkg_}
.       endfor
.endif

Pour aller plus loin, j'ai automatisé le process au maximum pour lancer des bulk build réguliers par cron, grâce au script bulk-builder. Ce script remplace do-sandbox-build et do-sandbox-upload, il est également capable de gérer des chemins alternatifs, mettre à jour pkgsrc avant de lancer le bulk.

La procédure pour mettre ça en place est donc :

1. Ajouter le bloc montré plus haut à /etc/mk.conf et y définir PKGCHK_CONF, il s'agit du chemin vers une liste de packages au format "catetgorie/package", un par ligne, qu'on peut automatiquement créer avec pkg_chk -g.

2. Créer la sandbox :

# cd /usr/pkgsrc/mk/bulk
# sh mksandbox --without-x /usr/sandbox

3. Créer et configurer /usr/pkgsrc/mk/bulk/build.conf :

# cd /usr/pkgsrc/mk/bulk
# cp build.conf-example build.conf
# vi build.conf

4. Lancer le bulk build :

# sh bulk-builder -u -R
  • -u demande de cvs up le répertoire /usr/pkgsrc avant de commencer
  • -R demande de ne pas uploader le résultat (les packages)

Enfin, il suffit d'utiliser la ligne suivante pour utiliser les packages avec pkgin, dans /usr/pkg/etc/pkgin/repositories.conf :

file:///usr/pkgsrc/packages/All

jeudi 18 août 2011

Montrer les dépendances avec make dans pkgsrc

Généralement, on peut savoir quelles sont les dépendances d'un package en utilisant make show-depends, mais cela ne montre que les dépendances pour l'installation, les dépendances pour la compilation ne sont pas montrées.

$ cd /usr/pkgsrc/databases/postgresql90-server/
$ make show-depends
postgresql90-client>=9.0.4:../../databases/postgresql90-client

Pour connaître les dépendances selon leur type (installation ou compilation), on peut utiliser la cible show-depends-pkgpaths alliée à la variable DEPENDS_TYPE.

Pour avoir seulement les dépendances de compilation :

$ make DEPENDS_TYPE=build show-depends-pkgpaths
devel/bison
devel/gmake
pkgtools/digest

Pour avoir seulement celles d'installation :

$ make DEPENDS_TYPE=install show-depends-pkgpaths
databases/postgresql90-client

Et enfin pour montrer les deux types, qui est aussi le comportement par défaut :

$ make DEPENDS_TYPE=all show-depends-pkgpaths
databases/postgresql90-client
devel/bison
devel/gmake
pkgtools/digest

$ make show-depends-pkgpaths
databases/postgresql90-client
devel/bison
devel/gmake
pkgtools/digest

Pour plus d'information, le Makefile qui contrôle cette cible est mk/bsd.utils.mk.

samedi 6 août 2011

Le client de la BuildFarm de PostgreSQL dans pkgsrc-wip

Comme j'annonçais précédemment, je contribue deux machines NetBSD à la BuildFarm de PostgreSQL. La compilation ne se fait automagiquement qu'après la configuration du client (écrit en Perl). Il n'est d'ailleurs pas forcément très convi à installer, c'est pourquoi je l'ai packagé pour pkgsrc : http://pkgsrc.se/wip/pgbuildfarm.

En espérant qu'il soit ajouté à l'arbre officiel...

Voici la configuration pour lancer des builds sur NetBSD, dans /usr/pkg/etc/pgbuildfarm/build-farm.conf, en commençant par le chemin du miroir du dépôt Git :

# Modifier dans %conf
scmrepo => '/usr/pgbuildfarm/pgsql-base.git',

Le client est destiné à être lancé par cron, connu pour son environnement light, c'est pourquoi les paramètres d'environnement doivent être adaptés :

  • Le make GNU s'appelle gmake chez nous
  • Pas mal de programmes proviennent de pkgsrc, il faut donc que le client ait /usr/pkg/bin dans son PATH, et puisse trouver les bibliothèques issues des packages.
make => 'gmake',
aux_path => "/usr/pkg/bin",

build_env =>
{
    PATH => "/usr/pkg/bin:$ENV{PATH}",
    LD_LIBRARY_PATH => "/usr/pkg/lib",
},

config_env =>
{
    CC => 'gcc',
    PATH => "/usr/pkg/bin:$ENV{PATH}",
    LD_LIBRARY_PATH => "/usr/pkg/lib",
},

Enfin le plus important, les options du configure, la plupart nécessitent des packages supplémentaires comme python ou la libxml. Ce qui est primordial ici est d'utiliser le « template » NetBSD :

config_opts =>
[qw(
    --enable-cassert
    --enable-debug
    --enable-nls
    --enable-integer-datetimes
    --with-perl
    --with-python
    --with-tcl
    --with-krb5
    --with-includes=/usr/include/krb5:/usr/pkg/include
    --with-libraries=/usr/pkg/lib
    --with-openssl
    --with-template=netbsd
    --enable-thread-safety
)],

Pour toutes ces options, les packages suivants ont été installés :

  • devel/bison
  • devel/flex
  • lang/python26 (et pkgtools/pkg_alternatives pour avoir le lien python)
  • lang/perl5
  • lang/tcl
  • textproc/libxml2
  • textproc/libxslt
  • devel/readline

P.S. : Il n'y a que les particularités de NetBSD décrites ici, en complément du wiki de PostgreSQL.

vendredi 5 août 2011

Could not open relation with oid N

On peut parfois trouver cet étrange message d'erreur dans les traces de PostgreSQL (N étant un nombre) ou lors de l'exécution d'une requête :

ERROR:  could not open relation with OID N

Si on recherche ce message dans les mailing-lists du projet, on peut facilement conclure que la base de données est corrompue, qu'il y a des problèmes matériels et que la sécurité des données est en péril. Et bien, ce n'est pas forcément le cas : obtenir ce message peut être tout à fait normal.

Pour démontrer cela, on a besoin d'une table :

$ createdb test
$ psql test
psql (9.0.4)
Type "help" for help.

test=# CREATE TABLE truc AS SELECT generate_series(0, 5) AS i;
SELECT 6
test=#

On lance une session qui bloquerait un DROP de cette table, pour cela on pose un verrou exclusif, le mode « ExclusiveLock » ne laisse passer que les lectures (c'est important pour la suite) :

$ psql test
psql (9.0.4)
Type "help" for help.

test=# BEGIN;
BEGIN
test=# LOCK TABLE truc IN EXCLUSIVE MODE;
LOCK TABLE
test=#

On laisse cette transaction « ouverte », avec le verrou posé et on lance une session pour supprimer la table :

$ psql test
psql (9.0.4)
Type "help" for help.

test=# BEGIN;
BEGIN
test=# DROP TABLE truc;

L'ordre SQL DROP TABLE ne rend pas la main, cette deuxième session attend le verrou « AccessExclusiveLock », qui est le plus restrictif, sur la table pour pouvoir la supprimer. La page http://wiki.postgresql.org/wiki/Loc... fournie une requête montrant les dépendances entre requêtes du point de vue du verrouillage. Dans ce cas, elle donne le résultat suivant :

 waiting_locktype | waiting_table |  waiting_query   |    waiting_mode     | waiting_pid | other_locktype | other_table |      other_query      |  other_mode   | other_pid | other_granted 
------------------+---------------+------------------+---------------------+-------------+----------------+-------------+-----------------------+---------------+-----------+---------------
 relation         | truc          | DROP TABLE truc; | AccessExclusiveLock |       25632 | relation       | truc        | <IDLE> in transaction | ExclusiveLock |     24217 | t

On lance une troisième session, avec un SELECT sur notre table :

$ psql test
psql (9.0.4)
Type "help" for help.

test=# SELECT * FROM truc;

L'ordre SELECT ne rend pas la main, cette troisième session se met à attendre le DROP TABLE et la première session. C'est d'ailleurs le DROP TABLE qui bloque réellement le SELECT, car la première session à verrouillé la table en lecture seule :

 waiting_locktype | waiting_table |    waiting_query    |    waiting_mode     | waiting_pid | other_locktype | other_table |      other_query      |     other_mode      | other_pid | other_granted 
------------------+---------------+---------------------+---------------------+-------------+----------------+-------------+-----------------------+---------------------+-----------+---------------
 relation         | truc          | SELECT * FROM truc; | AccessShareLock     |       28629 | relation       | truc        | DROP TABLE truc;      | AccessExclusiveLock |     25632 | f
 relation         | truc          | DROP TABLE truc;    | AccessExclusiveLock |       25632 | relation       | truc        | <IDLE> in transaction | ExclusiveLock       |     24217 | t
 relation         | truc          | SELECT * FROM truc; | AccessShareLock     |       28629 | relation       | truc        | <IDLE> in transaction | ExclusiveLock       |     24217 | t
 relation         | truc          | DROP TABLE truc;    | AccessExclusiveLock |       25632 | relation       | truc        | SELECT * FROM truc;   | AccessShareLock     |     28629 | f
(4 rows)

On libère la première session :

test=# ROLLBACK;
ROLLBACK
test=#

Le DROP TABLE passe, et le SELECT continue d'attendre :

 waiting_locktype | waiting_table |    waiting_query    |  waiting_mode   | waiting_pid | other_locktype | other_table |      other_query      |     other_mode      | other_pid | other_granted 
------------------+---------------+---------------------+-----------------+-------------+----------------+-------------+-----------------------+---------------------+-----------+---------------
 relation         | truc          | SELECT * FROM truc; | AccessShareLock |       28629 | relation       | truc        | <IDLE> in transaction | AccessExclusiveLock |     25632 | t

On voit que le SELECT attend la transaction qui a lancé le DROP TABLE. Même si le DROP TABLE est terminé, son effet ne sera connu des transactions concurrentes seulement ou moment du commit ou rollback, parce qu'on utilise le niveau d'isolation des transaction « read committed » (par défaut). Il n'y a pas de « UNLOCK » sur les tables dans PostgreSQL, il faut attendre la fin de la transaction pour que les verrous soient libérés, du moins lorsqu'on n'utilise pas de savepoints.

Maintenant, on valide le DROP TABLE, avec l'ordre COMMIT. Le SELECT termine en erreur, on voit alors le message dans les logs :

ERROR:  could not open relation with OID 17366 at character 15
STATEMENT:  SELECT * FROM truc;

Lorsque le SELECT n'est plus bloqué par le verrou, il ne peut accéder pas à la table car elle n'existe plus. Le message n'est pas très explicite parce que la requête est en cours d'exécution : le moteur a déjà terminé le travail de parsing et de planification, il ne travaille qu'avec les OID qu'il a récupéré du catalogue système à ce moment là.

Dans ce cas précis, obtenir ce message n'est un problème de corruption de la base ou du catalogue système.

Deux machines NetBSD dans la buildfarm de PostgreSQL

Il y a quelques temps, j'avais remarqué que seules deux machines NetBSD étaient présentes dans la buildfarm de PostgreSQL sur du powerpc et du mips. Pas de machines i386 et amd64, qui sont pourtant les architectures phares du TIER 1, et ce depuis plus d'un an.

L'affront est désormais réparé et deux nouveaux drapeaux oranges flottent fièrement dans les prés de la buildfarm, fournies par votre serviteur :

  • panther : NetBSD 5.1 gcc 4.1.3 i386
  • smilodon : NetBSD 5.1 gcc 4.1.3 amd64

Les machines tournent maintenant depuis quelques temps, sans aucun soucis.

jeudi 4 août 2011

pitrery : des scripts pour faciliter la sauvegarde PITR

Gérer une sauvegarde PITR (Point-In-Time Recovery), permettant de revenir à un point précis dans le temps sur un serveur PostgreSQL, n'est pas forcément évident à mettre en place et ensuite à gérer au quotidien : la restauration en cas de problème peut même prendre beaucoup de temps à mettre en place, avec des possibilités d'erreurs non négligeables. C'est d'autant plus stressant que la pression monte quand on en a besoin...

C'est pourquoi, dans le cadre de mon travail, j'ai écrit un ensemble de scripts shell en Bash (ce qui permet d'utiliser des commandes qui sont déjà sur une installation classique de Linux) pour faciliter ce travail. Le code est hébergé sur github : https://github.com/dalibo/pitrery. La version 1.0 vient d'être taguée. C'est encore un peu brut de décoffrage niveau documentation, mais les scripts ont été testés dans tous les sens, donc ce jeu d'outils devrait être utilisable en production sans trop de difficultés.

Il y a un Makefile GNU, à l'ancienne, les fichiers de configuration sont plein de commentaires pour expliquer et il y des « usage » pour chaque script à base de -? ou -h qui devraient parler à ceux qui savent déjà gérer du PITR.

mardi 5 juillet 2011

Configuration réseau pour virtualiser chez OVH

Sur mon serveur chez OVH, j'ai un ensemble de machines virtuelles KVM et (bientôt) de conteneurs LXC. Pour fournir du réseau à tout ce petit monde, j'utilise de l'IPv4 et de l'IPv6, voici comment c'est configuré.

Pour l'IPv4, on a un nombre limité d'IP publiques parce que ça vaut de la thune et que ça va être de plus en tendu de multiplier les adresses, il nous faut un réseau privé (beurk), du NAT (rebeurk) et des redirections à base d'iptables (re-rebeurk). Il nous faut surtout un bridge, c'est une Debian donc ça se passe dans /etc/network/interfaces :

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet manual

auto br0
iface br0 inet static
	bridge_ports eth0
	bridge_fd 0
	bridge_maxwait 0
	address 188.165.205.xxx
	netmask 255.255.255.0
	network 188.165.205.0
	broadcast 188.165.205.255
	gateway 188.165.205.254
	post-up iptables -t nat -A POSTROUTING -s 10.42.0.0/24 -o br0 -j SNAT --to 188.165.205.xxx
	pre-down iptables -t nat -D POSTROUTING -s 10.42.0.0/24 -o br0 -j SNAT --to 188.165.205.xxx

iface br0 inet6 static
	address 2001:41D0:xxxx:96ce::1
	netmask 64

auto br0:0
iface br0:0 inet static
	address 10.42.0.1
	netmask 255.255.255.0

Pour le bridge, on met l'IPv4 publique et une règle iptable pour NAT-er ce qui sort. Le réseau privé est 10.42.0.0/24, on peut bien sûr choisir ce qu'on veut dans ce que fourni la RFC 1918.

Pour l'IPv6, chez OVH c'est bizarre : il fournissent un prefix 64 bit et avec une route accessible sur le /56 incluant, mais pas à partir du /64. Il faut donc ruser de la façon suivante niveau routage :

  • On assigne l'IPv6 avec le prefix /64, c'est plus propre, sinon le kernel se plaint avec /56 :
# ip -6 addr add 2001:41D0:xxxx:96ce::1/64 dev br0
  • On met une route pour atteindre la gateway dans le /56 :
# ip -6 route add 2001:41d0:xxxx:96ce::/56 dev br0
  • On met la route par défaut :
# ip -6 route add default via 2001:41d0:xxxx:96ff:ff:ff:ff:ff

Pour avoir cette configuration appliquée automatiquement, il faut créer /etc/network/if-up.d/ovh-ipv6-route :

#!/bin/sh

if [ "$IFACE" = "br0" ]; then
	ip -6 route add 2001:41d0:2:96ce::/56 dev br0
	ip -6 route add default via 2001:41d0:2:96ff:ff:ff:ff:ff
fi

Et pour l'arrêt, /etc/network/if-down.d/ovh-ipv6-route :

#!/bin/sh

if [ "$IFACE" = "br0" ]; then
	ip -6 route del default via 2001:41d0:2:96ff:ff:ff:ff:ff
	ip -6 route del 2001:41d0:2:96ce::/56 dev br0
fi

Il faut ensuite activer le forward entre interfaces, sinon pas de routage vers les VM, dans /etc/sysctl.conf :

net.ipv6.conf.all.forwarding=1

Enfin, le routage chez OVH ne permet pas d'intercaler un routeur pour découper le /64, il faut donc ruser avec du proxy NDP (Neighbor Discovery Protocol) :

  • On active le proxy NDP, dans /etc/sysctl.conf :
net.ipv6.conf.all.proxy_ndp=1
  • On ajoute les IP à proxiser, celle de la gateway et chacune des IP fournies aux VM :
# ip neigh add proxy 2001:41d0:xxxx:96ff:ff:ff:ff:ff dev br0
# ip neigh add proxy 2001:41d0:xxxx:96ce::2 dev br0

En cas de reboot d'une VM, il faut d'abord virer ses IP du proxy, puis les remettre, sinon le kernel de la VM se plein d'une duplication d'adresse :

# ip neigh del proxy 2001:41d0:xxxx:96ce::2 dev br0
-> reboot de la VM
# ip neigh add proxy 2001:41d0:xxxx:96ce::2 dev br0

lundi 4 juillet 2011

NetBSD en KVM

Comme je viens d'investir dans un (le 16G), je me suis dis qu'avoir quelques machines NetBSD pour servir la bonne cause ça serait bien cool.

En fait, j'ai deux besoins :

  • Avoir un dépôt de paquet et de quoi fait des bulk build réduits de pkgsrc pour me permettre de n'utiliser uniquement l'excellent pkgin
  • Fournir des machines à la Build Farm de PostgreSQL, parce qu'il n'y a même pas de machine NetBSD en i386 et en amd64 (seulement powerpc et mips)

J'ai donc décidé de monter 2 machines NetBSD en KVM sur ma grosse box Debian, qui fait tourner ça grâce à KVM.

Pour l'installation, il y a 2 possibilités, soit on fait du VNC, soit on redirige la sortie VGA dans du curses. Pour le disque j'ai choisi de poser directement les données sur des volumes logiques, dans ce cas, il faut désactiver le cache ce qui permet une infime perte de puissance en I/O.

Il faut commencer par récupérer l'ISO d'installation :

wget ftp://ftp.fr.netbsd.org/pub/NetBSD/iso/5.1/amd64cd-5.1.iso

Ensuite, créer le volume logique (on a choisi de donner 50 Go) et lancer l'installation, en curses ça passe nickel, il faut choisir d'utiliser la console serie :

kvm -drive file=/dev/system/kvm-nb64-d1,cache=none \
    -m 1024 \
    -net nic,model=e1000 -net tap \
    -name nb64 \
    -curses \
    -cdrom /home/orgrim/netbsd/amd64cd-5.1.iso \
    -boot d \
    -k fr

Une fois installé, on lance la commande suivante dans un screen, on demande à KVM de fournir l'accès console en série dans un fichier, ce qui permet d'avoir la console QEMU disponible directement dans le screen :

kvm -nographic \
    -drive file=/dev/system/kvm-nb64-d1,cache=none \
    -m 1024 \
    -net nic,model=e1000,macaddr=DE:AD:BE:EF:37:D1 -net tap \
    -name nb64 \
    -boot c \
    -serial unix:/tmp/nb64.sock,server,nowait

Note: merci de changer la MAC de l'interface réseau, c'est utilisé en prod chez moi :)

Enfin, il est important de démarrer le noyau NetBSD sans ACPI ni SMP (en mettant le defaut à 4 dans /boot.cfg)

Pour accéder à la machine en console série :

minicom -D unix#/tmp/nd64.sock

Si on a oublié de choisir la console série, on peut l'activer de cette façon :

  1. Booter avec -curses -k fr à la place de -nographic
  2. Lancer la commande suivante pour activer la console série :
# installboot -v -o console=com0,speed=19200 /dev/rwd0a /usr/mdec/bootxx_ffsv1
File system:         /dev/rwd0a
Primary bootstrap:   /usr/mdec/bootxx_ffsv1
Boot options:        timeout 5, flags 0, speed 19200, ioaddr 0, console com0

mercredi 29 juin 2011

Rediriger stdout/stderr depuis un script avec du pipe

Pour rediriger stdout/stderr à l'interieur vers l'entrée standard d'un commande, il faut utiliser exec et du sous-shell. Cette astuce est un bashisme a priori.

L'objectif est de renvoyer tous les messages du script dans syslog sans mettre de redirection sur la ligne de commande. Le principe général est :

exec FD> >(COMMAND)
  • FD est le numéro du file descriptor, 1 pour stdout, 2 pour stderr
  • COMMAND est la commande a exécuter, elle doit bien sûr lire les données en entrée.

Un plus gros exemple :

SYSLOG="no"
 
# Load configuration file
CONFIG=/etc/myconfig.conf
if [ -f "$CONFIG" ]; then
    . $CONFIG
fi
 
# Redirect output to syslog if configured
if [ "$SYSLOG" = "yes" ]; then
    SYSLOG_FACILITY=${SYSLOG_FACILITY:-local0}
    SYSLOG_IDENT=${SYSLOG_IDENT:-postgres}
 
    exec 1> >(logger -t ${SYSLOG_IDENT} -p ${SYSLOG_FACILITY}.info)
    exec 2> >(logger -t ${SYSLOG_IDENT} -p ${SYSLOG_FACILITY}.err)
fi

avec dans la configuration :

  • SYSLOG : mettre à yes ou no pour activer
  • SYSLOG_FACILITY : ou envoyer
  • SYSLOG_IDENT : avec quel tag

L'exemple est pris d'un de mes scripts d'archivage pour PostgreSQL, ce qui permet de logguer dans syslog sans mettre de pipe dans archive_command (ce qu'il ne faut pas faire parce que ça casse : le code retour donné à PostgreSQL est celui de la dernière commande de la chaîne de pipe)

mardi 21 juin 2011

Combiner des PDF en un seul

Pour combiner des pdf en un seul, on peut essayer pdfjoin fournit par le projet pdfjam. En attendant que les 250 Mo de dépendances (Latex principalement) s'installent, on peut utiliser ghostscript :

gs -sDEVICE=pdfwrite -dNOPAUSE -dQUIET -dBATCH -sOutputFile=../combined_doc.pdf *.pdf

Merci à http://www.perlmonks.org/?node_id=4...

- page 1 de 2