From 7ebcbd0a7706c4072ce260243981ff16ae998ec3 Mon Sep 17 00:00:00 2001 From: Antonio Francisco Date: Thu, 13 May 2021 10:11:10 -0300 Subject: [PATCH 1/5] Avoid dictionary changed errors When iterating through a dictionary, get a tuple of keys instead of a view, avoiding errors when the dictionary changes. --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index c15c0abf..d0618d54 100644 --- a/main.py +++ b/main.py @@ -52,7 +52,7 @@ def setup(self): def execute(self): """Execute once when the napp is running.""" - for circuit in self.circuits.values(): + for circuit in tuple(self.circuits.values()): if circuit.is_enabled() and not circuit.is_active(): circuit.deploy() @@ -461,7 +461,7 @@ def _is_duplicated_evc(self, evc): boolean: True if the circuit is duplicated, otherwise False. """ - for circuit in self.circuits.values(): + for circuit in tuple(self.circuits.values()): if not circuit.archived and circuit.shares_uni(evc): return True return False From 07d357d6831020356a952a510bc08fc6bed296b5 Mon Sep 17 00:00:00 2001 From: Antonio Francisco Date: Thu, 13 May 2021 16:47:27 -0300 Subject: [PATCH 2/5] Do not load to memory archived EVCs Archived EVCs cannot be used or changed, so there is no need for them to be in memory. --- main.py | 2 ++ tests/unit/test_main.py | 6 ++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index d0618d54..71e688ec 100644 --- a/main.py +++ b/main.py @@ -488,6 +488,8 @@ def handle_link_down(self, event): def load_circuits_by_interface(self, circuits): """Load circuits in storehouse for in-memory dictionary.""" for circuit_id, circuit in circuits.items(): + if circuit['archived'] is True: + continue intf_a = circuit['uni_a']['interface_id'] self.add_to_dict_of_sets(intf_a, circuit_id) intf_z = circuit['uni_z']['interface_id'] diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index f598a13b..b0f5cd47 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -696,11 +696,9 @@ def test_load_circuits_by_interface(self): expected_result = { '00:00:00:00:00:00:00:03:12': - {'182f5bac84074017a262a2321195dbb4', - '65c4582cc8f249c2a5947ef500c19e37'}, + {'65c4582cc8f249c2a5947ef500c19e37'}, '00:00:00:00:00:00:00:06:11': - {'182f5bac84074017a262a2321195dbb4', - '65c4582cc8f249c2a5947ef500c19e37'}, + {'65c4582cc8f249c2a5947ef500c19e37'}, '00:00:00:00:00:00:00:03:3': {'65c4582cc8f249c2a5947ef500c19e37'}, '00:00:00:00:00:00:00:05:2': From 647a90adb25d36e05cb79cbebb1243cbfa418640 Mon Sep 17 00:00:00 2001 From: Antonio Francisco Date: Thu, 13 May 2021 18:23:06 -0300 Subject: [PATCH 3/5] Instantiate the lock only once --- storehouse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storehouse.py b/storehouse.py index 907eb91d..a6f950fa 100644 --- a/storehouse.py +++ b/storehouse.py @@ -22,7 +22,8 @@ def __init__(self, controller): """Create a storehouse instance.""" self.controller = controller self.namespace = 'kytos.mef_eline.circuits' - self._lock = threading.Lock() + if '_lock' not in self.__dict__: + self._lock = threading.Lock() if 'box' not in self.__dict__: self.box = None self.list_stored_boxes() From 147e2bb9adb86d689b7d932c6abc5c87d3cfedcc Mon Sep 17 00:00:00 2001 From: Antonio Francisco Date: Thu, 13 May 2021 18:26:25 -0300 Subject: [PATCH 4/5] Use more locks to avoid race conditions --- main.py | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/main.py b/main.py index 71e688ec..623aabdb 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,8 @@ NApp to provision circuits from user request. """ +from threading import Lock + from flask import jsonify, request from werkzeug.exceptions import (BadRequest, Conflict, Forbidden, MethodNotAllowed, NotFound, @@ -48,6 +50,8 @@ def setup(self): # dictionary of EVCs by interface self._circuits_by_interface = {} + self._lock = Lock() + self.execute_as_loop(settings.DEPLOY_EVCS_INTERVAL) def execute(self): @@ -510,28 +514,31 @@ def add_to_dict_of_sets(self, intf, circuit_id): @listen_to('kytos/topology.port.created') def load_evcs(self, event): """Try to load the unloaded EVCs from storehouse.""" - log.debug("Event load_evcs %s", event) - circuits = self.storehouse.get_data() - if not self._circuits_by_interface: - self.load_circuits_by_interface(circuits) - - interface_id = '{}:{}'.format(event.content['switch'], - event.content['port']) - - for circuit_id in self._circuits_by_interface.get(interface_id, []): - if circuit_id in circuits and circuit_id not in self.circuits: - try: - evc = self._evc_from_dict(circuits[circuit_id]) - except ValueError as exception: - log.info( - f'Could not load EVC {circuit_id} because {exception}') - continue - - evc.deactivate() - evc.current_path = Path([]) - evc.sync() - self.circuits.setdefault(circuit_id, evc) - self.sched.add(evc) + with self._lock: + log.debug("Event load_evcs %s", event) + circuits = self.storehouse.get_data() + if not self._circuits_by_interface: + self.load_circuits_by_interface(circuits) + + interface_id = '{}:{}'.format(event.content['switch'], + event.content['port']) + + for circuit_id in self._circuits_by_interface.get(interface_id, + []): + if circuit_id in circuits and circuit_id not in self.circuits: + try: + evc = self._evc_from_dict(circuits[circuit_id]) + except ValueError as exception: + log.info( + f'Could not load EVC {circuit_id} ' + f'because {exception}') + continue + + evc.deactivate() + evc.current_path = Path([]) + evc.sync() + self.circuits.setdefault(circuit_id, evc) + self.sched.add(evc) @listen_to('kytos/flow_manager.flow.error') def handle_flow_mod_error(self, event): From 1a438a21036b930addd649fc6c2324e5eb7383a1 Mon Sep 17 00:00:00 2001 From: Antonio Francisco Date: Fri, 11 Jun 2021 16:28:59 -0300 Subject: [PATCH 5/5] Change log from info to error When an EVC cannot be loaded, a message is logged as error instead of info. Co-authored-by: Italo Valcy S Brito --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 623aabdb..c156222b 100644 --- a/main.py +++ b/main.py @@ -529,7 +529,7 @@ def load_evcs(self, event): try: evc = self._evc_from_dict(circuits[circuit_id]) except ValueError as exception: - log.info( + log.error( f'Could not load EVC {circuit_id} ' f'because {exception}') continue