be3cbdbcea
Update interfaces of bgpcfd from swsssdk to swsscommon to unify a suit of interface with other component. Meanwhile, we can listen multiple tables at one thread under swsscommon interface. Signed-off-by: Ze Gan ganze718@gmail.com - What I did Move the interface of bgpcfgd from swsssdk to swsscommon. Because bgpcfgd need to listen more events in the future and we want to maintain one kind of APIs, swsscommon is more suitable than swsssdk. - How I did it Refactor the BGPConfigDaemon to two components, Daemon and BGPConfigManager. We can register new managers to the Daemon object if we want to listen more events.
125 lines
4.9 KiB
Python
Executable File
125 lines
4.9 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import sys
|
|
import copy
|
|
import Queue
|
|
import redis
|
|
import subprocess
|
|
import syslog
|
|
import os
|
|
from swsscommon import swsscommon
|
|
|
|
|
|
def run_command(command):
|
|
syslog.syslog(syslog.LOG_DEBUG, "execute command {}.".format(command))
|
|
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
|
stdout = p.communicate()[0]
|
|
p.wait()
|
|
if p.returncode != 0:
|
|
syslog.syslog(syslog.LOG_ERR, 'command execution returned {}. Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout))
|
|
|
|
|
|
class BGPConfigManager(object):
|
|
def __init__(self, daemon):
|
|
self.daemon = daemon
|
|
self.bgp_asn = None
|
|
self.bgp_message = Queue.Queue(0)
|
|
daemon.add_manager(swsscommon.CONFIG_DB, swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, self.__metadata_handler)
|
|
daemon.add_manager(swsscommon.CONFIG_DB, swsscommon.CFG_BGP_NEIGHBOR_TABLE_NAME, self.__bgp_handler)
|
|
|
|
def __metadata_handler(self, key, op, data):
|
|
if key != "localhost" \
|
|
or "bgp_asn" not in data \
|
|
or self.bgp_asn == data["bgp_asn"]:
|
|
return
|
|
|
|
# TODO add ASN update commands
|
|
|
|
self.bgp_asn = data["bgp_asn"]
|
|
self.__update_bgp()
|
|
|
|
def __update_bgp(self):
|
|
while not self.bgp_message.empty():
|
|
key, op, data = self.bgp_message.get()
|
|
syslog.syslog(syslog.LOG_INFO, 'value for {} changed to {}'.format(key, data))
|
|
if op == swsscommon.SET_COMMAND:
|
|
command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} remote-as {}'".format(self.bgp_asn, key, data['asn'])
|
|
run_command(command)
|
|
if "name" in data:
|
|
command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} description {}'".format(self.bgp_asn, key, data['name'])
|
|
run_command(command)
|
|
if "admin_status" in data:
|
|
command_mod = "no " if data["admin_status"] == "up" else ""
|
|
command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c '{}neighbor {} shutdown'".format(self.bgp_asn, command_mod, key)
|
|
run_command(command)
|
|
elif op == swsscommon.DEL_COMMAND:
|
|
# Neighbor is deleted
|
|
command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no neighbor {}'".format(self.bgp_asn, key)
|
|
run_command(command)
|
|
|
|
def __bgp_handler(self, key, op, data):
|
|
self.bgp_message.put((key, op, data))
|
|
# If ASN is not set, we just cache this message until the ASN is set.
|
|
if self.bgp_asn == None:
|
|
return
|
|
self.__update_bgp()
|
|
|
|
|
|
class Daemon(object):
|
|
|
|
SELECT_TIMEOUT = 1000
|
|
SUPPORT_DATABASE_LIST = (swsscommon.APPL_DB, swsscommon.CONFIG_DB)
|
|
|
|
def __init__(self):
|
|
self.appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, swsscommon.DBConnector.DEFAULT_UNIXSOCKET, 0)
|
|
self.conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, swsscommon.DBConnector.DEFAULT_UNIXSOCKET, 0)
|
|
self.selector = swsscommon.Select()
|
|
self.db_connectors = {}
|
|
self.callbacks = {}
|
|
self.subscribers = set()
|
|
|
|
def get_db_connector(self, db):
|
|
if db not in Daemon.SUPPORT_DATABASE_LIST:
|
|
raise ValueError("database {} not Daemon support list {}.".format(db, SUPPORT_DATABASE_LIST))
|
|
# if this database connector has been initialized
|
|
if db not in self.db_connectors:
|
|
self.db_connectors[db] = swsscommon.DBConnector(db, swsscommon.DBConnector.DEFAULT_UNIXSOCKET, 0)
|
|
return self.db_connectors[db]
|
|
|
|
def add_manager(self, db, table_name, callback):
|
|
if db not in self.callbacks:
|
|
self.callbacks[db] = {}
|
|
if table_name not in self.callbacks[db]:
|
|
self.callbacks[db][table_name] = []
|
|
conn = self.get_db_connector(db)
|
|
subscriber = swsscommon.SubscriberStateTable(conn, table_name)
|
|
self.subscribers.add(subscriber)
|
|
self.selector.addSelectable(subscriber)
|
|
self.callbacks[db][table_name].append(callback)
|
|
|
|
def start(self):
|
|
while True:
|
|
state, selectable = self.selector.select(Daemon.SELECT_TIMEOUT)
|
|
if not selectable:
|
|
continue
|
|
for subscriber in self.subscribers:
|
|
key, op, fvs = subscriber.pop()
|
|
# if no new message
|
|
if not key:
|
|
continue
|
|
data = dict(fvs)
|
|
syslog.syslog(syslog.LOG_DEBUG, "Receive message : {}".format((key, op, fvs)))
|
|
for callback in self.callbacks[subscriber.getDbConnector().getDbId()][subscriber.getTableName()]:
|
|
callback(key, op, data)
|
|
|
|
|
|
def main():
|
|
syslog.openlog("bgpcfgd")
|
|
daemon = Daemon()
|
|
bgp_manager = BGPConfigManager(daemon)
|
|
daemon.start()
|
|
syslog.closelog()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|