blob: 67d3bfaafd8d663ac6bc68075774034912a8560b [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
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):
Steve McIntyrefa1beea2015-10-19 18:52:29 +010035 valid_true = ('1', 'y', 'yes', 't', 'true')
36 valid_false = ('0', 'n', 'no', 'f', 'false')
37
38 if str(text) in valid_true or str(text).lower() in valid_true:
Steve McIntyrec9e72e82015-07-14 15:25:08 +010039 return True
Steve McIntyrefa1beea2015-10-19 18:52:29 +010040 elif str(text) in valid_false or str(text).lower() in valid_false:
Steve McIntyrec9e72e82015-07-14 15:25:08 +010041 return False
42
Steve McIntyrec3709802015-10-19 18:34:53 +010043def is_valid_logging_level(text):
44 valid = ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG')
45 if text in valid:
46 return True
47 return False
Steve McIntyre869ee5d2015-10-19 19:05:55 +010048
Steve McIntyrea9875062014-12-12 17:15:15 +000049class DaemonConfigClass:
50 """ Simple container for stuff to make for nicer syntax """
51
52 def __repr__(self):
53 return "<DaemonConfig: port: %s>" % (self.port)
54
Steve McIntyre6f59b972014-12-10 16:43:37 +000055class DBConfigClass:
56 """ Simple container for stuff to make for nicer syntax """
57
58 def __repr__(self):
Steve McIntyrea9875062014-12-12 17:15:15 +000059 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 +000060
Steve McIntyredbe6aa52015-02-12 07:48:22 +000061class LoggingConfigClass:
62 """ Simple container for stuff to make for nicer syntax """
63
64 def __repr__(self):
65 return "<LoggingConfig: level: %s, filename: %s>" % (self.level, self.filename)
66
Steve McIntyrea866e0c2015-09-18 14:11:06 +010067class VisualisationConfigClass:
68 """ Simple container for stuff to make for nicer syntax """
69 def __repr__(self):
70 return "<VisualisationConfig: enabled: %s, port: %s>" % (self.enabled, self.port)
71
Steve McIntyre6f59b972014-12-10 16:43:37 +000072class SwitchConfigClass:
73 """ Simple container for stuff to make for nicer syntax """
74 def __repr__(self):
Steve McIntyrea9875062014-12-12 17:15:15 +000075 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 +000076
77class VlanConfig:
78 """VLANd config class"""
79 def __init__(self, filenames):
Steve McIntyred773cac2014-12-10 23:27:07 +000080
Steve McIntyre6f59b972014-12-10 16:43:37 +000081 config = ConfigParser.RawConfigParser({
82 # Set default values
Steve McIntyre6f59b972014-12-10 16:43:37 +000083 'dbname': None,
Steve McIntyre7f359dd2015-10-08 14:28:56 +010084 'debug': False,
Steve McIntyre6f59b972014-12-10 16:43:37 +000085 'driver': None,
Steve McIntyrec9e72e82015-07-14 15:25:08 +010086 'enable_password': None,
Steve McIntyre7f359dd2015-10-08 14:28:56 +010087 'enabled': False,
88 'name': None,
89 'password': None,
90 'port': None,
Steve McIntyreb0aa4602015-10-08 15:33:28 +010091 'refresh': None,
Steve McIntyre7f359dd2015-10-08 14:28:56 +010092 'server': None,
93 'username': None,
Steve McIntyre6f59b972014-12-10 16:43:37 +000094 })
95
96 config.read(filenames)
97
98 # Parse out the config file
99 # Must have a [database] section
Steve McIntyrea9875062014-12-12 17:15:15 +0000100 # May have a [vland] section
Steve McIntyre6f59b972014-12-10 16:43:37 +0000101 # May have a [logging] section
102 # May have multiple [switch 'foo'] sections
103 if not config.has_section('database'):
104 raise ConfigError('No database configuration section found')
105
Steve McIntyrea9875062014-12-12 17:15:15 +0000106 # No DB-specific defaults to set
Steve McIntyre6f59b972014-12-10 16:43:37 +0000107 self.database = DBConfigClass()
Steve McIntyrea9875062014-12-12 17:15:15 +0000108
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000109 # Set defaults logging details
110 self.logging = LoggingConfigClass()
Steve McIntyrec3709802015-10-19 18:34:53 +0100111 self.logging.level = 'CRITICAL'
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000112 self.logging.filename = None
113
Steve McIntyrec12f6312014-12-15 15:00:12 +0000114 # Set default port number and VLAN tag
Steve McIntyrea9875062014-12-12 17:15:15 +0000115 self.vland = DaemonConfigClass()
116 self.vland.port = 3080
Steve McIntyrec12f6312014-12-15 15:00:12 +0000117 self.vland.default_vlan_tag = 1
Steve McIntyrea9875062014-12-12 17:15:15 +0000118
Steve McIntyrea866e0c2015-09-18 14:11:06 +0100119 # Visualisation is disabled by default
120 self.visualisation = VisualisationConfigClass()
121 self.visualisation.port = 3081
122 self.visualisation.enabled = False
Steve McIntyrecb258682015-09-18 14:25:06 +0100123
Steve McIntyrea9875062014-12-12 17:15:15 +0000124 # No switch-specific defaults to set
Steve McIntyre6f59b972014-12-10 16:43:37 +0000125 self.switches = {}
126
Steve McIntyreb826fc72015-07-27 17:57:40 +0100127 sw_regex = re.compile(r'(switch)\ (.*)', flags=re.I)
Steve McIntyre6f59b972014-12-10 16:43:37 +0000128 for section in config.sections():
129 if section == 'database':
130 try:
131 self.database.server = config.get(section, 'server')
Steve McIntyrea9875062014-12-12 17:15:15 +0000132 except ConfigParser.NoOptionError:
133 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000134 except:
135 raise ConfigError('Invalid database configuration (server)')
136
137 try:
Steve McIntyrea9875062014-12-12 17:15:15 +0000138 port = config.get(section, 'port')
139 if port is not None:
140 self.database.port = config.getint(section, 'port')
141 except ConfigParser.NoOptionError:
142 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000143 except:
144 raise ConfigError('Invalid database configuration (port)')
145
146 try:
Steve McIntyre6f59b972014-12-10 16:43:37 +0000147 self.database.dbname = config.get(section, 'dbname')
Steve McIntyrea9875062014-12-12 17:15:15 +0000148 except ConfigParser.NoOptionError:
149 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000150 except:
151 raise ConfigError('Invalid database configuration (dbname)')
152
153 try:
Steve McIntyre6f59b972014-12-10 16:43:37 +0000154 self.database.username = config.get(section, 'username')
Steve McIntyrea9875062014-12-12 17:15:15 +0000155 except ConfigParser.NoOptionError:
156 pass
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000157 except:
158 raise ConfigError('Invalid database configuration (username)')
159
160 try:
Steve McIntyre6f59b972014-12-10 16:43:37 +0000161 self.database.password = config.get(section, 'password')
Steve McIntyrea9875062014-12-12 17:15:15 +0000162 except ConfigParser.NoOptionError:
163 pass
Steve McIntyre6f59b972014-12-10 16:43:37 +0000164 except:
Steve McIntyre38a30eb2014-12-11 16:10:55 +0000165 raise ConfigError('Invalid database configuration (password)')
Steve McIntyrea9875062014-12-12 17:15:15 +0000166
167 # Other database config options are optional, but these are not
Steve McIntyre6f59b972014-12-10 16:43:37 +0000168 if self.database.dbname is None or self.database.username is None:
169 raise ConfigError('Database configuration section incomplete')
Steve McIntyrea9875062014-12-12 17:15:15 +0000170
Steve McIntyre6f59b972014-12-10 16:43:37 +0000171 elif section == 'logging':
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000172 try:
173 self.logging.level = config.get(section, 'level')
174 except ConfigParser.NoOptionError:
175 pass
176 except:
177 raise ConfigError('Invalid logging configuration (level)')
Steve McIntyrec3709802015-10-19 18:34:53 +0100178 self.logging.level = self.logging.level.upper()
179 if not is_valid_logging_level(self.logging.level):
Steve McIntyre869ee5d2015-10-19 19:05:55 +0100180 raise ConfigError('Invalid logging configuration (level)')
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000181
182 try:
Steve McIntyre19e6b062015-02-12 08:05:08 +0000183 self.logging.filename = config.get(section, 'filename')
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000184 except ConfigParser.NoOptionError:
185 pass
186 except:
187 raise ConfigError('Invalid logging configuration (filename)')
Steve McIntyrea9875062014-12-12 17:15:15 +0000188
189 elif section == 'vland':
190 try:
Steve McIntyred05265b2015-09-23 00:03:09 +0100191 self.vland.port = config.getint(section, 'port')
Steve McIntyrea9875062014-12-12 17:15:15 +0000192 except ConfigParser.NoOptionError:
193 pass
194 except:
195 raise ConfigError('Invalid vland configuration (port)')
196
Steve McIntyrec12f6312014-12-15 15:00:12 +0000197 try:
Steve McIntyrec6906982014-12-23 13:22:38 +0000198 self.vland.default_vlan_tag = config.getint(section, 'default_vlan_tag')
Steve McIntyrec12f6312014-12-15 15:00:12 +0000199 except ConfigParser.NoOptionError:
200 pass
201 except:
202 raise ConfigError('Invalid vland configuration (default_vlan_tag)')
203
Steve McIntyrea866e0c2015-09-18 14:11:06 +0100204 elif section == 'visualisation':
205 try:
206 self.visualisation.port = config.getint(section, 'port')
207 except ConfigParser.NoOptionError:
208 pass
209 except:
210 raise ConfigError('Invalid visualisation configuration (port)')
211
212 try:
213 self.visualisation.enabled = config.get(section, 'enabled')
214 if not is_positive(self.visualisation.enabled):
215 self.visualisation.enabled = False
216 elif is_positive(self.visualisation.enabled):
217 self.visualisation.enabled = True
218 except ConfigParser.NoOptionError:
219 pass
220 except:
221 raise ConfigError('Invalid visualisation configuration (enabled)')
222
Steve McIntyreb0aa4602015-10-08 15:33:28 +0100223 try:
224 self.visualisation.refresh = config.get(section, 'refresh')
225 if self.visualisation.refresh is not None:
Steve McIntyrec2308ff2015-10-27 12:07:17 +0000226 if not is_positive(self.visualisation.refresh):
227 self.visualisation.refresh = None
228 else:
229 self.visualisation.refresh = int(self.visualisation.refresh)
Steve McIntyreb0aa4602015-10-08 15:33:28 +0100230 except ConfigParser.NoOptionError:
231 pass
232 except:
233 raise ConfigError('Invalid visualisation configuration (refresh)')
234
Steve McIntyre6f59b972014-12-10 16:43:37 +0000235 else:
236 match = sw_regex.match(section)
237 if match:
238 # Constraint: switch names must be unique! See if
239 # there's already a switch with this name
240 name = config.get(section, 'name')
241 for key in self.switches.keys():
242 if name == key:
243 raise ConfigError('Found switches with the same name (%s)' % name)
244 self.switches[name] = SwitchConfigClass()
245 self.switches[name].name = name
246 self.switches[name].section = section
247 self.switches[name].driver = config.get(section, 'driver')
248 self.switches[name].username = config.get(section, 'username')
249 self.switches[name].password = config.get(section, 'password')
250 self.switches[name].enable_password = config.get(section, 'enable_password')
Steve McIntyrec9e72e82015-07-14 15:25:08 +0100251 self.switches[name].debug = config.get(section, 'debug')
252 if not is_positive(self.switches[name].debug):
253 self.switches[name].debug = False
254 elif is_positive(self.switches[name].debug):
255 self.switches[name].debug = True
256 else:
257 raise ConfigError('Invalid vland configuration (switch "%s", debug "%s"' % (name, self.switches[name].debug))
Steve McIntyre6f59b972014-12-10 16:43:37 +0000258 else:
259 raise ConfigError('Unrecognised config section %s' % section)
260
Steve McIntyre4e9fee52015-10-19 18:54:23 +0100261 # Generic checking for config values
262 if self.visualisation.enabled:
263 if self.visualisation.port == self.vland.port:
Steve McIntyre869ee5d2015-10-19 19:05:55 +0100264 raise ConfigError('Invalid configuration: VLANd and the visualisation service must use distinct port numbers')
Steve McIntyre4e9fee52015-10-19 18:54:23 +0100265
Steve McIntyre6f59b972014-12-10 16:43:37 +0000266 def __del__(self):
267 pass
268
269if __name__ == '__main__':
Steve McIntyre6d84ec12014-12-18 16:56:56 +0000270 c = VlanConfig(filenames=('./vland.cfg',))
271 print c.database
272 print c.vland
273 for switch in c.switches:
274 print c.switches[switch]
Steve McIntyre6f59b972014-12-10 16:43:37 +0000275