mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-08-27 12:30:13 +03:00
Fix issue when restoring a snapshot of a duplicated project
This commit is contained in:
parent
6869f2d71d
commit
fbea9b655d
@ -39,8 +39,19 @@ Handle the import of project from a .gns3project
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
async def import_project(controller, project_id, stream, location=None, name=None, keep_compute_ids=False,
|
async def import_project(
|
||||||
auto_start=False, auto_open=False, auto_close=True):
|
controller,
|
||||||
|
project_id,
|
||||||
|
stream,
|
||||||
|
location=None,
|
||||||
|
name=None,
|
||||||
|
keep_compute_ids=False,
|
||||||
|
restoring_snapshot=False,
|
||||||
|
project_name=None,
|
||||||
|
auto_start=False,
|
||||||
|
auto_open=False,
|
||||||
|
auto_close=True
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Import a project contain in a zip file
|
Import a project contain in a zip file
|
||||||
|
|
||||||
@ -52,6 +63,8 @@ async def import_project(controller, project_id, stream, location=None, name=Non
|
|||||||
:param location: Directory for the project if None put in the default directory
|
:param location: Directory for the project if None put in the default directory
|
||||||
:param name: Wanted project name, generate one from the .gns3 if None
|
:param name: Wanted project name, generate one from the .gns3 if None
|
||||||
:param keep_compute_ids: keep compute IDs unchanged
|
:param keep_compute_ids: keep compute IDs unchanged
|
||||||
|
:param restoring_snapshot: True if the project is imported as part of a snapshot restore, False otherwise
|
||||||
|
:param project_name: Original project name when restoring a snapshot
|
||||||
|
|
||||||
:returns: Project
|
:returns: Project
|
||||||
"""
|
"""
|
||||||
@ -69,10 +82,7 @@ async def import_project(controller, project_id, stream, location=None, name=Non
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
topology = json.loads(project_file)
|
topology = json.loads(project_file)
|
||||||
# We import the project on top of an existing project (snapshots)
|
if not project_name:
|
||||||
if topology["project_id"] == project_id:
|
|
||||||
project_name = topology["name"]
|
|
||||||
else:
|
|
||||||
# If the project name is already used we generate a new one
|
# If the project name is already used we generate a new one
|
||||||
if name:
|
if name:
|
||||||
project_name = controller.get_free_project_name(name)
|
project_name = controller.get_free_project_name(name)
|
||||||
@ -105,27 +115,30 @@ async def import_project(controller, project_id, stream, location=None, name=Non
|
|||||||
topology["auto_open"] = auto_open
|
topology["auto_open"] = auto_open
|
||||||
topology["auto_close"] = auto_close
|
topology["auto_close"] = auto_close
|
||||||
|
|
||||||
# Generate a new node id
|
if not restoring_snapshot:
|
||||||
node_old_to_new = {}
|
# Do not re-generate IDs if we are restoring a snapshot because they should be the same as the main project
|
||||||
for node in topology["topology"]["nodes"]:
|
|
||||||
if "node_id" in node:
|
|
||||||
node_old_to_new[node["node_id"]] = str(uuid.uuid4())
|
|
||||||
_move_node_file(path, node["node_id"], node_old_to_new[node["node_id"]])
|
|
||||||
node["node_id"] = node_old_to_new[node["node_id"]]
|
|
||||||
else:
|
|
||||||
node["node_id"] = str(uuid.uuid4())
|
|
||||||
|
|
||||||
# Update link to use new id
|
# Generate a new node id
|
||||||
for link in topology["topology"]["links"]:
|
node_old_to_new = {}
|
||||||
link["link_id"] = str(uuid.uuid4())
|
for node in topology["topology"]["nodes"]:
|
||||||
for node in link["nodes"]:
|
if "node_id" in node:
|
||||||
node["node_id"] = node_old_to_new[node["node_id"]]
|
node_old_to_new[node["node_id"]] = str(uuid.uuid4())
|
||||||
|
_move_node_file(path, node["node_id"], node_old_to_new[node["node_id"]])
|
||||||
|
node["node_id"] = node_old_to_new[node["node_id"]]
|
||||||
|
else:
|
||||||
|
node["node_id"] = str(uuid.uuid4())
|
||||||
|
|
||||||
# Generate new drawings id
|
# Update link to use new id
|
||||||
for drawing in topology["topology"]["drawings"]:
|
for link in topology["topology"]["links"]:
|
||||||
drawing["drawing_id"] = str(uuid.uuid4())
|
link["link_id"] = str(uuid.uuid4())
|
||||||
|
for node in link["nodes"]:
|
||||||
|
node["node_id"] = node_old_to_new[node["node_id"]]
|
||||||
|
|
||||||
# Modify the compute id of the node depending of compute capacity
|
# Generate new drawings id
|
||||||
|
for drawing in topology["topology"]["drawings"]:
|
||||||
|
drawing["drawing_id"] = str(uuid.uuid4())
|
||||||
|
|
||||||
|
# Modify the compute id of the node depending on compute capacity
|
||||||
if not keep_compute_ids:
|
if not keep_compute_ids:
|
||||||
# For some VM type we move them to the GNS3 VM if possible
|
# For some VM type we move them to the GNS3 VM if possible
|
||||||
# unless it's a linux host without GNS3 VM
|
# unless it's a linux host without GNS3 VM
|
||||||
|
|||||||
@ -959,7 +959,7 @@ class Project:
|
|||||||
Load topology elements
|
Load topology elements
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self._closing is True:
|
if self._closing:
|
||||||
raise aiohttp.web.HTTPConflict(text="Project is closing, please try again in a few seconds...")
|
raise aiohttp.web.HTTPConflict(text="Project is closing, please try again in a few seconds...")
|
||||||
|
|
||||||
if self._status == "opened":
|
if self._status == "opened":
|
||||||
|
|||||||
@ -134,7 +134,10 @@ class Snapshot:
|
|||||||
self._project.id,
|
self._project.id,
|
||||||
f,
|
f,
|
||||||
location=self._project.path,
|
location=self._project.path,
|
||||||
auto_start=self._project.auto_start, auto_open=self._project.auto_open,
|
project_name=self._project.name,
|
||||||
|
restoring_snapshot=True,
|
||||||
|
auto_start=self._project.auto_start,
|
||||||
|
auto_open=self._project.auto_open,
|
||||||
auto_close=self._project.auto_close
|
auto_close=self._project.auto_close
|
||||||
)
|
)
|
||||||
except (OSError, PermissionError) as e:
|
except (OSError, PermissionError) as e:
|
||||||
|
|||||||
@ -94,7 +94,8 @@ async def test_restore(project, controller):
|
|||||||
response.json = {"console": 2048}
|
response.json = {"console": 2048}
|
||||||
compute.post = AsyncioMagicMock(return_value=response)
|
compute.post = AsyncioMagicMock(return_value=response)
|
||||||
|
|
||||||
await project.add_node(compute, "test1", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
|
node1_id = str(uuid4())
|
||||||
|
await project.add_node(compute, "test1", node1_id, node_type="vpcs", properties={"startup_config": "test.cfg"})
|
||||||
snapshot = await project.snapshot(name="test")
|
snapshot = await project.snapshot(name="test")
|
||||||
|
|
||||||
# We add a node after the snapshots
|
# We add a node after the snapshots
|
||||||
@ -112,8 +113,11 @@ async def test_restore(project, controller):
|
|||||||
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
|
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
|
||||||
await snapshot.restore()
|
await snapshot.restore()
|
||||||
|
|
||||||
|
# make sure the original node IDs are restored
|
||||||
|
assert list(project.nodes.keys())[0] == node1_id
|
||||||
|
|
||||||
assert "snapshot.restored" in [c[0][0] for c in controller.notification.project_emit.call_args_list]
|
assert "snapshot.restored" in [c[0][0] for c in controller.notification.project_emit.call_args_list]
|
||||||
# project.closed notification should not be send when restoring snapshots
|
# project.closed notification should not be sent when restoring snapshots
|
||||||
assert "project.closed" not in [c[0][0] for c in controller.notification.project_emit.call_args_list]
|
assert "project.closed" not in [c[0][0] for c in controller.notification.project_emit.call_args_list]
|
||||||
|
|
||||||
project = controller.get_project(project.id)
|
project = controller.get_project(project.id)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user