scripts/gestion/iscsi/slonlib.py
2015-02-01 13:27:02 +01:00

163 lines
5.3 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# slonlib.py
# ----------
# Copyright : (c) 2008, Jeremie Dimino <jeremie@dimino.org>
# Licence : BSD3
'''Bibliothèque pour accéder à la baie de stockage'''
import telnetlib
import re
from xml.etree.ElementTree import ElementTree, fromstring
# Message envoyé par le serveur pour attendre l'appuie sur touche
junk_regexp = re.compile("Press any key to continue \(Q to quit\)\r *\r")
# Matching des fin de lignes avec \r\n
crlf_regexp = re.compile("\r\n")
username = ""
password = ""
# Récupére des identifiants
execfile("/etc/crans/secrets/slon.py")
# La réponse à la commande "show volume-maps" est une séquence
# d'éléments de la forme:
#
# nom du volume
# |
# v
# > Volume [SN 00c0ffd5e51800004ca1454901000000, Name (o2_slash)] mapping view:
# > CH ID LUN Access Host-Port-Identifier Nickname
# > -------------------------------------------------------------------------------
# > 0,1 0 4 rw all other hosts
# ^
# |
# lun
#
# On utilise cette expression un peu barbare pour récupérer les deux
# informations qui nous intéresssent :
volume_map_regexp = re.compile("Volume \[SN [0-9a-f]+, Name \(([^)]*)\)\][^\n]*\n[^\n]*\n[^\n]*\n[0-9,-]+ *[0-9]+ *([0-9]+)[^\n]*\n")
class Slon(object):
'''Objet représentant la baie de stockage'''
def __init__(self, host="10.231.136.85"):
self.tn = telnetlib.Telnet(host)
# Identification
self.tn.read_until("Login: ")
self.tn.write(username + "\r\n")
self.tn.read_until("Password: ")
self.tn.write(password + "\r\n")
# Ça c'est pour attendre l'invite de commande initiale:
self.tn.read_until("# ")
# On met un nombre de lignes le plus élévé possible pour
# éviter que le serveur ne se mette en attente de l'appuie sur
# une touche
self.cmd("set cli-parameters pager off")
# Au delà de cette valeur il y a un overflow
def logout(self):
'''Déconnexion de la baie'''
self.tn.write("exit\r\n")
self.tn.read_all()
self.tn.close()
print ("Si vous avez effectué des modifications pensez à exécuter:\n" +
"/usr/scripts/gestion/iscsi/update.sh sur chacun des dom0\n")
def cmd(self, cmd, mode='text'):
'''Exécute une commande et renvoie le résultat. Lève
l'exception Error si la commande échoue'''
# Si c'est le script qui bosse, on utilise le mode XML, sinon
# on peut aussi lancer des commandes en mode texte
if mode == 'XML':
self.tn.write("set cli-parameters xml\r\n")
self.tn.read_until("# ")
else:
self.tn.write("set cli-parameters console\r\n")
self.tn.read_until("# ")
self.tn.write(cmd + "\r\n")
resp = ""
# Lecture de la réponse, c'est terminé quand on atteint un
# nouveau prompt:
resp = self.tn.read_until("# ")
# On retire les messages parasites s'il en reste par hasard
resp = junk_regexp.sub("", resp)
# On vire la commande qui est là et dont on veut pas
[_, resp] = resp.split(cmd+"\r\n", 1)
# On retire le prompt
[resp, _] = resp.rsplit("\r\n", 1)
# Remplace les fins de ligne dos par des fin de lignes unix
resp = crlf_regexp.sub("\n", resp)
if resp.lower().startswith("error"):
raise NolsError(resp.replace("Error: ", ""))
return resp
def show(self, what):
'''Raccourci pour: print slon.cmd("show <what>")'''
print self.cmd("show " + what)
def help(self, what = ""):
'''Raccourci pour: print slon.cmd("help <what>")'''
print self.cmd("help " + what)
def volume_map(self):
'''Retourne le mapping lun<->nom de volume'''
map = {}
XML_map = self.cmd("show volume-maps", mode="XML")
root = fromstring(XML_map)
tree = ElementTree(root)
# XML c'est trobyien
tree = tree.findall("volume-view")[0]
Objects = tree.findall("volume_view")
for Object in Objects:
name = None
lun = None
name = Object.findall("volume_name")[0].text
serial = Object.findall("volume_serial")[0].text
lun = Object.findall("lun")[0].text
if lun is None:
pass
else:
map[int(lun)] = (name, serial)
return map
def create_volume(self, name, size, unit="GB", vdisk="slon2"):
'''Créé un nouveau volume. Retourne le lun sur lequel il est
mappé. La taille est en Giga-octet. L'unité doit être "KB",
"MB" ou "GB". Par défault c'est "GB".'''
# Création du volume
self.cmd("create volume vdisk %s size %d%s %s" % (vdisk, size, unit, name))
# On récupère le mapping pour chercher un lun de libre
map = self.volume_map()
lun = 0
while lun in map: lun = lun + 1
# Mapping du volume
self.cmd("map volume %s lun %d" % (name, lun))
return lun
def delete_volume(self, name):
'''Supprime un volume'''
self.cmd("delete volume %s")