ordner werden nicht gelöscht

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
uwerothfeld
User
Beiträge: 17
Registriert: Freitag 24. Juni 2011, 14:18

hallo zusammen,

ich bastle gerade an einem testskript wo ich ein komisches verhalten habe.

in der main methode wird folgendes aufgerufen:

Code: Alles auswählen

        self.setup_environment()

       .... do something with virtual box

      clean()
Die methode setup_environment() macht folgendes:

Code: Alles auswählen

        #store the original environment
        try:
            self.__old_user_home = os.environ['VBOX_USER_HOME']
        except KeyError:
            pass

        #set the VBOX_USER_HOME
        os.environ['VBOX_USER_HOME'] = self.__test_suite.get("General", "TEST_SUITE_VM_SETTING_FOLDER")

        #create the vbox_user_home folder, if not exist
        if not os.path.exists(os.environ['VBOX_USER_HOME']):
            os.makedirs(os.environ['VBOX_USER_HOME'])

        #save the orginal machine store of virtual box
        p1 = subprocess.Popen(["VBoxManage", "list", "systemproperties"],
                            env=os.environ.copy(),
                            stdout=subprocess.PIPE)
        p2 = subprocess.Popen(["grep", "Default machine folder:"],
                            env=os.environ.copy(),
                            stdout=subprocess.PIPE,
                            stdin=p1.stdout)
        p1.stdout.close()

        out = p2.communicate()[0]
        self.__old_machine_home = out.split(":")[1].strip()

        #set the machine store of virtual box
        set_command = ["VBoxManage", "setproperty", "machinefolder",
                        self.__test_suite.get("General", "TEST_SUITE_VM_STORE_FOLDER") ]
        subprocess.call(set_command, env=os.environ.copy())

        #create the machine store folder, if not exist
        if not os.path.exists(self.__test_suite.get("General", "TEST_SUITE_VM_STORE_FOLDER")):
            os.makedirs(self.__test_suite.get("General", "TEST_SUITE_VM_STORE_FOLDER"))
Im Grunde werden nur 2 Pfade auf Ordner umgebogen. Existieren diese nicht, werden sie erstellt. Unter anderem wird auch der Ordner angelegt, in dem VirtualBox seine Einstellungen speichert. Dann werden Images importiert, modifiziert, laufen gelassen, was auch immer und clean(self) aufgerufen:

Code: Alles auswählen

    def clean(self):
...
            if os.path.exists(self.__test_suite.get("General", "TEST_SUITE_VM_STORE_FOLDER")):
            shutil.rmtree(self.__test_suite.get("General", "TEST_SUITE_VM_STORE_FOLDER"))

        if os.path.exists(self.__test_suite.get("General", "TEST_SUITE_VM_SETTING_FOLDER")):
            shutil.rmtree(self.__test_suite.get("General", "TEST_SUITE_VM_SETTING_FOLDER"))
Am Ende sollen die Ordner wieder gelöscht werden. Komischerweise wird aber der Ordner TEST_SUITE_VM_SETTING_FOLDER nicht gelöscht. Der andere ist aber wie erwartet weg. Kann man irgendwie feststellen, ob irgendwas den Ordner blockiert?

Vielen Dank.
BlackJack

@uwerothfeld: Da `shutil.rmtree()` eine Ausnahme auslöst wenn es Probleme gab wird es wahrscheinlich gar nicht aufgerufen. Denn wenn es aufgerufen würde und der Ordner nach dem Löschen noch da ist, hätte es ja eine Ausnahme geben müssen. Es sei denn Du „verschluckst” die irgendwo mit einem ``except`` was die Ausnahme nicht richtig behandelt.

Diese vielen `__` sind übrigens „unpythonisch”. Um zu zeigen das ein Attribut nicht zur öffentlichen Schnittstelle gehört, wird konventionell *ein* führender Unterstrich verwendet. Die `__` sind zum Verhindern von Namenskollisionen bei tiefen Vererbungshierarchien und Mehrfachvererbung gedacht — beides Sachen die in Python sehr selten vorkommen.

Diese elend langen Zeilen mit den ständigen Wiederholungen zum Abfragen von Konfigurationswerten könnte man auch mal vereinfachen. Das liest sich grauenvoll und muss doch auch beim schreiben nervig sein!?

Die Kommentare sind teilweise extremst überflüssig. Beispiel:

Code: Alles auswählen

        #create the vbox_user_home folder, if not exist
        if not os.path.exists(os.environ['VBOX_USER_HOME']):
            os.makedirs(os.environ['VBOX_USER_HOME'])
