mirror of
https://github.com/GNS3/gns3-server
synced 2024-12-27 17:28:08 +00:00
Copying project files directly, rather than copying them in an import-export fashion, can make copying projects many times faster
This commit is contained in:
parent
225779bc11
commit
5ffe5fd9b3
@ -1052,6 +1052,16 @@ class Project:
|
||||
|
||||
self.dump()
|
||||
assert self._status != "closed"
|
||||
|
||||
try:
|
||||
proj = await self._duplicate_fast(name, location, reset_mac_addresses)
|
||||
if proj:
|
||||
if previous_status == "closed":
|
||||
await self.close()
|
||||
return proj
|
||||
except Exception as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Cannot duplicate project: {}".format(str(e)))
|
||||
|
||||
try:
|
||||
begin = time.time()
|
||||
|
||||
@ -1237,3 +1247,66 @@ class Project:
|
||||
|
||||
def __repr__(self):
|
||||
return "<gns3server.controller.Project {} {}>".format(self._name, self._id)
|
||||
|
||||
async def _duplicate_fast(self, name=None, location=None, reset_mac_addresses=True):
|
||||
# remote replication is not supported
|
||||
if not sys.platform.startswith("linux") and not sys.platform.startswith("win"):
|
||||
return None
|
||||
for compute in self.computes:
|
||||
if compute.id != "local":
|
||||
log.warning("Duplicate fast not support remote compute: '{}'".format(compute.id))
|
||||
return None
|
||||
# work dir
|
||||
p_work = pathlib.Path(location or self.path).parent.absolute()
|
||||
t0 = time.time()
|
||||
new_project_id = str(uuid.uuid4())
|
||||
new_project_path = p_work.joinpath(new_project_id)
|
||||
# copy dir
|
||||
scripts_path = os.path.join(pathlib.Path(__file__).resolve().parent.parent.parent, 'scripts')
|
||||
process = await asyncio.create_subprocess_exec('python', os.path.join(scripts_path, 'copy_tree.py'), '--src',
|
||||
self.path, '--dst',
|
||||
new_project_path.as_posix())
|
||||
await process.wait()
|
||||
log.info("[FAST] Copy project: {} to: '{}', cost={}s".format(self.path, new_project_path, time.time() - t0))
|
||||
topology = json.loads(new_project_path.joinpath('{}.gns3'.format(self.name)).read_bytes())
|
||||
project_name = name or topology["name"]
|
||||
# If the project name is already used we generate a new one
|
||||
project_name = self.controller.get_free_project_name(project_name)
|
||||
topology["name"] = project_name
|
||||
# To avoid unexpected behavior (project start without manual operations just after import)
|
||||
topology["auto_start"] = False
|
||||
topology["auto_open"] = False
|
||||
topology["auto_close"] = False
|
||||
# change node ID
|
||||
node_old_to_new = {}
|
||||
for node in topology["topology"]["nodes"]:
|
||||
new_node_id = str(uuid.uuid4())
|
||||
if "node_id" in node:
|
||||
node_old_to_new[node["node_id"]] = new_node_id
|
||||
_move_node_file(new_project_path, node["node_id"], new_node_id)
|
||||
node["node_id"] = new_node_id
|
||||
if reset_mac_addresses:
|
||||
if "properties" in node and node["node_type"] != "docker":
|
||||
for prop, value in node["properties"].items():
|
||||
# reset the MAC address
|
||||
if prop in ("mac_addr", "mac_address"):
|
||||
node["properties"][prop] = None
|
||||
# change link ID
|
||||
for link in topology["topology"]["links"]:
|
||||
link["link_id"] = str(uuid.uuid4())
|
||||
for node in link["nodes"]:
|
||||
node["node_id"] = node_old_to_new[node["node_id"]]
|
||||
# Generate new drawings id
|
||||
for drawing in topology["topology"]["drawings"]:
|
||||
drawing["drawing_id"] = str(uuid.uuid4())
|
||||
|
||||
# And we dump the updated.gns3
|
||||
dot_gns3_path = new_project_path.joinpath('{}.gns3'.format(project_name))
|
||||
topology["project_id"] = new_project_id
|
||||
with open(dot_gns3_path, "w+") as f:
|
||||
json.dump(topology, f, indent=4)
|
||||
|
||||
os.remove(new_project_path.joinpath('{}.gns3'.format(self.name)))
|
||||
project = await self.controller.load_project(dot_gns3_path, load=False)
|
||||
log.info("[FAST] Project '{}' duplicated in {:.4f} seconds".format(project.name, time.time() - t0))
|
||||
return project
|
15
scripts/copy_tree.py
Normal file
15
scripts/copy_tree.py
Normal file
@ -0,0 +1,15 @@
|
||||
import argparse
|
||||
import shutil
|
||||
|
||||
|
||||
# 复制目录
|
||||
def copy_tree(src, dst):
|
||||
shutil.copytree(src, dst)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='for test')
|
||||
parser.add_argument('--src', type=str, help='', default='')
|
||||
parser.add_argument('--dst', type=str, help='', default='')
|
||||
args = parser.parse_args()
|
||||
copy_tree(args.src, args.dst)
|
Loading…
Reference in New Issue
Block a user