1
0
mirror of https://github.com/GNS3/gns3-server synced 2025-01-13 01:20:58 +00:00

Merge branch 'master' into unstable

This commit is contained in:
Julien Duponchelle 2015-04-15 16:29:54 +02:00
commit 789e24795e
33 changed files with 289 additions and 328 deletions

View File

@ -4,7 +4,7 @@ include INSTALL
include LICENSE include LICENSE
include MANIFEST.in include MANIFEST.in
include tox.ini include tox.ini
recursive-exclude tests * recursive-include tests *
recursive-exclude docs * recursive-exclude docs *
recursive-include gns3server * recursive-include gns3server *
recursive-exclude * __pycache__ recursive-exclude * __pycache__

View File

@ -181,10 +181,8 @@ class DynamipsDeviceHandler:
dynamips_manager = Dynamips.instance() dynamips_manager = Dynamips.instance()
device = dynamips_manager.get_device(request.match_info["device_id"], project_id=request.match_info["project_id"]) device = dynamips_manager.get_device(request.match_info["device_id"], project_id=request.match_info["project_id"])
port_number = int(request.match_info["port_number"]) port_number = int(request.match_info["port_number"])
if asyncio.iscoroutinefunction(device.remove_nio): nio = yield from device.remove_nio(port_number)
yield from device.remove_nio(port_number) yield from nio.delete()
else:
device.remove_nio(port_number)
response.set_status(204) response.set_status(204)
@Route.post( @Route.post(

View File

@ -290,7 +290,8 @@ class DynamipsVMHandler:
vm = dynamips_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) vm = dynamips_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
slot_number = int(request.match_info["adapter_number"]) slot_number = int(request.match_info["adapter_number"])
port_number = int(request.match_info["port_number"]) port_number = int(request.match_info["port_number"])
yield from vm.slot_remove_nio_binding(slot_number, port_number) nio = yield from vm.slot_remove_nio_binding(slot_number, port_number)
yield from nio.delete()
response.set_status(204) response.set_status(204)
@Route.post( @Route.post(

View File

@ -19,7 +19,6 @@ import os
from aiohttp.web import HTTPConflict from aiohttp.web import HTTPConflict
from ...web.route import Route from ...web.route import Route
from ...modules.port_manager import PortManager
from ...schemas.iou import IOU_CREATE_SCHEMA from ...schemas.iou import IOU_CREATE_SCHEMA
from ...schemas.iou import IOU_UPDATE_SCHEMA from ...schemas.iou import IOU_UPDATE_SCHEMA
from ...schemas.iou import IOU_OBJECT_SCHEMA from ...schemas.iou import IOU_OBJECT_SCHEMA
@ -52,20 +51,16 @@ class IOUHandler:
def create(request, response): def create(request, response):
iou = IOU.instance() iou = IOU.instance()
vm = yield from iou.create_vm(request.json["name"], vm = yield from iou.create_vm(request.json.pop("name"),
request.match_info["project_id"], request.match_info["project_id"],
request.json.get("vm_id"), request.json.get("vm_id"),
console=request.json.get("console"), console=request.json.get("console"))
serial_adapters=request.json.get("serial_adapters"),
ethernet_adapters=request.json.get("ethernet_adapters"), for name, value in request.json.items():
ram=request.json.get("ram"), if hasattr(vm, name) and getattr(vm, name) != value:
nvram=request.json.get("nvram"), setattr(vm, name, value)
use_default_iou_values=request.json.get("use_default_iou_values"), if "initial_config_content" in request.json:
l1_keepalives=request.json.get("l1_keepalives"), vm.initial_config = request.json.get("initial_config_content")
initial_config=request.json.get("initial_config_content"),
iourc_content=request.json.get("iourc_content")
)
vm.path = request.json.get("path", vm.path)
response.set_status(201) response.set_status(201)
response.json(vm) response.json(vm)
@ -109,18 +104,12 @@ class IOUHandler:
iou_manager = IOU.instance() iou_manager = IOU.instance()
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
vm.name = request.json.get("name", vm.name)
vm.console = request.json.get("console", vm.console)
vm.path = request.json.get("path", vm.path)
vm.ethernet_adapters = request.json.get("ethernet_adapters", vm.ethernet_adapters)
vm.serial_adapters = request.json.get("serial_adapters", vm.serial_adapters)
vm.ram = request.json.get("ram", vm.ram)
vm.nvram = request.json.get("nvram", vm.nvram)
vm.use_default_iou_values = request.json.get("use_default_iou_values", vm.use_default_iou_values)
vm.l1_keepalives = request.json.get("l1_keepalives", vm.l1_keepalives)
vm.initial_config = request.json.get("initial_config_content", vm.initial_config)
vm.iourc_content = request.json.get("iourc_content", None)
for name, value in request.json.items():
if hasattr(vm, name) and getattr(vm, name) != value:
setattr(vm, name, value)
if "initial_config_content" in request.json:
vm.initial_config = request.json.get("initial_config_content")
response.json(vm) response.json(vm)
@classmethod @classmethod
@ -273,7 +262,7 @@ class IOUHandler:
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"]) pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
if not vm.is_running(): if not vm.is_running():
raise HTTPConflict(text="You can't capture the traffic on a non started VM") raise HTTPConflict(text="Cannot capture traffic on a non started VM")
yield from vm.start_capture(adapter_number, port_number, pcap_file_path, request.json["data_link_type"]) yield from vm.start_capture(adapter_number, port_number, pcap_file_path, request.json["data_link_type"])
response.json({"pcap_file_path": str(pcap_file_path)}) response.json({"pcap_file_path": str(pcap_file_path)})
@ -298,7 +287,7 @@ class IOUHandler:
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
if not vm.is_running(): if not vm.is_running():
raise HTTPConflict(text="You can't capture the traffic on a non started VM") raise HTTPConflict(text="Cannot capture traffic on a non started VM")
adapter_number = int(request.match_info["adapter_number"]) adapter_number = int(request.match_info["adapter_number"])
port_number = int(request.match_info["port_number"]) port_number = int(request.match_info["port_number"])

View File

@ -41,24 +41,21 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
409: "Conflict" 409: "Conflict"
}, },
description="Create a new Qemu.instance", description="Create a new Qemu VM instance",
input=QEMU_CREATE_SCHEMA, input=QEMU_CREATE_SCHEMA,
output=QEMU_OBJECT_SCHEMA) output=QEMU_OBJECT_SCHEMA)
def create(request, response): def create(request, response):
qemu = Qemu.instance() qemu = Qemu.instance()
vm = yield from qemu.create_vm(request.json["name"], vm = yield from qemu.create_vm(request.json.pop("name"),
request.match_info["project_id"], request.match_info["project_id"],
request.json.get("vm_id"), request.json.get("vm_id"),
qemu_path=request.json.get("qemu_path"), qemu_path=request.json.get("qemu_path"),
console=request.json.get("console")) console=request.json.get("console"))
# Clear already used keys for name, value in request.json.items():
map(request.json.__delitem__, ["name", "project_id", "vm_id", if hasattr(vm, name) and getattr(vm, name) != value:
"qemu_path", "console"]) setattr(vm, name, value)
for field in request.json:
setattr(vm, field, request.json[field])
response.set_status(201) response.set_status(201)
response.json(vm) response.json(vm)
@ -75,7 +72,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Get a Qemu.instance", description="Get a Qemu VM instance",
output=QEMU_OBJECT_SCHEMA) output=QEMU_OBJECT_SCHEMA)
def show(request, response): def show(request, response):
@ -96,15 +93,17 @@ class QEMUHandler:
404: "Instance doesn't exist", 404: "Instance doesn't exist",
409: "Conflict" 409: "Conflict"
}, },
description="Update a Qemu.instance", description="Update a Qemu VM instance",
input=QEMU_UPDATE_SCHEMA, input=QEMU_UPDATE_SCHEMA,
output=QEMU_OBJECT_SCHEMA) output=QEMU_OBJECT_SCHEMA)
def update(request, response): def update(request, response):
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
vm = qemu_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) vm = qemu_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
for field in request.json:
setattr(vm, field, request.json[field]) for name, value in request.json.items():
if hasattr(vm, name) and getattr(vm, name) != value:
setattr(vm, name, value)
response.json(vm) response.json(vm)
@ -120,7 +119,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Delete a Qemu.instance") description="Delete a Qemu VM instance")
def delete(request, response): def delete(request, response):
yield from Qemu.instance().delete_vm(request.match_info["vm_id"]) yield from Qemu.instance().delete_vm(request.match_info["vm_id"])
@ -138,7 +137,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Start a Qemu.instance") description="Start a Qemu VM instance")
def start(request, response): def start(request, response):
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
@ -158,7 +157,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Stop a Qemu.instance") description="Stop a Qemu VM instance")
def stop(request, response): def stop(request, response):
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
@ -178,7 +177,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Reload a Qemu.instance") description="Reload a Qemu VM instance")
def reload(request, response): def reload(request, response):
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
@ -198,7 +197,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Suspend a Qemu.instance") description="Suspend a Qemu VM instance")
def suspend(request, response): def suspend(request, response):
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
@ -218,7 +217,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Resume a Qemu.instance") description="Resume a Qemu VM instance")
def resume(request, response): def resume(request, response):
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
@ -239,7 +238,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Add a NIO to a Qemu.instance", description="Add a NIO to a Qemu VM instance",
input=QEMU_NIO_SCHEMA, input=QEMU_NIO_SCHEMA,
output=QEMU_NIO_SCHEMA) output=QEMU_NIO_SCHEMA)
def create_nio(request, response): def create_nio(request, response):
@ -265,7 +264,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Remove a NIO from a Qemu.instance") description="Remove a NIO from a Qemu VM instance")
def delete_nio(request, response): def delete_nio(request, response):
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()

