blob: 0b0b59df74cb7c2f0e4936407f1cd40e02744583 [file] [log] [blame]
Steve McIntyre6f59b972014-12-10 16:43:37 +00001#! /usr/bin/python
2
3# Copyright 2014 Linaro Limited
4# Author: Steve McIntyre <steve.mcintyre@linaro.org>
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19# MA 02110-1301, USA.
20#
21# VLANd simple config parser
22#
23
24import ConfigParser
25import os, sys, re
26
27if __name__ == '__main__':
28 vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
29 sys.path.insert(0, vlandpath)
30 sys.path.insert(0, "%s/.." % vlandpath)
31
Steve McIntyre6d84ec12014-12-18 16:56:56 +000032from errors import ConfigError
Steve McIntyre6f59b972014-12-10 16:43:37 +000033
Steve McIntyrec9e72e82015-07-14 15:25:08 +010034def is_positive(text):
35 if text in ('1', 'y', 'Y', 't', 'T', 'True', 'true'):
36 return True
37 elif text in ('0', 'n', 'N', 'f', 'F', 'False', 'false'):
38 return False
39
Steve McIntyrea9875062014-12-12 17:15:15 +000040class DaemonConfigClass:
41 """ Simple container for stuff to make for nicer syntax """
42
43 def __repr__(self):
44 return "<DaemonConfig: port: %s>" % (self.port)
45
Steve McIntyre6f59b972014-12-10 16:43:37 +000046class DBConfigClass:
47 """ Simple container for stuff to make for nicer syntax """
48
49 def __repr__(self):
Steve McIntyrea9875062014-12-12 17:15:15 +000050 return "<DBConfig: server: %s, port: %s, dbname: %s, username: %s, password: %s>" % (self.server, self.port, self.dbname, self.username, self.password)
Steve McIntyre6f59b972014-12-10 16:43:37 +000051
Steve McIntyredbe6aa52015-02-12 07:48:22 +000052class LoggingConfigClass:
53 """ Simple container for stuff to make for nicer syntax """
54
55 def __repr__(self):
56 return "<LoggingConfig: level: %s, filename: %s>" % (self.level, self.filename)
57
Steve McIntyrea866e0c2015-09-18 14:11:06 +010058class VisualisationConfigClass:
59 """ Simple container for stuff to make for nicer syntax """
60 def __repr__(self):
61 return "<VisualisationConfig: enabled: %s, port: %s>" % (self.enabled, self.port)
62
Steve McIntyre6f59b972014-12-10 16:43:37 +000063class SwitchConfigClass:
64 """ Simple container for stuff to make for nicer syntax """
65 def __repr__(self):
Steve McIntyrea9875062014-12-12 17:15:15 +000066 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)
Steve McIntyre6f59b972014-12-10 16:43:37 +000067
68class VlanConfig:
69 """VLANd config class"""
70 def __init__(self, filenames):
Steve McIntyred773cac2014-12-10 23:27:07 +000071
Steve McIntyre6f59b972014-12-10 16:43:37 +000072 config = ConfigParser.RawConfigParser({
73 # Set default values
74 'server': None,
75 'port': None,
Steve McIntyrea866e0c2015-09-18 14:11:06 +010076 'enabled': False,
Steve McIntyre6f59b972014-12-10 16:43:37 +000077 'dbname': None,
78 'username': None,
79 'password': None,
80 'name': None,
81 'driver': None,
Steve McIntyrec9e72e82015-07-14 15:25:08 +010082 'enable_password': None,
83 'debug': False,
Steve McIntyre6f59b972014-12-10 16:43:37 +000084 })
85
86 config.read(filenames)
87
88 # Parse out the config file
89 # Must have a [database] section
Steve McIntyrea9875062014-12-12 17:15:15 +000090 # May have a [vland] section
Steve McIntyre6f59b972014-12-10 16:43:37 +000091 # May have a [logging] section
92 # May have multiple [switch 'foo'] sections
93 if not config.has_section('database'):
94 raise ConfigError('No database configuration section found')
95
Steve McIntyrea9875062014-12-12 17:15:15 +000096 # No DB-specific defaults to set
Steve McIntyre6f59b972014-12-10 16:43:37 +000097 self.database = DBConfigClass()
Steve McIntyrea9875062014-12-12 17:15:15 +000098
Steve McIntyredbe6aa52015-02-12 07:48:22 +000099 # Set defaults logging details
100 self.logging = LoggingConfigClass()
101 self.logging.level = None
102 self.logging.filename = None
103
Steve McIntyrec12f6312014-12-15 15:00:12 +0000104 # Set default port number and VLAN tag
Steve McIntyrea9875062014-12-12 17:15:15 +0000105 self.vland = DaemonConfigClass()
106 self.vland.port = 3080
Steve McIntyrec12f6312014-12-15 15:00:12 +0000107 self.vland.default_vlan_tag = 1
Steve McIntyrea9875062014-12-12 17:15:15 +0000108
Steve McIntyrea866e0c2015-09-18 14:11:06 +0100109 # Visualisation is disabled by default
110 self.visualisation = VisualisationConfigClass()
111 self.visualisation.port = 3081
112 self.visualisation.enabled = False
Steve McIntyrecb258682015-09-18 14:25:06 +0100113
Steve McIntyrea9875062014-12-12 17:15:15 +0000114 # No switch-specific defaults to set
Steve McIntyre6f59b972014-12-10 16:43:37 +0000115 self.switches = {}
116
Steve McIntyreb826fc72015-07-27 17:57:40 +0100117 sw_regex = re.compile(r'(switch)\ (.*)', flags=re.I)
Steve McIntyre6f59b972014-12-10 16:43:37 +0000118 for section in config.sections():
119 if section == 'database':
120 try:
121 self.database.server = config.get(section, 'server')
Steve McIntyrea9875062014-12-12 17:15:15 +0000122 except ConfigParser.NoOptionError:
123 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000124 except:
125 raise ConfigError('Invalid database configuration (server)')
126
127 try:
Steve McIntyrea9875062014-12-12 17:15:15 +0000128 port = config.get(section, 'port')
129 if port is not None:
130 self.database.port = config.getint(section, 'port')
131 except ConfigParser.NoOptionError:
132 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000133 except:
134 raise ConfigError('Invalid database configuration (port)')
135
136 try:
Steve McIntyre6f59b972014-12-10 16:43:37 +0000137 self.database.dbname = config.get(section, 'dbname')
Steve McIntyrea9875062014-12-12 17:15:15 +0000138 except ConfigParser.NoOptionError:
139 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000140 except:
141 raise ConfigError('Invalid database configuration (dbname)')
142
143 try:
Steve McIntyre6f59b972014-12-10 16:43:37 +0000144 self.database.username = config.get(section, 'username')
Steve McIntyrea9875062014-12-12 17:15:15 +0000145 except ConfigParser.NoOptionError:
146 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000147 except:
148 raise ConfigError('Invalid database configuration (username)')
149
150 try:
Steve McIntyre6f59b972014-12-10 16:43:37 +0000151 self.database.password = config.get(section, 'password')
Steve McIntyrea9875062014-12-12 17:15:15 +0000152 except ConfigParser.NoOptionError:
153 pass
Steve McIntyre6f59b972014-12-10 16:43:37 +0000154 except:
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000155 raise ConfigError('Invalid database configuration (password)')
Steve McIntyrea9875062014-12-12 17:15:15 +0000156
157 # Other database config options are optional, but these are not
Steve McIntyre6f59b972014-12-10 16:43:37 +0000158 if self.database.dbname is None or self.database.username is None:
159 raise ConfigError('Database configuration section incomplete')
Steve McIntyrea9875062014-12-12 17:15:15 +0000160
Steve McIntyre6f59b972014-12-10 16:43:37 +0000161 elif section == 'logging':
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000162 try:
163 self.logging.level = config.get(section, 'level')
164 except ConfigParser.NoOptionError:
165 pass
166 except:
167 raise ConfigError('Invalid logging configuration (level)')
168
169 try:
Steve McIntyre19e6b062015-02-12 08:05:08 +0000170 self.logging.filename = config.get(section, 'filename')
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000171 except ConfigParser.NoOptionError:
172 pass
173 except:
174 raise ConfigError('Invalid logging configuration (filename)')
Steve McIntyrea9875062014-12-12 17:15:15 +0000175
176 elif section == 'vland':
177 try:
Steve McIntyrea866e0c2015-09-18 14:11:06 +0100178 self.vland = config.getint(section, 'port')
Steve McIntyrea9875062014-12-12 17:15:15 +0000179 except ConfigParser.NoOptionError:
180 pass
181 except:
182 raise ConfigError('Invalid vland configuration (port)')
183
Steve McIntyrec12f6312014-12-15 15:00:12 +0000184 try:
Steve McIntyrec6906982014-12-23 13:22:38 +0000185 self.vland.default_vlan_tag = config.getint(section, 'default_vlan_tag')
Steve McIntyrec12f6312014-12-15 15:00:12 +0000186 except ConfigParser.NoOptionError:
187 pass
188 except:
189 raise ConfigError('Invalid vland configuration (default_vlan_tag)')
190
Steve McIntyrea866e0c2015-09-18 14:11:06 +0100191 elif section == 'visualisation':
192 try:
193 self.visualisation.port = config.getint(section, 'port')
194 except ConfigParser.NoOptionError:
195 pass
196 except:
197 raise ConfigError('Invalid visualisation configuration (port)')
198
199 try:
200 self.visualisation.enabled = config.get(section, 'enabled')
201 if not is_positive(self.visualisation.enabled):
202 self.visualisation.enabled = False
203 elif is_positive(self.visualisation.enabled):
204 self.visualisation.enabled = True
205 except ConfigParser.NoOptionError:
206 pass
207 except:
208 raise ConfigError('Invalid visualisation configuration (enabled)')
209
Steve McIntyre6f59b972014-12-10 16:43:37 +0000210 else:
211 match = sw_regex.match(section)
212 if match:
213 # Constraint: switch names must be unique! See if
214 # there's already a switch with this name
215 name = config.get(section, 'name')
216 for key in self.switches.keys():
217 if name == key:
218 raise ConfigError('Found switches with the same name (%s)' % name)
219 self.switches[name] = SwitchConfigClass()
220 self.switches[name].name = name
221 self.switches[name].section = section
222 self.switches[name].driver = config.get(section, 'driver')
223 self.switches[name].username = config.get(section, 'username')
224 self.switches[name].password = config.get(section, 'password')
225 self.switches[name].enable_password = config.get(section, 'enable_password')
Steve McIntyrec9e72e82015-07-14 15:25:08 +0100226 self.switches[name].debug = config.get(section, 'debug')
227 if not is_positive(self.switches[name].debug):
228 self.switches[name].debug = False
229 elif is_positive(self.switches[name].debug):
230 self.switches[name].debug = True
231 else:
232 raise ConfigError('Invalid vland configuration (switch "%s", debug "%s"' % (name, self.switches[name].debug))
Steve McIntyre6f59b972014-12-10 16:43:37 +0000233 else:
234 raise ConfigError('Unrecognised config section %s' % section)
235
236 def __del__(self):
237 pass
238
239if __name__ == '__main__':
Steve McIntyre6d84ec12014-12-18 16:56:56 +0000240 c = VlanConfig(filenames=('./vland.cfg',))
241 print c.database
242 print c.vland
243 for switch in c.switches:
244 print c.switches[switch]
Steve McIntyre6f59b972014-12-10 16:43:37 +0000245