mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-11 17:35:53 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42a8c7147a | ||
|
|
a0fe9bb498 | ||
|
|
57f9d875ca | ||
|
|
a2e51ac090 | ||
|
|
887f9b298e | ||
|
|
d99047ce72 | ||
|
|
ad27fdf8b9 | ||
|
|
9df290f192 | ||
|
|
05aafb9538 | ||
|
|
da72a9501a | ||
|
|
a2dfeab315 | ||
|
|
16cad8426a | ||
|
|
0476f2932e | ||
|
|
91c0f05a4e | ||
|
|
74ee73581a | ||
|
|
a86bac4214 | ||
|
|
8abf22ef24 | ||
|
|
7cad25eb1a | ||
|
|
ecf4e91e55 | ||
|
|
ea67f4aeb9 | ||
|
|
c98bcedd39 | ||
|
|
528bb7a7c6 | ||
|
|
d31420b3e1 | ||
|
|
50d7a4f335 | ||
|
|
4216724d0b | ||
|
|
c03c66ec48 | ||
|
|
dfd18f9483 | ||
|
|
8636d3e337 | ||
|
|
c43b26d787 | ||
|
|
08f82e02a0 | ||
|
|
33bca1a85c | ||
|
|
4d50d00b3e | ||
|
|
21cc41fd16 | ||
|
|
f8d95291fa | ||
|
|
9fa873751d | ||
|
|
8c9758d16b | ||
|
|
0c5b753211 | ||
|
|
221a35baae | ||
|
|
5bb870dc0f | ||
|
|
76be91d544 |
No files matched your search
@@ -1,5 +1,23 @@
|
||||
# Change Log
|
||||
|
||||
## 1.3.4 02/06/15
|
||||
|
||||
* Drop useless dependencie dateutil
|
||||
* Check if port or adapter is connected before starting/stopping a packet capture. Fixes #196.
|
||||
* Prevent users to add links to running Qemu VMs and start a capture on running VirtualBox VMs.
|
||||
* Fixes bug: couldn't set PCMCIA disk1 size for IOS routers.
|
||||
* Fix crash if you pass an invalid hostname
|
||||
* Catch VPCS kill errors
|
||||
* Raise a VirtualBox error if adapter doesn't exists
|
||||
* Ignore VirtualBox VM Name with a carriage return in name
|
||||
* Cleanup the temporary project after modules have been notified of the path change
|
||||
* Do not return error if we can't remove the old project directory
|
||||
* Catch encoding errors in windows logger
|
||||
* Use setter for the qemu_path (allow to pass only the binary name)
|
||||
* Fixes TAP connection when using VPCS.
|
||||
* Fix crash launching qemu on OSX from another location.
|
||||
* Adds NAT NIO in device schema validation so they can return an error that it is not supported.
|
||||
|
||||
## 1.3.3 14/05/15
|
||||
|
||||
* Check for empty iourc path.
|
||||
|
||||
@@ -6,4 +6,3 @@ pep8==1.5.7
|
||||
pytest-timeout
|
||||
pytest-capturelog
|
||||
pytest-cov
|
||||
python-coveralls
|
||||
@@ -0,0 +1,2 @@
|
||||
SET PYTHONPATH=.
|
||||
python.exe gns3server/main.py --debug --local
|
||||
@@ -20,6 +20,7 @@ import sys
|
||||
import struct
|
||||
import platform
|
||||
|
||||
|
||||
try:
|
||||
import raven
|
||||
RAVEN_AVAILABLE = True
|
||||
@@ -34,13 +35,23 @@ import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Dev build
|
||||
if __version__[4] != 0:
|
||||
import faulthandler
|
||||
|
||||
# Display a traceback in case of segfault crash. Usefull when frozen
|
||||
# Not enabled by default for security reason
|
||||
log.info("Enable catching segfault")
|
||||
faulthandler.enable()
|
||||
|
||||
|
||||
class CrashReport:
|
||||
|
||||
"""
|
||||
Report crash to a third party service
|
||||
"""
|
||||
|
||||
DSN = "sync+https://45147533567b4d529ca09c093758681f:12d8b456cdb34d23aba771325aa64ee6@app.getsentry.com/38482"
|
||||
DSN = "sync+https://9e6f04df72c74b6894a6dcd2928d069e:2035d1beb1654136b170f1e91f05ee51@app.getsentry.com/38482"
|
||||
if hasattr(sys, "frozen"):
|
||||
cacert = os.path.join(os.getcwd(), "cacert.pem")
|
||||
if os.path.isfile(cacert):
|
||||
|
||||
@@ -84,9 +84,11 @@ class ProjectHandler:
|
||||
project.name = request.json.get("name", project.name)
|
||||
project_path = request.json.get("path", project.path)
|
||||
if project_path != project.path:
|
||||
old_path = project.path
|
||||
project.path = project_path
|
||||
for module in MODULES:
|
||||
yield from module.instance().project_moved(project)
|
||||
yield from project.clean_old_path(old_path)
|
||||
# Very important we need to remove temporary flag after moving the project
|
||||
project.temporary = request.json.get("temporary", project.temporary)
|
||||
response.json(project)
|
||||
|
||||
@@ -344,7 +344,7 @@ class VirtualBoxHandler:
|
||||
vm = vbox_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
vm.start_capture(adapter_number, pcap_file_path)
|
||||
yield from vm.start_capture(adapter_number, pcap_file_path)
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
|
||||
+7
-1
@@ -186,7 +186,13 @@ def main():
|
||||
Project.clean_project_directory()
|
||||
|
||||
CrashReport.instance()
|
||||
host = server_config["host"]
|
||||
|
||||
try:
|
||||
host = server_config["host"].encode("idna").decode()
|
||||
except UnicodeError:
|
||||
log.critical("Invalid hostname %s", server_config["host"])
|
||||
return
|
||||
|
||||
port = int(server_config["port"])
|
||||
server = Server.instance(host, port)
|
||||
try:
|
||||
|
||||
@@ -298,6 +298,9 @@ class EthernetSwitch(Device):
|
||||
|
||||
nio = self._nios[port_number]
|
||||
|
||||
if not nio:
|
||||
raise DynamipsError("Port {} is not connected".format(port_number))
|
||||
|
||||
data_link_type = data_link_type.lower()
|
||||
if data_link_type.startswith("dlt_"):
|
||||
data_link_type = data_link_type[4:]
|
||||
@@ -324,6 +327,10 @@ class EthernetSwitch(Device):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._nios[port_number]
|
||||
|
||||
if not nio:
|
||||
raise DynamipsError("Port {} is not connected".format(port_number))
|
||||
|
||||
yield from nio.unbind_filter("both")
|
||||
log.info('Ethernet switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
||||
@@ -836,7 +836,7 @@ class Router(BaseVM):
|
||||
return self._disk1
|
||||
|
||||
@asyncio.coroutine
|
||||
def disk1(self, disk1):
|
||||
def set_disk1(self, disk1):
|
||||
"""
|
||||
Sets the size (MB) for PCMCIA disk1.
|
||||
|
||||
@@ -1303,6 +1303,10 @@ class Router(BaseVM):
|
||||
|
||||
nio = adapter.get_nio(port_number)
|
||||
|
||||
if not nio:
|
||||
raise DynamipsError("Port {slot_number}/{port_number} is not connected".format(slot_number=slot_number,
|
||||
port_number=port_number))
|
||||
|
||||
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
|
||||
raise DynamipsError("Port {port_number} has already a filter applied on {adapter}".format(adapter=adapter,
|
||||
port_number=port_number))
|
||||
@@ -1335,6 +1339,11 @@ class Router(BaseVM):
|
||||
port_number=port_number))
|
||||
|
||||
nio = adapter.get_nio(port_number)
|
||||
|
||||
if not nio:
|
||||
raise DynamipsError("Port {slot_number}/{port_number} is not connected".format(slot_number=slot_number,
|
||||
port_number=port_number))
|
||||
|
||||
yield from nio.unbind_filter("both")
|
||||
|
||||
log.info('Router "{name}" [{id}]: stopping packet capture on port {slot_number}/{port_number}'.format(name=self._name,
|
||||
|
||||
@@ -146,13 +146,17 @@ class Project:
|
||||
self._path = path
|
||||
self._update_temporary_file()
|
||||
|
||||
# The order of operation is important because we want to avoid losing
|
||||
# data
|
||||
if old_path:
|
||||
@asyncio.coroutine
|
||||
def clean_old_path(self, old_path):
|
||||
"""
|
||||
Called after a project location change. All the modules should
|
||||
have been notified before
|
||||
"""
|
||||
if self._temporary:
|
||||
try:
|
||||
shutil.rmtree(old_path)
|
||||
yield from wait_run_in_executor(shutil.rmtree, old_path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Can't remove temporary directory {}: {}".format(old_path, e))
|
||||
log.warn("Can't remove temporary directory {}: {}".format(old_path, e))
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
@@ -47,13 +47,13 @@ class Qemu(BaseManager):
|
||||
"""
|
||||
|
||||
qemus = []
|
||||
paths = []
|
||||
paths = set()
|
||||
try:
|
||||
paths.append(os.getcwd())
|
||||
paths.add(os.getcwd())
|
||||
except FileNotFoundError:
|
||||
log.warning("The current working directory doesn't exist")
|
||||
if "PATH" in os.environ:
|
||||
paths.extend(os.environ["PATH"].split(os.pathsep))
|
||||
paths.update(os.environ["PATH"].split(os.pathsep))
|
||||
else:
|
||||
log.warning("The PATH environment variable doesn't exist")
|
||||
# look for Qemu binaries in the current working directory and $PATH
|
||||
@@ -64,17 +64,21 @@ class Qemu(BaseManager):
|
||||
exec_dir = os.path.dirname(os.path.abspath(sys.executable))
|
||||
for f in os.listdir(exec_dir):
|
||||
if f.lower().startswith("qemu"):
|
||||
paths.append(os.path.join(exec_dir, f))
|
||||
paths.add(os.path.join(exec_dir, f))
|
||||
|
||||
if "PROGRAMFILES(X86)" in os.environ and os.path.exists(os.environ["PROGRAMFILES(X86)"]):
|
||||
paths.append(os.path.join(os.environ["PROGRAMFILES(X86)"], "qemu"))
|
||||
paths.add(os.path.join(os.environ["PROGRAMFILES(X86)"], "qemu"))
|
||||
if "PROGRAMFILES" in os.environ and os.path.exists(os.environ["PROGRAMFILES"]):
|
||||
paths.append(os.path.join(os.environ["PROGRAMFILES"], "qemu"))
|
||||
paths.add(os.path.join(os.environ["PROGRAMFILES"], "qemu"))
|
||||
elif sys.platform.startswith("darwin"):
|
||||
# add specific locations on Mac OS X regardless of what's in $PATH
|
||||
paths.extend(["/usr/local/bin", "/opt/local/bin"])
|
||||
paths.update(["/usr/local/bin", "/opt/local/bin"])
|
||||
if hasattr(sys, "frozen"):
|
||||
paths.append(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/")))
|
||||
try:
|
||||
paths.add(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/")))
|
||||
# If the user run the server by hand from outside
|
||||
except FileNotFoundError:
|
||||
paths.add("/Applications/GNS3.app/Contents/Resources/qemu/bin")
|
||||
for path in paths:
|
||||
try:
|
||||
for f in os.listdir(path):
|
||||
|
||||
@@ -69,7 +69,7 @@ class QemuVM(BaseVM):
|
||||
self._stdout_file = ""
|
||||
|
||||
# QEMU VM settings
|
||||
self._qemu_path = qemu_path
|
||||
self.qemu_path = qemu_path
|
||||
self._hda_disk_image = ""
|
||||
self._hdb_disk_image = ""
|
||||
self._hdc_disk_image = ""
|
||||
@@ -741,6 +741,8 @@ class QemuVM(BaseVM):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
if self.is_running():
|
||||
raise QemuError("Sorry, adding a link to a started Qemu VM is not supported.")
|
||||
# FIXME: does the code below work? very undocumented feature...
|
||||
# dynamically configure an UDP tunnel on the QEMU VM adapter
|
||||
if nio and isinstance(nio, NIOUDP):
|
||||
if self._legacy_networking:
|
||||
@@ -751,7 +753,6 @@ class QemuVM(BaseVM):
|
||||
nio.rport,
|
||||
nio.rhost))
|
||||
else:
|
||||
# FIXME: does it work? very undocumented feature...
|
||||
# Apparently there is a bug in Qemu...
|
||||
# netdev_add [user|tap|socket|hubport|netmap],id=str[,prop=value][,...] -- add host network device
|
||||
# netdev_del id -- remove host network device
|
||||
@@ -785,6 +786,7 @@ class QemuVM(BaseVM):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
if self.is_running():
|
||||
# FIXME: does the code below work? very undocumented feature...
|
||||
# dynamically disable the QEMU VM adapter
|
||||
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_number, adapter_number))
|
||||
yield from self._control_vm("host_net_add user vlan={},name=gns3-{}".format(adapter_number, adapter_number))
|
||||
|
||||
@@ -168,6 +168,8 @@ class VirtualBox(BaseManager):
|
||||
vms = []
|
||||
result = yield from self.execute("list", ["vms"])
|
||||
for line in result:
|
||||
if line[0] != '"' or line[-1:] != "}":
|
||||
continue # Broken output (perhaps a carriage return in VM name
|
||||
vmname, _ = line.rsplit(' ', 1)
|
||||
vmname = vmname.strip('"')
|
||||
if vmname == "<inaccessible>":
|
||||
|
||||
@@ -799,18 +799,22 @@ class VirtualBoxVM(BaseVM):
|
||||
|
||||
try:
|
||||
adapter = self._ethernet_adapters[adapter_number]
|
||||
except IndexError:
|
||||
except KeyError:
|
||||
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
vm_state = yield from self._get_vm_state()
|
||||
if vm_state == "running":
|
||||
# dynamically configure an UDP tunnel on the VirtualBox adapter
|
||||
yield from self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1))
|
||||
yield from self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
||||
yield from self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
||||
yield from self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
if isinstance(nio, NIOUDP):
|
||||
# dynamically configure an UDP tunnel on the VirtualBox adapter
|
||||
yield from self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1))
|
||||
yield from self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
||||
yield from self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
||||
yield from self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
elif isinstance(nio, NIONAT):
|
||||
yield from self._control_vm("nic{} nat".format(adapter_number + 1))
|
||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
|
||||
adapter.add_nio(0, nio)
|
||||
log.info("VirtualBox VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name,
|
||||
@@ -830,7 +834,7 @@ class VirtualBoxVM(BaseVM):
|
||||
|
||||
try:
|
||||
adapter = self._ethernet_adapters[adapter_number]
|
||||
except IndexError:
|
||||
except KeyError:
|
||||
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@@ -851,6 +855,7 @@ class VirtualBoxVM(BaseVM):
|
||||
adapter_number=adapter_number))
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
@@ -861,11 +866,19 @@ class VirtualBoxVM(BaseVM):
|
||||
|
||||
try:
|
||||
adapter = self._ethernet_adapters[adapter_number]
|
||||
except IndexError:
|
||||
except KeyError:
|
||||
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
vm_state = yield from self._get_vm_state()
|
||||
if vm_state == "running" or vm_state == "paused" or vm_state == "stuck":
|
||||
raise VirtualBoxError("Sorry, packet capturing on a started VirtualBox VM is not supported.")
|
||||
|
||||
nio = adapter.get_nio(0)
|
||||
|
||||
if not nio:
|
||||
raise VirtualBoxError("Adapter {} is not connected".format(adapter_number))
|
||||
|
||||
if nio.capturing:
|
||||
raise VirtualBoxError("Packet capture is already activated on adapter {adapter_number}".format(adapter_number=adapter_number))
|
||||
|
||||
@@ -883,11 +896,15 @@ class VirtualBoxVM(BaseVM):
|
||||
|
||||
try:
|
||||
adapter = self._ethernet_adapters[adapter_number]
|
||||
except IndexError:
|
||||
except KeyError:
|
||||
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
nio = adapter.get_nio(0)
|
||||
|
||||
if not nio:
|
||||
raise VirtualBoxError("Adapter {} is not connected".format(adapter_number))
|
||||
|
||||
nio.stopPacketCapture()
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
|
||||
@@ -254,7 +254,10 @@ class VPCSVM(BaseVM):
|
||||
except asyncio.TimeoutError:
|
||||
if self._process.returncode is None:
|
||||
log.warn("VPCS process {} is still running... killing it".format(self._process.pid))
|
||||
self._process.kill()
|
||||
try:
|
||||
self._process.kill()
|
||||
except OSError as e:
|
||||
raise VPCSError("Can not stop the VPCS process: {}".format(e))
|
||||
|
||||
self._process = None
|
||||
self._started = False
|
||||
@@ -390,6 +393,9 @@ class VPCSVM(BaseVM):
|
||||
|
||||
command = [self.vpcs_path]
|
||||
command.extend(["-p", str(self._console)]) # listen to console port
|
||||
command.extend(["-m", str(self._manager.get_mac_id(self.id))]) # the unique ID is used to set the MAC address offset
|
||||
command.extend(["-i", "1"]) # option to start only one VPC instance
|
||||
command.extend(["-F"]) # option to avoid the daemonization of VPCS
|
||||
|
||||
nio = self._ethernet_adapter.get_nio(0)
|
||||
if nio:
|
||||
@@ -404,10 +410,6 @@ class VPCSVM(BaseVM):
|
||||
command.extend(["-e"])
|
||||
command.extend(["-d", nio.tap_device])
|
||||
|
||||
command.extend(["-m", str(self._manager.get_mac_id(self.id))]) # the unique ID is used to set the MAC address offset
|
||||
command.extend(["-i", "1"]) # option to start only one VPC instance
|
||||
command.extend(["-F"]) # option to avoid the daemonization of VPCS
|
||||
|
||||
if self.script_file:
|
||||
command.extend([os.path.basename(self.script_file)])
|
||||
return command
|
||||
|
||||
@@ -218,6 +218,16 @@ DEVICE_NIO_SCHEMA = {
|
||||
"required": ["type", "ethernet_device"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
"NAT": {
|
||||
"description": "NAT Network Input/Output",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": ["nio_nat"]
|
||||
},
|
||||
},
|
||||
"required": ["type"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
"TAP": {
|
||||
"description": "TAP Network Input/Output",
|
||||
"properties": {
|
||||
@@ -291,6 +301,7 @@ DEVICE_NIO_SCHEMA = {
|
||||
{"$ref": "#/definitions/UDP"},
|
||||
{"$ref": "#/definitions/Ethernet"},
|
||||
{"$ref": "#/definitions/LinuxEthernet"},
|
||||
{"$ref": "#/definitions/NAT"},
|
||||
{"$ref": "#/definitions/TAP"},
|
||||
{"$ref": "#/definitions/UNIX"},
|
||||
{"$ref": "#/definitions/VDE"},
|
||||
|
||||
@@ -23,5 +23,5 @@
|
||||
# or negative for a release candidate or beta (after the base version
|
||||
# number has been incremented)
|
||||
|
||||
__version__ = "1.3.3"
|
||||
__version_info__ = (1, 3, 3, 0)
|
||||
__version__ = "1.3.4"
|
||||
__version_info__ = (1, 3, 4, 0)
|
||||
@@ -81,12 +81,30 @@ class ColouredStreamHandler(logging.StreamHandler):
|
||||
self.handleError(record)
|
||||
|
||||
|
||||
class WinStreamHandler(logging.StreamHandler):
|
||||
|
||||
def emit(self, record):
|
||||
|
||||
if sys.stdin.encoding != "utf-8":
|
||||
record = record
|
||||
|
||||
stream = self.stream
|
||||
try:
|
||||
msg = self.formatter.format(record, stream.isatty())
|
||||
stream.write(msg.encode(stream.encoding, errors="replace").decode(stream.encoding))
|
||||
stream.write(self.terminator)
|
||||
self.flush()
|
||||
pass
|
||||
except Exception:
|
||||
self.handleError(record)
|
||||
|
||||
|
||||
def init_logger(level, logfile=None, quiet=False):
|
||||
if logfile and len(logfile) > 0:
|
||||
stream_handler = logging.FileHandler(logfile)
|
||||
stream_handler.formatter = ColouredFormatter("{asctime} {levelname} {filename}:{lineno} {message}", "%Y-%m-%d %H:%M:%S", "{")
|
||||
elif sys.platform.startswith("win"):
|
||||
stream_handler = logging.StreamHandler(sys.stdout)
|
||||
stream_handler = WinStreamHandler(sys.stdout)
|
||||
stream_handler.formatter = ColouredFormatter("{asctime} {levelname} {filename}:{lineno} {message}", "%Y-%m-%d %H:%M:%S", "{")
|
||||
else:
|
||||
stream_handler = ColouredStreamHandler(sys.stdout)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
netifaces==0.10.4
|
||||
jsonschema==2.4.0
|
||||
python-dateutil==2.3
|
||||
aiohttp==0.14.4
|
||||
Jinja2==2.7.3
|
||||
raven==5.2.0
|
||||
@@ -15,6 +15,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
|
||||
def test_udp_allocation(server, project):
|
||||
response = server.post('/projects/{}/ports/udp'.format(project.id), {}, example=True)
|
||||
@@ -22,6 +25,8 @@ def test_udp_allocation(server, project):
|
||||
assert response.json == {'udp_port': 10000}
|
||||
|
||||
|
||||
# Netfifaces is not available on Travis
|
||||
@pytest.mark.skipif(os.environ.get("TRAVIS", False) is not False, reason="Not supported on Travis")
|
||||
def test_interfaces(server):
|
||||
response = server.get('/interfaces', example=True)
|
||||
assert response.status == 200
|
||||
|
||||
@@ -20,6 +20,7 @@ This test suite check /project endpoint
|
||||
"""
|
||||
|
||||
import uuid
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
@@ -85,18 +86,43 @@ def test_update_temporary_project(server):
|
||||
assert response.json["temporary"] is False
|
||||
|
||||
|
||||
def test_update_path_project(server, tmpdir):
|
||||
def test_update_path_project_temporary(server, tmpdir):
|
||||
|
||||
os.makedirs(str(tmpdir / "a"))
|
||||
os.makedirs(str(tmpdir / "b"))
|
||||
|
||||
with patch("gns3server.modules.project.Project.is_local", return_value=True):
|
||||
response = server.post("/projects", {"name": "first_name"})
|
||||
response = server.post("/projects", {"name": "first_name", "path": str(tmpdir / "a"), "temporary": True})
|
||||
assert response.status == 201
|
||||
assert response.json["name"] == "first_name"
|
||||
query = {"name": "second_name", "path": str(tmpdir)}
|
||||
query = {"name": "second_name", "path": str(tmpdir / "b")}
|
||||
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
|
||||
assert response.status == 200
|
||||
assert response.json["path"] == str(tmpdir)
|
||||
assert response.json["path"] == str(tmpdir / "b")
|
||||
assert response.json["name"] == "second_name"
|
||||
|
||||
assert not os.path.exists(str(tmpdir / "a"))
|
||||
assert os.path.exists(str(tmpdir / "b"))
|
||||
|
||||
|
||||
def test_update_path_project_non_temporary(server, tmpdir):
|
||||
|
||||
os.makedirs(str(tmpdir / "a"))
|
||||
os.makedirs(str(tmpdir / "b"))
|
||||
|
||||
with patch("gns3server.modules.project.Project.is_local", return_value=True):
|
||||
response = server.post("/projects", {"name": "first_name", "path": str(tmpdir / "a")})
|
||||
assert response.status == 201
|
||||
assert response.json["name"] == "first_name"
|
||||
query = {"name": "second_name", "path": str(tmpdir / "b")}
|
||||
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
|
||||
assert response.status == 200
|
||||
assert response.json["path"] == str(tmpdir / "b")
|
||||
assert response.json["name"] == "second_name"
|
||||
|
||||
assert os.path.exists(str(tmpdir / "a"))
|
||||
assert os.path.exists(str(tmpdir / "b"))
|
||||
|
||||
|
||||
def test_update_path_project_non_local(server, tmpdir):
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ def test_vpcs_get(server, project, vm):
|
||||
assert response.route == "/projects/{project_id}/vpcs/vms/{vm_id}"
|
||||
assert response.json["name"] == "PC TEST 1"
|
||||
assert response.json["project_id"] == project.id
|
||||
assert response.json["startup_script_path"] == None
|
||||
assert response.json["startup_script_path"] is None
|
||||
|
||||
|
||||
def test_vpcs_create_startup_script(server, project):
|
||||
@@ -51,7 +51,7 @@ def test_vpcs_create_startup_script(server, project):
|
||||
assert response.route == "/projects/{project_id}/vpcs/vms"
|
||||
assert response.json["name"] == "PC TEST 1"
|
||||
assert response.json["project_id"] == project.id
|
||||
assert response.json["startup_script"] == "ip 192.168.1.2\necho TEST"
|
||||
assert response.json["startup_script"] == os.linesep.join(["ip 192.168.1.2", "echo TEST"])
|
||||
assert response.json["startup_script_path"] == "startup.vpc"
|
||||
|
||||
|
||||
|
||||
@@ -73,10 +73,6 @@ def test_changing_path_temporary_flag(tmpdir):
|
||||
assert os.path.exists(os.path.join(p.path, ".gns3_temporary"))
|
||||
|
||||
p.path = str(tmpdir)
|
||||
p.temporary = False
|
||||
assert not os.path.exists(os.path.join(p.path, ".gns3_temporary"))
|
||||
assert not os.path.exists(os.path.join(str(tmpdir), ".gns3_temporary"))
|
||||
assert not os.path.exists(original_path)
|
||||
|
||||
|
||||
def test_temporary_path():
|
||||
|
||||
@@ -20,10 +20,14 @@ import pytest
|
||||
import tempfile
|
||||
import os
|
||||
import stat
|
||||
import asyncio
|
||||
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from gns3server.modules.virtualbox import VirtualBox
|
||||
from gns3server.modules.virtualbox.virtualbox_error import VirtualBoxError
|
||||
from unittest.mock import patch
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
@@ -65,3 +69,31 @@ def test_vboxmanage_path(manager, tmpdir):
|
||||
tmpfile = tempfile.NamedTemporaryFile()
|
||||
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": path}):
|
||||
assert manager.find_vboxmanage() == path
|
||||
|
||||
|
||||
def test_get_list(manager, loop):
|
||||
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||
'"Carriage',
|
||||
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||
'"<inaccessible>" {42b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||
'"Linux Microcore 4.7.1" {ccd8c50b-c172-457d-99fa-dd69371ede0e}'
|
||||
]
|
||||
|
||||
@asyncio.coroutine
|
||||
def execute_mock(cmd, args):
|
||||
if cmd == "list":
|
||||
return vm_list
|
||||
else:
|
||||
if args[0] == "Windows 8.1":
|
||||
return ["memory=512"]
|
||||
elif args[0] == "Linux Microcore 4.7.1":
|
||||
return ["memory=256"]
|
||||
assert False, "Unknow {} {}".format(cmd, args)
|
||||
|
||||
with asyncio_patch("gns3server.modules.virtualbox.VirtualBox.execute") as mock:
|
||||
mock.side_effect = execute_mock
|
||||
vms = loop.run_until_complete(asyncio.async(manager.get_list()))
|
||||
assert vms == [
|
||||
{"vmname": "Windows 8.1", "ram": 512},
|
||||
{"vmname": "Linux Microcore 4.7.1", "ram": 256}
|
||||
]
|
||||
@@ -54,3 +54,9 @@ def test_vm_invalid_virtualbox_api_version(loop, project, manager):
|
||||
with pytest.raises(VirtualBoxError):
|
||||
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
|
||||
loop.run_until_complete(asyncio.async(vm.create()))
|
||||
|
||||
|
||||
def test_vm_adapter_add_nio_binding_adapter_not_exist(loop, vm, manager, free_console_port):
|
||||
nio = manager.create_nio(manager.vboxmanage_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "192.168.1.2"})
|
||||
with pytest.raises(VirtualBoxError):
|
||||
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(15, nio)))
|
||||
@@ -190,9 +190,9 @@ def test_update_startup_script_h(vm):
|
||||
|
||||
|
||||
def test_get_startup_script(vm):
|
||||
content = "echo GNS3 VPCS\nip 192.168.1.2\n"
|
||||
content = "echo GNS3 VPCS\nip 192.168.1.2"
|
||||
vm.startup_script = content
|
||||
assert vm.startup_script == content
|
||||
assert vm.startup_script == os.linesep.join(["echo GNS3 VPCS","ip 192.168.1.2"])
|
||||
|
||||
|
||||
def test_get_startup_script_using_default_script(vm):
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2014 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from gns3server.utils.interfaces import interfaces
|
||||
|
||||
|
||||
def test_interfaces():
|
||||
# This test should pass on all platforms without crash
|
||||
assert isinstance(interfaces(), list)
|
||||
Reference in new issue
Block a user