Serverliste importieren und abarbeiten

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.
morytox
User
Beiträge: 25
Registriert: Dienstag 15. September 2009, 13:17

hallo ich habe ein tool geschrieben welches mir Daten auf einem Server sichert.

Dazu habe ich in den Funktionen systembefehle in denen Daten zum server sind (bsp: ip adresse , username und pfad auf server)
Nun habe ich das Programm erst auf einen Server Speziell angepasst.

Mein vorhaben ist es Eine datei mit den Einträgen der Server zu haben welche Das Programm so ausliest dass man dann eine Liste hat in welcher vllt separiert Die 3 Serverspeziefischen angaben stehen(und das dann für Mehrere Server) und dass das Script dann in einer for schleife läuft und mit jedem Durchgang den nächsten server hat.
Also im ersten durchgang auf 3 Variablen die 3 Daten hat und im nächsten die vom nächsten server und das alles so lange wie server in der datei / liste sind

um das mal visuell zu machen...
Datei mit :
Serverip1 Username1 pfad1
Serverip2 Username2 pfad2
Serverip3 Username3 pfad3

Das dann in ne Liste packen und dann im for loop
for server in liste
funktion1() <-- führt befehle mit den 3 variablen aus und dass für jeden server einmal

[/code]
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Was genau ist nun Deine Frage? Ich habe das Problem so ehrlich gesagt nicht verstanden! (Und kann mir vorstellen, dass das anderen genauso geht ;-) )
morytox
User
Beiträge: 25
Registriert: Dienstag 15. September 2009, 13:17

naja wie kann ich mein vorhaben konkret realisieren ? bin noch relativ frisch in der programmsprache und ich hab keine ahnung wie ich die datei so improtieren kann dass die sortierung klar ist etc ... also wie kann ich das konkret umsetzen ? gibt es da beispiele oder so ?

z.B. auch wie ich ne forschleife sinnvoll mit ner liste anstatt des in range() befehls machen kann
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Zuerst mal solltest du deine ``funktion1`` aendern und die Konfiguration zu Parametern machen, dann reicht das hier:

Code: Alles auswählen

with open(config_fname) as config:
    for line in config:
        ip, user, path = config.strip().split()
        magic(ip, user, path)
Edit: Wenn du natuerlich Leerzeichen in den Angaben brauchst, dann solltest du dir einen anderen Trenner als " " suchen.
morytox
User
Beiträge: 25
Registriert: Dienstag 15. September 2009, 13:17

