blob: 802f881a7c6dfcaf4c78d7551282977833ebcc6f [file] [log] [blame]
Steve McIntyre6f59b972014-12-10 16:43:37 +00001#! /usr/bin/python
2
Steve McIntyre94ef65e2015-09-25 01:08:14 +01003# Copyright 2014-2015 Linaro Limited
Steve McIntyre6f59b972014-12-10 16:43:37 +00004# 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
Steve McIntyre6c7ca7f2018-03-05 18:15:51 +000027from Vland.errors import ConfigError
Steve McIntyre6f59b972014-12-10 16:43:37 +000028
Steve McIntyrec9e72e82015-07-14 15:25:08 +010029def is_positive(text):
Steve McIntyrefa1beea2015-10-19 18:52:29 +010030 valid_true = ('1', 'y', 'yes', 't', 'true')
31 valid_false = ('0', 'n', 'no', 'f', 'false')
32
33 if str(text) in valid_true or str(text).lower() in valid_true:
Steve McIntyrec9e72e82015-07-14 15:25:08 +010034 return True
Steve McIntyrefa1beea2015-10-19 18:52:29 +010035 elif str(text) in valid_false or str(text).lower() in valid_false:
Steve McIntyrec9e72e82015-07-14 15:25:08 +010036 return False
37
Steve McIntyrec3709802015-10-19 18:34:53 +010038def is_valid_logging_level(text):
39 valid = ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG')
40 if text in valid:
41 return True
42 return False
Steve McIntyre869ee5d2015-10-19 19:05:55 +010043
Steve McIntyrea9875062014-12-12 17:15:15 +000044class DaemonConfigClass:
45 """ Simple container for stuff to make for nicer syntax """
46
47 def __repr__(self):
48 return "<DaemonConfig: port: %s>" % (self.port)
49
Steve McIntyre6f59b972014-12-10 16:43:37 +000050class DBConfigClass:
51 """ Simple container for stuff to make for nicer syntax """
52
53 def __repr__(self):
Steve McIntyrea9875062014-12-12 17:15:15 +000054 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 +000055
Steve McIntyredbe6aa52015-02-12 07:48:22 +000056class LoggingConfigClass:
57 """ Simple container for stuff to make for nicer syntax """
58
59 def __repr__(self):
60 return "<LoggingConfig: level: %s, filename: %s>" % (self.level, self.filename)
61
Steve McIntyrea866e0c2015-09-18 14:11:06 +010062class VisualisationConfigClass:
63 """ Simple container for stuff to make for nicer syntax """
64 def __repr__(self):
65 return "<VisualisationConfig: enabled: %s, port: %s>" % (self.enabled, self.port)
66
Steve McIntyre6f59b972014-12-10 16:43:37 +000067class SwitchConfigClass:
68 """ Simple container for stuff to make for nicer syntax """
69 def __repr__(self):
Steve McIntyrea9875062014-12-12 17:15:15 +000070 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 +000071
72class VlanConfig:
73 """VLANd config class"""
74 def __init__(self, filenames):
Steve McIntyred773cac2014-12-10 23:27:07 +000075
Steve McIntyre6f59b972014-12-10 16:43:37 +000076 config = ConfigParser.RawConfigParser({
77 # Set default values
Steve McIntyre6f59b972014-12-10 16:43:37 +000078 'dbname': None,
Steve McIntyre7f359dd2015-10-08 14:28:56 +010079 'debug': False,
Steve McIntyre6f59b972014-12-10 16:43:37 +000080 'driver': None,
Steve McIntyrec9e72e82015-07-14 15:25:08 +010081 'enable_password': None,
Steve McIntyre7f359dd2015-10-08 14:28:56 +010082 'enabled': False,
83 'name': None,
84 'password': None,
85 'port': None,
Steve McIntyreb0aa4602015-10-08 15:33:28 +010086 'refresh': None,
Steve McIntyre7f359dd2015-10-08 14:28:56 +010087 'server': None,
88 'username': None,
Steve McIntyre6f59b972014-12-10 16:43:37 +000089 })
90
91 config.read(filenames)
92
93 # Parse out the config file
94 # Must have a [database] section
Steve McIntyrea9875062014-12-12 17:15:15 +000095 # May have a [vland] section
Steve McIntyre6f59b972014-12-10 16:43:37 +000096 # May have a [logging] section
97 # May have multiple [switch 'foo'] sections
98 if not config.has_section('database'):
99 raise ConfigError('No database configuration section found')
100
Steve McIntyrea9875062014-12-12 17:15:15 +0000101 # No DB-specific defaults to set
Steve McIntyre6f59b972014-12-10 16:43:37 +0000102 self.database = DBConfigClass()
Steve McIntyrea9875062014-12-12 17:15:15 +0000103
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000104 # Set defaults logging details
105 self.logging = LoggingConfigClass()
Steve McIntyrec3709802015-10-19 18:34:53 +0100106 self.logging.level = 'CRITICAL'
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000107 self.logging.filename = None
108
Steve McIntyrec12f6312014-12-15 15:00:12 +0000109 # Set default port number and VLAN tag
Steve McIntyrea9875062014-12-12 17:15:15 +0000110 self.vland = DaemonConfigClass()
111 self.vland.port = 3080
Steve McIntyrec12f6312014-12-15 15:00:12 +0000112 self.vland.default_vlan_tag = 1
Steve McIntyrea9875062014-12-12 17:15:15 +0000113
Steve McIntyrea866e0c2015-09-18 14:11:06 +0100114 # Visualisation is disabled by default
115 self.visualisation = VisualisationConfigClass()
116 self.visualisation.port = 3081
117 self.visualisation.enabled = False
Steve McIntyrecb258682015-09-18 14:25:06 +0100118
Steve McIntyrea9875062014-12-12 17:15:15 +0000119 # No switch-specific defaults to set
Steve McIntyre6f59b972014-12-10 16:43:37 +0000120 self.switches = {}
121
Steve McIntyreb826fc72015-07-27 17:57:40 +0100122 sw_regex = re.compile(r'(switch)\ (.*)', flags=re.I)
Steve McIntyre6f59b972014-12-10 16:43:37 +0000123 for section in config.sections():
124 if section == 'database':
125 try:
126 self.database.server = config.get(section, 'server')
Steve McIntyrea9875062014-12-12 17:15:15 +0000127 except ConfigParser.NoOptionError:
128 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000129 except:
130 raise ConfigError('Invalid database configuration (server)')
131
132 try:
Steve McIntyrea9875062014-12-12 17:15:15 +0000133 port = config.get(section, 'port')
134 if port is not None:
135 self.database.port = config.getint(section, 'port')
136 except ConfigParser.NoOptionError:
137 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000138 except:
139 raise ConfigError('Invalid database configuration (port)')
140
141 try:
Steve McIntyre6f59b972014-12-10 16:43:37 +0000142 self.database.dbname = config.get(section, 'dbname')
Steve McIntyrea9875062014-12-12 17:15:15 +0000143 except ConfigParser.NoOptionError:
144 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000145 except:
146 raise ConfigError('Invalid database configuration (dbname)')
147
148 try:
Steve McIntyre6f59b972014-12-10 16:43:37 +0000149 self.database.username = config.get(section, 'username')
Steve McIntyrea9875062014-12-12 17:15:15 +0000150 except ConfigParser.NoOptionError:
151 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000152 except:
153 raise ConfigError('Invalid database configuration (username)')
154
155 try:
Steve McIntyre6f59b972014-12-10 16:43:37 +0000156 self.database.password = config.get(section, 'password')
Steve McIntyrea9875062014-12-12 17:15:15 +0000157 except ConfigParser.NoOptionError:
158 pass
Steve McIntyre6f59b972014-12-10 16:43:37 +0000159 except:
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000160 raise ConfigError('Invalid database configuration (password)')
Steve McIntyrea9875062014-12-12 17:15:15 +0000161
162 # Other database config options are optional, but these are not
Steve McIntyre6f59b972014-12-10 16:43:37 +0000163 if self.database.dbname is None or self.database.username is None:
164 raise ConfigError('Database configuration section incomplete')
Steve McIntyrea9875062014-12-12 17:15:15 +0000165
Steve McIntyre6f59b972014-12-10 16:43:37 +0000166 elif section == 'logging':
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000167 try:
168 self.logging.level = config.get(section, 'level')
169 except ConfigParser.NoOptionError:
170 pass
171 except:
172 raise ConfigError('Invalid logging configuration (level)')
Steve McIntyrec3709802015-10-19 18:34:53 +0100173 self.logging.level = self.logging.level.upper()
174 if not is_valid_logging_level(self.logging.level):
Steve McIntyre869ee5d2015-10-19 19:05:55 +0100175 raise ConfigError('Invalid logging configuration (level)')
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000176
177 try:
Steve McIntyre19e6b062015-02-12 08:05:08 +0000178 self.logging.filename = config.get(section, 'filename')
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000179 except ConfigParser.NoOptionError:
180 pass
181 except:
182 raise ConfigError('Invalid logging configuration (filename)')
Steve McIntyrea9875062014-12-12 17:15:15 +0000183
184 elif section == 'vland':
185 try:
Steve McIntyred05265b2015-09-23 00:03:09 +0100186 self.vland.port = config.getint(section, 'port')
Steve McIntyrea9875062014-12-12 17:15:15 +0000187 except ConfigParser.NoOptionError:
188 pass
189 except:
190 raise ConfigError('Invalid vland configuration (port)')
191
Steve McIntyrec12f6312014-12-15 15:00:12 +0000192 try:
Steve McIntyrec6906982014-12-23 13:22:38 +0000193 self.vland.default_vlan_tag = config.getint(section, 'default_vlan_tag')
Steve McIntyrec12f6312014-12-15 15:00:12 +0000194 except ConfigParser.NoOptionError:
195 pass
196 except:
197 raise ConfigError('Invalid vland configuration (default_vlan_tag)')
198
Steve McIntyrea866e0c2015-09-18 14:11:06 +0100199 elif section == 'visualisation':
200 try:
201 self.visualisation.port = config.getint(section, 'port')
202 except ConfigParser.NoOptionError:
203 pass
204 except:
205 raise ConfigError('Invalid visualisation configuration (port)')
206
207 try:
208 self.visualisation.enabled = config.get(section, 'enabled')
209 if not is_positive(self.visualisation.enabled):
210 self.visualisation.enabled = False
211 elif is_positive(self.visualisation.enabled):
212 self.visualisation.enabled = True
213 except ConfigParser.NoOptionError:
214 pass
215 except:
216 raise ConfigError('Invalid visualisation configuration (enabled)')
217
Steve McIntyreb0aa4602015-10-08 15:33:28 +0100218 try:
219 self.visualisation.refresh = config.get(section, 'refresh')
220 if self.visualisation.refresh is not None:
Steve McIntyrec2308ff2015-10-27 12:07:17 +0000221 if not is_positive(self.visualisation.refresh):
222 self.visualisation.refresh = None
223 else:
224 self.visualisation.refresh = int(self.visualisation.refresh)
Steve McIntyreb0aa4602015-10-08 15:33:28 +0100225 except ConfigParser.NoOptionError:
226 pass
227 except:
228 raise ConfigError('Invalid visualisation configuration (refresh)')
229
Steve McIntyre6f59b972014-12-10 16:43:37 +0000230 else:
231 match = sw_regex.match(section)
232 if match:
233 # Constraint: switch names must be unique! See if
234 # there's already a switch with this name
235 name = config.get(section, 'name')
236 for key in self.switches.keys():
237 if name == key:
238 raise ConfigError('Found switches with the same name (%s)' % name)
239 self.switches[name] = SwitchConfigClass()
240 self.switches[name].name = name
241 self.switches[name].section = section
242 self.switches[name].driver = config.get(section, 'driver')
243 self.switches[name].username = config.get(section, 'username')
244 self.switches[name].password = config.get(section, 'password')
245 self.switches[name].enable_password = config.get(section, 'enable_password')
Steve McIntyrec9e72e82015-07-14 15:25:08 +0100246 self.switches[name].debug = config.get(section, 'debug')
247 if not is_positive(self.switches[name].debug):
248 self.switches[name].debug = False
249 elif is_positive(self.switches[name].debug):
250 self.switches[name].debug = True
251 else:
252 raise ConfigError('Invalid vland configuration (switch "%s", debug "%s"' % (name, self.switches[name].debug))
Steve McIntyre6f59b972014-12-10 16:43:37 +0000253 else:
254 raise ConfigError('Unrecognised config section %s' % section)
255
Steve McIntyre4e9fee52015-10-19 18:54:23 +0100256 # Generic checking for config values
257 if self.visualisation.enabled:
258 if self.visualisation.port == self.vland.port:
Steve McIntyre869ee5d2015-10-19 19:05:55 +0100259 raise ConfigError('Invalid configuration: VLANd and the visualisation service must use distinct port numbers')
Steve McIntyre4e9fee52015-10-19 18:54:23 +0100260
Steve McIntyre6f59b972014-12-10 16:43:37 +0000261 def __del__(self):
262 pass
263
264if __name__ == '__main__':
Steve McIntyre6d84ec12014-12-18 16:56:56 +0000265 c = VlanConfig(filenames=('./vland.cfg',))
266 print c.database
267 print c.vland
268 for switch in c.switches:
269 print c.switches[switch]
Steve McIntyre6f59b972014-12-10 16:43:37 +0000270