View File

@ -79,8 +79,9 @@ class VirtualBoxHandler:
yield from vm.set_ram(ram) yield from vm.set_ram(ram)
for name, value in request.json.items(): for name, value in request.json.items():
if hasattr(vm, name) and getattr(vm, name) != value: if name != "vm_id":
setattr(vm, name, value) if hasattr(vm, name) and getattr(vm, name) != value:
setattr(vm, name, value)
response.set_status(201) response.set_status(201)
response.json(vm) response.json(vm)

View File

@ -371,3 +371,40 @@ class BaseManager:
nio = NIOGenericEthernet(nio_settings["ethernet_device"]) nio = NIOGenericEthernet(nio_settings["ethernet_device"])
assert nio is not None assert nio is not None
return nio return nio
def get_abs_image_path(self, path):
"""
Get the absolute path of an image
:param path: file path
:return: file path
"""
img_directory = self.get_images_directory()
if not os.path.isabs(path):
s = os.path.split(path)
return os.path.normpath(os.path.join(img_directory, *s))
return path
def get_relative_image_path(self, path):
"""
Get a path relative to images directory path
or an abspath if the path is not located inside
image directory
:param path: file path
:return: file path
"""
img_directory = self.get_images_directory()
path = self.get_abs_image_path(path)
if os.path.dirname(path) == img_directory:
return os.path.basename(path)
return path
def get_images_directory(self):
"""
Get the image directory on disk
"""
raise NotImplementedError

View File

@ -616,3 +616,9 @@ class Dynamips(BaseManager):
if was_auto_started: if was_auto_started:
yield from vm.stop() yield from vm.stop()
return validated_idlepc return validated_idlepc
def get_images_directory(self):
"""
Return the full path of the images directory on disk
"""
return os.path.join(os.path.expanduser(self.config.get_section_config("Server").get("images_path", "~/GNS3/images")), "IOS")

