#!/usr/bin/env python # -*- coding: utf-8 -*- # baie_lib.py # ---------- # Type à taper si ça chie : Pierre-Elliott Bécue # Licence : WTFPL '''Bibliothèque pour accéder à la baie de stockage nols, récupère les données formatées en XML''' import telnetlib, 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 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) return resp def show(self, what): '''Raccourci pour: print slon.cmd("show ")''' print self.cmd("show " + what) def help(self, what = ""): '''Raccourci pour: print slon.cmd("help ")''' 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: # 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="VD1"): '''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 = 0 while lun in map: lun = lun + 1 # Création du volume self.cmd("create volume vdisk %s size %d%s lun %d %s" % (vdisk, size, unit, lun, name)) return "Le volume %s a été créé, son numéri 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))