-
Notifications
You must be signed in to change notification settings - Fork 529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
new BGP Monitor Daemon to pull BGP peer state and store in State DB for MIB consumption #1429
Closed
Closed
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
c31e43c
Implement bgpmon (BGP Monitor) daemon to run under docker-fpm-frr doc…
gechiang f805107
Implement bgpmon (BGP Monitor) daemon to run under docker-fpm-frr doc…
gechiang c6a9a80
Implement bgpmon (BGP Monitor) daemon to run under docker-fpm-frr doc…
gechiang daa5b7c
Added comment on variables used, fixed typo, remove hard coded local …
gechiang 7274983
Combined IPV4 and IPV6 to use the same DS to eliminate duplication co…
gechiang 85dce8e
Combined IPV4 and IPV6 peer state reading, use specific Exception and…
gechiang c98e906
Fixed LGTM alert due to unused import traceback
gechiang 103d95b
Let flush_pipe() clear data{}, use len(data) instead of another counter
gechiang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
swssconfig/sample/netbouncer.json etc/swss/config.d | ||
neighsyncd/restore_neighbors.py usr/bin | ||
fpmsyncd/bgp_eoiu_marker.py usr/bin | ||
fpmsyncd/bgpmon.py usr/bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
#!/usr/bin/env python2 | ||
|
||
"""" | ||
Description: bgpmon.py -- populating bgp related information in stateDB. | ||
script is started by supervisord in bgp docker when the docker is started. | ||
|
||
Initial creation of this daemon is to assist SNMP agent in obtaining the | ||
BGP related information for its MIB support. The MIB that this daemon is | ||
assiting is for the CiscoBgp4MIB (Neighbor state only). If there are other | ||
BGP related items that needs to be updated in a periodic manner in the | ||
future, then more can be added into this process. | ||
|
||
The script check if there are any bgp activities by monitoring the bgp | ||
frr.log file timestamp. If activity is detected, then it will request bgp | ||
neighbor state via vtysh cli interface. This bgp activity monitoring is | ||
done periodically (every 15 second). When triggered, it looks specifically | ||
for the neighbor state in the json output of show ip bgp neighbors json | ||
and update the state DB for each neighbor accordingly. | ||
In order to not disturb and hold on to the State DB access too long and | ||
removal of the stale neighbors (neighbors that was there previously on | ||
previous get request but no longer there in the current get request), a | ||
"previous" neighbor dictionary will be kept and used to determine if there | ||
is a need to perform update or the peer is stale to be removed from the | ||
state DB | ||
""" | ||
import commands | ||
import json | ||
import os | ||
import syslog | ||
import swsssdk | ||
import time | ||
|
||
PIPE_BATCH_MAX_COUNT = 50 | ||
|
||
class BgpStateGet(): | ||
def __init__(self): | ||
# list peer_l stores the Neighbor peer Ip address | ||
# dic peer_state stores the Neighbor peer state entries | ||
# list new_peer_l stores the new snapshot of Neighbor peer ip address | ||
# dic new_peer_state stores the new snapshot of Neighbor peer states | ||
self.peer_l = [] | ||
self.peer_state = {} | ||
self.new_peer_l = [] | ||
self.new_peer_state = {} | ||
self.cached_timestamp = 0 | ||
self.db = swsssdk.SonicV2Connector() | ||
self.db.connect(self.db.STATE_DB, False) | ||
client = self.db.get_redis_client(self.db.STATE_DB) | ||
self.pipe = client.pipeline() | ||
self.db.delete_all_by_pattern(self.db.STATE_DB, "NEIGH_STATE_TABLE|*" ) | ||
|
||
# A quick way to check if there are anything happening within BGP is to | ||
# check its log file has any activities. This is by checking its modified | ||
# timestamp against the cached timestamp that we keep and if there is a | ||
# difference, there is activity detected. In case the log file got wiped | ||
# out, it will default back to constant pulling every 15 seconds | ||
def bgp_activity_detected(self): | ||
try: | ||
timestamp = os.stat("/var/log/frr/frr.log").st_mtime | ||
if timestamp != self.cached_timestamp: | ||
self.cached_timestamp = timestamp | ||
return True | ||
else: | ||
return False | ||
except (IOError, OSError): | ||
return True | ||
|
||
def update_new_peer_states(self, peer_dict): | ||
peer_l = peer_dict["peers"].keys() | ||
self.new_peer_l.extend(peer_l) | ||
for i in range (0, len(peer_l)): | ||
self.new_peer_state[peer_l[i]] = peer_dict["peers"][peer_l[i]]["state"] | ||
|
||
# Get a new snapshot of BGP neighbors and store them in the "new" location | ||
def get_all_neigh_states(self): | ||
cmd = "vtysh -c 'show bgp summary json'" | ||
rc, output = commands.getstatusoutput(cmd) | ||
if rc: | ||
syslog.syslog(syslog.LOG_ERR, "*ERROR* Failed with rc:{} when execute: {}".format(rc, cmd)) | ||
return | ||
|
||
peer_info = json.loads(output) | ||
# cmd ran successfully, safe to Clean the "new" lists/dic for new sanpshot | ||
del self.new_peer_l[:] | ||
self.new_peer_state.clear() | ||
for key, value in peer_info.items(): | ||
if key == "ipv4Unicast" or key == "ipv6Unicast": | ||
self.update_new_peer_states(value) | ||
|
||
# This method will take the caller's dictionary which contains the peer state operation | ||
# That need to be updated in StateDB using Redis pipeline. | ||
# The data{} will be cleared at the end of this method before returning to caller. | ||
def flush_pipe(self, data): | ||
"""Dump each entry in data{} into State DB via redis pipeline. | ||
Args: | ||
data: Neighbor state in dictionary format | ||
{ | ||
'NEIGH_STATE_TABLE|ip_address_a': {'state':state}, | ||
'NEIGH_STATE_TABLE|ip_address_b': {'state':state}, | ||
'NEIGH_STATE_TABLE|ip_address_c': {'state':state}, | ||
'NEIGH_STATE_TABLE|ip_address_x': None, | ||
'NEIGH_STATE_TABLE|ip_address_z': None | ||
... | ||
} | ||
""" | ||
for key, value in data.items(): | ||
if value is None: | ||
# delete case | ||
self.pipe.delete(key) | ||
else: | ||
# Add or Modify case | ||
self.pipe.hmset(key, value) | ||
self.pipe.execute() | ||
data.clear() | ||
|
||
def update_neigh_states(self): | ||
data = {} | ||
for i in range (0, len(self.new_peer_l)): | ||
peer = self.new_peer_l[i] | ||
key = "NEIGH_STATE_TABLE|%s" % peer | ||
if peer in self.peer_l: | ||
# only update the entry if state changed | ||
if self.peer_state[peer] != self.new_peer_state[peer]: | ||
# state changed. Update state DB for this entry | ||
state = self.new_peer_state[peer] | ||
data[key] = {'state':state} | ||
self.peer_state[peer] = state | ||
# remove this neighbor from old list since it is accounted for | ||
self.peer_l.remove(peer) | ||
else: | ||
# New neighbor found case. Add to dictionary and state DB | ||
state = self.new_peer_state[peer] | ||
data[key] = {'state':state} | ||
self.peer_state[peer] = state | ||
if len(data) > PIPE_BATCH_MAX_COUNT: | ||
self.flush_pipe(data) | ||
# Check for stale state entries to be cleaned up | ||
while len(self.peer_l) > 0: | ||
# remove this from the stateDB and the current nighbor state entry | ||
peer = self.peer_l.pop(0) | ||
del_key = "NEIGH_STATE_TABLE|%s" % peer | ||
data[del_key] = None | ||
del self.peer_state[peer] | ||
if len(data) > PIPE_BATCH_MAX_COUNT: | ||
self.flush_pipe(data) | ||
# If anything in the pipeline not yet flushed, flush them now | ||
if len(data) > 0: | ||
self.flush_pipe(data) | ||
# Save the new List | ||
self.peer_l = self.new_peer_l[:] | ||
|
||
def main(): | ||
|
||
print "bgpmon service started" | ||
|
||
try: | ||
bgp_state_get = BgpStateGet() | ||
except Exception as e: | ||
syslog.syslog(syslog.LOG_ERR, "{}: error exit 1, reason {}".format(THIS_MODULE, str(e))) | ||
exit(1) | ||
|
||
# periodically obtain the new neighbor infomraton and update if necessary | ||
while True: | ||
time.sleep(15) | ||
if bgp_state_get.bgp_activity_detected(): | ||
bgp_state_get.get_all_neigh_states() | ||
bgp_state_get.update_neigh_states() | ||
|
||
if __name__ == '__main__': | ||
main() |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add some unit test or vs test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@qiluo-msft Will add unit test in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer adding in the this PR if you can. It even benefit yourself in iterations.