View File

@ -20,6 +20,7 @@ Interface for FIFO NIOs.
""" """
import asyncio import asyncio
import uuid
from .nio import NIO from .nio import NIO
import logging import logging
@ -34,24 +35,12 @@ class NIOFIFO(NIO):
:param hypervisor: Dynamips hypervisor instance :param hypervisor: Dynamips hypervisor instance
""" """
_instance_count = 0
def __init__(self, hypervisor): def __init__(self, hypervisor):
# create an unique ID and name # create an unique name
nio_id = NIOFIFO._instance_count name = 'fifo-{}'.format(uuid.uuid4())
NIOFIFO._instance_count += 1
name = 'nio_fifo' + str(nio_id)
super().__init__(name, hypervisor) super().__init__(name, hypervisor)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):

View File

@ -20,6 +20,7 @@ Interface for generic Ethernet NIOs (PCAP library).
""" """
import asyncio import asyncio
import uuid
from .nio import NIO from .nio import NIO
import logging import logging
@ -35,25 +36,13 @@ class NIOGenericEthernet(NIO):
:param ethernet_device: Ethernet device name (e.g. eth0) :param ethernet_device: Ethernet device name (e.g. eth0)
""" """
_instance_count = 0
def __init__(self, hypervisor, ethernet_device): def __init__(self, hypervisor, ethernet_device):
# create an unique ID and name # create an unique name
nio_id = NIOGenericEthernet._instance_count name = 'generic_ethernet-{}'.format(uuid.uuid4())
NIOGenericEthernet._instance_count += 1
name = 'nio_gen_eth' + str(nio_id)
self._ethernet_device = ethernet_device self._ethernet_device = ethernet_device
super().__init__(name, hypervisor) super().__init__(name, hypervisor)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):

View File

@ -20,6 +20,7 @@ Interface for Linux Ethernet NIOs (Linux only).
""" """
import asyncio import asyncio
import uuid
from .nio import NIO from .nio import NIO
import logging import logging
@ -35,25 +36,12 @@ class NIOLinuxEthernet(NIO):
:param ethernet_device: Ethernet device name (e.g. eth0) :param ethernet_device: Ethernet device name (e.g. eth0)
""" """
_instance_count = 0
def __init__(self, hypervisor, ethernet_device): def __init__(self, hypervisor, ethernet_device):
# create an unique name
# create an unique ID and name name = 'linux_ethernet-{}'.format(uuid.uuid4())
nio_id = NIOLinuxEthernet._instance_count
NIOLinuxEthernet._instance_count += 1
name = 'nio_linux_eth' + str(nio_id)
self._ethernet_device = ethernet_device self._ethernet_device = ethernet_device
super().__init__(name, hypervisor) super().__init__(name, hypervisor)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):

View File

@ -20,6 +20,7 @@ Interface for multicast NIOs.
""" """
import asyncio import asyncio
import uuid
from .nio import NIO from .nio import NIO
import logging import logging
@ -36,27 +37,15 @@ class NIOMcast(NIO):
:param port: port for binding :param port: port for binding
""" """
_instance_count = 0
def __init__(self, hypervisor, group, port): def __init__(self, hypervisor, group, port):
# create an unique ID and name # create an unique name
nio_id = NIOMcast._instance_count name = 'mcast-{}'.format(uuid.uuid4())
NIOMcast._instance_count += 1
name = 'nio_mcast' + str(nio_id)
self._group = group self._group = group
self._port = port self._port = port
self._ttl = 1 # default TTL self._ttl = 1 # default TTL
super().__init__(name, hypervisor) super().__init__(name, hypervisor)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):

View File

@ -20,6 +20,7 @@ Interface for dummy NIOs (mostly for tests).
""" """
import asyncio import asyncio
import uuid
from .nio import NIO from .nio import NIO
import logging import logging
@ -34,24 +35,12 @@ class NIONull(NIO):
:param hypervisor: Dynamips hypervisor instance :param hypervisor: Dynamips hypervisor instance
""" """
_instance_count = 0
def __init__(self, hypervisor): def __init__(self, hypervisor):
# create an unique ID and name # create an unique name
nio_id = NIONull._instance_count name = 'null-{}'.format(uuid.uuid4())
NIONull._instance_count += 1
name = 'nio_null' + str(nio_id)
super().__init__(name, hypervisor) super().__init__(name, hypervisor)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):

View File

@ -20,6 +20,7 @@ Interface for TAP NIOs (UNIX based OSes only).
""" """
import asyncio import asyncio
import uuid
from .nio import NIO from .nio import NIO
import logging import logging
@ -35,25 +36,13 @@ class NIOTAP(NIO):
:param tap_device: TAP device name (e.g. tap0) :param tap_device: TAP device name (e.g. tap0)
""" """
_instance_count = 0
def __init__(self, hypervisor, tap_device): def __init__(self, hypervisor, tap_device):
# create an unique ID and name # create an unique name
nio_id = NIOTAP._instance_count name = 'tap-{}'.format(uuid.uuid4())
NIOTAP._instance_count += 1
name = 'nio_tap' + str(nio_id)
self._tap_device = tap_device self._tap_device = tap_device
super().__init__(name, hypervisor) super().__init__(name, hypervisor)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):

View File

@ -20,6 +20,7 @@ Interface for UDP NIOs.
""" """
import asyncio import asyncio
import uuid
from .nio import NIO from .nio import NIO
import logging import logging
@ -37,27 +38,15 @@ class NIOUDP(NIO):
:param rport: remote port number :param rport: remote port number
""" """
_instance_count = 0
def __init__(self, hypervisor, lport, rhost, rport): def __init__(self, hypervisor, lport, rhost, rport):
# create an unique ID and name # create an unique name
nio_id = NIOUDP._instance_count name = 'udp-{}'.format(uuid.uuid4())
NIOUDP._instance_count += 1
name = 'nio_udp' + str(nio_id)
self._lport = lport self._lport = lport
self._rhost = rhost self._rhost = rhost
self._rport = rport self._rport = rport
super().__init__(name, hypervisor) super().__init__(name, hypervisor)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):

