2019-05-09 01:00:49 -05:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
import sys
|
2019-08-29 12:26:48 -05:00
|
|
|
import copy
|
|
|
|
import Queue
|
2019-05-09 01:00:49 -05:00
|
|
|
import redis
|
|
|
|
import subprocess
|
|
|
|
import syslog
|
2019-08-29 12:26:48 -05:00
|
|
|
import os
|
|
|
|
from swsscommon import swsscommon
|
2019-05-09 01:00:49 -05:00
|
|
|
|
2019-08-29 12:26:48 -05:00
|
|
|
|
|
|
|
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)
|
2019-05-09 01:00:49 -05:00
|
|
|
|
|
|
|
def __init__(self):
|
2019-08-29 12:26:48 -05:00
|
|
|
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)
|
2019-05-09 01:00:49 -05:00
|
|
|
|
|
|
|
def start(self):
|
2019-08-29 12:26:48 -05:00
|
|
|
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)
|
2019-05-09 01:00:49 -05:00
|
|
|
|
|
|
|
|
|
|
|
def main():
|
2019-08-29 12:26:48 -05:00
|
|
|
syslog.openlog("bgpcfgd")
|
|
|
|
daemon = Daemon()
|
|
|
|
bgp_manager = BGPConfigManager(daemon)
|
2019-05-09 01:00:49 -05:00
|
|
|
daemon.start()
|
2019-08-29 12:26:48 -05:00
|
|
|
syslog.closelog()
|
2019-05-09 01:00:49 -05:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|