Ça fait un bon moment que je devais le publier ici-même : mon article sur Plague initialement publié dans GNU/Linux Magazine France est en ligne.
Planet
Nouvel article : Plague
Intimate Shared Memory (ISM) et Solaris x86 (2eme partie)
The Extreme Performance Tour (retour)
Le piège à con du jour : variables locales en python
Note pour plus tard. Soit un bout de code python qui ressemble à ça:
mavariable = None def fonction(): if mavariable is None: mavariable = 'toto' blablabla()
Ne pas oublier que dans ce cas la variable mavariable est locale à la fonction à cause de l’assignation à l’intérieur du if. La preuve :
>>> fonction() Traceback (most recent call last): File "", line 1, in File " ", line 2, in fonction UnboundLocalError: local variable 'mavariable' referenced before assignment
Grmpf.
Merci à Sam qui à fouillé dans la doc : c’est effectivement écrit là.
Installation et configuration de Munin, le maître des graphes
Déplacer une VM KVM «à la main»
Le contexte
J’ai une petite ferme de serveurs physiques sous Scientific Linux 6 qui hébergent des machines virtuelles KVM gérées par libvirt. J’utilise des volumes logiques LVM comme disques virtuels.
La plupart des VM ne sont pas critiques et supportent d’être arrêtées quelques minutes; par contre il vaut mieux ne pas les arrêter trop longtemps. Tout le problème est donc de recopier au moins le gros des données sans arrêter la VM.
Pour l’exemple, je déplacerai une VM vm1, dont le disque virtuel est sur /dev/vg00/vm1, de la machine physique host1 à sa soeur host2. On supposera pour l’exemple que les deux machines sont identiques; en tout cas il vaut mieux qu’elles disposent de la même version du paquet qemu-kvm. Et quand je dis la même version c’est la même version au patch près, sinon la VM risque de ne pas démarrer sur le nouvel hôte.
L’outil
Je sais faire la même manip avec des isolateurs à la place des VM: un coup de rsync et c’est marre. Ah, si rsync savait se débrouiller à peu près efficacement avec un périphérique bloc… Et ben ça existe, ça s’appelle lvmsync, c’est un script ruby qu’il suffit de poser sur les deux machines hôtes. En dehors de ça, vous aurez besoin de dmsetup (normalement il est installé, il fait partie du paquet device-mapper qui est lui-même une dépendance de libvirt) et root devra pouvoir se connecter en ssh sur host2 depuis host1.
La méthode
On commence par créer le volume logique sur host2:
lvcreate -l640 -nvm1 vg00
À adapter évidemment, il doit être identique à l’original qui se trouve sur host1.
Ensuite, sur host1, on prend un instantané du disque de la VM et on le copie sur host2:
lvcreate -L10G -s -nvm1-snap /dev/vg00/vm1 dd if=/dev/vg00/vm1-snap bs=10M | ssh root@host2 dd of=/dev/vg00/vm1 bs=10M
On peut maintenant éteindre la VM, puis synchroniser le disque. Ensuite on exporte la configuration de la VM.
virsh shutdown vm1 lvmsync /dev/vg00/vm1-snap host2:/dev/vg00/vm1 virsh dumpxml vm1 | ssh root@host2 'cat > /var/tmp/vm1.xml'
Reste plus, sur host2, qu’à importer et démarrer la VM:
virsh define /var/tmp/vm1.xml virsh start vm1
Une fois que c’est fait, on peut faire le ménage sur host1:
virsh undefine vm1 lvremove /dev/vg00/vm1-snap lvremove /dev/vg00/vm1
C’est prêt!
Et ça va quand même vachement plus vite comme ça. Maintenant, il va falloir écrire un bout de script pour emballer tout ça, on verra ça un autre jour.
Migrer une VM de Xen vers KVM
Un petit aide-mémoire sur la migration d’une machine virtuelle Linux de Xen (sur CentOS 5.x, mais ça ne doit pas jouer énormément) vers KVM (sur Scientific Linux 6.x, donc avec libvirt). L’essentiel de ce qui suit vient de cette page.
Préparation de la machine virtuelle
J’ai donc une VM Xen, appelons-la testxen1 (parce que c’est son nom), qui tourne sur l’ancien serveur. L’OS installé sur la VM est un CentOS 5. Sur cette machine virtuelle (qui tourne encore) :
- éditer /etc/inittab, enlever le getty(8) qui tourne sur la console Xen et décommenter ceux des terminaux virtuels habituels ;
- installer un noyau Linux «normal» (pas un noyau Xen) ;
- éditer /etc/fstab et remplacer les disques virtuels Xen (xvda, xvdb et ainsi de suite) par des disques IDE (hda, hdb…). NB : pas évident que cette étape soit partout la même, ça dépend peut-être de la version du noyau ; à tester.
- enlever le paramètre console=xvc0 du nouveau noyau dans /boot/grub/menu.lst, et s’assurer que le nouveau noyau est bien sélectionné par défaut ;
- installer grub :
[root@testxen1 ~]# grub Probing devices to guess BIOS drives. This may take a long time. GNU GRUB version 0.97 (640K lower / 3072K upper memory) [ Minimal BASH-like line editing is supported. For the first word, TAB lists possible command completions. Anywhere else TAB lists the possible completions of a device/filename.] grub> device (hd0) /dev/xvda device (hd0) /dev/xvda grub> root (hd0,0) root (hd0,0) Filesystem type is ext2fs, partition type 0x83 grub> setup (hd0) setup (hd0) Checking if "/boot/grub/stage1" exists... yes Checking if "/boot/grub/stage2" exists... yes Checking if "/boot/grub/e2fs_stage1_5" exists... yes Running "embed /boot/grub/e2fs_stage1_5 (hd0)"... 15 sectors are embedded. succeeded Running "install /boot/grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/boot/grub/stage2 /boot/grub/grub.conf"... succeeded Done. grub> quit quit
C’est fini pour la machine virtuelle, on peut maintenant l’arrêter.
Sur la nouvelle machine physique
On commence, bien sûr, par copier l’ancien (ou les anciens) disque(s) virtuel(s) sur la nouvelle machine. Dans mon cas, il s’agit de volumes logiques LVM, on fait bêtement ça à grands coups de dd(1). Reste juste à créer la nouvelle VM :
virt-install --connect qemu:///system -n testxen1 -r 1024 --vcpus=1 \ --disk /dev/vg00/testxen1 --vnc --os-type linux --accelerate \ --network=bridge=br102,model=virtio,mac=12:00:02:e3:00:01 \ --noreboot --keymap us --import
Quelques notes en vrac :
- bien évidemment, remplacer le nom de la machine (ici testxen1) par ce qu’on veut ;
- idem pour la mémoire (option -r) et le nombre de processeurs (option –vcpus) ;
- dans le cas où on a plusieurs disques virtuels, on peut répéter plusieurs fois l’option –disk, avec le disque de démarrage en premier ;
- pour l’option –network, replacer br102 par le nom du pont réseau qui va bien (si vous avez une config réseau différente, va falloir chercher
) et bien entendu l’adresse MAC par la bonne valeur.
Voilà, vous allez voir démarrer votre nouvelle VM sous vos yeux zébahis. Elle est pas belle, la vie ?
Intimate Shared Memory (ISM) et Solaris x86 (1er partie)
Retour (raté) au pays du wifi
J’en ai parlé ici même il y a quelques temps, j’ai échoué un peu par hasard et beaucoup par malchance avec un point d’accès wifi D-Link. Je l’avais plus ou moins laissé moisir dans un coin (et avec son firmware d’origine) faute de possibilité de l’utiliser avec OpenWrt. J’ai voulu m’en resservir il y a quelques jours : une brique, 2 ou 3 mois après l’expiration de sa garantie. Ça s’allume, témoin de mise en marche ambre (il devrait pas être vert, normalement ?), pas de wifi, pas de lien ethernet sur aucun de ses ports, pas de cheat code magique pour remettre un firmware en face des trous.
Au programme : acheter un point d’accès qui marche. Pas un D-Link, cette fois-ci.
Du modèle EAV, ou comment s’arracher les cheveux, pour plein de raisons
Dans le cadre du projet SOIF (qui fera l’objet d’un prochain billet), je travaille actuellement sur un job de transformation d’une base Puppet facts pour injecter dans iTop.
Avant tout, un peu de contexte : Puppet intègre la commande facter qui renvoie tout un ensemble d’informations sous la forme attribut=valeur, et qui est facilement extensible, bref c’est très intéressant. Là où le bat blesse, c’est que – logiquement – ces informations sont stockées en base de données en utilisant le modèle de conception EAV pour Entity Attribute Value.
Ce qui donne :
+------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | MUL | NULL | | | updated_at | datetime | YES | | NULL | | | created_at | datetime | YES | | NULL | | +------------+--------------+------+-----+---------+----------------+
et
mysql> desc fact_values; +--------------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | value | text | NO | | NULL | | | fact_name_id | int(11) | NO | MUL | NULL | | | host_id | int(11) | NO | MUL | NULL | | +--------------+----------+------+-----+---------+----------------+
Donc, pour obtenir par exemple la fact serialnumber pour un hôte donné, cela donne quelque chose du genre :
select fact_values.value FROM fact_values LEFT JOIN hosts ON fact_values.host_id = hosts.id LEFT JOIN fact_names ON fact_values.fact_name_id = fact_names.id WHERE hosts.name = 'xxx' AND fact_names.name = 'serialnumber';
Mais maintenant, retour à mon objectif : travailler par lot, c’est à dire pour tous les enregistrements de la table hosts. Se pose donc la question de comment faire pour obtenir la liste de tous les hôtes, avec la valeur serialnumber, à froid, pour l’expert SQL que je ne suis pas, mon premier jet à donné :
SELECT hosts.id, fact_values.value FROM hosts LEFT JOIN fact_values ON hosts.id = fact_values.host_id LEFT JOIN fact_names ON fact_names.id = fact_values.fact_name_id WHERE fact_names.name = 'serialnumber';
qui semble fonctionné à priori, mais, MAIS, cela ne renvoie que les enregistrements pour lesquels il existe la valeur serialnumber.
Après de nombreuses recherches, il semblerait que la seule solution à mon problème soit :
SELECT
hosts.id,
(SELECT fact_values.value
FROM fact_values
JOIN fact_names
ON fact_names.id = fact_values.fact_name_id
WHERE fact_names.name = 'serialnumber'
AND fact_values.host_id = hosts.id) as serialnumber,
FROM hosts;
et là, on le voit tout de suite, ça devient compliqué. Pourquoi ? parce que je n’ai pas seulement besoin du serialnumber mais de nombreux autres champs, et pour chaque champ que je veux, je dois donc rajouter un nouveau SELECT, je vous laisse deviner l’impact sur les performances.
Bref, EAV, ça pue. Bien évidemment si vous avez une autre solution, n’hésitez pas à commenter !