mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-04 14:31:13 +02:00
Merge branch 'master' into unstable
This commit is contained in:
commit
00fa1cc370
27 files changed
+307
-116
No files matched your search
@@ -34,6 +34,7 @@ from .project_manager import ProjectManager
|
||||
|
||||
from .nios.nio_udp import NIOUDP
|
||||
from .nios.nio_tap import NIOTAP
|
||||
from .nios.nio_nat import NIONAT
|
||||
from .nios.nio_generic_ethernet import NIOGenericEthernet
|
||||
|
||||
|
||||
@@ -370,6 +371,8 @@ class BaseManager:
|
||||
nio = NIOTAP(tap_device)
|
||||
elif nio_settings["type"] == "nio_generic_ethernet":
|
||||
nio = NIOGenericEthernet(nio_settings["ethernet_device"])
|
||||
elif nio_settings["type"] == "nio_nat":
|
||||
nio = NIONAT()
|
||||
assert nio is not None
|
||||
return nio
|
||||
|
||||
@@ -386,7 +389,16 @@ class BaseManager:
|
||||
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))
|
||||
path = os.path.normpath(os.path.join(img_directory, *s))
|
||||
|
||||
# Compatibility with old topologies we look in parent directory
|
||||
# We look at first in new location
|
||||
if not os.path.exists(path):
|
||||
old_path = os.path.normpath(os.path.join(img_directory, '..', *s))
|
||||
if os.path.exists(old_path):
|
||||
return old_path
|
||||
|
||||
return path
|
||||
return path
|
||||
|
||||
def get_relative_image_path(self, path):
|
||||
|
||||
@@ -410,6 +410,8 @@ class Dynamips(BaseManager):
|
||||
nio = NIOVDE(node.hypervisor, control_file, local_file)
|
||||
elif nio_settings["type"] == "nio_null":
|
||||
nio = NIONull(node.hypervisor)
|
||||
else:
|
||||
raise aiohttp.web.HTTPConflict(text="NIO of type {} is not supported".format(nio_settings["type"]))
|
||||
|
||||
yield from nio.create()
|
||||
return nio
|
||||
@@ -471,31 +473,42 @@ class Dynamips(BaseManager):
|
||||
if hasattr(vm, "set_{}".format(name)):
|
||||
setter = getattr(vm, "set_{}".format(name))
|
||||
yield from setter(value)
|
||||
|
||||
elif name.startswith("slot") and value in ADAPTER_MATRIX:
|
||||
slot_id = int(name[-1])
|
||||
adapter_name = value
|
||||
adapter = ADAPTER_MATRIX[adapter_name]()
|
||||
if vm.slots[slot_id] and not isinstance(vm.slots[slot_id], type(adapter)):
|
||||
yield from vm.slot_remove_binding(slot_id)
|
||||
if not isinstance(vm.slots[slot_id], type(adapter)):
|
||||
yield from vm.slot_add_binding(slot_id, adapter)
|
||||
try:
|
||||
if vm.slots[slot_id] and not isinstance(vm.slots[slot_id], type(adapter)):
|
||||
yield from vm.slot_remove_binding(slot_id)
|
||||
if not isinstance(vm.slots[slot_id], type(adapter)):
|
||||
yield from vm.slot_add_binding(slot_id, adapter)
|
||||
except IndexError:
|
||||
raise DynamipsError("Slot {} doesn't exist on this router".format(slot_id))
|
||||
elif name.startswith("slot") and value is None:
|
||||
slot_id = int(name[-1])
|
||||
if vm.slots[slot_id]:
|
||||
yield from vm.slot_remove_binding(slot_id)
|
||||
try:
|
||||
if vm.slots[slot_id]:
|
||||
yield from vm.slot_remove_binding(slot_id)
|
||||
except IndexError:
|
||||
raise DynamipsError("Slot {} doesn't exist on this router".format(slot_id))
|
||||
elif name.startswith("wic") and value in WIC_MATRIX:
|
||||
wic_slot_id = int(name[-1])
|
||||
wic_name = value
|
||||
wic = WIC_MATRIX[wic_name]()
|
||||
if vm.slots[0].wics[wic_slot_id] and not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
|
||||
yield from vm.uninstall_wic(wic_slot_id)
|
||||
if not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
|
||||
yield from vm.install_wic(wic_slot_id, wic)
|
||||
try:
|
||||
if vm.slots[0].wics[wic_slot_id] and not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
|
||||
yield from vm.uninstall_wic(wic_slot_id)
|
||||
if not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
|
||||
yield from vm.install_wic(wic_slot_id, wic)
|
||||
except IndexError:
|
||||
raise DynamipsError("WIC slot {} doesn't exist on this router".format(wic_slot_id))
|
||||
elif name.startswith("wic") and value is None:
|
||||
wic_slot_id = int(name[-1])
|
||||
if vm.slots[0].wics and vm.slots[0].wics[wic_slot_id]:
|
||||
yield from vm.uninstall_wic(wic_slot_id)
|
||||
try:
|
||||
if vm.slots[0].wics and vm.slots[0].wics[wic_slot_id]:
|
||||
yield from vm.uninstall_wic(wic_slot_id)
|
||||
except IndexError:
|
||||
raise DynamipsError("WIC slot {} doesn't exist on this router".format(wic_slot_id))
|
||||
|
||||
mmap_support = self.config.get_section_config("Dynamips").getboolean("mmap_support", True)
|
||||
if mmap_support is False:
|
||||
@@ -521,38 +534,32 @@ class Dynamips(BaseManager):
|
||||
default_startup_config_path = os.path.join(module_workdir, "configs", "i{}_startup-config.cfg".format(vm.dynamips_id))
|
||||
default_private_config_path = os.path.join(module_workdir, "configs", "i{}_private-config.cfg".format(vm.dynamips_id))
|
||||
|
||||
startup_config_content = settings.get("startup_config_content")
|
||||
if startup_config_content:
|
||||
startup_config_path = self._create_config(vm, startup_config_content, default_startup_config_path)
|
||||
startup_config_path = settings.get("startup_config")
|
||||
if startup_config_path:
|
||||
yield from vm.set_configs(startup_config_path)
|
||||
else:
|
||||
startup_config_path = settings.get("startup_config")
|
||||
if startup_config_path:
|
||||
yield from vm.set_configs(startup_config_path)
|
||||
startup_config_path = self._create_config(vm, default_startup_config_path, settings.get("startup_config_content"))
|
||||
yield from vm.set_configs(startup_config_path)
|
||||
|
||||
private_config_content = settings.get("private_config_content")
|
||||
if private_config_content:
|
||||
private_config_path = self._create_config(vm, private_config_content, default_private_config_path)
|
||||
private_config_path = settings.get("private_config")
|
||||
if private_config_path:
|
||||
yield from vm.set_configs(vm.startup_config, private_config_path)
|
||||
else:
|
||||
private_config_path = settings.get("private_config")
|
||||
if private_config_path:
|
||||
yield from vm.set_configs(vm.startup_config, private_config_path)
|
||||
private_config_path = self._create_config(vm, default_private_config_path, settings.get("private_config_content"))
|
||||
yield from vm.set_configs(vm.startup_config, private_config_path)
|
||||
|
||||
def _create_config(self, vm, content, path):
|
||||
def _create_config(self, vm, path, content=None):
|
||||
"""
|
||||
Creates a config file.
|
||||
|
||||
:param vm: VM instance
|
||||
:param content: config content
|
||||
:param path: path to the destination config file
|
||||
:param content: config content
|
||||
|
||||
:returns: relative path to the created config file
|
||||
"""
|
||||
|
||||
log.info("Creating config file {}".format(path))
|
||||
content = "!\n" + content.replace("\r", "")
|
||||
content = content.replace('%h', vm.name)
|
||||
config_dir = os.path.dirname(path)
|
||||
try:
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
@@ -561,7 +568,10 @@ class Dynamips(BaseManager):
|
||||
|
||||
try:
|
||||
with open(path, "wb") as f:
|
||||
f.write(content.encode("utf-8"))
|
||||
if content:
|
||||
content = "!\n" + content.replace("\r", "")
|
||||
content = content.replace('%h', vm.name)
|
||||
f.write(content.encode("utf-8"))
|
||||
except OSError as e:
|
||||
raise DynamipsError("Could not create config file {}: {}".format(path, e))
|
||||
|
||||
|
||||
@@ -276,15 +276,16 @@ class DynamipsHypervisor:
|
||||
while True:
|
||||
try:
|
||||
try:
|
||||
line = yield from self._reader.readline()
|
||||
#line = yield from self._reader.readline() # this can lead to ValueError: Line is too long
|
||||
chunk = yield from self._reader.read(1024) # match to Dynamips' buffer size
|
||||
except asyncio.CancelledError:
|
||||
# task has been canceled but continue to read
|
||||
# any remaining data sent by the hypervisor
|
||||
continue
|
||||
if not line:
|
||||
if not chunk:
|
||||
raise DynamipsError("No data returned from {host}:{port}, Dynamips process running: {run}"
|
||||
.format(host=self._host, port=self._port, run=self.is_running()))
|
||||
buf += line.decode("utf-8")
|
||||
buf += chunk.decode("utf-8")
|
||||
except OSError as e:
|
||||
raise DynamipsError("Lost communication with {host}:{port} :{error}, Dynamips process running: {run}"
|
||||
.format(host=self._host, port=self._port, error=e, run=self.is_running()))
|
||||
|
||||
@@ -1436,6 +1436,17 @@ class Router(BaseVM):
|
||||
private_config = private_config.replace("\\", '/')
|
||||
|
||||
if self._startup_config != startup_config or self._private_config != private_config:
|
||||
self._startup_config = startup_config
|
||||
self._private_config = private_config
|
||||
|
||||
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
|
||||
private_config_path = os.path.join(module_workdir, private_config)
|
||||
try:
|
||||
if not os.path.getsize(private_config_path):
|
||||
# an empty private-config can prevent a router to boot.
|
||||
private_config = ''
|
||||
except OSError as e:
|
||||
raise DynamipsError("Cannot access the private-config {}: {}".format(private_config_path, e))
|
||||
|
||||
yield from self._hypervisor.send('vm set_config "{name}" "{startup}" "{private}"'.format(name=self._name,
|
||||
startup=startup_config,
|
||||
@@ -1445,15 +1456,11 @@ class Router(BaseVM):
|
||||
id=self._id,
|
||||
startup=startup_config))
|
||||
|
||||
self._startup_config = startup_config
|
||||
|
||||
if private_config:
|
||||
log.info('Router "{name}" [{id}]: has a new private-config set: "{private}"'.format(name=self._name,
|
||||
id=self._id,
|
||||
private=private_config))
|
||||
|
||||
self._private_config = private_config
|
||||
|
||||
@asyncio.coroutine
|
||||
def extract_config(self):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2013 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/>.
|
||||
|
||||
"""
|
||||
Interface for NAT NIOs.
|
||||
"""
|
||||
|
||||
from .nio import NIO
|
||||
|
||||
|
||||
class NIONAT(NIO):
|
||||
|
||||
"""
|
||||
NAT NIO.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return "NIO TAP"
|
||||
|
||||
def __json__(self):
|
||||
|
||||
return {"type": "nio_nat"}
|
||||
@@ -140,7 +140,7 @@ class PortManager:
|
||||
"""
|
||||
|
||||
if end_port < start_port:
|
||||
raise Exception("Invalid port range {}-{}".format(start_port, end_port))
|
||||
raise HTTPConflict(text="Invalid port range {}-{}".format(start_port, end_port))
|
||||
|
||||
if socket_type == "UDP":
|
||||
socket_type = socket.SOCK_DGRAM
|
||||
|
||||
@@ -139,9 +139,21 @@ class Project:
|
||||
if path != self._path and self.is_local() is False:
|
||||
raise aiohttp.web.HTTPForbidden(text="You are not allowed to modify the project directory location")
|
||||
|
||||
old_path = None
|
||||
if hasattr(self, "_path"):
|
||||
old_path = self._path
|
||||
|
||||
self._path = path
|
||||
self._update_temporary_file()
|
||||
|
||||
# The order of operation is important because we want to avoid losing
|
||||
# data
|
||||
if old_path:
|
||||
try:
|
||||
shutil.rmtree(old_path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Can't remove temporary directory {}: {}".format(old_path, e))
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
@@ -228,7 +240,10 @@ class Project:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not create temporary project: {}".format(e))
|
||||
else:
|
||||
if os.path.exists(os.path.join(self._path, ".gns3_temporary")):
|
||||
os.remove(os.path.join(self._path, ".gns3_temporary"))
|
||||
try:
|
||||
os.remove(os.path.join(self._path, ".gns3_temporary"))
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not mark project as no longer temporary: {}".format(e))
|
||||
|
||||
def module_working_directory(self, module_name):
|
||||
"""
|
||||
|
||||
@@ -23,7 +23,6 @@ order to run a QEMU VM.
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import random
|
||||
import subprocess
|
||||
import shlex
|
||||
import asyncio
|
||||
@@ -33,6 +32,7 @@ from .qemu_error import QemuError
|
||||
from ..adapters.ethernet_adapter import EthernetAdapter
|
||||
from ..nios.nio_udp import NIOUDP
|
||||
from ..nios.nio_tap import NIOTAP
|
||||
from ..nios.nio_nat import NIONAT
|
||||
from ..base_vm import BaseVM
|
||||
from ...schemas.qemu import QEMU_OBJECT_SCHEMA
|
||||
|
||||
@@ -981,46 +981,47 @@ class QemuVM(BaseVM):
|
||||
|
||||
return options
|
||||
|
||||
def _get_random_mac(self, adapter_number):
|
||||
# TODO: let users specify a base mac address
|
||||
return "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_number)
|
||||
|
||||
def _network_options(self):
|
||||
|
||||
network_options = []
|
||||
adapter_number = 0
|
||||
for adapter in self._ethernet_adapters:
|
||||
mac = self._get_random_mac(adapter_number)
|
||||
if self._legacy_networking:
|
||||
network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_number, mac, self._adapter_type)])
|
||||
else:
|
||||
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_number)])
|
||||
network_options.extend(["-net", "none"]) # we do not want any user networking back-end if no adapter is connected.
|
||||
for adapter_number, adapter in enumerate(self._ethernet_adapters):
|
||||
# TODO: let users specify a base mac address
|
||||
mac = "00:00:ab:%s:%s:%02x" % (self.id[-4:-2], self.id[-2:], adapter_number)
|
||||
nio = adapter.get_nio(0)
|
||||
if nio:
|
||||
if isinstance(nio, NIOUDP):
|
||||
if self._legacy_networking:
|
||||
if self._legacy_networking:
|
||||
# legacy QEMU networking syntax (-net)
|
||||
if nio:
|
||||
network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_number, mac, self._adapter_type)])
|
||||
if isinstance(nio, NIOUDP):
|
||||
network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number,
|
||||
adapter_number,
|
||||
nio.lport,
|
||||
nio.rport,
|
||||
nio.rhost)])
|
||||
else:
|
||||
elif isinstance(nio, NIOTAP):
|
||||
network_options.extend(["-net", "tap,name=gns3-{},ifname={}".format(adapter_number, nio.tap_device)])
|
||||
elif isinstance(nio, NIONAT):
|
||||
network_options.extend(["-net", "user,vlan={},name=gns3-{}".format(adapter_number, adapter_number)])
|
||||
else:
|
||||
network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_number, mac, self._adapter_type)])
|
||||
|
||||
else:
|
||||
# newer QEMU networking syntax
|
||||
if nio:
|
||||
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_number)])
|
||||
if isinstance(nio, NIOUDP):
|
||||
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
|
||||
nio.rhost,
|
||||
nio.rport,
|
||||
self._host,
|
||||
nio.lport)])
|
||||
elif isinstance(nio, NIOTAP):
|
||||
if self._legacy_networking:
|
||||
network_options.extend(["-net", "tap,name=gns3-{},ifname={}".format(adapter_number, nio.tap_device)])
|
||||
else:
|
||||
elif isinstance(nio, NIOTAP):
|
||||
network_options.extend(["-netdev", "tap,id=gns3-{},ifname={}".format(adapter_number, nio.tap_device)])
|
||||
else:
|
||||
if self._legacy_networking:
|
||||
network_options.extend(["-net", "user,vlan={},name=gns3-{}".format(adapter_number, adapter_number)])
|
||||
elif isinstance(nio, NIONAT):
|
||||
network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_number)])
|
||||
else:
|
||||
network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_number)])
|
||||
adapter_number += 1
|
||||
network_options.extend(["-device", "{},mac={}".format(self._adapter_type, mac)])
|
||||
|
||||
return network_options
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import asyncio
|
||||
from pkg_resources import parse_version
|
||||
from .virtualbox_error import VirtualBoxError
|
||||
from ..nios.nio_udp import NIOUDP
|
||||
from ..nios.nio_nat import NIONAT
|
||||
from ..adapters.ethernet_adapter import EthernetAdapter
|
||||
from .telnet_server import TelnetServer # TODO: port TelnetServer to asyncio
|
||||
from ..base_vm import BaseVM
|
||||
@@ -659,12 +660,12 @@ class VirtualBoxVM(BaseVM):
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
nio = self._ethernet_adapters[adapter_number].get_nio(0)
|
||||
if nio:
|
||||
if not self._use_any_adapter and attachment not in ("none", "null", "generic"):
|
||||
if not isinstance(nio, NIONAT) and not self._use_any_adapter and attachment not in ("none", "null", "generic"):
|
||||
raise VirtualBoxError("Attachment ({}) already configured on adapter {}. "
|
||||
"Please set it to 'Not attached' to allow GNS3 to use it.".format(attachment,
|
||||
adapter_number + 1))
|
||||
yield from self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
|
||||
yield from self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
vbox_adapter_type = "82540EM"
|
||||
if self._adapter_type == "PCnet-PCI II (Am79C970A)":
|
||||
vbox_adapter_type = "Am79C970A"
|
||||
@@ -681,13 +682,17 @@ class VirtualBoxVM(BaseVM):
|
||||
args = [self._vmname, "--nictype{}".format(adapter_number + 1), vbox_adapter_type]
|
||||
yield from self.manager.execute("modifyvm", args)
|
||||
|
||||
log.debug("setting UDP params on adapter {}".format(adapter_number))
|
||||
yield from self._modify_vm("--nic{} generic".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nicgenericdrv{} UDPTunnel".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
||||
yield from self._modify_vm("--nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
||||
yield from self._modify_vm("--nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
||||
yield from self._modify_vm("--cableconnected{} on".format(adapter_number + 1))
|
||||
if isinstance(nio, NIOUDP):
|
||||
log.debug("setting UDP params on adapter {}".format(adapter_number))
|
||||
yield from self._modify_vm("--nic{} generic".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nicgenericdrv{} UDPTunnel".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
||||
yield from self._modify_vm("--nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
||||
yield from self._modify_vm("--nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
||||
yield from self._modify_vm("--cableconnected{} on".format(adapter_number + 1))
|
||||
elif isinstance(nio, NIONAT):
|
||||
yield from self._modify_vm("--nic{} nat".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--cableconnected{} on".format(adapter_number + 1))
|
||||
|
||||
if nio.capturing:
|
||||
yield from self._modify_vm("--nictrace{} on".format(adapter_number + 1))
|
||||
|
||||
@@ -181,15 +181,15 @@ class VPCSVM(BaseVM):
|
||||
"""
|
||||
|
||||
try:
|
||||
script_file = os.path.join(self.working_dir, 'startup.vpc')
|
||||
with open(script_file, "wb+") as f:
|
||||
startup_script_path = os.path.join(self.working_dir, 'startup.vpc')
|
||||
with open(startup_script_path, "w+", encoding='utf-8') as f:
|
||||
if startup_script is None:
|
||||
f.write(b'')
|
||||
f.write('')
|
||||
else:
|
||||
startup_script = startup_script.replace("%h", self._name)
|
||||
f.write(startup_script.encode("utf-8"))
|
||||
f.write(startup_script)
|
||||
except OSError as e:
|
||||
raise VPCSError('Cannot write the startup script file "{}": {}'.format(self.script_file, e))
|
||||
raise VPCSError('Cannot write the startup script file "{}": {}'.format(startup_script_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_vpcs_version(self):
|
||||
|
||||
Reference in new issue
Block a user