blob: dc2ad673b330d5840672c84682c6d2f2bf9378eb [file] [log] [blame]
#! /usr/bin/python
# Copyright 2014-2015 Linaro Limited
# Author: Steve McIntyre <steve.mcintyre@linaro.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
# VLANd simple config parser
#
import ConfigParser
import os, sys, re
if __name__ == '__main__':
vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
sys.path.insert(0, vlandpath)
sys.path.insert(0, "%s/.." % vlandpath)
from errors import ConfigError
def is_positive(text):
valid_true = ('1', 'y', 'yes', 't', 'true')
valid_false = ('0', 'n', 'no', 'f', 'false')
if str(text) in valid_true or str(text).lower() in valid_true:
return True
elif str(text) in valid_false or str(text).lower() in valid_false:
return False
def is_valid_logging_level(text):
valid = ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG')
if text in valid:
return True
return False
class DaemonConfigClass:
""" Simple container for stuff to make for nicer syntax """
def __repr__(self):
return "<DaemonConfig: port: %s>" % (self.port)
class DBConfigClass:
""" Simple container for stuff to make for nicer syntax """
def __repr__(self):
return "<DBConfig: server: %s, port: %s, dbname: %s, username: %s, password: %s>" % (self.server, self.port, self.dbname, self.username, self.password)
class LoggingConfigClass:
""" Simple container for stuff to make for nicer syntax """
def __repr__(self):
return "<LoggingConfig: level: %s, filename: %s>" % (self.level, self.filename)
class VisualisationConfigClass:
""" Simple container for stuff to make for nicer syntax """
def __repr__(self):
return "<VisualisationConfig: enabled: %s, port: %s>" % (self.enabled, self.port)
class SwitchConfigClass:
""" Simple container for stuff to make for nicer syntax """
def __repr__(self):
return "<SwitchConfig: name: %s, section: %s, driver: %s, username: %s, password: %s, enable_password: %s>" % (self.name, self.section, self.driver, self.username, self.password, self.enable_password)
class VlanConfig:
"""VLANd config class"""
def __init__(self, filenames):
config = ConfigParser.RawConfigParser({
# Set default values
'dbname': None,
'debug': False,
'driver': None,
'enable_password': None,
'enabled': False,
'name': None,
'password': None,
'port': None,
'refresh': None,
'server': None,
'username': None,
})
config.read(filenames)
# Parse out the config file
# Must have a [database] section
# May have a [vland] section
# May have a [logging] section
# May have multiple [switch 'foo'] sections
if not config.has_section('database'):
raise ConfigError('No database configuration section found')
# No DB-specific defaults to set
self.database = DBConfigClass()
# Set defaults logging details
self.logging = LoggingConfigClass()
self.logging.level = 'CRITICAL'
self.logging.filename = None
# Set default port number and VLAN tag
self.vland = DaemonConfigClass()
self.vland.port = 3080
self.vland.default_vlan_tag = 1
# Visualisation is disabled by default
self.visualisation = VisualisationConfigClass()
self.visualisation.port = 3081
self.visualisation.enabled = False
# No switch-specific defaults to set
self.switches = {}
sw_regex = re.compile(r'(switch)\ (.*)', flags=re.I)
for section in config.sections():
if section == 'database':
try:
self.database.server = config.get(section, 'server')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid database configuration (server)')
try:
port = config.get(section, 'port')
if port is not None:
self.database.port = config.getint(section, 'port')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid database configuration (port)')
try:
self.database.dbname = config.get(section, 'dbname')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid database configuration (dbname)')
try:
self.database.username = config.get(section, 'username')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid database configuration (username)')
try:
self.database.password = config.get(section, 'password')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid database configuration (password)')
# Other database config options are optional, but these are not
if self.database.dbname is None or self.database.username is None:
raise ConfigError('Database configuration section incomplete')
elif section == 'logging':
try:
self.logging.level = config.get(section, 'level')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid logging configuration (level)')
self.logging.level = self.logging.level.upper()
if not is_valid_logging_level(self.logging.level):
raise ConfigError('Invalid logging configuration (level)')
try:
self.logging.filename = config.get(section, 'filename')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid logging configuration (filename)')
elif section == 'vland':
try:
self.vland.port = config.getint(section, 'port')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid vland configuration (port)')
try:
self.vland.default_vlan_tag = config.getint(section, 'default_vlan_tag')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid vland configuration (default_vlan_tag)')
elif section == 'visualisation':
try:
self.visualisation.port = config.getint(section, 'port')
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid visualisation configuration (port)')
try:
self.visualisation.enabled = config.get(section, 'enabled')
if not is_positive(self.visualisation.enabled):
self.visualisation.enabled = False
elif is_positive(self.visualisation.enabled):
self.visualisation.enabled = True
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid visualisation configuration (enabled)')
try:
self.visualisation.refresh = config.get(section, 'refresh')
if self.visualisation.refresh is not None:
self.visualisation.refresh = int(self.visualisation.refresh)
except ConfigParser.NoOptionError:
pass
except:
raise ConfigError('Invalid visualisation configuration (refresh)')
else:
match = sw_regex.match(section)
if match:
# Constraint: switch names must be unique! See if
# there's already a switch with this name
name = config.get(section, 'name')
for key in self.switches.keys():
if name == key:
raise ConfigError('Found switches with the same name (%s)' % name)
self.switches[name] = SwitchConfigClass()
self.switches[name].name = name
self.switches[name].section = section
self.switches[name].driver = config.get(section, 'driver')
self.switches[name].username = config.get(section, 'username')
self.switches[name].password = config.get(section, 'password')
self.switches[name].enable_password = config.get(section, 'enable_password')
self.switches[name].debug = config.get(section, 'debug')
if not is_positive(self.switches[name].debug):
self.switches[name].debug = False
elif is_positive(self.switches[name].debug):
self.switches[name].debug = True
else:
raise ConfigError('Invalid vland configuration (switch "%s", debug "%s"' % (name, self.switches[name].debug))
else:
raise ConfigError('Unrecognised config section %s' % section)
# Generic checking for config values
if self.visualisation.enabled:
if self.visualisation.port == self.vland.port:
raise ConfigError('Invalid configuration: VLANd and the visualisation service must use distinct port numbers')
def __del__(self):
pass
if __name__ == '__main__':
c = VlanConfig(filenames=('./vland.cfg',))
print c.database
print c.vland
for switch in c.switches:
print c.switches[switch]