blob: 5410831f4242d4ed065450d61cea199d4e2898dc [file] [log] [blame]
Steve McIntyree5043dd2014-12-10 16:49:28 +00001#! /usr/bin/python
2
3# Copyright 2014 Linaro Limited
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18# MA 02110-1301, USA.
19#
20# Main VLANd module
21#
22
Steve McIntyre1cb0b482014-12-22 17:18:11 +000023import os, sys
Steve McIntyree5043dd2014-12-10 16:49:28 +000024import time
25import logging
26
27vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
28sys.path.insert(0, vlandpath)
29
Steve McIntyree5043dd2014-12-10 16:49:28 +000030from config.config import VlanConfig
31from db.db import VlanDB
Steve McIntyre6c44fb22014-12-12 22:11:45 +000032from ipc.ipc import VlanIpc
Steve McIntyre5fa22652015-04-01 18:01:45 +010033from errors import InputError, SocketError
Steve McIntyref1c04f92014-12-16 18:23:15 +000034from util import VlanUtil
Steve McIntyree5043dd2014-12-10 16:49:28 +000035
Steve McIntyre231354b2014-12-16 19:22:12 +000036class DaemonState:
37 """ Simple container for stuff to make for nicer syntax """
Steve McIntyree5043dd2014-12-10 16:49:28 +000038
Steve McIntyre231354b2014-12-16 19:22:12 +000039state = DaemonState()
Steve McIntyre77fd71c2015-07-31 17:16:01 +010040state.version = "0.4-DEV"
Steve McIntyre231354b2014-12-16 19:22:12 +000041state.banner = "Linaro VLANd version %s" % state.version
42state.starttime = time.time()
43
44print '%s' % state.banner
Steve McIntyree5043dd2014-12-10 16:49:28 +000045
46print 'Parsing Config...'
Steve McIntyre231354b2014-12-16 19:22:12 +000047state.config = VlanConfig(filenames=('./vland.cfg',))
48print ' Config knows about %d switches' % len(state.config.switches)
Steve McIntyree5043dd2014-12-10 16:49:28 +000049
Steve McIntyref1c04f92014-12-16 18:23:15 +000050util = VlanUtil()
51
Steve McIntyree5043dd2014-12-10 16:49:28 +000052print 'Connecting to DB...'
Steve McIntyre283d5f22014-12-17 13:12:04 +000053state.db = VlanDB(db_name=state.config.database.dbname,
54 username=state.config.database.username)
Steve McIntyree5043dd2014-12-10 16:49:28 +000055
Steve McIntyre283d5f22014-12-17 13:12:04 +000056switches = state.db.all_switches()
Steve McIntyree5043dd2014-12-10 16:49:28 +000057print ' DB knows about %d switches' % len(switches)
Steve McIntyre283d5f22014-12-17 13:12:04 +000058ports = state.db.all_ports()
Steve McIntyree5043dd2014-12-10 16:49:28 +000059print ' DB knows about %d ports' % len(ports)
Steve McIntyre283d5f22014-12-17 13:12:04 +000060vlans = state.db.all_vlans()
Steve McIntyree5043dd2014-12-10 16:49:28 +000061print ' DB knows about %d vlans' % len(vlans)
Steve McIntyrec4890132015-08-07 15:19:11 +010062trunks = state.db.all_trunks()
63print ' DB knows about %d trunks' % len(trunks)
Steve McIntyree5043dd2014-12-10 16:49:28 +000064
Steve McIntyreae286192014-12-12 22:10:43 +000065# Initial startup sanity chacking
Steve McIntyree5043dd2014-12-10 16:49:28 +000066
Steve McIntyrec12f6312014-12-15 15:00:12 +000067# For sanity, we need to know the vlan_id for the default vlan (tag
68# 1). Make sure we know that before anybody attempts to create things
69# that depend on it.
Steve McIntyre283d5f22014-12-17 13:12:04 +000070state.default_vlan_id = state.db.get_vlan_id_by_tag(state.config.vland.default_vlan_tag)
71if state.default_vlan_id is None:
Steve McIntyrec12f6312014-12-15 15:00:12 +000072 # It doesn't exist - create it and try again
Steve McIntyre283d5f22014-12-17 13:12:04 +000073 state.default_vlan_id = state.db.create_vlan("DEFAULT",
74 state.config.vland.default_vlan_tag,
75 True)
Steve McIntyrec12f6312014-12-15 15:00:12 +000076
Steve McIntyre231354b2014-12-16 19:22:12 +000077if len(switches) != len(state.config.switches):
78 print 'You have configured access details for %d switch(es), ' % len(state.config.switches)
Steve McIntyre96855db2014-12-15 16:52:00 +000079 print 'but have %d switch(es) registered in your database.' % len(switches)
Steve McIntyreae286192014-12-12 22:10:43 +000080 print 'You must fix this difference for VLANd to work sensibly.'
Steve McIntyre8c84fb32015-04-29 17:39:31 +010081 print 'HINT: Running admin.py --auto_import_switch <switch_name>'
Steve McIntyrebd7ce372014-12-17 16:27:41 +000082 print 'for each of your switches may help!'
Steve McIntyreae286192014-12-12 22:10:43 +000083 print
Steve McIntyree5043dd2014-12-10 16:49:28 +000084
Steve McIntyredbe6aa52015-02-12 07:48:22 +000085# Set up logging and maybe go quiet...
86if state.config.logging.level is None:
87 state.config.logging.level = "CRITICAL"
Steve McIntyre504ba602015-02-12 07:51:28 +000088
89loglevel = logging.CRITICAL
90if state.config.logging.level == "ERROR":
Steve McIntyredbe6aa52015-02-12 07:48:22 +000091 loglevel = logging.ERROR
92elif state.config.logging.level == "WARNING":
93 loglevel = logging.WARNING
94elif state.config.logging.level == "INFO":
95 loglevel = logging.INFO
96elif state.config.logging.level == "DEBUG":
97 loglevel = logging.DEBUG
98
Steve McIntyre27039832015-07-13 17:49:40 +010099os.environ['TZ'] = 'UTC'
100time.tzset()
101
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000102# Should we log to stderr?
103if state.config.logging.filename is None:
104 logging.basicConfig(level = loglevel,
105 format = '%(asctime)s %(levelname)-8s %(message)s')
106else:
Steve McIntyre7918f742015-02-12 08:07:23 +0000107 print "Logging to %s" % state.config.logging.filename
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000108 logging.basicConfig(level = loglevel,
109 format = '%(asctime)s %(levelname)-8s %(message)s',
Steve McIntyre322b0ff2015-02-12 08:11:40 +0000110 datefmt = '%Y-%m-%d %H:%M:%S %Z',
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000111 filename = state.config.logging.filename,
112 filemode = 'a')
Steve McIntyrec5fc28b2015-07-13 17:50:10 +0100113 logging.info('%s starting up', state.banner)
114 switches = state.db.all_switches()
115 logging.info(' DB knows about %d switches', len(switches))
116 ports = state.db.all_ports()
117 logging.info(' DB knows about %d ports', len(ports))
118 vlans = state.db.all_vlans()
119 logging.info(' DB knows about %d vlans', len(vlans))
Steve McIntyrec4890132015-08-07 15:19:11 +0100120 trunks = state.db.all_trunks()
121 logging.info(' DB knows about %d trunks', len(trunks))
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000122
Steve McIntyre6c44fb22014-12-12 22:11:45 +0000123# Now start up the core loop. Listen for command connections and
124# process them
Steve McIntyre06fe6422015-01-23 17:55:43 +0000125state.running = True
Steve McIntyre6c44fb22014-12-12 22:11:45 +0000126ipc = VlanIpc()
Steve McIntyre231354b2014-12-16 19:22:12 +0000127ipc.server_init('localhost', state.config.vland.port)
Steve McIntyre06fe6422015-01-23 17:55:43 +0000128while state.running:
Steve McIntyref1c04f92014-12-16 18:23:15 +0000129 try:
130 ipc.server_listen()
131 json_data = ipc.server_recv()
132 except SocketError as e:
133 print e
Steve McIntyre7cf80982015-02-12 07:03:40 +0000134 logging.debug('Caught IPC error, ignoring')
Steve McIntyref1c04f92014-12-16 18:23:15 +0000135 continue
Steve McIntyref1c04f92014-12-16 18:23:15 +0000136 except:
137 ipc.server_close()
138 raise
Steve McIntyre6c44fb22014-12-12 22:11:45 +0000139
Steve McIntyre5fa22652015-04-01 18:01:45 +0100140 logging.debug("client %s sent us:", json_data['client_name'])
Steve McIntyreeb0a4b82015-02-12 07:06:56 +0000141 logging.debug(json_data)
Steve McIntyre6c44fb22014-12-12 22:11:45 +0000142
Steve McIntyref1c04f92014-12-16 18:23:15 +0000143 response = {}
144
Steve McIntyre0ebf5832014-12-16 19:23:10 +0000145 # Several types of IPC message here, with potentially different
146 # access control and safety
147
148 # First - simple queries to the database only. Should be safe!
149 if json_data['type'] == 'db_query':
150 response['type'] = 'response'
Steve McIntyref1c04f92014-12-16 18:23:15 +0000151 try:
Steve McIntyre208241a2014-12-17 13:12:26 +0000152 response['data'] = util.perform_db_query(state, json_data['command'], json_data['data'])
Steve McIntyre0ebf5832014-12-16 19:23:10 +0000153 response['response'] = 'ACK'
154 except InputError as e:
155 print e
156 response['response'] = 'ERROR'
157 response['error'] = e.__str__()
Steve McIntyre0ebf5832014-12-16 19:23:10 +0000158
159 # Next - simple queries about daemon state only. Should be safe!
160 if json_data['type'] == 'daemon_query':
161 response['type'] = 'response'
162 try:
163 response['data'] = util.perform_daemon_query(state, json_data['command'], json_data['data'])
Steve McIntyref1c04f92014-12-16 18:23:15 +0000164 response['response'] = 'ACK'
165 except InputError as e:
166 print e
167 response['response'] = 'ERROR'
168 response['error'] = e.__str__()
Steve McIntyref1c04f92014-12-16 18:23:15 +0000169
Steve McIntyre995a10c2014-12-17 13:12:58 +0000170 # Next, calls that manipulate objects in the database only
171 # (switches and ports). These are safe and don't need actual
Steve McIntyrec3b659b2014-12-17 16:28:37 +0000172 # co-ordinating with hardware directly.
Steve McIntyre995a10c2014-12-17 13:12:58 +0000173 #
174 # As/when/if we add authentication, use of this function will need
175 # it.
176 if json_data['type'] == 'db_update':
177 response['type'] = 'response'
178 try:
179 response['data'] = util.perform_db_update(state, json_data['command'], json_data['data'])
180 response['response'] = 'ACK'
181 except InputError as e:
182 print e
183 response['response'] = 'ERROR'
184 response['error'] = e.__str__()
Steve McIntyre995a10c2014-12-17 13:12:58 +0000185
Steve McIntyre50767152014-12-17 16:29:01 +0000186 # Next, calls that may manipulate switch state *as well* as state
187 # in the database - changes to VLAN setup.
188 #
189 # As/when/if we add authentication, use of this function will need
190 # it.
191 if json_data['type'] == 'vlan_update':
192 response['type'] = 'response'
193 try:
194 response['data'] = util.perform_vlan_update(state, json_data['command'], json_data['data'])
195 response['response'] = 'ACK'
196 except InputError as e:
197 print e
198 response['response'] = 'ERROR'
199 response['error'] = e.__str__()
Steve McIntyre50767152014-12-17 16:29:01 +0000200
201 # Finally, IPC interface for more complex API calls.
202 # NOT IMPLEMENTED YET
203 if json_data['type'] == 'vland_api':
204 response['type'] = 'response'
205 response['response'] = 'ERROR'
206 response['error'] = 'VLANd API not yet implemented...'
207
Steve McIntyre7cf80982015-02-12 07:03:40 +0000208 logging.debug("sending reply:")
Steve McIntyre72f1a5c2015-02-12 07:05:39 +0000209 logging.debug(response)
Steve McIntyre6c44fb22014-12-12 22:11:45 +0000210
211 ipc.server_reply(response)
212
Steve McIntyre06fe6422015-01-23 17:55:43 +0000213# We've been asked to shut down. Do that as cleanly as we can
214ipc.server_close()
Steve McIntyrec5fc28b2015-07-13 17:50:10 +0100215
216logging.info('%s shutting down', state.banner)
217switches = state.db.all_switches()
218logging.info(' DB knows about %d switches', len(switches))
219ports = state.db.all_ports()
220logging.info(' DB knows about %d ports', len(ports))
221vlans = state.db.all_vlans()
222logging.info(' DB knows about %d vlans', len(vlans))
Steve McIntyrec4890132015-08-07 15:19:11 +0100223trunks = state.db.all_trunks()
224logging.info(' DB knows about %d trunks', len(trunks))
Steve McIntyrec5fc28b2015-07-13 17:50:10 +0100225
Steve McIntyredbe6aa52015-02-12 07:48:22 +0000226logging.shutdown()