[lldpmgrd] Fix potential race condition when interfaces are created (#1469)

This commit is contained in:
Joe LeVeque 2018-03-07 17:08:45 -08:00 committed by GitHub
parent e18e15f31b
commit c161de406a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 137 additions and 25 deletions

3
.gitignore vendored
View File

@ -23,6 +23,9 @@ src/libnl3/*
!src/libnl3/Makefile !src/libnl3/Makefile
src/libteam/* src/libteam/*
!src/libteam/Makefile !src/libteam/Makefile
src/lldpd/*
!src/lldpd/Makefile
!src/lldpd/patch/
src/mpdecimal/* src/mpdecimal/*
!src/mpdecimal/Makefile !src/mpdecimal/Makefile
src/python3/* src/python3/*

3
.gitmodules vendored
View File

@ -26,9 +26,6 @@
[submodule "src/sonic-py-swsssdk"] [submodule "src/sonic-py-swsssdk"]
path = src/sonic-py-swsssdk path = src/sonic-py-swsssdk
url = https://github.com/Azure/sonic-py-swsssdk.git url = https://github.com/Azure/sonic-py-swsssdk.git
[submodule "src/lldpd"]
path = src/lldpd
url = https://github.com/vincentbernat/lldpd.git
[submodule "src/sonic-snmpagent"] [submodule "src/sonic-snmpagent"]
path = src/sonic-snmpagent path = src/sonic-snmpagent
url = https://github.com/Azure/sonic-snmpagent url = https://github.com/Azure/sonic-snmpagent

View File

@ -31,6 +31,12 @@ SYSLOG_IDENTIFIER = "lldpmgrd"
# ========================== Syslog wrappers ========================== # ========================== Syslog wrappers ==========================
def log_debug(msg):
syslog.openlog(SYSLOG_IDENTIFIER)
syslog.syslog(syslog.LOG_DEBUG, msg)
syslog.closelog()
def log_info(msg): def log_info(msg):
syslog.openlog(SYSLOG_IDENTIFIER) syslog.openlog(SYSLOG_IDENTIFIER)
syslog.syslog(syslog.LOG_INFO, msg) syslog.syslog(syslog.LOG_INFO, msg)
@ -75,25 +81,29 @@ class LldpManager(object):
Attributes: Attributes:
state_db: Handle to Redis State database via swsscommon lib state_db: Handle to Redis State database via swsscommon lib
config_db: Handle to Redis Config database via swsscommon lib config_db: Handle to Redis Config database via swsscommon lib
pending_cmds: Dictionary where key is port name, value is pending
LLDP configuration command to run
""" """
REDIS_HOSTNAME = "localhost" REDIS_HOSTNAME = "localhost"
REDIS_PORT = 6379 REDIS_PORT = 6379
REDIS_TIMEOUT_USECS = 0 REDIS_TIMEOUT_MS = 0
def __init__(self): def __init__(self):
# Open a handle to the State database # Open a handle to the State database
self.state_db = swsscommon.DBConnector(swsscommon.STATE_DB, self.state_db = swsscommon.DBConnector(swsscommon.STATE_DB,
self.REDIS_HOSTNAME, self.REDIS_HOSTNAME,
self.REDIS_PORT, self.REDIS_PORT,
self.REDIS_TIMEOUT_USECS) self.REDIS_TIMEOUT_MS)
# Open a handle to the Config database # Open a handle to the Config database
self.config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, self.config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB,
self.REDIS_HOSTNAME, self.REDIS_HOSTNAME,
self.REDIS_PORT, self.REDIS_PORT,
self.REDIS_TIMEOUT_USECS) self.REDIS_TIMEOUT_MS)
def update_lldp_config_for_port(self, port_name): self.pending_cmds = {}
def generate_pending_lldp_config_cmd_for_port(self, port_name):
""" """
For port `port_name`, look up the neighboring device's hostname and For port `port_name`, look up the neighboring device's hostname and
corresponding port alias in the Config database, then form the corresponding port alias in the Config database, then form the
@ -142,14 +152,34 @@ class LldpManager(object):
else: else:
log_info("Unable to retrieve neighbor information for port '{}'. Not adding port description.".format(port_name)) log_info("Unable to retrieve neighbor information for port '{}'. Not adding port description.".format(port_name))
log_info("Running command: '{}'".format(lldpcli_cmd)) # Add the command to our dictionary of pending commands, overwriting any
# previous pending command for this port
self.pending_cmds[port_name] = lldpcli_cmd
proc = subprocess.Popen(lldpcli_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) def process_pending_cmds(self):
# List of port names (keys of elements) to delete from self.pending_cmds
to_delete = []
(stdout, stderr) = proc.communicate() for (port_name, cmd) in self.pending_cmds.iteritems():
log_debug("Running command: '{}'".format(cmd))
if proc.returncode != 0: proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
log_error("Error running command '{}': {}".format(cmd, stderr))
(stdout, stderr) = proc.communicate()
# If the command succeeds, add the port name to our to_delete list.
# We will delete this command from self.pending_cmds below.
# If the command fails, log a message, but don't delete the command
# from self.pending_cmds, so that the command will be retried the
# next time this method is called.
if proc.returncode == 0:
to_delete.append(port_name)
else:
log_warning("Command failed '{}': {}".format(cmd, stderr))
# Delete all successful commands from self.pending_cmds
for port_name in to_delete:
self.pending_cmds.pop(port_name, None)
def run(self): def run(self):
""" """
@ -157,6 +187,9 @@ class LldpManager(object):
of the Redis State database. When we are notified of the creation of an of the Redis State database. When we are notified of the creation of an
interface, update LLDP configuration accordingly. interface, update LLDP configuration accordingly.
""" """
# Set select timeout to 10 seconds
SELECT_TIMEOUT_MS = 1000 * 10
# Subscribe to PORT table notifications in the State DB # Subscribe to PORT table notifications in the State DB
sel = swsscommon.Select() sel = swsscommon.Select()
sst = swsscommon.SubscriberStateTable(self.state_db, swsscommon.STATE_PORT_TABLE_NAME) sst = swsscommon.SubscriberStateTable(self.state_db, swsscommon.STATE_PORT_TABLE_NAME)
@ -164,16 +197,18 @@ class LldpManager(object):
# Listen indefinitely for changes to the PORT table in the State DB # Listen indefinitely for changes to the PORT table in the State DB
while True: while True:
(state, c, fd) = sel.select() (state, c, fd) = sel.select(SELECT_TIMEOUT_MS)
if state != swsscommon.Select.OBJECT:
log_warning("sel.select() did not return swsscommon.Select.OBJECT")
continue
(key, op, fvp) = sst.pop() if state == swsscommon.Select.OBJECT:
fvp_dict = dict(fvp) (key, op, fvp) = sst.pop()
if op == "SET" and fvp_dict.get("state") == "ok": fvp_dict = dict(fvp)
self.update_lldp_config_for_port(key)
if op == "SET" and fvp_dict.get("state") == "ok":
self.generate_pending_lldp_config_cmd_for_port(key)
# Process all pending commands
self.process_pending_cmds()
# ============================= Functions ============================= # ============================= Functions =============================

View File

@ -1,12 +1,17 @@
# lldpd package # lldpd package
LLDPD_VERSION = 0.9.5-0 LLDPD_VERSION = 0.9.5
LLDPD = lldpd_$(LLDPD_VERSION)_amd64.deb LLDPD = lldpd_$(LLDPD_VERSION)-0_amd64.deb
$(LLDPD)_DEPENDS += $(LIBSNMP_DEV) $(LLDPD)_DEPENDS += $(LIBSNMP_DEV)
$(LLDPD)_RDEPENDS += $(LIBSNMP) $(LLDPD)_RDEPENDS += $(LIBSNMP)
$(LLDPD)_SRC_PATH = $(SRC_PATH)/lldpd $(LLDPD)_SRC_PATH = $(SRC_PATH)/lldpd
SONIC_DPKG_DEBS += $(LLDPD) SONIC_MAKE_DEBS += $(LLDPD)
LIBLLDPCTL = liblldpctl-dev_$(LLDPD_VERSION)_amd64.deb LIBLLDPCTL = liblldpctl-dev_$(LLDPD_VERSION)-0_amd64.deb
$(eval $(call add_derived_package,$(LLDPD),$(LIBLLDPCTL))) $(eval $(call add_derived_package,$(LLDPD),$(LIBLLDPCTL)))
# Export these variables so they can be used in a sub-make
export LLDPD_VERSION
export LLDPD
export LIBLLDPCTL

@ -1 +0,0 @@
Subproject commit 396961a038a38675d46f96eaa7b430b2a1f8701b

32
src/lldpd/Makefile Normal file
View File

@ -0,0 +1,32 @@
.ONESHELL:
SHELL = /bin/bash
.SHELLFLAGS += -e
MAIN_TARGET = $(LLDPD)
DERIVED_TARGETS = $(LIBLLDPCTL)
$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
# Remove any stale files
rm -rf ./lldpd
# Clone lldpd repo
git clone https://github.com/vincentbernat/lldpd.git
pushd ./lldpd
# Reset HEAD to the commit of the proper tag
# NOTE: Using "git checkout <tag_name>" here detaches our HEAD,
# which stg doesn't like, so we use this method instead
git reset --hard $(LLDPD_VERSION)
# Apply patches
stg init
stg import -s ../patch/series
# Build source and Debian packages
dpkg-buildpackage -rfakeroot -b -us -uc -j$(SONIC_CONFIG_MAKE_JOBS)
popd
# Move the newly-built .deb packages to the destination directory
mv $* $(DERIVED_TARGETS) $(DEST)/
$(addprefix $(DEST)/, $(DERIVED_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET)

View File

@ -0,0 +1,39 @@
From 15e692fb82a61203624829cdd872315211920077 Mon Sep 17 00:00:00 2001
From: Guohan Lu <gulv@microsoft.com>
Date: Tue, 6 Mar 2018 09:36:51 +0000
Subject: [PATCH] return error when port does not exist
---
src/client/conf-lldp.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/client/conf-lldp.c b/src/client/conf-lldp.c
index c16219b..b51e4eb 100644
--- a/src/client/conf-lldp.c
+++ b/src/client/conf-lldp.c
@@ -148,6 +148,8 @@ cmd_portid_type_local(struct lldpctl_conn_t *conn, struct writer *w,
const char *name;
const char *id = cmdenv_get(env, "port-id");
const char *descr = cmdenv_get(env, "port-descr");
+ const char *portname = cmdenv_get(env, "ports");
+ int find_port = 0;
log_debug("lldpctl", "lldp PortID TLV Subtype Local port-id '%s' port-descr '%s'", id, descr);
@@ -165,6 +167,12 @@ cmd_portid_type_local(struct lldpctl_conn_t *conn, struct writer *w,
log_warnx("lldpctl", "unable to set LLDP Port Description for %s."
" %s", name, lldpctl_last_strerror(conn));
}
+ find_port = 1;
+ }
+
+ if (!find_port) {
+ log_warnx("lldpctl", "cannot find port %s", portname);
+ return 0;
}
return 1;
--
2.7.4

2
src/lldpd/patch/series Normal file
View File

@ -0,0 +1,2 @@
# This series applies on GIT commit 396961a038a38675d46f96eaa7b430b2a1f8701b
0001-return-error-when-port-does-not-exist.patch