View File

@ -20,6 +20,7 @@ Interface for UNIX NIOs (Unix based OSes only).
""" """
import asyncio import asyncio
import uuid
from .nio import NIO from .nio import NIO
import logging import logging
@ -36,26 +37,14 @@ class NIOUNIX(NIO):
:param remote_file: remote UNIX socket filename :param remote_file: remote UNIX socket filename
""" """
_instance_count = 0
def __init__(self, hypervisor, local_file, remote_file): def __init__(self, hypervisor, local_file, remote_file):
# create an unique ID and name # create an unique name
nio_id = NIOUNIX._instance_count name = 'unix-{}'.format(uuid.uuid4())
NIOUNIX._instance_count += 1
name = 'nio_unix' + str(nio_id)
self._local_file = local_file self._local_file = local_file
self._remote_file = remote_file self._remote_file = remote_file
super().__init__(name, hypervisor) super().__init__(name, hypervisor)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):

View File

@ -20,6 +20,7 @@ Interface for VDE (Virtual Distributed Ethernet) NIOs (Unix based OSes only).
""" """
import asyncio import asyncio
import uuid
from .nio import NIO from .nio import NIO
import logging import logging
@ -36,26 +37,14 @@ class NIOVDE(NIO):
:param local_file: VDE local filename :param local_file: VDE local filename
""" """
_instance_count = 0
def __init__(self, hypervisor, control_file, local_file): def __init__(self, hypervisor, control_file, local_file):
# create an unique ID and name # create an unique name
nio_id = NIOVDE._instance_count name = 'vde-{}'.format(uuid.uuid4())
NIOVDE._instance_count += 1
name = 'nio_vde' + str(nio_id)
self._control_file = control_file self._control_file = control_file
self._local_file = local_file self._local_file = local_file
super().__init__(name, hypervisor) super().__init__(name, hypervisor)
@classmethod
def reset(cls):
"""
Reset the instance count.
"""
cls._instance_count = 0
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):

View File

@ -150,6 +150,7 @@ class ATMSwitch(Device):
self._nios[port_number] = nio self._nios[port_number] = nio
@asyncio.coroutine
def remove_nio(self, port_number): def remove_nio(self, port_number):
""" """
Removes the specified NIO as member of this ATM switch. Removes the specified NIO as member of this ATM switch.
@ -160,6 +161,23 @@ class ATMSwitch(Device):
if port_number not in self._nios: if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number)) raise DynamipsError("Port {} is not allocated".format(port_number))
# remove VCs mapped with the port
for source, destination in self._mappings.copy().items():
if len(source) == 3 and len(destination) == 3:
# remove the virtual channels mapped with this port/nio
source_port, source_vpi, source_vci = source
destination_port, destination_vpi, destination_vci = destination
if port_number == source_port:
yield from self.unmap_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
yield from self.unmap_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
else:
# remove the virtual paths mapped with this port/nio
source_port, source_vpi = source
destination_port, destination_vpi = destination
if port_number == source_port:
yield from self.unmap_vp(source_port, source_vpi, destination_port, destination_vpi)
yield from self.unmap_vp(destination_port, destination_vpi, source_port, source_vpi)
nio = self._nios[port_number] nio = self._nios[port_number]
if isinstance(nio, NIOUDP): if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project) self.manager.port_manager.release_udp_port(nio.lport, self._project)

View File

@ -149,6 +149,7 @@ class FrameRelaySwitch(Device):
self._nios[port_number] = nio self._nios[port_number] = nio
@asyncio.coroutine
def remove_nio(self, port_number): def remove_nio(self, port_number):
""" """
Removes the specified NIO as member of this Frame Relay switch. Removes the specified NIO as member of this Frame Relay switch.
@ -161,6 +162,14 @@ class FrameRelaySwitch(Device):
if port_number not in self._nios: if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number)) raise DynamipsError("Port {} is not allocated".format(port_number))
# remove VCs mapped with the port
for source, destination in self._mappings.copy().items():
source_port, source_dlci = source
destination_port, destination_dlci = destination
if port_number == source_port:
yield from self.unmap_vc(source_port, source_dlci, destination_port, destination_dlci)
yield from self.unmap_vc(destination_port, destination_dlci, source_port, source_dlci)
nio = self._nios[port_number] nio = self._nios[port_number]
if isinstance(nio, NIOUDP): if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project) self.manager.port_manager.release_udp_port(nio.lport, self._project)

View File

@ -151,10 +151,7 @@ class Router(BaseVM):
"system_id": self._system_id} "system_id": self._system_id}
# return the relative path if the IOS image is in the images_path directory # return the relative path if the IOS image is in the images_path directory
server_config = self.manager.config.get_section_config("Server") router_info["image"] = self.manager.get_relative_image_path(self._image)
relative_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "IOS", self._image)
if os.path.exists(relative_image):
router_info["image"] = os.path.basename(self._image)
# add the slots # add the slots
slot_number = 0 slot_number = 0
@ -172,14 +169,6 @@ class Router(BaseVM):
return router_info return router_info
@classmethod
def reset(cls):
"""
Resets the instance count and the allocated instances list.
"""
cls._dynamips_ids.clear()
@property @property
def dynamips_id(self): def dynamips_id(self):
""" """
@ -427,9 +416,7 @@ class Router(BaseVM):
:param image: path to IOS image file :param image: path to IOS image file
""" """
if not os.path.isabs(image): image = self.manager.get_abs_image_path(image)
server_config = self.manager.config.get_section_config("Server")
image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "IOS", image)
if not os.path.isfile(image): if not os.path.isfile(image):
raise DynamipsError("IOS image '{}' is not accessible".format(image)) raise DynamipsError("IOS image '{}' is not accessible".format(image))

