Skip to content
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

[Fix][Issue 107] RuntimeError: dictionary changed size during iteration #109

Merged
merged 5 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Fixed
=====
- Send topology.switches and topology.links shallow copy on ``kytos/topology.topology_loaded`` and ``kytos/topology.updated`` events
- Send object metadata shallow copy on ``kytos/topology.{entities}.metadata.{action}`` event
- Shallow copy shared iterables that are used on REST endpoints

Security
========
Expand Down
45 changes: 23 additions & 22 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def _get_link_or_create(self, endpoint_a, endpoint_b):
def _get_switches_dict(self):
"""Return a dictionary with the known switches."""
switches = {'switches': {}}
for idx, switch in enumerate(self.controller.switches.values()):
for idx, switch in enumerate(self.controller.switches.copy().values()):
switch_data = switch.as_dict()
if not all(key in switch_data['metadata']
for key in ('lat', 'lng')):
Expand All @@ -111,7 +111,7 @@ def _get_switches_dict(self):
def _get_links_dict(self):
"""Return a dictionary with the known links."""
return {'links': {link.id: link.as_dict() for link in
self.links.values()}}
self.links.copy().values()}}

def _get_topology_dict(self):
"""Return a dictionary with the known topology."""
Expand All @@ -120,14 +120,15 @@ def _get_topology_dict(self):

def _get_topology(self):
"""Return an object representing the topology."""
return Topology(dict(self.controller.switches), dict(self.links))
return Topology(self.controller.switches.copy(), self.links.copy())

def _get_link_from_interface(self, interface):
"""Return the link of the interface, or None if it does not exist."""
for link in self.links.values():
if interface in (link.endpoint_a, link.endpoint_b):
return link
return None
with self._links_lock:
for link in self.links.values():
if interface in (link.endpoint_a, link.endpoint_b):
return link
return None

def _load_link(self, link_att):
endpoint_a = link_att['endpoint_a']['id']
Expand Down Expand Up @@ -356,7 +357,7 @@ def enable_interface(self, interface_enable_id=None, dpid=None):
msg = f"Switch {dpid} interface {interface_number} not found"
return jsonify(msg), 404
else:
for interface in switch.interfaces.values():
for interface in switch.interfaces.copy().values():
interface.enable()
self.topo_controller.upsert_switch(switch.id, switch.as_dict())
self.notify_topology_update()
Expand Down Expand Up @@ -384,7 +385,7 @@ def disable_interface(self, interface_disable_id=None, dpid=None):
msg = f"Switch {dpid} interface {interface_number} not found"
return jsonify(msg), 404
else:
for interface in switch.interfaces.values():
for interface in switch.interfaces.copy().values():
interface.disable()
self.topo_controller.upsert_switch(switch.id, switch.as_dict())
self.notify_topology_update()
Expand Down Expand Up @@ -569,15 +570,16 @@ def on_link_liveness_disabled(self, event) -> None:

def get_links_from_interfaces(self, interfaces) -> dict:
"""Get links from interfaces."""
links = {}
for interface in interfaces:
for link in self.links.values():
if any((
interface.id == link.endpoint_a.id,
interface.id == link.endpoint_b.id,
)):
links[link.id] = link
return links
links_found = {}
with self._links_lock:
for interface in interfaces:
for link in self.links.values():
if any((
interface.id == link.endpoint_a.id,
interface.id == link.endpoint_b.id,
)):
links_found[link.id] = link
return links_found

def handle_link_liveness_disabled(self, interfaces) -> None:
"""Handle link liveness disabled."""
Expand Down Expand Up @@ -739,8 +741,7 @@ def handle_link_up(self, interface):
interface.activate()
self.topo_controller.activate_interface(interface.id)
self.notify_topology_update()
with self._links_lock:
link = self._get_link_from_interface(interface)
link = self._get_link_from_interface(interface)
if not link:
return
if link.endpoint_a == interface:
Expand Down Expand Up @@ -954,8 +955,8 @@ def notify_metadata_changes(self, obj, action):
)

name = f'kytos/topology.{entities}.metadata.{action}'
event = KytosEvent(name=name, content={entity: obj,
'metadata': dict(obj.metadata)})
content = {entity: obj, 'metadata': obj.metadata.copy()}
event = KytosEvent(name=name, content=content)
self.controller.buffers.app.put(event)
log.debug(f'Metadata from {obj.id} was {action}.')

Expand Down