From 5d1fdceb98e0114cb09dbd7aecef6794604e92aa Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 27 Oct 2020 19:41:24 +1030 Subject: [PATCH] Fix bug with application id allocation for IOU nodes. Fixes #3079 --- gns3server/controller/project.py | 18 ++++++++++++++++-- gns3server/utils/application_id.py | 15 ++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 5a289e15..33bb27a8 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -174,6 +174,7 @@ class Project: self._links = {} self._drawings = {} self._snapshots = {} + self._computes = [] # List the available snapshots snapshot_dir = os.path.join(self.path, "snapshots") @@ -564,6 +565,9 @@ class Project: if node_id in self._nodes: return self._nodes[node_id] + if compute.id not in self._computes: + self._computes.append(compute.id) + if node_type == "iou": async with self._iou_id_lock: # wait for a IOU node to be completely created before adding a new one @@ -571,10 +575,10 @@ class Project: # to generate MAC addresses) when creating multiple IOU node at the same time if "properties" in kwargs.keys(): # allocate a new application id for nodes loaded from the project - kwargs.get("properties")["application_id"] = get_next_application_id(self._controller.projects, compute) + kwargs.get("properties")["application_id"] = get_next_application_id(self._controller.projects, self._computes) elif "application_id" not in kwargs.keys() and not kwargs.get("properties"): # allocate a new application id for nodes added to the project - kwargs["application_id"] = get_next_application_id(self._controller.projects, compute) + kwargs["application_id"] = get_next_application_id(self._controller.projects, self._computes) node = await self._create_node(compute, name, node_id, node_type, **kwargs) else: node = await self._create_node(compute, name, node_id, node_type, **kwargs) @@ -604,6 +608,8 @@ class Project: self.remove_allocated_node_name(node.name) del self._nodes[node.id] await node.destroy() + # refresh the compute IDs list + self._computes = [n.compute.id for n in self.nodes.values()] self.dump() self.emit_notification("node.deleted", node.__json__()) @@ -931,6 +937,14 @@ class Project: topology = project_data["topology"] for compute in topology.get("computes", []): await self.controller.add_compute(**compute) + + # Get all compute used in the project + # used to allocate application IDs for IOU nodes. + for node in topology.get("nodes", []): + compute_id = node.get("compute_id") + if compute_id not in self._computes: + self._computes.append(compute_id) + for node in topology.get("nodes", []): compute = self.controller.get_compute(node.pop("compute_id")) name = node.pop("name") diff --git a/gns3server/utils/application_id.py b/gns3server/utils/application_id.py index 95fc76ad..d1ad984f 100644 --- a/gns3server/utils/application_id.py +++ b/gns3server/utils/application_id.py @@ -21,26 +21,27 @@ import logging log = logging.getLogger(__name__) -def get_next_application_id(projects, compute): +def get_next_application_id(projects, computes): """ Calculates free application_id from given nodes :param projects: all projects managed by controller - :param compute: Compute instance + :param computes: all computes used by the project :raises HTTPConflict when exceeds number :return: integer first free id """ nodes = [] - # look for application id for in all nodes across all opened projects that share the same compute + # look for application id for in all nodes across all opened projects that share the same computes for project in projects.values(): - if project.status == "opened" and compute in project.computes: + if project.status == "opened": nodes.extend(list(project.nodes.values())) - used = set([n.properties["application_id"] for n in nodes if n.node_type == "iou"]) + used = set([n.properties["application_id"] for n in nodes if n.node_type == "iou" and n.compute.id in computes]) pool = set(range(1, 512)) try: - return (pool - used).pop() + application_id = (pool - used).pop() + return application_id except KeyError: - raise aiohttp.web.HTTPConflict(text="Cannot create a new IOU node (limit of 512 nodes across all opened projects using compute {} reached".format(compute.name)) + raise aiohttp.web.HTTPConflict(text="Cannot create a new IOU node (limit of 512 nodes across all opened projects using the same computes)")