[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.
This commit is contained in:
parent
3323e9b817
commit
be3cbdbcea
@ -1,65 +1,124 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import copy
|
||||
import Queue
|
||||
import redis
|
||||
import subprocess
|
||||
import syslog
|
||||
from swsssdk import ConfigDBConnector
|
||||
import os
|
||||
from swsscommon import swsscommon
|
||||
|
||||
class BGPConfigDaemon:
|
||||
|
||||
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.config_db = ConfigDBConnector()
|
||||
self.config_db.connect()
|
||||
self.bgp_asn = self.config_db.get_entry('DEVICE_METADATA', 'localhost')['bgp_asn']
|
||||
self.bgp_neighbor = self.config_db.get_table('BGP_NEIGHBOR')
|
||||
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 __run_command(self, command):
|
||||
# print 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, '[bgp cfgd] command execution returned {}. Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout))
|
||||
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 metadata_handler(self, key, data):
|
||||
if key == 'localhost' and data.has_key('bgp_asn'):
|
||||
if data['bgp_asn'] != self.bgp_asn:
|
||||
syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] ASN changed to {} from {}, restart BGP...'.format(data['bgp_asn'], self.bgp_asn))
|
||||
self.__run_command("supervisorctl restart start.sh")
|
||||
self.__run_command("service quagga restart")
|
||||
self.bgp_asn = data['bgp_asn']
|
||||
|
||||
def bgp_handler(self, key, data):
|
||||
syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data))
|
||||
if not data:
|
||||
# Neighbor is deleted
|
||||
command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'no neighbor {}'".format(self.bgp_asn, key)
|
||||
self.__run_command(command)
|
||||
self.bgp_neighbor.pop(key)
|
||||
else:
|
||||
command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} remote-as {}'".format(self.bgp_asn, key, data['asn'])
|
||||
self.__run_command(command)
|
||||
if data.has_key('name'):
|
||||
command = "vtysh -c 'configure terminal' -c 'router bgp {}' -c 'neighbor {} description {}'".format(self.bgp_asn, key, data['name'])
|
||||
self.__run_command(command)
|
||||
if data.has_key('admin_status'):
|
||||
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)
|
||||
self.__run_command(command)
|
||||
self.bgp_neighbor[key] = data
|
||||
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):
|
||||
self.config_db.subscribe('BGP_NEIGHBOR',
|
||||
lambda table, key, data: self.bgp_handler(key, data))
|
||||
self.config_db.subscribe('DEVICE_METADATA',
|
||||
lambda table, key, data: self.metadata_handler(key, data))
|
||||
self.config_db.listen()
|
||||
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():
|
||||
daemon = BGPConfigDaemon()
|
||||
syslog.openlog("bgpcfgd")
|
||||
daemon = Daemon()
|
||||
bgp_manager = BGPConfigManager(daemon)
|
||||
daemon.start()
|
||||
syslog.closelog()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
Reference in New Issue
Block a user