View File

@ -91,3 +91,9 @@ class IOU(BaseManager):
""" """
return os.path.join("iou", "device-{}".format(legacy_vm_id)) return os.path.join("iou", "device-{}".format(legacy_vm_id))
def get_images_directory(self):
"""
Return the full path of the images directory on disk
"""
return os.path.join(os.path.expanduser(self.config.get_section_config("Server").get("images_path", "~/GNS3/images")), "IOU")

View File

@ -61,25 +61,9 @@ class IOUVM(BaseVM):
:param project: Project instance :param project: Project instance
:param manager: Manager instance :param manager: Manager instance
:param console: TCP console port :param console: TCP console port
:params ethernet_adapters: number of ethernet adapters
:params serial_adapters: number of serial adapters
:params ram: amount of RAM in MB
:params nvram: amount of NVRAM in KB
:params l1_keepalives: always keep the Ethernet interfaces up
:params initial_config: content of the initial configuration file
:params iourc_content: content of the iourc file if no licence is installed on the machine
""" """
def __init__(self, name, vm_id, project, manager, def __init__(self, name, vm_id, project, manager, console=None):
console=None,
ram=None,
nvram=None,
use_default_iou_values=None,
ethernet_adapters=None,
serial_adapters=None,
l1_keepalives=None,
initial_config=None,
iourc_content=None):
super().__init__(name, vm_id, project, manager, console=console) super().__init__(name, vm_id, project, manager, console=console)
@ -94,17 +78,13 @@ class IOUVM(BaseVM):
# IOU settings # IOU settings
self._ethernet_adapters = [] self._ethernet_adapters = []
self._serial_adapters = [] self._serial_adapters = []
self.ethernet_adapters = 2 if ethernet_adapters is None else ethernet_adapters # one adapter = 4 interfaces self.ethernet_adapters = 2 # one adapter = 4 interfaces
self.serial_adapters = 2 if serial_adapters is None else serial_adapters # one adapter = 4 interfaces self.serial_adapters = 2 # one adapter = 4 interfaces
self._use_default_iou_values = True if use_default_iou_values is None else use_default_iou_values # for RAM & NVRAM values self._use_default_iou_values = True # for RAM & NVRAM values
self._nvram = 128 if nvram is None else nvram # Kilobytes self._nvram = 128 # Kilobytes
self._initial_config = "" self._initial_config = ""
self._ram = 256 if ram is None else ram # Megabytes self._ram = 256 # Megabytes
self._l1_keepalives = False if l1_keepalives is None else l1_keepalives # used to overcome the always-up Ethernet interfaces (not supported by all IOSes). self._l1_keepalives = False # used to overcome the always-up Ethernet interfaces (not supported by all IOSes).
self.iourc_content = iourc_content
if initial_config is not None:
self.initial_config = initial_config
@asyncio.coroutine @asyncio.coroutine
def close(self): def close(self):
@ -145,14 +125,7 @@ class IOUVM(BaseVM):
:param path: path to the IOU image executable :param path: path to the IOU image executable
""" """
if not os.path.isabs(path): self._path = self.manager.get_abs_image_path(path)
server_config = self.manager.config.get_section_config("Server")
relative_path = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), path)
if not os.path.exists(relative_path):
relative_path = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "IOU", path)
path = relative_path
self._path = path
# In 1.2 users uploaded images to the images roots # In 1.2 users uploaded images to the images roots
# after the migration their images are inside images/IOU # after the migration their images are inside images/IOU
@ -236,15 +209,11 @@ class IOUVM(BaseVM):
"nvram": self._nvram, "nvram": self._nvram,
"l1_keepalives": self._l1_keepalives, "l1_keepalives": self._l1_keepalives,
"initial_config": self.relative_initial_config_file, "initial_config": self.relative_initial_config_file,
"use_default_iou_values": self._use_default_iou_values, "iourc_path": self.iourc_path,
"iourc_path": self.iourc_path} "use_default_iou_values": self._use_default_iou_values}
# return the relative path if the IOU image is in the images_path directory # return the relative path if the IOU image is in the images_path directory
server_config = self.manager.config.get_section_config("Server") iou_vm_info["path"] = self.manager.get_relative_image_path(self.path)
relative_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "IOU", self.path)
if os.path.exists(relative_image):
iou_vm_info["path"] = os.path.basename(self.path)
return iou_vm_info return iou_vm_info
@property @property

View File

@ -252,19 +252,14 @@ class PortManager:
project.record_udp_port(port) project.record_udp_port(port)
log.debug("UDP port {} has been reserved".format(port)) log.debug("UDP port {} has been reserved".format(port))
def release_udp_port(self, port, project, force_remove=False): def release_udp_port(self, port, project):
""" """
Release a specific UDP port number Release a specific UDP port number
:param port: UDP port number :param port: UDP port number
:param project: Project instance :param project: Project instance
:param force_remove: Force port removal even on Darwnin
""" """
# A bug with Dynamips on Darwin which doesn't correctly free UDP ports, they are freed only when changing the project
if sys.platform.startswith("darwin") and force_remove is False:
return
if port in self._used_udp_ports: if port in self._used_udp_ports:
self._used_udp_ports.remove(port) self._used_udp_ports.remove(port)
project.remove_udp_port(port) project.remove_udp_port(port)

View File

@ -365,7 +365,7 @@ class Project:
for port in self._used_tcp_ports.copy(): for port in self._used_tcp_ports.copy():
port_manager.release_tcp_port(port, self) port_manager.release_tcp_port(port, self)
for port in self._used_udp_ports.copy(): for port in self._used_udp_ports.copy():
port_manager.release_udp_port(port, self, force_remove=True) port_manager.release_udp_port(port, self)
@asyncio.coroutine @asyncio.coroutine
def commit(self): def commit(self):