das heißt ich öffne die Datei mit dem namen "config_fname" als config (ist das eine art die datei zu lesen in python ?) und öffne dann die for schleife in der jede zeile (mit 3 einheiten) auf die variablen ip, user, path gelegt wird (reicht das wenn ich in der datei die elemente nur mit leerzeichen getrennt habe oder sollte man da was erkennbares wie arg1:arg2:arg3 nehmen ? und wenn ja wie wirkt sich dann die änderung heir aus ? muss die datei ne bestimmte endung haben ?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Deine Schlussfolgerungen sind richtig und die Datei muss keine bestimmte Endung haben. Das ":" statt " " als Trenner macht es ja auch nicht besser, denn jetzt darf dann eben dieses Zeichen nicht in Pfad, Namen und Adresse vorkommen. Sind es IPv6-Adressen, so haben die z.B: ":".

Nachtrag: Ich würde `split(" ", 2)` empfehlen, da dies einen String in maximal drei Teile teilt. Wenn Adresse und User garantiert kein Leerzeichen enthalten, dann kann der dritte Teil, der Pfad, auch Leerzeichen haben und es funktioniert trotzdem.

Stefan
Zuletzt geändert von sma am Mittwoch 16. September 2009, 10:24, insgesamt 1-mal geändert.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

sma hat geschrieben:Deine Schlussfolgerungen sind richtig
Bis auf den Dateinnamen. Hier wird eine Variable namens ``config_fname`` erwartet, die den Pfad zur Datei enthaelt.

Du kannst auch eine Zeichenkombination als Trenner verwenden um ganz sicher zu gehen, zb. ``/-\`` und splittest dann mit ``split(r"/-")`` (das r steht fuer einen Rawstring, wg dem Backslash)

Das ``with`` oeffnet die Datei per Kontextmanager, d.h. es wird der Block abgearbeitet und dann die Datei geschlossen.
morytox
User
Beiträge: 25
Registriert: Dienstag 15. September 2009, 13:17

okay danke erstmal für eure hilfe aber jetzt hat sich etwas meine aufgabenstellung verändert. in der liste befinden sich jetzt verschiedene server .. jetzt heißt es dass je nach anforderung verschiedene server abgearbeitet werden sollen ... kann man das erste element durch ne art index ersetzen bsp
xy serverip1 usrname1 pfad1
xy serverip2 usrname2 pfad2
yz serverip1 usrname1 pfad1
yz serverip2 usrname2 pfad2

und dann bei ner bestimmten angabe nur die linien mit dem bestimmten index abgearbeitet werden ?


achso und wenn config_fname eine variable ist auf der der pfad liegt, wie ordne ich dann den pfad zu ? ganz einfach mit bsp:
config_fname=/home/usr/ordner/serverconfig , wobei serverconfig dann die datei ist ?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich würde anstatt mit split() zu experimentieren einfach auf ein anderes Format setzen. INI, JSON, (XML,) ... es gibt doch so viele einfach zu schreibende Formate, für die es fertige Parser gibt. Damit erspart man sich Grübeleien über mögliche Trennzeichen oder das Escaping von Steuerzeichen.

In der Doku findest Du da einige Module: csv, ConfigParser, json und für xml ElementTree.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

morytox hat geschrieben:okay danke erstmal für eure hilfe aber jetzt hat sich etwas meine aufgabenstellung verändert. in der liste befinden sich jetzt verschiedene server .. jetzt heißt es dass je nach anforderung verschiedene server abgearbeitet werden sollen ... kann man das erste element durch ne art index ersetzen bsp
xy serverip1 usrname1 pfad1
xy serverip2 usrname2 pfad2
yz serverip1 usrname1 pfad1
yz serverip2 usrname2 pfad2
"xy" und "yz" sind also Anforderungen, die in jeweils einer passenden Funktion ausgeführt werden?

Ist jetzt immer noch die Frage, wie das Dateiformat aufgebaut sein soll, oder wie man diese "Liste" abarbeitet?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Das ist jetzt nicht unbedingt ideomatisch widerspruchsfreies Python aber...

Code: Alles auswählen

class Line:
    def process(self, line):
        func, self.ip, self.name, self.path = line.split(" ", 3)
        return getattr(self, func)()
    
    def xy(self):
        return "XY"
    
    def yz(self):
        return "YZ"

print Line().process("xy ip1 user1 path1")
Stefan
morytox
User
Beiträge: 25
Registriert: Dienstag 15. September 2009, 13:17

@Stefan Das versteh ich jetzt nich ganz. was genau macht der prog auszug den du mir geschrieben hast und wo muss ich ihn einbaun um was zu erreichen ?

jes schau ich echt wie n schein ins uhrwerk ^^ angenommen ich würde mir son ding in xml zusammenschustern wie müsste diese Datei aufgebaut sein ? und wie implementiere ich das dann in die schleife?

Und das script soll dafür da sein dateipakete je nach produktionsstatus/fortschrit (xy oder yz oder weiß ich was) auf die dazu zuständigen server zu schieben / kopieren die funktionen für den vorgang hab ich geschrieben und will da via variablen nurnoch die daten einsetzen wohin der weg nun gehen soll. das will ich anhand einer serverliste realisieren.

Das heißt man gibt zum anfang an welcher produktionsstatus erreicht ist und anhand der angabe werden die zuständigen server aus der liste ausgelesen und und das programm arbeitet die server dann ab
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Wenn du noch auf der Suche fuer ein Dateiformat bist, wuerde ich csv empfehlen, Python hat sogar ein [mod]csv[/mod]-Modul. 8) XML waere hier wirklich mit Kanonen auf Spatzen geschossen...
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Noch mal zum Verständnis: Wozu sind "xy" und "yz" nun gut?
morytox
User
Beiträge: 25
Registriert: Dienstag 15. September 2009, 13:17

Also wenn der Scriptnutzer xy oder yz eingibt werden je nach eingabe andere Server in der liste abgearbeitet

ich geb ma n auszug:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-
import subprocess
import os
import logging

logging.basicConfig(filename = "/home/mory/svndeploy/data/log/Deploymentlog.log", filemode = "a", level = logging.DEBUG, format = "%(asctime)s [%(levelname)-8s] %(message)s")

serverconfig=/home/mory/svndeploy/bin/serverconfig
stand=" ist auf dem aktuellen Stand"
standn=" ist auf neuem Stand" 
pfadex=" /home/mory/svndeploy/data/exports"
pfadco=" /home/mory/svndeploy/data/checkout"

logging.info("Subversion wird auf Updates geprüft.")
uppng = subprocess.Popen([r"svn", "up", "/home/mory/svndeploy/data/checkout/symbole"], stdout=subprocess.PIPE).communicate()[0]
upjs = subprocess.Popen([r"svn", "up", "/home/mory/svndeploy/data/checkout/WebApp_JS_Skripts"], stdout=subprocess.PIPE).communicate()[0]
upcss = subprocess.Popen([r"svn", "up", "/home/mory/svndeploy/data/checkout/trunk"], stdout=subprocess.PIPE).communicate()[0]


