From 82274351cfb367adc5808fe5e9aecdf7e055b069 Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Thu, 1 Aug 2019 22:22:19 +0800 Subject: [PATCH 1/6] [docker-fpm-frr/bgpcfgd]: Update interface of bgpcfgd from swsssdk to swsscommon Signed-off-by: Ze Gan --- dockers/docker-fpm-frr/bgpcfgd | 143 ++++++++++++++++++++++----------- 1 file changed, 98 insertions(+), 45 deletions(-) diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd index 012a766c20b9..2abdd60ecc42 100755 --- a/dockers/docker-fpm-frr/bgpcfgd +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -1,64 +1,117 @@ #!/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 __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') +def run_command(command): + syslog.syslog(syslog.LOG_DEBUG, "[bgp cfgd] 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, '[bgp cfgd] 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 + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] ASN changed to {} from {}, restart BGP...'.format(data['bgp_asn'], self.bgp_asn)) + run_command("supervisorctl restart start.sh") + run_command("service frr restart") + self.bgp_asn = data["bgp_asn"] + + def __bgp_handler(self, key, op, data): + self.bgp_message.put((key, op, data)) + # If ASN was setted + if self.bgp_asn == None: + return + while not self.bgp_message.empty(): + key, op, data = self.bgp_message.get() + syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] 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 __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 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'] +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 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() + daemon = Daemon() + bgp_manager = BGPConfigManager(daemon) daemon.start() if __name__ == "__main__": From 748dfa982722892d796b3cbb5a97c745c65d2a9d Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Fri, 2 Aug 2019 13:23:18 +0800 Subject: [PATCH 2/6] Update log Signed-off-by: Ze Gan --- dockers/docker-fpm-frr/bgpcfgd | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd index 2abdd60ecc42..73a700cb2087 100755 --- a/dockers/docker-fpm-frr/bgpcfgd +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -11,12 +11,12 @@ from swsscommon import swsscommon def run_command(command): - syslog.syslog(syslog.LOG_DEBUG, "[bgp cfgd] execute command {}.".format(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, '[bgp cfgd] command execution returned {}. Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout)) + syslog.syslog(syslog.LOG_ERR, 'command execution returned {}. Command: "{}", stdout: "{}"'.format(p.returncode, command, stdout)) class BGPConfigManager(object): @@ -32,19 +32,19 @@ class BGPConfigManager(object): or "bgp_asn" not in data \ or self.bgp_asn == data["bgp_asn"]: return - syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] ASN changed to {} from {}, restart BGP...'.format(data['bgp_asn'], self.bgp_asn)) + syslog.syslog(syslog.LOG_INFO, 'ASN changed to {} from {}, restart BGP...'.format(data['bgp_asn'], self.bgp_asn)) run_command("supervisorctl restart start.sh") run_command("service frr restart") self.bgp_asn = data["bgp_asn"] def __bgp_handler(self, key, op, data): self.bgp_message.put((key, op, data)) - # If ASN was setted + # If ASN was set if self.bgp_asn == None: return while not self.bgp_message.empty(): key, op, data = self.bgp_message.get() - syslog.syslog(syslog.LOG_INFO, '[bgp cfgd] value for {} changed to {}'.format(key, data)) + 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) @@ -110,9 +110,11 @@ class Daemon(object): def main(): + syslog.openlog("bgpcfgd") daemon = Daemon() bgp_manager = BGPConfigManager(daemon) daemon.start() + syslog.closelog() if __name__ == "__main__": main() From a2d5fc92c6ea3bc97e94ae230a8e84d2f0218ece Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Fri, 2 Aug 2019 13:38:17 +0800 Subject: [PATCH 3/6] Fix update bgp bug and polish the comment If the metadata update message is after all of bgp update message, we will lose the update of bgp. So we need check the bgp meesage cache when we update the ASN. Signed-off-by: Ze Gan --- dockers/docker-fpm-frr/bgpcfgd | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd index 73a700cb2087..60687fad5a25 100755 --- a/dockers/docker-fpm-frr/bgpcfgd +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -36,12 +36,9 @@ class BGPConfigManager(object): run_command("supervisorctl restart start.sh") run_command("service frr restart") self.bgp_asn = data["bgp_asn"] - - def __bgp_handler(self, key, op, data): - self.bgp_message.put((key, op, data)) - # If ASN was set - if self.bgp_asn == None: - return + 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)) @@ -60,6 +57,13 @@ class BGPConfigManager(object): 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 doesn't be set, we just cache this message until the ASN is set. + if self.bgp_asn == None: + return + self.__update_bgp() + class Daemon(object): From e35772dcb707c45eab9eac96e59249b8859b3ddf Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Mon, 5 Aug 2019 17:41:46 +0800 Subject: [PATCH 4/6] Remove useless commands and add TODO comment for ASN Signed-off-by: Ze Gan --- dockers/docker-fpm-frr/bgpcfgd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd index 60687fad5a25..68d2044a8205 100755 --- a/dockers/docker-fpm-frr/bgpcfgd +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -33,8 +33,9 @@ class BGPConfigManager(object): or self.bgp_asn == data["bgp_asn"]: return syslog.syslog(syslog.LOG_INFO, 'ASN changed to {} from {}, restart BGP...'.format(data['bgp_asn'], self.bgp_asn)) - run_command("supervisorctl restart start.sh") - run_command("service frr restart") + + # TODO add ASN update commands + self.bgp_asn = data["bgp_asn"] self.__update_bgp() From 642165c46c30345e5fd4afb3da0c262a467a3c15 Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Tue, 13 Aug 2019 14:36:32 +0800 Subject: [PATCH 5/6] Polish comment --- dockers/docker-fpm-frr/bgpcfgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd index 68d2044a8205..7d6ad89de033 100755 --- a/dockers/docker-fpm-frr/bgpcfgd +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -60,7 +60,7 @@ class BGPConfigManager(object): def __bgp_handler(self, key, op, data): self.bgp_message.put((key, op, data)) - # If ASN doesn't be set, we just cache this message until the ASN is set. + # If ASN is not set, we just cache this message until the ASN is set. if self.bgp_asn == None: return self.__update_bgp() From a19f6ff72be136dde881104c3e2fbbbdacefcab2 Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Fri, 16 Aug 2019 17:27:43 +0800 Subject: [PATCH 6/6] Remove useless log Signed-off-by: Ze Gan --- dockers/docker-fpm-frr/bgpcfgd | 1 - 1 file changed, 1 deletion(-) diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd index 7d6ad89de033..f6d0b86385b5 100755 --- a/dockers/docker-fpm-frr/bgpcfgd +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -32,7 +32,6 @@ class BGPConfigManager(object): or "bgp_asn" not in data \ or self.bgp_asn == data["bgp_asn"]: return - syslog.syslog(syslog.LOG_INFO, 'ASN changed to {} from {}, restart BGP...'.format(data['bgp_asn'], self.bgp_asn)) # TODO add ASN update commands