ich bin eine Java-Entwicklerin (eigentlich), die letzten Monate hab ich viel an einem Projekt gearbeitet, dass ich derzeit mit Perl schreibe. Verwaltung von mehreren KVM-Servern über eine Weboberfläche mittels libvirt. Der Unterschied zu den unzähligen anderen Programmen, die es schon gibt, wird sein, dass es möglich ist statt VNC oder Spice - also grafischen Oberflächen - eine serielle Textconsole im Browser zu bekommen. Und das Ganze ohne Java (*heul*), damit es auch mit einem Smartphone Browser und über Umts funktioniert. Das Programm ist soweit "fertig", bis auf diese Textconsole, die ja das Alleinstellungsmerkmal sein soll. Die Perl libvirt-api hat mich an der Stelle schmählich im Stich gelassen (so langsam, dass der Login wegen 180sec timeout nicht geht, sendet alles doppelt und dreifach usw...), da bin ich per Zufall auf ein Python-Script gestoßen, das mehr oder weniger genau das in perfekt und schnell macht, was ich mit Perl nicht hinbekomme. Leider macht es ein bisschen zu viel... Ich hoffe, dass Ihr mir helfen könnt, das zu ändern. Ich kann null Python, hab nie zuvor damit gearbeitet, und verstehe nach mehrmaligem Lesen vom Quellcode ca 2/3 was leider nicht genug ist um es selber so zu ändern, wie ich es gerne hätte. Ein bisschen was hab ich anpassen können, aber das Wichtigste leider nicht.
Das Python Script baut über libvirt eine Verbindung zu der seriellen Console mit Hilfe von einem Stream auf. Die Ausgabe konnte ich so ändern, dass er die Ergebnisse zusätzlich in einen Textfile schreibt. Was ich nicht geschafft habe ist, dass es nicht in Endlosschleife in der Console bleibt, sondern nur einen Befehl abarbeitet. Ich würde gerne das Script mit einem "Befehl" als drittes Argument aufrufen, den er dann abarbeitet, das Ergebnis in die Datei schreibt, und sich dann wieder beendet, statt dass er den Befehl über stdin liest. Ich werde die Stellen im Code kommentieren, wo ich denke, dass ich dort etwas ändern muss, und hoffe dass Ihr mir sagen könnt wie ich das machen kann. Außerdem kann alles Überflüssige raus... Ich hoffe wirklich auf Hilfe, Erklärungen zu Stellen, die ich noch nicht verstehe und dann endlich auf die Fertigstellung meines Programmes...
So ziemlich alles was unten an Kommentaren drin ist hab ich schon versucht, aber hab mehr oder weniger viele Fehlermeldungen produziert, mit denen ich zum Teil nichts anfangen kann... Most welcome ist eine Erklärung zu callback events, die ich nirgendwo für mich verständlich finden konnte, ebenso Codesnippets oder angepasste Programmteile, oder auch nur Bsp. zum abgucken, die mir helfen könnten.
Quelle des gefunden Programmes ist diese Website: http://libvirt.org/git/?p=libvirt.git;a ... allback.py
Code: Alles auswählen
#!/usr/bin/env python
# consolecallback - provide a persistent console that survives guest reboots
import sys, os, libvirt, tty, termios, atexit
#import logging
global filed
filed = open("/var/tmp/testfile", 'ab+')
# die ganzen terminos und tty sachen könnten dann weg - aber ich bin mir doch recht sicher, dass ich die nicht einfach löschen kann....
def reset_term():
termios.tcsetattr(0, termios.TCSADRAIN, attrs)
# dass die Verbindung bei einem Error offen bleibt ist überflüssig, da ich die verbindung eh nach jedem Befehl wieder schließen würde (ich kann meines Wissens die Verbindung ja nicht offenlassen und trotzdem über Html-Post Daten an das Python Proggie übergeben, während es läuft...) Kann ich das einfach löschen? - Edit: ja, scheint so!
#def error_handler(unused, error):
# The console stream errors on VM shutdown; we don't care
#if (error[0] == libvirt.VIR_ERR_RPC and
#error[1] == libvirt.VIR_FROM_STREAMS):
#return
#logging.warn(error)
class Console(object):
def __init__(self, uri, uuid):
self.uri = uri
self.uuid = uuid
self.connection = libvirt.open(uri)
self.domain = self.connection.lookupByUUIDString(uuid)
self.state = self.domain.state(0)
self.connection.domainEventRegister(lifecycle_callback, self)
self.stream = None
self.run_console = True
# logging.info("%s initial state %d, reason %d",
# self.uuid, self.state[0], self.state[1])
def check_console(console):
if (console.state[0] == libvirt.VIR_DOMAIN_RUNNING or
console.state[0] == libvirt.VIR_DOMAIN_PAUSED):
if console.stream is None:
console.stream = console.connection.newStream(libvirt.VIR_STREAM_NONBLOCK)
console.domain.openConsole(None, console.stream, 0)
#auch hiermir hab ich meine Probleme - stream_callback und so - wie funktionieren diese stream-events genau?
console.stream.eventAddCallback(libvirt.VIR_STREAM_EVENT_READABLE, stream_callback, console)
else:
if console.stream:
#hier genauso - was macht das?
console.stream.eventRemoveCallback()
console.stream = None
return console.run_console
#ich denke diese Methode muss ich in 2 aufteilen, eine die den Stream beendet und eine die "com" sendet?
def stdin_callback(watch, fd, events, console):
readbuf = os.read(fd, 1024)
if readbuf.startswith("\x1d"):
console.run_console = False
returnfiled.close()
if console.stream:
console.stream.send(readbuf)
#ich weiß einfach nicht wie ich diese methode manuell aufgerufen kann, aber hier lese ich die daten von der console...
def stream_callback(stream, events, console):
try:
received_data = console.stream.recv(1024)
except:
return
#wenn man das os.write auskommentiert geht garnichts mehr, obwohl ich das doch in eine datei schreibe - warum?
os.write(0, received_data)
filed.write(received_data)
# Erklärung bitte - dieser ganze callback Kram ist für mich gerade ein Buch mit sieben Siegeln...
def lifecycle_callback (connection, domain, event, detail, console):
console.state = console.domain.state(0)
# logging.info("%s transitioned to state %d, reason %d",
# console.uuid, console.state[0], console.state[1])
# main
def main():
if len(sys.argv) < 2:
print "Usage:", sys.argv[0], "URI UUID Command"
print "for example:", sys.argv[0], "'qemu:///system' 'a3953fe1-39bc-2338-eada-295638ad13e' ls -la"
sys.exit(1)
uri = sys.argv[1]
uuid = sys.argv[2]
com = ' '.join(sys.argv[3:])
print "Escape character is ^]"
# logging.basicConfig(filename='msg.log', level=logging.DEBUG)
# logging.info("URI: %s", uri)
# logging.info("UUID: %s", uuid)
libvirt.virEventRegisterDefaultImpl()
#libvirt.registerErrorHandler(error_handler, None)
atexit.register(reset_term)
attrs = termios.tcgetattr(0)
tty.setraw(0)
console = Console(uri, uuid)
# mit dieser Zeile hab ich wieder Probleme... ist ja klar....
console.stdin_watch = libvirt.virEventAddHandle(0, libvirt.VIR_EVENT_HANDLE_READABLE, stdin_callback, console)
# ich denke das hier kann weg, da es dafür sorgt, dass ich in der Konsole bleibe
while check_console(console):
libvirt.virEventRunDefaultImpl()
if __name__=="__main__":
main();