# Funktionen für JS Skripte
    
def exportjs():
    os.system("svn export -q"+pfadco+"/WebApp_JS_Skripts"+pfadex+"/WebApp_JS_Skripts")
    logging.info("Skripts sind exportiert.")

def cleanupjs():
    os.system("rm -f"+pfadex+"/WebApp_JS_Skripts.tgz")
    os.system("rm -rf"+pfadex+"/WebApp_JS_Skripts")
    logging.info("Alte Skripte sind gelöscht.")

def packenjs():
    os.system("cd"+pfadex+" && tar czf"+pfadex+"/WebApp_JS_Skripts.tgz WebApp_JS_Skripts")
    logging.info("Skripte sind gepackt.")

def uploadjs():
    os.system("scp"+pfadex+"/WebApp_JS_Skripts.tgz "+server+":"+path)
    logging.info("Skripte wurden transferiert.")

def detarjs():
    os.system("ssh "+server+" \"cd "+path+" && tar xzf "+path+"/WebApp_JS_Skripts.tgz > /dev/null\"")
    logging.info("Skripte entpackt.")

def cleanupwsjs():
    os.system("ssh "+server+" \"rm -f "+path+"/WebApp_JS_Skripts.tgz\"")
    os.system("ssh "+server+" \"mv "+path+"/WebApp_JS_Skripts "+path+"/`date +'%Y%m%d%H%M%S'`WebApp_JS_Skripts\"")
    logging.info("Webserver bereinigt.")

