blob: 3dc50f967850347f403c4bb3907228887e1f7d72 [file] [log] [blame]
#! /usr/bin/python
# Copyright 2014-2015 Linaro Limited
#
# 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.
#
# Main VLANd module
#
import os, sys
import time
import logging
vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
sys.path.insert(0, vlandpath)
from config.config import VlanConfig
from db.db import VlanDB
from ipc.ipc import VlanIpc
from errors import InputError, SocketError
from util import VlanUtil
from visualisation.visualisation import Visualisation
class DaemonState:
""" Simple container for stuff to make for nicer syntax """
state = DaemonState()
state.version = "0.5-DEV"
state.banner = "Linaro VLANd version %s" % state.version
state.starttime = time.time()
state.vis = None
os.environ['TZ'] = 'UTC'
time.tzset()
print '%s' % state.banner
print 'Parsing Config...'
state.config = VlanConfig(filenames=('./vland.cfg',))
print ' Config knows about %d switches' % len(state.config.switches)
if state.config.visualisation.enabled:
state.vis = Visualisation(state)
util = VlanUtil()
print 'Connecting to DB...'
state.db = VlanDB(db_name=state.config.database.dbname,
username=state.config.database.username, readonly=False)
switches = state.db.all_switches()
print ' DB knows about %d switches' % len(switches)
ports = state.db.all_ports()
print ' DB knows about %d ports' % len(ports)
vlans = state.db.all_vlans()
print ' DB knows about %d vlans' % len(vlans)
trunks = state.db.all_trunks()
print ' DB knows about %d trunks' % len(trunks)
# Initial startup sanity chacking
# For sanity, we need to know the vlan_id for the default vlan (tag
# 1). Make sure we know that before anybody attempts to create things
# that depend on it.
state.default_vlan_id = state.db.get_vlan_id_by_tag(state.config.vland.default_vlan_tag)
if state.default_vlan_id is None:
# It doesn't exist - create it and try again
state.default_vlan_id = state.db.create_vlan("DEFAULT",
state.config.vland.default_vlan_tag,
True)
if len(switches) != len(state.config.switches):
print 'You have configured access details for %d switch(es), ' % len(state.config.switches)
print 'but have %d switch(es) registered in your database.' % len(switches)
print 'You must fix this difference for VLANd to work sensibly.'
print 'HINT: Running admin.py --auto_import_switch <switch_name>'
print 'for each of your switches may help!'
print
loglevel = util.set_logging_level(state.config.logging.level)
# Should we log to stderr?
if state.config.logging.filename is None:
logging.basicConfig(level = loglevel,
format = '%(asctime)s %(levelname)-8s %(message)s')
else:
print "Logging to %s" % state.config.logging.filename
logging.basicConfig(level = loglevel,
format = '%(asctime)s %(levelname)-8s MAIN %(message)s',
datefmt = '%Y-%m-%d %H:%M:%S %Z',
filename = state.config.logging.filename,
filemode = 'a')
logging.info('%s main daemon starting up', state.banner)
switches = state.db.all_switches()
logging.info('DB knows about %d switches', len(switches))
ports = state.db.all_ports()
logging.info('DB knows about %d ports', len(ports))
vlans = state.db.all_vlans()
logging.info('DB knows about %d vlans', len(vlans))
trunks = state.db.all_trunks()
logging.info('DB knows about %d trunks', len(trunks))
# Now start up the core loop. Listen for command connections and
# process them
state.running = True
ipc = VlanIpc()
ipc.server_init('localhost', state.config.vland.port)
while state.running:
try:
ipc.server_listen()
json_data = ipc.server_recv()
except SocketError as e:
print e
logging.debug('Caught IPC error, ignoring')
continue
except:
ipc.server_close()
raise
logging.debug("client %s sent us:", json_data['client_name'])
logging.debug(json_data)
response = {}
# Several types of IPC message here, with potentially different
# access control and safety
# First - simple queries to the database only. Should be safe!
if json_data['type'] == 'db_query':
response['type'] = 'response'
try:
response['data'] = util.perform_db_query(state, json_data['command'], json_data['data'])
response['response'] = 'ACK'
except InputError as e:
print e
response['response'] = 'ERROR'
response['error'] = e.__str__()
# Next - simple queries about daemon state only. Should be safe!
if json_data['type'] == 'daemon_query':
response['type'] = 'response'
try:
response['data'] = util.perform_daemon_query(state, json_data['command'], json_data['data'])
response['response'] = 'ACK'
except InputError as e:
print e
response['response'] = 'ERROR'
response['error'] = e.__str__()
# Next, calls that manipulate objects in the database only
# (switches and ports). These are safe and don't need actual
# co-ordinating with hardware directly.
#
# As/when/if we add authentication, use of this function will need
# it.
if json_data['type'] == 'db_update':
response['type'] = 'response'
try:
response['data'] = util.perform_db_update(state, json_data['command'], json_data['data'])
response['response'] = 'ACK'
except InputError as e:
print e
response['response'] = 'ERROR'
response['error'] = e.__str__()
# Next, calls that may manipulate switch state *as well* as state
# in the database - changes to VLAN setup.
#
# As/when/if we add authentication, use of this function will need
# it.
if json_data['type'] == 'vlan_update':
response['type'] = 'response'
try:
response['data'] = util.perform_vlan_update(state, json_data['command'], json_data['data'])
response['response'] = 'ACK'
except InputError as e:
print e
response['response'] = 'ERROR'
response['error'] = e.__str__()
# Finally, IPC interface for more complex API calls.
# NOT IMPLEMENTED YET
if json_data['type'] == 'vland_api':
response['type'] = 'response'
response['response'] = 'ERROR'
response['error'] = 'VLANd API not yet implemented...'
logging.debug("sending reply:")
logging.debug(response)
ipc.server_reply(response)
# We've been asked to shut down. Do that as cleanly as we can
ipc.server_close()
if state.config.visualisation.enabled:
state.vis.shutdown()
logging.info('%s shutting down', state.banner)
switches = state.db.all_switches()
logging.info(' DB knows about %d switches', len(switches))
ports = state.db.all_ports()
logging.info(' DB knows about %d ports', len(ports))
vlans = state.db.all_vlans()
logging.info(' DB knows about %d vlans', len(vlans))
trunks = state.db.all_trunks()
logging.info(' DB knows about %d trunks', len(trunks))
logging.shutdown()