View File

@ -112,3 +112,9 @@ class Qemu(BaseManager):
""" """
return os.path.join("qemu", "vm-{}".format(legacy_vm_id)) return os.path.join("qemu", "vm-{}".format(legacy_vm_id))
def get_images_directory(self):
"""
Return the full path of the images directory on disk
"""
return os.path.join(os.path.expanduser(self.config.get_section_config("Server").get("images_path", "~/GNS3/images")), "QEMU")

View File

@ -148,14 +148,10 @@ class QemuVM(BaseVM):
:param hda_disk_image: QEMU hda disk image path :param hda_disk_image: QEMU hda disk image path
""" """
if not os.path.isabs(hda_disk_image): self._hda_disk_image = self.manager.get_abs_image_path(hda_disk_image)
server_config = self.manager.config.get_section_config("Server")
hda_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hda_disk_image)
log.info('QEMU VM "{name}" [{id}] has set the QEMU hda disk image path to {disk_image}'.format(name=self._name, log.info('QEMU VM "{name}" [{id}] has set the QEMU hda disk image path to {disk_image}'.format(name=self._name,
id=self._id, id=self._id,
disk_image=hda_disk_image)) disk_image=self._hda_disk_image))
self._hda_disk_image = hda_disk_image
@property @property
def hdb_disk_image(self): def hdb_disk_image(self):
@ -175,14 +171,10 @@ class QemuVM(BaseVM):
:param hdb_disk_image: QEMU hdb disk image path :param hdb_disk_image: QEMU hdb disk image path
""" """
if not os.path.isabs(hdb_disk_image): self._hdb_disk_image = self.manager.get_abs_image_path(hdb_disk_image)
server_config = self.manager.config.get_section_config("Server")
hdb_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdb_disk_image)
log.info('QEMU VM "{name}" [{id}] has set the QEMU hdb disk image path to {disk_image}'.format(name=self._name, log.info('QEMU VM "{name}" [{id}] has set the QEMU hdb disk image path to {disk_image}'.format(name=self._name,
id=self._id, id=self._id,
disk_image=hdb_disk_image)) disk_image=self._hdb_disk_image))
self._hdb_disk_image = hdb_disk_image
@property @property
def hdc_disk_image(self): def hdc_disk_image(self):
@ -202,14 +194,10 @@ class QemuVM(BaseVM):
:param hdc_disk_image: QEMU hdc disk image path :param hdc_disk_image: QEMU hdc disk image path
""" """
if not os.path.isabs(hdc_disk_image): self._hdc_disk_image = self.manager.get_abs_image_path(hdc_disk_image)
server_config = self.manager.config.get_section_config("Server")
hdc_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdc_disk_image)
log.info('QEMU VM "{name}" [{id}] has set the QEMU hdc disk image path to {disk_image}'.format(name=self._name, log.info('QEMU VM "{name}" [{id}] has set the QEMU hdc disk image path to {disk_image}'.format(name=self._name,
id=self._id, id=self._id,
disk_image=hdc_disk_image)) disk_image=self._hdc_disk_image))
self._hdc_disk_image = hdc_disk_image
@property @property
def hdd_disk_image(self): def hdd_disk_image(self):
@ -229,14 +217,11 @@ class QemuVM(BaseVM):
:param hdd_disk_image: QEMU hdd disk image path :param hdd_disk_image: QEMU hdd disk image path
""" """
if not os.path.isabs(hdd_disk_image): self._hdd_disk_image = hdd_disk_image
server_config = self.manager.config.get_section_config("Server") self._hdd_disk_image = self.manager.get_abs_image_path(hdd_disk_image)
hdd_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdd_disk_image)
log.info('QEMU VM "{name}" [{id}] has set the QEMU hdd disk image path to {disk_image}'.format(name=self._name, log.info('QEMU VM "{name}" [{id}] has set the QEMU hdd disk image path to {disk_image}'.format(name=self._name,
id=self._id, id=self._id,
disk_image=hdd_disk_image)) disk_image=self._hdd_disk_image))
self._hdd_disk_image = hdd_disk_image
@property @property
def adapters(self): def adapters(self):
@ -778,9 +763,9 @@ class QemuVM(BaseVM):
adapter.add_nio(0, nio) adapter.add_nio(0, nio)
log.info('QEMU VM "{name}" [{id}]: {nio} added to adapter {adapter_number}'.format(name=self._name, log.info('QEMU VM "{name}" [{id}]: {nio} added to adapter {adapter_number}'.format(name=self._name,
id=self._id, id=self._id,
nio=nio, nio=nio,
adapter_number=adapter_number)) adapter_number=adapter_number))
@asyncio.coroutine @asyncio.coroutine
def adapter_remove_nio_binding(self, adapter_number): def adapter_remove_nio_binding(self, adapter_number):
@ -1068,23 +1053,6 @@ class QemuVM(BaseVM):
command.extend(self._graphic()) command.extend(self._graphic())
return command return command
def _get_relative_disk_image_path(self, disk_image):
"""
Returns a relative image path if the disk image is in the images directory.
:param disk_image: path to the disk image
:returns: relative or full path
"""
if disk_image:
# return the relative path if the disk image is in the images_path directory
server_config = self.manager.config.get_section_config("Server")
relative_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", disk_image)
if os.path.exists(relative_image):
return os.path.basename(disk_image)
return disk_image
def __json__(self): def __json__(self):
answer = { answer = {
"project_id": self.project.id, "project_id": self.project.id,
@ -1095,11 +1063,11 @@ class QemuVM(BaseVM):
if field not in answer: if field not in answer:
answer[field] = getattr(self, field) answer[field] = getattr(self, field)
answer["hda_disk_image"] = self._get_relative_disk_image_path(self._hda_disk_image) answer["hda_disk_image"] = self.manager.get_relative_image_path(self._hda_disk_image)
answer["hdb_disk_image"] = self._get_relative_disk_image_path(self._hdb_disk_image) answer["hdb_disk_image"] = self.manager.get_relative_image_path(self._hdb_disk_image)
answer["hdc_disk_image"] = self._get_relative_disk_image_path(self._hdc_disk_image) answer["hdc_disk_image"] = self.manager.get_relative_image_path(self._hdc_disk_image)
answer["hdd_disk_image"] = self._get_relative_disk_image_path(self._hdd_disk_image) answer["hdd_disk_image"] = self.manager.get_relative_image_path(self._hdd_disk_image)
answer["initrd"] = self._get_relative_disk_image_path(self._initrd) answer["initrd"] = self.manager.get_relative_image_path(self._initrd)
answer["kernel_image"] = self._get_relative_disk_image_path(self._kernel_image) answer["kernel_image"] = self.manager.get_relative_image_path(self._kernel_image)
return answer return answer

View File

@ -41,6 +41,7 @@ class VirtualBox(BaseManager):
super().__init__() super().__init__()
self._vboxmanage_path = None self._vboxmanage_path = None
self._execute_lock = asyncio.Lock()
@property @property
def vboxmanage_path(self): def vboxmanage_path(self):
@ -82,34 +83,38 @@ class VirtualBox(BaseManager):
@asyncio.coroutine @asyncio.coroutine
def execute(self, subcommand, args, timeout=60): def execute(self, subcommand, args, timeout=60):
vboxmanage_path = self.vboxmanage_path # We use a lock prevent parallel execution due to strange errors
if not vboxmanage_path: # reported by a user and reproduced by us.
vboxmanage_path = self.find_vboxmanage() # https://github.com/GNS3/gns3-gui/issues/261
command = [vboxmanage_path, "--nologo", subcommand] with (yield from self._execute_lock):
command.extend(args) vboxmanage_path = self.vboxmanage_path
log.debug("Executing VBoxManage with command: {}".format(command)) if not vboxmanage_path:
try: vboxmanage_path = self.find_vboxmanage()
vbox_user = self.config.get_section_config("VirtualBox").get("vbox_user") command = [vboxmanage_path, "--nologo", subcommand]
if vbox_user: command.extend(args)
# TODO: test & review this part log.debug("Executing VBoxManage with command: {}".format(command))
sudo_command = "sudo -i -u {}".format(vbox_user) + " ".join(command) try:
process = yield from asyncio.create_subprocess_shell(sudo_command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) vbox_user = self.config.get_section_config("VirtualBox").get("vbox_user")
else: if vbox_user:
process = yield from asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) # TODO: test & review this part
except (OSError, subprocess.SubprocessError) as e: sudo_command = "sudo -i -u {}".format(vbox_user) + " ".join(command)
raise VirtualBoxError("Could not execute VBoxManage: {}".format(e)) process = yield from asyncio.create_subprocess_shell(sudo_command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
else:
process = yield from asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
except (OSError, subprocess.SubprocessError) as e:
raise VirtualBoxError("Could not execute VBoxManage: {}".format(e))
try: try:
stdout_data, stderr_data = yield from asyncio.wait_for(process.communicate(), timeout=timeout) stdout_data, stderr_data = yield from asyncio.wait_for(process.communicate(), timeout=timeout)
except asyncio.TimeoutError: except asyncio.TimeoutError:
raise VirtualBoxError("VBoxManage has timed out after {} seconds!".format(timeout)) raise VirtualBoxError("VBoxManage has timed out after {} seconds!".format(timeout))
if process.returncode: if process.returncode:
# only the first line of the output is useful # only the first line of the output is useful
vboxmanage_error = stderr_data.decode("utf-8", errors="ignore") vboxmanage_error = stderr_data.decode("utf-8", errors="ignore")
raise VirtualBoxError("VirtualBox has returned an error: {}".format(vboxmanage_error)) raise VirtualBoxError("VirtualBox has returned an error: {}".format(vboxmanage_error))
return stdout_data.decode("utf-8", errors="ignore").splitlines() return stdout_data.decode("utf-8", errors="ignore").splitlines()
@asyncio.coroutine @asyncio.coroutine
def get_list(self): def get_list(self):

View File

@ -105,9 +105,10 @@ class VirtualBoxVM(BaseVM):
results = yield from self.manager.execute("showvminfo", [self._vmname, "--machinereadable"]) results = yield from self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
for info in results: for info in results:
name, value = info.split('=', 1) if '=' in info:
if name == "VMState": name, value = info.split('=', 1)
return value.strip('"') if name == "VMState":
return value.strip('"')
raise VirtualBoxError("Could not get VM state for {}".format(self._vmname)) raise VirtualBoxError("Could not get VM state for {}".format(self._vmname))
@asyncio.coroutine @asyncio.coroutine
@ -139,6 +140,8 @@ class VirtualBoxVM(BaseVM):
def create(self): def create(self):
yield from self._get_system_properties() yield from self._get_system_properties()
if "API version" not in self._system_properties:
raise VirtualBoxError("Can't access to VirtualBox API Version")
if parse_version(self._system_properties["API version"]) < parse_version("4_3"): if parse_version(self._system_properties["API version"]) < parse_version("4_3"):
raise VirtualBoxError("The VirtualBox API version is lower than 4.3") raise VirtualBoxError("The VirtualBox API version is lower than 4.3")
log.info("VirtualBox VM '{name}' [{id}] created".format(name=self.name, id=self.id)) log.info("VirtualBox VM '{name}' [{id}] created".format(name=self.name, id=self.id))

View File

@ -62,9 +62,9 @@ class VPCS(BaseManager):
""" """
vm = self.get_vm(vm_id) vm = self.get_vm(vm_id)
i = self._used_mac_ids[vm_id]
self._free_mac_ids[vm.project.id].insert(0, i)
if vm_id in self._used_mac_ids: if vm_id in self._used_mac_ids:
i = self._used_mac_ids[vm_id]
self._free_mac_ids[vm.project.id].insert(0, i)
del self._used_mac_ids[vm_id] del self._used_mac_ids[vm_id]
yield from super().close_vm(vm_id, *args, **kwargs) yield from super().close_vm(vm_id, *args, **kwargs)
return vm return vm

View File

@ -70,8 +70,12 @@ IOU_CREATE_SCHEMA = {
"description": "Use default IOU values", "description": "Use default IOU values",
"type": ["boolean", "null"] "type": ["boolean", "null"]
}, },
"initial_config": {
"description": "Path to the initial configuration of IOU",
"type": ["string", "null"]
},
"initial_config_content": { "initial_config_content": {
"description": "Initial configuration of the IOU", "description": "Initial configuration of IOU",
"type": ["string", "null"] "type": ["string", "null"]
}, },
"iourc_content": { "iourc_content": {
@ -123,8 +127,12 @@ IOU_UPDATE_SCHEMA = {
"description": "Always up ethernet interface", "description": "Always up ethernet interface",
"type": ["boolean", "null"] "type": ["boolean", "null"]
}, },
"initial_config": {
"description": "Path to the initial configuration of IOU",
"type": ["string", "null"]
},
"initial_config_content": { "initial_config_content": {
"description": "Initial configuration of the IOU", "description": "Initial configuration of IOU",
"type": ["string", "null"] "type": ["string", "null"]
}, },
"use_default_iou_values": { "use_default_iou_values": {

View File

@ -17,7 +17,9 @@
import pytest import pytest
from unittest.mock import patch
import uuid import uuid
import os
from gns3server.modules.iou import IOU from gns3server.modules.iou import IOU
@ -25,6 +27,15 @@ from gns3server.modules.iou.iou_error import IOUError
from gns3server.modules.project_manager import ProjectManager from gns3server.modules.project_manager import ProjectManager
@pytest.fixture(scope="function")
def iou(port_manager):
# Cleanup the IOU object
IOU._instance = None
iou = IOU.instance()
iou.port_manager = port_manager
return iou
def test_get_application_id(loop, project, port_manager): def test_get_application_id(loop, project, port_manager):
# Cleanup the IOU object # Cleanup the IOU object
IOU._instance = None IOU._instance = None
@ -71,3 +82,8 @@ def test_get_application_id_no_id_available(loop, project, port_manager):
vm_id = str(uuid.uuid4()) vm_id = str(uuid.uuid4())
loop.run_until_complete(iou.create_vm("PC {}".format(i), project.id, vm_id)) loop.run_until_complete(iou.create_vm("PC {}".format(i), project.id, vm_id))
assert iou.get_application_id(vm_id) == i assert iou.get_application_id(vm_id) == i
def test_get_images_directory(iou, tmpdir):
with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}):
assert iou.get_images_directory() == str(tmpdir / "IOU")

View File

@ -82,7 +82,8 @@ def test_vm(project, manager):
def test_vm_initial_config(project, manager): def test_vm_initial_config(project, manager):
vm = IOUVM("test", "00010203-0405-0607-0808-0a0b0c0d0e0f", project, manager, initial_config="hostname %h") vm = IOUVM("test", "00010203-0405-0607-0808-0a0b0c0d0e0f", project, manager)
vm.initial_config = "hostname %h"
assert vm.name == "test" assert vm.name == "test"
assert vm.initial_config == "hostname test" assert vm.initial_config == "hostname test"
assert vm.id == "00010203-0405-0607-0808-0a0b0c0d0e0f" assert vm.id == "00010203-0405-0607-0808-0a0b0c0d0e0f"

View File

@ -22,6 +22,15 @@ from unittest.mock import patch
from gns3server.modules.vpcs import VPCS from gns3server.modules.vpcs import VPCS
from gns3server.modules.iou import IOU
@pytest.fixture(scope="function")
def iou(port_manager):
IOU._instance = None
iou = IOU.instance()
iou.port_manager = port_manager
return iou
def test_create_vm_new_topology(loop, project, port_manager): def test_create_vm_new_topology(loop, project, port_manager):
@ -82,3 +91,33 @@ def test_create_vm_old_topology(loop, project, tmpdir, port_manager):
vm_dir = os.path.join(project_dir, "project-files", "vpcs", vm.id) vm_dir = os.path.join(project_dir, "project-files", "vpcs", vm.id)
with open(os.path.join(vm_dir, "startup.vpc")) as f: with open(os.path.join(vm_dir, "startup.vpc")) as f:
assert f.read() == "1" assert f.read() == "1"
def test_get_abs_image_path(iou, tmpdir):
os.makedirs(str(tmpdir / "IOU"))
path1 = str(tmpdir / "test1.bin")
open(path1, 'w+').close()
path2 = str(tmpdir / "IOU" / "test2.bin")
open(path2, 'w+').close()
with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}):
assert iou.get_abs_image_path(path1) == path1
assert iou.get_abs_image_path(path2) == path2
assert iou.get_abs_image_path("test2.bin") == path2
assert iou.get_abs_image_path("../test1.bin") == path1
def test_get_relative_image_path(iou, tmpdir):
os.makedirs(str(tmpdir / "IOU"))
path1 = str(tmpdir / "test1.bin")
open(path1, 'w+').close()
path2 = str(tmpdir / "IOU" / "test2.bin")
open(path2, 'w+').close()
with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}):
assert iou.get_relative_image_path(path1) == path1
assert iou.get_relative_image_path(path2) == "test2.bin"
assert iou.get_relative_image_path("test2.bin") == "test2.bin"
assert iou.get_relative_image_path("../test1.bin") == path1