Nach dem update des nagios-plugins check_file_age schlagen alle Checks fehl mit der Meldung: „File not found“, obwohl die Datei vorhanden ist.
Continue reading nagios check_file_age: File not found nach update auf 2.1.4
Status während eines Prozesses anzeigen
Wer seine Shellscripts etwas interaktiver gestalten möchte: Mittels dem Programm pv kann man in der Linux Shell einen schönen Statusbalken anzeigen lassen, indem man einfach den Prozess durch pv piped:
tar -xzf grosses_archiv.tgz | pv |
Python logger für Konsole und Logfiles
Mittels des logging Moduls in python lassen sich nicht nur log messages für Log-Dateien erstellen, sondern auch die normalen Nachrichten welche man auf der Konsole ausgibt. – Das hat den Vorteil, dass man die Ausgabe danach beliebig nur noch in logfiles schreiben- (z.B. mit einem „–quiet / -q“ Paramater), oder mehr Nachrichten auf der Konsole ausgeben kann (z.B. mit einem –verbose / -v) Parameter.
Zuerst erstellt man am besten eine eigene Funktion, welche das logging initialisiert:
import logging def init_logging(): # create logger global logger logger = logging.getLogger("script name") # create handlers logfile_handle = logging.FileHandler(filename = "log.log") console_handle = logging.StreamHandler() # set loglevel logger.setLevel(logging.DEBUG) logfile_handle.setLevel(logging.DEBUG) console_handle.setLevel(logging.INFO) # add handlers to logger logger.addHandler(logfile_handle) logger.addHandler(console_handle) # create Formatting format_file = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") format_console = logging.Formatter("\033[0;32m%(asctime)s\033[0m - %(message)s") # add formatter to handler logfile_handle.setFormatter(format_file) console_handle.setFormatter(format_console) logger.debug("Logging initialized") init_logging() |
Danach kann die Ausgabe wie folgt aufrufen:
logger.info("This informational message only goes to console.") logger.warn("This warning message only goes to console") |
sqlite mit python
Zur Datenspeicherung in lokalen Programmen bietet sich die Datei-basierte Datenbank sqlite ideal an!
Die folgende Klasse stellt dabei die Grundlegenden Funktionen zum Umgang mit einer sqlite Datenbank zur Verfügung:
import os import sqlite3 ############################################################################### # modifies the delivery database # # lists, adds and removes projects or recipients # ############################################################################### class sqliteDB: ############################################################################# # Creates a new sqlite database # ############################################################################# def create(self, dbfile): if not os.path.isfile(dbfile): self.sqlquery(dbfile, "CREATE TABLE 'table1' ('TID' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'TOtherTableID' INTEGER NOT NULL, 'TName' TEXT NOT NULL, 'TDescription' TEXT)") return dbfile else: return 1 ############################################################################### # queries the database and returns a dictionary of rows # # # # takes: string = String of SQL-Code # # returns: dict = dict of rows # ############################################################################### def sqlquery(self, dbfile, sql): try: con = sqlite3.connect(dbfile) con.row_factory = sqlite3.Row cur = con.cursor() cur.execute(sql) con.commit() rows = cur.fetchall() con.close() except sqlite3.Error, e: msg = "Error %d: %s" % (e.args[0], e.args[1]) print msg return rows |
Und so wird die sqliteDB-Klasse Benutzt:
# Define database file myDBfile = "sqlite.db" # Create a new database myDB = sqliteDB().create(myDBfile) if myDB == myDBfile: print "Created database: '{myDB}'".format(myDB = myDB) else: print "DB file already existent. If you want to create a NEW database, please delete the file:\n{filename}\n".format(filename = myDBfile) # Insert data in database sqliteDB().sqlquery(myDBfile, "INSERT INTO table1 (TOtherTableID, TName) VALUES (1, 'My first row')") sqliteDB().sqlquery(myDBfile, "INSERT INTO table1 (TOtherTableID, TName) VALUES (2, 'My second row')") # Select data from database rows = sqliteDB().sqlquery(myDBfile, "SELECT TName FROM table1") # Show data for row in rows: print row['TName'] # Delete data sqliteDB().sqlquery(myDBfile, "DELETE FROM table1 WHERE TOtherTableID = 2") |
Bestätigungsabfrage
Will man in seinen python Scripts fragen ob weiter gemacht werden soll (ähnlich wie beim Installieren von Paketen in Paketmanagern) bietet sich diese F8unktion ans:
def ask_proceed(question, default="yes"): valid = {"yes":"yes", "y":"yes", "no":"no", "n":"no"} if default == None: prompt = " [y/n] " elif default == "yes": prompt = " [Y/n] " elif default == "no": prompt = " [y/N] " else: raise ValueError("invalid default answer: '%s'" % default) while 1: print (question + prompt) choice = raw_input().lower() if default is not None and choice == '': return default elif choice in valid.keys(): return valid[choice] else: print ("Please respond with 'yes' or 'no' (or 'y' or 'n').\n") result = ask_proceed("Do you want to continue?") if result == "yes": print "A good choise! I will continue with whatever I was doing..." if result == "no": print "Ok, bye!" |
Konfigurationsdateien für python Scripts
Bei manchen python Scripts gibt es so viele Konfigurationen, dass das Steuern via Kommandozeilenparametern zu unübersichtlich werden würden. Für diesen Fall kann man mit dem Modul ConfigParser eine Konfigurationsdatei anlegen und diese
Das folgende Beispiel zeigt wie es benutzt wird. Falls noch keine Konfigurationsdatei besteht, wird eine neue angelegt und mit Standardwerten gefüllt.
import os import ConfigParser # Get configurations from file CONFIG_FILE = "cfg.conf" Config = ConfigParser.ConfigParser() # If the configuration file is not found, create a default configuration if not os.path.isfile(CONFIG_FILE): print "ERROR: Configuration file not found. Creating a default configuration" cfgfile = open(CONFIG_FILE,'w') # add the settings to the structure of the file, and lets write it out... Config.add_section('SectionOne') Config.set('SectionOne', 'OptionOne', 'ValueOne') Config.set('SectionOne', 'OptionTwo', 'ValueTwo') Config.add_section('SectionTwo') Config.set('SectionTwo', 'OptionOne', 'ValueOne') Config.write(cfgfile) cfgfile.close() Config.read(CONFIG_FILE) S_ONE_O_ONE = Config.get('SectionOne', 'OptionOne') S_ONE_O_TWO = Config.get('SectionOne', 'OptionTwo') S_TWO_O_ONE = Config.get('SectionTwo', 'OptionOne') print "Section one has two options, OptionOne set to: '{S_ONE_O_ONE}' and OptionTwo set to: '{S_ONE_O_TWO}'.".format(S_ONE_O_ONE = S_ONE_O_ONE, S_ONE_O_TWO=S_ONE_O_TWO) print "Section two has one option, OptionOne set to: '{S_TWO_O_ONE}'.".format(S_TWO_O_ONE = S_TWO_O_ONE) |
Kommandozeilenparameter mit python
Kommandozeilenparameter werden häufig in Scripts verwaltet und python bietet mit dem Modul argparse eine exzellente Lösung dafür an!
Das folgende Beispiel kann als Vorlage für eigene Scripte benutzt werden, das Tutorial zu argparse liefert dabei weitere Informationen.
import argparse def main(): # Initialisieren des parsers und setzen des Hilfetextes parser = argparse.ArgumentParser(description='Add the default firewall ruleset to a given VM/network.') # Kurze Option mit Hilfe-Text ('help=') parser.add_argument('-H', help='Zeigt diese Hilfe an') # Kurze Option; zwingend anzugeben (required='True') parser.add_argument('-f', required='True') # Lange Option; optional (falls 'required=' fehlt, wird Standardmaessig "false" angenommen) parser.add_argument('--filename') # Kombinierte kurze und lange Option parser.add_argument('-m', '--message') # Einfacher Schalter; wenn gesetzt wird die Variable auf "True" gesetzt parser.add_argument('-v', action='store_true') # Gruppe von Argumenten erstellen, welche sich gegenseitig auschliessen und somit nicht zusammen verwendet werden duerfen group_output = parser.add_mutually_exclusive_group(required=False) group_output.add_argument('-q', '--quiet', action='store_true', help='Ausgabe unterdruecken') group_output.add_argument('--debug', action='store_true', help='Mehr Ausgabe als normal') # Option mit Standardwert; falls nichts angegeben wird der Wert aus 'default=' genommen parser.add_argument('--say', default='Hello World!') # Option bei der nur bestimmte Werte zugelassen sind parser.add_argument('--operating-system', choices=['linux', 'windows']) global args args = parser.parse_args() ## Verwenden der Argumente # Die Argumente sind unter args.<ARGUMENT> gespeichert # Pruefen ob Argument gesetzt ist if args.v: print "-v ist gesetzt" else: print "-v ist nicht gesetzt" # Argument ausgeben print "In der Option -f steht {args_f}.".format(args_f = args.f) # Falls ein Bindestrich (-) im Argument vorkommt, muss dieses beim Zugriff mit einem Unterstrich (_) ersetzt werden print "Dein Betriebssystem ist: {os}".format(os = args.operating_system) if __name__ == "__main__": exit ( main() ) |
Sub-Commands
Bei grösseren Programmen möchte man das Programm vielleicht in verschiedene „Unter Programme“ aufteilen, wie beispielsweise beim Programm SVN.
Dabei macht man fuer jedes Sub-Kommando eine Funktion. welche von argparse dann aufgerufen wird:
import argparse ## Sub-Kommando Funktionen # Funktion fuer das 'subcom' Kommando def subcom(args): if hasattr(args, 'etwas'): print "Tu etwas mit dem Sub-Kommando {etwas}".format(etwas = args.etwas) # Falls die Argumente: '--add' oder '--remove' gesetzt sind, rufe weitere # Funktion in der Klasse: someClass() auf und uebergebe den Wert if args.add: someClass().add(args.add) elif args.remove: someClass().remove(args.remove) # Klasse fuer das --add und --remove Kommando class someClass: def add(self, add): print "Fuege {add} hinzu.".format(add = add) def remove(self, remove): print "Entferne {remove}".format(remove = remove) def main(): # Initialisieren des parsers und setzen des Hilfetextes parser = argparse.ArgumentParser(description='This is a command line arguments parser.') # # Initialisieren des subparsers und setzen des Hilfetextes subparsers = parser.add_subparsers(help='sub-command help') ## Allgemeine Optionen # Diese Option kann unabhaengig vom Sub-Kommando vorkommen parser.add_argument('-v', '--version', action='store_true', help='Version anzeigen') ## Sub-Kommando optionen # Falls dieses Kommando benutzt wird, springt es zur Funktion: 'subcom()' parser_subcom = subparsers.add_parser('subcom', help='Subkommando') parser_subcom.add_argument('-e', '--etwas', required='true', help='Tu etwas') group_subcom = parser_subcom.add_mutually_exclusive_group(required=False) group_subcom.add_argument('--add', help='Fuege etwas hinzu') group_subcom.add_argument('--remove', help='Entferne etwas') parser_subcom.set_defaults(func=subcom) # Parser laufen lassen global args args = parser.parse_args() # Falls das Argument 'message' definiert ist, gebe die Nachricht aus if hasattr(args, 'message'): print "Tu etwa smit dem Haupt-Argument: {message}".format(message=args.message) args.func(args) if __name__ == "__main__": exit ( main() ) |
E-Mail mit Attachments senden
import os import smtplib import email # Compose mail mail_message = """Hi This is just a test mail from python with attachments. Best Regards """ msg = email.MIMEMultipart.MIMEMultipart() msg['Message-ID'] = email.utils.make_msgid() msg['Date'] = email.utils.formatdate(localtime=True) msg['From'] = "from@example.org" msg['To'] = "to@example.org, svarco" msg['Subject'] = "Example Subject" msg.attach(email.MIMEText.MIMEText(mail_message)) # add the attachments lst_files_attach = ["/etc/passwd", "/etc/group"] for file in lst_files_attach: try: with open(file, "rb") as newfile: attachment = email.MIMEBase.MIMEBase('application', "octet-stream") attachment.set_payload(newfile.read()) email.Encoders.encode_base64(attachment) attachment.add_header('Content-Disposition', 'attachment; filename="{filename}"'.format(filename = os.path.basename(file))) msg.attach(attachment) except IOError: Output().error("Could not read file: {file}".format(file=file)) # Send the mail smtp = smtplib.SMTP(MAIL_HOST) smtp.sendmail(msg['From'], msg['To'], msg.as_string()) smtp.quit() |
Datei-Prüfsumme erstellen
Mittels dieser Funktion lässt sich die (MD5-) Prüfsumme für eine Datei erstellen.
Diese kann z.B. eingesetzt werden wenn man Dateien transferiert und sicherstellen will, dass die Datei korrekt übertragen wurde.
############################################################################# # returns the checksum of a given file # # # # takes: filepath = full path to the file # # blocksize = Block size (optional) # # returns: checksum = checksum of the file # ############################################################################# import hashlib def checksum(filename, blocksize=2**20): RC_FILE_ERROR = 1 # File not found or is not readable md5 = hashlib.md5() try: with open(filename, "rb") as file: while True: buf = file.read(blocksize) if not buf: break md5.update(buf) return md5.hexdigest() except IOError: print ("Could not read file: {filename}".format(filename=filename)) return RC_FILE_ERROR result = checksum("/etc/passwd") print result |
e-mail syntaktisch prüfen
Mittels folgender Funktion lässt sich eine E-Mail Adresse in python syntaktisch nach deren Richtigkeit prüfen:
# Checks for correctness of an E-Mail address import re def checkEmail(email): RC_EMAIL_OK = 0 # E-Mail is correct RC_EMAIL_ERROR = 1 # E-Mail is semantically wrong # RegEx to check the semantic of the e-mail address EMAIL_REGEX = r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?" if (re.match(EMAIL_REGEX, email.lower())): return RC_EMAIL_OK else: return RC_EMAIL_ERROR result = checkEmail("test@example.org") print result |