with open(serverconfig) as config:
    for line in config:
        ip, usr, path = config.strip().split(r"/-\")
        server=usr+"@"+ip
        if "zu" in upjs:
            cleanupjs()
            cleanupwsjs(server, path)
            exportjs()
            packenjs()
            uploadjs(server, path)
            detarjs(server, path)
            logging.info("WebApp_JS_Skripts"+standn)
        else:
            logging.info("WebApp_JS_Skripts"+stand)

     logging.info("Deployment durchgeführt")

logging.shutdown()
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ok, dann schlage ich mal folgende JSON-Datei vor:

Code: Alles auswählen

{
    "yz": [
        {
            "ip": "noch ne ip", 
            "user": "noche einer", 
            "path": "wieder einer"
        }
    ], 
    "xy": [
        {
            "ip": "ip-adresse", 
            "user": "user1", 
            "path": "path1"
        }, 
        {
            "ip": "noch ne ip", 
            "user": "user99", 
            "path": "path56"
        }
    ]
}
Letztlich hast Du ein Dict, in welchem nach den Stati ("xy" und "yz") unterschiedliche Listen zugerodnet sind. Innerhalb der Listen habe ich hier wieder ein Dict gewählt, wenn es wirklich immer nur "ip", "user" und "path" in einer festen Reiehnfolge sind, kann man da auch eine Liste nehmen.

Das ganze könnte man so abarbeiten:

Code: Alles auswählen

import json

# Exception Handling überlasse ich mal Dir ;-)
with open("jobs.json", "r") as in_file:
    jobs = json.load(in_file)

try:
    for job in jobs[user_eingabe]:
        print "Job: {ip}, {user}, {path}".format(**job)
        process(job)
except KeyError, e:
    print "Ungültiger / Undefinierter Status"
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Zu Deinem Code:

- Zeilen > 80 Zeichen ([wiki]Lange Zeilen im Sourcecode)[/wiki]
- Wieso verwendest Du os.system, wo Du doch weiter oben subprocess nutzt?
- Code auf Modulebene ist nicht wirklich toll
- Strings besser nicht mit "+" zusammensetzen
- für das Zusammensetzen von Pfaden gibt es os.join
morytox
User
Beiträge: 25
Registriert: Dienstag 15. September 2009, 13:17

- was bedeutet code auf modulebene ?
- subprocess fand ich nciht so gut , ich hatte es anfangs mit subprocess versucht hab aber nur fehler bekommen weil da so viele ergänzungeen und alles war , hab einfahc nicht durchgesehn und os.system sieht verdammt einfach aus bzw. war für mich sehr einfach anzuwenden
-zeiln kannsch kürzen da hast recht
-wieso nicht + ? soll ich in dem fall dann os.system(os.join("string", var, "string")) schreiben ?

*edit*

wo kommen jetzt eigentlich die Auszuführenden Funktionen hin und was muss ich als parameter übergeben ...
*hmz* klar bei process ^^

print "Job: {ip}, {user}, {path}".format(**job) wofür ist das noch gleich gut ?

und wenn man jetzt den prozess mit dem parameter ausführt hat man in der funktion die 3 variablen ip usr und path mit den zugehörigen daten belegt ja ?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

morytox hat geschrieben:- was bedeutet code auf modulebene ?
Beispiel:

Code: Alles auswählen

print "Hallo Welt"

def func():
    #mache etwas


def func2():
    # mache was anderes

...
Das print steht hier auf Modulebene. Wenn Du nun dieses Script (wir nennen es mal foo.py) in ein anderes Python-Script einbinden willst, würdest Du folgendes schreiben:

Code: Alles auswählen

import foo

def bar():
    foo.func2()
Du willst also in der Funktion bar die Funktion func2 aus dem Script (Modul) "foo.py" nutzen. Nun passiert nach dem import aber folgendes: Es werden alle Befehle abgearbeitet in foo, die sich auf Modulebene (sprich außerhalb von Funktionen und Klassen) befinden. Du würdest also die Ausgabe des print Befehls sehen. Das ist aber in diesem Falle vermutlich nicht erwünscht.

Also nutzt man folgenden Trick:

Code: Alles auswählen

import irgendwas

def func1():
    pass


def funcx():
    pass


def main():
    # hier ist der Einstiegspunkt in die Abarbeitung des Scriptes
    print "Hallo Welt!"
    func1()
    ...

if __name__ == "__main__":
    main()
Startest Du dieses Script, so "merkt" der Interpreter, dass Du dieses Script ausfürehn möchtest und verzweigt in die main()-Funktion (Kann auch anders heißen oder direkt unterhalb des if __name__-Hooks stehen).

Importierst Du nun dieses Script in einem anderen, so weiß der Interpreter, dass er eben nicht in dieses main()-Funktion reinrutschen soll. Genau das erwartet man eben bei dieser Benutzung.

Ein weiterer Grund ist die Vermeidung von unnötigen globalen Variablen. Schreibst Du alles "planlos" auf Modulebene, so sind natürlich alle Namen in allen Funktionen verfügbar. Das ist idR gefährlich und unnötig. So kannst Du Dir überlegen, was Du ggf. in der main()-Funktion definierst und was nicht.
- subprocess fand ich nciht so gut , ich hatte es anfangs mit subprocess versucht hab aber nur fehler bekommen weil da so viele ergänzungeen und alles war , hab einfahc nicht durchgesehn und os.system sieht verdammt einfach aus bzw. war für mich sehr einfach anzuwenden
Was genau hat denn da nicht funktioniert? Man wird Dir hier immer aus guten Gründen zu subprocess raten. Es ersetzt eben das alte Sammelsurium an verschiedenen Funktionen (popen, popen2, usw) durch eine Abstraktion. Also: Auf subprocess umstellen lohnt sich.
-wieso nicht + ? soll ich in dem fall dann os.system(os.join("string", var, "string")) schreiben ?
Strings sind immutable in Python, d.h. man kann diese Zeichenketten nicht verändern. "foo" + "bar" erzeugt in Wirklichkeit also eine neue Zeichenkette. Das ist für Laufzeit und Ressourcen nicht förderlich. Man kann einzelne Zeichenketten z.B. durch "".join() zusammenfügen.

Code: Alles auswählen

"".join(["foo", "bar"])
Man beachte die "[" und "]". join() verlangt als Parameter eine Sequenz.

Und ja, genauso sollte man os.join() verwenden. Damit hast Du einen garantiert fehlerfreien Pfad.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich habe mein Snippet mal überarbeitet und kommentiert.

Code: Alles auswählen

import json

# Exception Handling überlasse ich mal Dir ;-)
with open("jobs.json", "r") as in_file:
    jobs = json.load(in_file)

try:
    for job in jobs[user_eingabe]:
        # dient nur zur Ausgabe des Jobs
        print "Job: {ip}, {user}, {path}".format(**job)
        # hier rufst Du eben die Funktion(en) auf, die mit diesen Parametern
        # abgearbeitet werden sollen.
        # das "**" vor job erkläre ich unten, Du kannst aber auch direkt die
        # Parameter übergeben, also job["ip"] z.B.
        process(**job)
except KeyError, e:
    print "Ungültiger / Undefinierter Status" 
Das "**job" ist eine gute Idee, wenn die Funktion eben genau diese Parameter erwartet, oder beliebige Parameter annehmen soll. Damit wird das Dict quasi direkt als Parameter in die Funktion übergeben.

Code: Alles auswählen

In [23]: foo(**jobs)
{'ip': '192.168.0.1', 'user': 'chris'}

In [24]: def foo(**kwargs):
   ....:     print kwargs
   ....:
   ....:

In [25]: jobs = {"ip": "192.168.0.1", "user": "chris"}

In [26]: foo(**jobs)
{'ip': '192.168.0.1', 'user': 'chris'}

In [31]: def bar(user, ip):
   ....:     print user, ip
   ....:
   ....:

In [32]: bar(**jobs)
chris 192.168.0.1
Antworten