scripts/gestion/iscsi/nolslib.py
Pierre-Elliott Bécue c37b3b0f34 [iscsi] Kludges sales pour virer les liens morts supplémentaires.
Si seulement udev se débarassait des modules physiques qui n'existent plus...
2013-04-25 00:09:48 +02:00

182 lines
6 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# baie_lib.py
# ----------
# Type à taper si ça chie : Pierre-Elliott Bécue <peb@crans.org>
# Licence : WTFPL
'''Bibliothèque pour accéder à la baie de stockage nols, récupère les données
formatées en XML'''
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/nols.py")
class NolsError(Exception):
def __init__(self, msg):
Exception.__init__(self, msg)
class Nols(object):
'''Objet représentant la baie de stockage'''
def __init__(self, host="nols.adm.crans.org"):
self.tn = telnetlib.Telnet(host)
# Identification
self.tn.expect(["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 veut des sorties en XML
self.cmd("set cli-parameters api-embed")
# On se débarasse des "appuie sur un bouton pour voir la suite"
self.cmd("set cli-parameters pager off")
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 api-embed\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-map", mode="XML")
root = fromstring(XML_map)
tree = ElementTree(root)
## Cf juste en dessous
Objects = tree.findall("OBJECT")
#Objects = tree.findall("OBJECT[@name='volume-view']")
## Fin cf juste en dessous
for Object in Objects:
name = None
lun = None
# Quand on passera à wheezy, décommenter ces lignes, et virer
# la merde que j'ai fait juste après.
#name = Object.findall("PROPERTY[@name='volume-name']")[0].text
#lun = Object.findall("OBJECT/PROPERTY[@name='lun']")[0].text
######## Début merde que j'ai faite juste après ###########
if not (Object.attrib['name'] == "volume-view"):
pass
properties = Object.findall("PROPERTY")
for property in properties:
if property.attrib['name'] == "volume-name":
name = property.text
else:
pass
subObjects = Object.findall("OBJECT")
for subObject in subObjects:
properties = subObject.findall("PROPERTY")
for property in properties:
if property.attrib['name'] == "lun":
lun = property.text
else:
pass
####### Fin merde que j'ai faite juste après #############
if lun is None:
pass
else:
map[int(lun)] = name
return map
def create_volume(self, name, size, unit="GiB", vdisk="slon1"):
'''Crée un nouveau volume. Retourne le lun sur lequel il est
mappé. L'unité doit être "B", "K(i)B", "M(i)B", "G(i)B" ou T(i)B.
Par défault l'unité est le giga octet binaire : Gib.'''
# On récupère le mapping pour chercher un lun de libre
map = self.volume_map()
lun = 1
while lun in map: lun = lun + 1
# Création du volume
result = self.cmd("create volume vdisk %s size %d%s lun %d %s" % (vdisk, size, unit, lun, name))
print "Le volume %s a été créé, son numéro d'identification est %d" %(name, lun)
def delete_volume(self, name):
'''Supprime un volume'''
self.cmd("delete volume %s" % (name))
def expand_volume(self, name, size, unit="GiB"):
'''Permet d'étendre un volume, la taille par défaut est le giga octet
binaire (GiB), les unités possibles sont B, K(i)B, M(i)B, G(i)B, T(i)B.'''
self.cmd("expand volume size %d%s %s" % (size, unit, name))
def rename_volume(self, origin_name, new_name):
'''Renomme un volume.'''
self.cmd("set volume name %s %s" % (new_name, orig_name))