Wer aus dem Code nicht erkennt was hier passiert, dem hilft auch der Kommentar nicht mehr weiter. Kommentare sollten dem Leser Mehrwert vermitteln, also Informationen die man nicht direkt und trivialerweise am Code ablesen kann und nicht das offensichtliche einfach noch mal wiederholen.

Die ``if``-Abfrage bei so etwas ist auch immer mit einer „race condition” verbunden. Sicherer wäre es den Pfad einfach anzulegen und auf eine eventuelle Ausnahme zu reagieren. Ich würde mir dazu eine Funktion schreiben die `os.makedirs()` aufruft und den „Datei existiert schon”-Fall ignoriert.

Ein ``grep`` von Python aus aufrufen um die Ausgabe eines von Python aus gestarteten Prozesses zu filtern? Ernsthaft?

Die `VBoxManage`-Aufrufe kann man sicher auch gut in Python-Funktionen kapseln.
uwerothfeld
User
Beiträge: 17
Registriert: Freitag 24. Juni 2011, 14:18

Hallo BlackJack,

erst einmal danke für deine Tipps, ich werde die Anregungen aufnehmen. Zu den einzelnen Punkten:
@uwerothfeld: Da `shutil.rmtree()` eine Ausnahme auslöst wenn es Probleme gab wird es wahrscheinlich gar nicht aufgerufen. Denn wenn es aufgerufen würde und der Ordner nach dem Löschen noch da ist, hätte es ja eine Ausnahme geben müssen. Es sei denn Du „verschluckst” die irgendwo mit einem ``except`` was die Ausnahme nicht richtig behandelt.
Also wenn ich nach dem IF ein print "rm vm settings ..." vor das löschen setze, dann bekomme ich die ausgabe. daher geh ich schon davon aus, dass es aufgerufen wird. Muß man evtl. einen spez. exception handler definieren oder so????

EDIT:
Nach dem anlegen der Ordner per os.makedir:
drwxr-xr-x 2 auser agroup 4096 Nov 26 11:57 virtualbox_settings
drwxr-xr-x 3 auser agroup 4096 Nov 26 11:57 vms
nach dem entfernen per shutils.rmtree:
drwx------ 2 auser agroup 4096 Nov 26 11:58 virtualbox_settings
Ist doch eigenartig, oder???

Das __ Thema habe ich angepasst. :D

Die Konfig ist nur in der einen Datei von Relevanz und noch im Fluß. So richtig gestört hat es mich erhlich gesagt nicht, aber ich denke mal drüber nach. Python lern ich gerade erst. Nen Profi hätte vermutlich schon längst ne Lösung.

Die Kommentare stammen aus meiner Vorgehensweise. Ich schreibe erst, was ich machen muss, und proge dann. Aber ich putze. ;)
Die ``if``-Abfrage bei so etwas ist auch immer mit einer „race condition” verbunden. Sicherer wäre es den Pfad einfach anzulegen und auf eine eventuelle Ausnahme zu reagieren. Ich würde mir dazu eine Funktion schreiben die `os.makedirs()` aufruft und den „Datei existiert schon”-Fall ignoriert.
Gemacht.
Ein ``grep`` von Python aus aufrufen um die Ausgabe eines von Python aus gestarteten Prozesses zu filtern? Ernsthaft?
Nun, die Ausgabe sieht so aus:
VBoxManage list systemproperties
API version: 4_2
Minimum guest RAM size: 4 Megabytes
Maximum guest RAM size: 2097152 Megabytes
Minimum video RAM size: 1 Megabytes
Maximum video RAM size: 256 Megabytes
Minimum guest CPU count: 1
Maximum guest CPU count: 32
....
Default machine folder: /datensilo/ext3/HardDisk
...
Ich brauche nur die Angabe für die machine folder. Einzeln auslesen kann man dies irgendwie wohl nicht. Hast ne bessere Idee? Ich wäre dankbar.


EDIT: Ergänzungen beim Ordner löschen Stichpunkt
BlackJack

@uwerothfeld: Das mit dem Ordner ist in der Tat eigenartig. `rmtree()` löst sowohl eine Ausnahme aus wenn der Ordner nicht gelöscht werden konnte, als auch wenn versucht wird einen nicht existierenden Ordner zu löschen.

