diff --git a/vpp-snmp-agent.py b/vpp-snmp-agent.py index c6451db..1049b4a 100755 --- a/vpp-snmp-agent.py +++ b/vpp-snmp-agent.py @@ -108,7 +108,6 @@ class MyAgent(agentx.Agent): num_ifaces = len(ifaces) num_vppstat = len(self.vppstat["/if/names"]) num_lcp = len(lcp) - self.logger.debug("LCP: %s" % (lcp)) self.logger.debug( "Retrieved Interfaces: vppapi=%d vppstat=%d lcp=%d" % (num_ifaces, num_vppstat, num_lcp) diff --git a/vppapi.py b/vppapi.py index 0cc4c0d..bf86107 100644 --- a/vppapi.py +++ b/vppapi.py @@ -25,6 +25,23 @@ class VPPApi: self.connected = False self.clientname = clientname self.vpp = None + self.iface_dict = None + self.lcp_dict = None + + def _sw_interface_event(self, event): + # NOTE(pim): this callback runs in a background thread, so we just clear the + # cached interfaces and LCPs here, subsequent call to get_ifaces() or get_lcp() + # will refresh them in the main thread. + logger.info(f"Clearing iface and LCP cache due to interface event") + self.iface_dict = None + self.lcp_dict = None + + def _event_callback(self, msg_type_name, msg_type): + logger.debug(f"Received callback: {msg_type_name} => {msg_type}") + if msg_type_name == "sw_interface_event": + self._sw_interface_event(msg_type) + else: + logger.warning(f"Ignoring unkonwn event: {msg_type_name} => {msg_type}") def connect(self): if self.connected: @@ -37,6 +54,7 @@ class VPPApi: return False self.vpp = VPPApiClient(apifiles=vpp_jsonfiles, server_address=self.address) + self.vpp.register_event_callback(self._event_callback) try: logger.info("Connecting to VPP") self.vpp.connect(self.clientname) @@ -46,6 +64,13 @@ class VPPApi: v = self.vpp.api.show_version() logger.info("VPP version is %s" % v.version) + logger.info("Enabling VPP API interface events") + r = self.vpp.api.want_interface_events(enable_disable=True) + if r.retval != 0: + logger.error("Could not enable VPP API interface events, disconnecting") + self.disconnect() + return False + self.connected = True return True @@ -58,37 +83,51 @@ class VPPApi: def get_ifaces(self): ret = {} - if not self.connected: + if not self.connected and not self.connect(): + logger.warning("Can't connect to VPP API") return ret + if type(self.iface_dict) is dict: + logger.debug("Returning cached interfaces") + return self.iface_dict + + ret = {} try: + logger.info("Requesting interfaces from VPP API") iface_list = self.vpp.api.sw_interface_dump() except Exception as e: - logger.error("VPP communication error, disconnecting", e) - self.vpp.disconnect() - self.connected = False + logger.error("VPP API communication error, disconnecting", e) + self.disconnect() return ret if not iface_list: - logger.error("Can't get interface list") + logger.error("Can't get interface list, disconnecting") + self.disconnect() return ret for iface in iface_list: ret[iface.interface_name] = iface - return ret + self.iface_dict = ret + logger.debug(f"Caching interfaces: {ret}") + return self.iface_dict def get_lcp(self): ret = {} - if not self.connected: + if not self.connected and not self.connect(): + logger.warning("Can't connect to VPP API") return ret + if type(self.lcp_dict) is dict: + logger.debug("Returning cached LCPs") + return self.lcp_dict + try: + logger.info("Requesting LCPs from VPP API") lcp_list = self.vpp.api.lcp_itf_pair_get() except Exception as e: logger.error("VPP communication error, disconnecting", e) - self.vpp.disconnect() - self.connected = False + self.disconnect() return ret if not lcp_list: @@ -97,4 +136,7 @@ class VPPApi: for lcp in lcp_list[1]: ret[lcp.host_if_name] = lcp - return ret + + self.lcp_dict = ret + logger.debug(f"Caching LCPs: {ret}") + return self.lcp_dict