Fake fencing sur Red Hat6

Plop …

Un petit retour sur l’article du failover mysql avec les clusters de red hat.

Lorsque l’on test son système de failover (machines virtuelles, ou …) sans matériel de fencing (switchs/routeurs/périph dédiés), les tests d’arrêts complets (coupure de courant barbare) échouent lamentablement, car pour switcher les services d’une machine à une autre, le daemon fenced envoie un signal de reboot au serveur ayant la main sur le service via le système de fencing, et si celui-ci ne répond pas, le service n’est pas redémarré (il considère qu’il a lui-même un problème), ne permettant pas de valider le test du « Si la machine crash, est-ce qu’une autre prendra le service ? ».

Il existe cependant des services permettant de faker ces systèmes lors d’utilisation de machines virtuelles, malheureusement pour moi, je n’utilise pas le système de machine virtuelle xen directement sur red hat6, ne me permettant de de jouir des agents fence RHEV-M et libvirt pour les machines virtuelles, et désactiver l’accès au groupe de fence empêche le rgmanager de se lancer (pose de lock sur dlm-control et dlm-rgmanager qui n’existe pas sans fence) donc passage obligé à la création d’un « fake fence ».

Pour simuler le système de fence de RHEV-M (un simple client http qui balance 3 requêtes pour vérifier si la machine existe et lance l’action de reboot), j’ai donc crée un serveur Web en python, qui renvoit des XMLs complétement faux, mais qui contiennent les infos que l’agent fence_rhevm cherche, lui permettant de valider le reboot et de débloquer le service!!!

Le codage est très simple (avec une belle XSS en prime ^^, mais comme l’agent fence_rhevm crée ses propres urls de requêtes … osef ;=)) :


#!/usr/bin/python

import SocketServer
import re

status="down"
class FenceHandler(SocketServer.BaseRequestHandler):
    def handle(self):
	global status
        self.data = self.request.recv(4096)
	re_get_name = re.compile("GET /api/vms/\?search=name%3D(.*) HTTP/1.1",re.IGNORECASE)	
	result_name = re_get_name.search(self.data)

	#If there is no Get, it's a posted action
	if result_name == None:
		self.request.send("HTTP/1.1 200 OK\r\nContent-Type: application/xml\r\n\r\n\r\n")
		status="up"
		return

	#Else it's a normal request, answer ...
	name = "none"

	if result_name != None:
		name = result_name.group(1)
	self.request.send("HTTP/1.1 200 OK\r\nContent-Type: application/xml\r\n\r\n"+name+"server"+status+"\r\n")
	status = "down"

if __name__ == "__main__":
    HOST, PORT = "0.0.0.0", 9999
    server = SocketServer.TCPServer((HOST, PORT), FenceHandler)
    server.serve_forever()

Une fois lancé sur une machine, il ne reste plus qu’à fournir les infos dans le cluster.conf (à titre d’exemple) :


	
		
			
		
	


	

Si jamais le script est modifié par WP, il reste accessible ici : https://raw.github.com/cipher16/script/master/fence.py (à n’utiliser que pour des tests bien sûr ;))