Also entweder wird eine Ausnahme ausgelöst die von einer Ausnahmebehandlung weiter oben in der Aufrufhierarchie ”verschluckt” wird, so dass man sie nicht zu Gesicht bekommt, oder es wird ein anderes Verzeichnis gelöscht, oder irgendein Prozess legt dieses Verzeichnis wieder an.

Änderst *Du* irgendwo die Zugriffsrechte von dem Verzeichnis? Denn das scheint ja auch irgendwann und irgendwo zu passieren.

Statt ``grep`` könnte man doch einfach mit Python nach der Zeile suchen. `stdout` von dem ``VBoxManage``-Prozessobjekt enthält doch das Dateiobjekt über das man die Ausgabe von dem Prozess lesen kann. Zeilenweise in einer ``for``-Schleife beispielsweise. Ungetestet:

Code: Alles auswählen

VBOX_MANAGE = 'VBoxManage'


def vbox_get_property(name):
    name += ':'
    process = subprocess.Popen(
        [VBOX_MANAGE, 'list', 'systemproperties'], stdout=subprocess.PIPE
    )
    result = None
    for line in process.stdout:
        if line.startswith(name):
            result = line[len(name):]
    if result is None:
        raise KeyError('property {0!r} not found'.format(name[:-1]))
    return result.strip()


def vbox_set_property(name, value):
    # 
    # TODO Maybe use `subprocess.check_call` and analyse the return code.
    # 
    subprocess.call([VBOX_MANAGE, 'setproperty', name, str(value)])
Dann sieht der Quelltext an der Stelle wo das benutzt wird, auch gleich etwas aufgeräumter aus.

Das `env`-Argument habe ich weg gelassen weil es keinen Sinn macht die aktuellen Umgebungsvariablen zu übergeben. Das passiert sowieso. `env` ist dazu gedacht wenn man genau das *nicht* haben will. Das kopieren des Wörterbuchs war zusätzlich unnötig, weil das von einem Kindprozess genau so wenig verändert werden kann wie die tatsächlichen Umgebungsvariablen eines Elternprozesses.
uwerothfeld
User
Beiträge: 17
Registriert: Freitag 24. Juni 2011, 14:18

Hallo BlackJack,

also erst einmal vielen Dank für deine vielen Verbesserungsvorschläge und Anregungen. Ich habe diese soweit es geht umgesetzt. :)

Nun zum eigentlichen Lösch-Problem: Also die Exception wird nirgends anders geschluckt, dies habe ich durch auskommentieren probiert. Interessanter Weise landet im message.log eine Ausgabe, dass das Script eine unhandled exception geworfen hat. Allerdings ohne nähere Informationen. Also scheint doch nicht alles so klar, wie ich es mir einbilde. Ich setze nirgends Zugriffsrechte und von mir legt auch keiner wieder die Dateien an. Das einzige was ich mir vorstellen könnte, ist das irgendwie VirtualBox noch läuft und das löschen blockiert. Obwohl da top/ps keinen Ahnhalt dafür bietet. Interessanter Weise zum 2. klappt aber mit dem shutil.rmtree heute wenigstens ab und an das löschen, ohne dass ich den Code geändert habe. Daher tippe ich halt wirklich auf externe Einflüsse, welche ich in den nächsten Tagen weitere untersuchen werde.

Für die Hilfe auf jeden Fall danke!

Gruß uwerothfeld
BlackJack

@uwerothfeld: Ein laufendes Programm kann das löschen nicht blockieren. Auch wenn ein Programm noch eine Datei innerhalb eines Verzeichnisses offen hat, kann man das Verzeichnis selbst trotzdem löschen. Zumindest unter Linux.

Was hast Du denn auskommentiert zum Prüfen? Einfacher wäre direkt um das `rmtree()` ein ``try``/``except`` zu legen:

Code: Alles auswählen

try:
    ...rmtree...
except Exception as error:
    print 'rmtree:', error
    raise
Wie kommt denn das `message.log` zustande? Wie startest Du das Programm? Ist das ein GUI-Programm?
uwerothfeld
User
Beiträge: 17
Registriert: Freitag 24. Juni 2011, 14:18

hallo blackjack,

leider war ich viel dienstlich unterwegs und krank, so dass das thema verwaist ist. hier nochmal zum abschluß:

es ist ein konsolenprogramm und die log angaben stammen aus dem centos message.log. wie die dahin kamen, keine ahnung. ich habe ein systemupdate eingespielt, was auch unteranderem python betroffen hat, seit dem ist der fehler nicht mehr aufgetreten. von daher würde ich erst mal sagen thread closed?! danke auf jeden fall für deine hilfe. :D
Antworten