diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0693220..d08f6c7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,6 +24,15 @@ Security ======== +[1.2.0] - 2021-12-13 +******************** +Changed +======= +- Added support for retries when sending a request to ``flow_manager`` +- Parametrized ``force`` option as a fallback +- Added more logs for request errors + + [1.1.1] - 2021-04-22 ******************** Changed diff --git a/kytos.json b/kytos.json index 74e1d0f..1ad35e2 100644 --- a/kytos.json +++ b/kytos.json @@ -3,7 +3,7 @@ "username": "kytos", "name": "of_lldp", "description": "Discover network-to-network interfaces (NNIs) using the LLDP protocol.", - "version": "1.1.1", + "version": "1.2.0", "napp_dependencies": ["kytos/of_core", "kytos/flow_manager", "kytos/topology"], "license": "MIT", "url": "https://github.com/kytos/of_lldp.git", diff --git a/main.py b/main.py index aa6292b..9abc54b 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,6 @@ """NApp responsible to discover new switches and hosts.""" import struct +import time import requests from flask import jsonify, request @@ -115,6 +116,15 @@ def handle_lldp_flows(self, event): event (:class:`~kytos.core.events.KytosEvent`): Event with new switch information. + """ + self._handle_lldp_flows(event) + + def _handle_lldp_flows(self, event): + """Install or remove flows in a switch. + + Install a flow to send LLDP packets to the controller. The proactive + flow is installed whenever a switch is enabled. If the switch is + disabled the flow is removed. """ try: dpid = event.content['dpid'] @@ -124,15 +134,45 @@ def handle_lldp_flows(self, event): except AttributeError: of_version = None + def _retry_if_status_code(response, endpoint, data, status_codes, + retries=3, wait=2): + """Retry if the response is in the status_codes.""" + if response.status_code not in status_codes: + return + if retries - 1 <= 0: + return + data = dict(data) + data["force"] = True + res = requests.post(endpoint, json=data) + method = res.request.method + if res.status_code != 202: + log.error(f"Failed to retry on {endpoint}, error: {res.text}," + f" status: {res.status_code}, method: {method}," + f" data: {data}") + time.sleep(wait) + return _retry_if_status_code(response, endpoint, data, + status_codes, retries - 1, wait) + log.info(f"Successfully forced {method} flows to {endpoint}") + flow = self._build_lldp_flow(of_version) if flow: destination = switch.id endpoint = f'{settings.FLOW_MANAGER_URL}/flows/{destination}' data = {'flows': [flow]} if event.name == 'kytos/topology.switch.enabled': - requests.post(endpoint, json=data) + res = requests.post(endpoint, json=data) + if res.status_code != 202: + log.error(f"Failed to push flows on {destination}," + f" error: {res.text}, status: {res.status_code}," + f" data: {data}") + _retry_if_status_code(res, endpoint, data, [424, 500]) else: - requests.delete(endpoint, json=data) + res = requests.delete(endpoint, json=data) + if res.status_code != 202: + log.error(f"Failed to delete flows on {destination}," + f" error: {res.text}, status: {res.status_code}", + f" data: {data}") + _retry_if_status_code(res, endpoint, data, [424, 500]) @listen_to('kytos/of_core.v0x0[14].messages.in.ofpt_packet_in') def notify_uplink_detected(self, event): diff --git a/setup.cfg b/setup.cfg index fd06696..f98c96e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ exclude = .eggs,ENV,build,docs/conf.py,venv [yala] radon mi args = --min C -pylint args = --disable=too-many-locals,too-few-public-methods,too-many-instance-attributes --ignored-modules=napps.kytos.of_lldp +pylint args = --disable=too-many-locals,too-few-public-methods,too-many-instance-attributes,too-many-arguments,inconsistent-return-statements --ignored-modules=napps.kytos.of_lldp [pydocstyle] add-ignore = D105 diff --git a/setup.py b/setup.py index e949afd..c2c325d 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ BASE_ENV = Path(os.environ.get('VIRTUAL_ENV', '/')) NAPP_NAME = 'of_lldp' -NAPP_VERSION = '1.1.1' +NAPP_VERSION = '1.2.0' # Kytos var folder VAR_PATH = BASE_ENV / 'var' / 'lib' / 'kytos' diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index baaf777..7da29bf 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -81,6 +81,24 @@ def test_handle_lldp_flows(self, mock_post, mock_delete): self.napp.handle_lldp_flows(event_del) mock_delete.assert_called() + @patch("time.sleep") + @patch("requests.post") + def test_handle_lldp_flows_retries(self, mock_post, _): + """Test handle_lldp_flow method retries.""" + dpid = "00:00:00:00:00:00:00:01" + switch = get_switch_mock("00:00:00:00:00:00:00:01", 0x04) + self.napp.controller.switches = {dpid: switch} + event_post = get_kytos_event_mock(name="kytos/topology.switch.enabled", + content={"dpid": dpid}) + + mock = MagicMock() + mock.request.method = "POST" + mock.status_code = 500 + mock.text = "some_err" + mock_post.return_value = mock + self.napp._handle_lldp_flows(event_post) + self.assertTrue(mock_post.call_count, 3) + @patch('kytos.core.buffers.KytosEventBuffer.put') @patch('napps.kytos.of_lldp.main.KytosEvent') @patch('kytos.core.controller.Controller.get_switch_by_dpid')