sonic-buildimage/dockers/docker-fpm-frr/bgpcfgd
Ze Gan be3cbdbcea [docker-fpm-frr/bgpcfgd]: Update interface of bgpcfgd from swsssdk to swsscommon (#3264)
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.
2019-08-29 10:26:48 -07:00

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()