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

pfsense: sshd auf zwei Ports

Möchte man auf Firewalls sich eine „Not-Türe“ offen lassen, mit der es sich auch per SSH verbinden lässt, wenn z.B. das VPN down ist, tut man gut daran SSH auf einen separaten Port (>1024) zu legen, da der Standard SSH-Port 22 sehr häufig gescannt wird.
Am idealsten ist es nebst dem Standard-Port noch einen zusätzlichen, alternativen Port zu konfigurieren. Dann kann man nämlich beispielsweise den Standard-Port nur von den internen Netzen zu lassen und von aussen nur den alternativen Port, auf dessen Zugriff man gff. auch genauer Protokollieren kann.

Leider geht dies mit pfsense standardmässig nicht – Mit einem kleinen Hack in der Konfiguration ist es aber möglich. Continue reading pfsense: sshd auf zwei Ports

Swisscom FTTH ohne Internetbox

Die neue Glasfaser Technik für Internetanschlüsse verbreitet sich in der Schweiz immer mehr. Einer der Marktführer ist Swisscom.
Zum grossen Leid vieler technisch versierter Kunden lässt sich mit dem Swisscom Glasfaser-Router „Internetbox“ leider kein sogenanntes „bridging“, oder „PPPoE Passthrough“ mehr machen. Das heisst man bekommt die öffentliche IP nicht mehr direkt an einem externen Gateway oder Firewall, die „Internetbox“ gibt nur noch private Netwerk IPs an die angeschlossenen Geräte weiter. Zwar lassen sich mit der „DMZ-Funktion“ die meisten Ports an ein Gerät weiter leiten, doch das ist nicht wirklich zufriedenstellend, wenn man selbst ein Gateway/Firewall/Router betreibt und „Herr über den Datenverkehr“ sein will.

Continue reading Swisscom FTTH ohne Internetbox

VMware vCenter Server Appliance: nrpe daemon installieren

Die vCenter Server Appliance von VMware ist eine tolle Sache. Noch toller, wenn man diese mittels icinga und dem nrpe daemon Überwachen kann. 😉

Continue reading VMware vCenter Server Appliance: nrpe daemon installieren

Firefox: Häufige Schreibvorgänge verlangsamen das Systemen

Der Firefox Browser besitzt eine Funktion die nach einen Browser-Absturz die offenen Fenster- Tabs automatisch wiederherstellen kann. Das ist extrem nützlich, doch Firefox speichert den Zustand der aktuellen Fenster/Tabs jede Minute (bei SSD-Festplatten sogar alle 15 Sekunden!), was zu immensen Schreibvorgängen und damit nicht nur zu einem Verlangsamen des Browsers, sondern des ganzen Systems führt!
Continue reading Firefox: Häufige Schreibvorgänge verlangsamen das Systemen