mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-04 14:31:13 +02:00
Merge 2.1 into 2.2 branch
This commit is contained in:
1 parent
2a5f3221b9
commit
658aa4bae9
69 files changed
+846
-448
No files matched your search
@@ -555,7 +555,7 @@ class BaseManager:
|
||||
# We store the file under his final name only when the upload is finished
|
||||
tmp_path = path + ".tmp"
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(tmp_path, 'wb+') as f:
|
||||
with open(tmp_path, 'wb') as f:
|
||||
while True:
|
||||
packet = yield from stream.read(4096)
|
||||
if not packet:
|
||||
|
||||
@@ -25,6 +25,7 @@ import asyncio
|
||||
import tempfile
|
||||
import psutil
|
||||
import platform
|
||||
import re
|
||||
|
||||
from gns3server.utils.interfaces import interfaces
|
||||
from ..compute.port_manager import PortManager
|
||||
@@ -598,15 +599,24 @@ class BaseNode:
|
||||
@asyncio.coroutine
|
||||
def _ubridge_apply_filters(self, bridge_name, filters):
|
||||
"""
|
||||
Apply filter like rate limiting
|
||||
Apply packet filters
|
||||
|
||||
:param bridge_name: bridge name in uBridge
|
||||
:param filters: Array of filter dictionnary
|
||||
:param filters: Array of filter dictionary
|
||||
"""
|
||||
yield from self._ubridge_send('bridge reset_packet_filters ' + bridge_name)
|
||||
for filter in self._build_filter_list(filters):
|
||||
cmd = 'bridge add_packet_filter {} {}'.format(bridge_name, filter)
|
||||
yield from self._ubridge_send(cmd)
|
||||
for packet_filter in self._build_filter_list(filters):
|
||||
cmd = 'bridge add_packet_filter {} {}'.format(bridge_name, packet_filter)
|
||||
try:
|
||||
yield from self._ubridge_send(cmd)
|
||||
except UbridgeError as e:
|
||||
match = re.search("Cannot compile filter '(.*)': syntax error", str(e))
|
||||
if match:
|
||||
message = "Warning: ignoring BPF packet filter '{}' due to syntax error".format(self.name, match.group(1))
|
||||
log.warning(message)
|
||||
self.project.emit("log.warning", {"message": message})
|
||||
else:
|
||||
raise
|
||||
|
||||
def _build_filter_list(self, filters):
|
||||
"""
|
||||
|
||||
@@ -99,7 +99,7 @@ class Docker(BaseManager):
|
||||
|
||||
:param method: HTTP method
|
||||
:param path: Endpoint in API
|
||||
:param data: Dictionnary with the body. Will be transformed to a JSON
|
||||
:param data: Dictionary with the body. Will be transformed to a JSON
|
||||
:param params: Parameters added as a query arg
|
||||
"""
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ class DockerVM(BaseNode):
|
||||
params["Env"].append("GNS3_VOLUMES={}".format(":".join(self._volumes)))
|
||||
|
||||
if self._environment:
|
||||
for e in self._environment.split("\n"):
|
||||
for e in self._environment.strip().split("\n"):
|
||||
e = e.strip()
|
||||
if not e.startswith("GNS3_"):
|
||||
params["Env"].append(e)
|
||||
@@ -352,7 +352,11 @@ class DockerVM(BaseNode):
|
||||
def start(self):
|
||||
"""Starts this Docker container."""
|
||||
|
||||
state = yield from self._get_container_state()
|
||||
try:
|
||||
state = yield from self._get_container_state()
|
||||
except DockerHttp404Error:
|
||||
raise DockerError("Docker container '{name}' with ID {cid} does not exist or is not ready yet. Please try again in a few seconds.".format(name=self.name,
|
||||
cid=self._cid))
|
||||
if state == "paused":
|
||||
yield from self.unpause()
|
||||
elif state == "running":
|
||||
|
||||
@@ -547,7 +547,7 @@ class Dynamips(BaseManager):
|
||||
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))
|
||||
raise DynamipsError("Could not create config file '{}': {}".format(path, e))
|
||||
|
||||
return os.path.join("configs", os.path.basename(path))
|
||||
|
||||
|
||||
@@ -260,6 +260,8 @@ class DynamipsHypervisor:
|
||||
# Now retrieve the result
|
||||
data = []
|
||||
buf = ''
|
||||
retries = 0
|
||||
max_retries = 10
|
||||
while True:
|
||||
try:
|
||||
try:
|
||||
@@ -276,8 +278,14 @@ class DynamipsHypervisor:
|
||||
log.warning("Connection reset received while reading Dynamips response: {}".format(e))
|
||||
continue
|
||||
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()))
|
||||
if retries > max_retries:
|
||||
raise DynamipsError("No data returned from {host}:{port}, Dynamips process running: {run}"
|
||||
.format(host=self._host, port=self._port, run=self.is_running()))
|
||||
else:
|
||||
retries += 1
|
||||
yield from asyncio.sleep(0.1)
|
||||
continue
|
||||
retries = 0
|
||||
buf += chunk.decode("utf-8", errors="ignore")
|
||||
except OSError as e:
|
||||
raise DynamipsError("Could not read response for '{command}' from {host}:{port}: {error}, process running: {run}"
|
||||
|
||||
@@ -283,12 +283,15 @@ class Router(BaseNode):
|
||||
if not self._ghost_flag:
|
||||
self.check_available_ram(self.ram)
|
||||
|
||||
# config paths are relative to the working directory configured on Dynamips hypervisor
|
||||
startup_config_path = os.path.join("configs", "i{}_startup-config.cfg".format(self._dynamips_id))
|
||||
private_config_path = os.path.join("configs", "i{}_private-config.cfg".format(self._dynamips_id))
|
||||
|
||||
if not os.path.exists(private_config_path) or not os.path.getsize(private_config_path):
|
||||
if not os.path.exists(os.path.join(self._working_directory, private_config_path)) or \
|
||||
not os.path.getsize(os.path.join(self._working_directory, private_config_path)):
|
||||
# an empty private-config can prevent a router to boot.
|
||||
private_config_path = ''
|
||||
|
||||
yield from self._hypervisor.send('vm set_config "{name}" "{startup}" "{private}"'.format(
|
||||
name=self._name,
|
||||
startup=startup_config_path,
|
||||
|
||||
@@ -25,6 +25,7 @@ import asyncio
|
||||
from ..base_manager import BaseManager
|
||||
from .iou_error import IOUError
|
||||
from .iou_vm import IOUVM
|
||||
from .utils.application_id import get_next_application_id
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -38,8 +39,7 @@ class IOU(BaseManager):
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
self._free_application_ids = list(range(1, 512))
|
||||
self._used_application_ids = {}
|
||||
self._iou_id_lock = asyncio.Lock()
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_node(self, *args, **kwargs):
|
||||
@@ -49,40 +49,14 @@ class IOU(BaseManager):
|
||||
:returns: IOUVM instance
|
||||
"""
|
||||
|
||||
node = yield from super().create_node(*args, **kwargs)
|
||||
try:
|
||||
self._used_application_ids[node.id] = self._free_application_ids.pop(0)
|
||||
except IndexError:
|
||||
raise IOUError("Cannot create a new IOU VM (limit of 512 VMs reached on this host)")
|
||||
with (yield from self._iou_id_lock):
|
||||
# wait for a node to be completely created before adding a new one
|
||||
# this is important otherwise we allocate the same application ID
|
||||
# when creating multiple IOU node at the same time
|
||||
application_id = get_next_application_id(self.nodes)
|
||||
node = yield from super().create_node(*args, application_id=application_id, **kwargs)
|
||||
return node
|
||||
|
||||
@asyncio.coroutine
|
||||
def close_node(self, node_id, *args, **kwargs):
|
||||
"""
|
||||
Closes an IOU VM.
|
||||
|
||||
:returns: IOUVM instance
|
||||
"""
|
||||
|
||||
node = self.get_node(node_id)
|
||||
if node_id in self._used_application_ids:
|
||||
i = self._used_application_ids[node_id]
|
||||
self._free_application_ids.insert(0, i)
|
||||
del self._used_application_ids[node_id]
|
||||
yield from super().close_node(node_id, *args, **kwargs)
|
||||
return node
|
||||
|
||||
def get_application_id(self, node_id):
|
||||
"""
|
||||
Get an unique application identifier for IOU.
|
||||
|
||||
:param node_id: Node identifier
|
||||
|
||||
:returns: IOU application identifier
|
||||
"""
|
||||
|
||||
return self._used_application_ids.get(node_id, 1)
|
||||
|
||||
@staticmethod
|
||||
def get_legacy_vm_workdir(legacy_vm_id, name):
|
||||
"""
|
||||
|
||||
@@ -65,7 +65,7 @@ class IOUVM(BaseNode):
|
||||
:param console: TCP console port
|
||||
"""
|
||||
|
||||
def __init__(self, name, node_id, project, manager, path=None, console=None):
|
||||
def __init__(self, name, node_id, project, manager, application_id=None, path=None, console=None):
|
||||
|
||||
super().__init__(name, node_id, project, manager, console=console)
|
||||
|
||||
@@ -86,7 +86,7 @@ class IOUVM(BaseNode):
|
||||
self._startup_config = ""
|
||||
self._private_config = ""
|
||||
self._ram = 256 # Megabytes
|
||||
self._application_id = None
|
||||
self._application_id = application_id
|
||||
self._l1_keepalives = False # used to overcome the always-up Ethernet interfaces (not supported by all IOSes).
|
||||
|
||||
def _config(self):
|
||||
@@ -348,14 +348,14 @@ class IOUVM(BaseNode):
|
||||
# reload
|
||||
path = os.path.join(os.path.expanduser("~/"), ".iourc")
|
||||
try:
|
||||
with open(path, "wb+") as f:
|
||||
with open(path, "wb") as f:
|
||||
f.write(value.encode("utf-8"))
|
||||
except OSError as e:
|
||||
raise IOUError("Could not write the iourc file {}: {}".format(path, e))
|
||||
|
||||
path = os.path.join(self.temporary_directory, "iourc")
|
||||
try:
|
||||
with open(path, "wb+") as f:
|
||||
with open(path, "wb") as f:
|
||||
f.write(value.encode("utf-8"))
|
||||
except OSError as e:
|
||||
raise IOUError("Could not write the iourc file {}: {}".format(path, e))
|
||||
@@ -1141,9 +1141,7 @@ class IOUVM(BaseNode):
|
||||
|
||||
:returns: integer between 1 and 512
|
||||
"""
|
||||
if self._application_id is None:
|
||||
#FIXME: is this necessary? application ID is allocated by controller and should not be None
|
||||
return self._manager.get_application_id(self.id)
|
||||
|
||||
return self._application_id
|
||||
|
||||
@application_id.setter
|
||||
|
||||
@@ -24,13 +24,14 @@ log = logging.getLogger(__name__)
|
||||
def get_next_application_id(nodes):
|
||||
"""
|
||||
Calculates free application_id from given nodes
|
||||
|
||||
:param nodes:
|
||||
:raises IOUError when exceeds number
|
||||
:return: integer first free id
|
||||
"""
|
||||
used = set([n.properties.get('application_id') for n in nodes if n.node_type == 'iou'])
|
||||
used = set([n.application_id for n in nodes])
|
||||
pool = set(range(1, 512))
|
||||
try:
|
||||
return (pool - used).pop()
|
||||
except KeyError:
|
||||
raise IOUError("Cannot create a new IOU VM (limit of 512 VMs reached)")
|
||||
raise IOUError("Cannot create a new IOU VM (limit of 512 VMs on one host reached)")
|
||||
@@ -533,7 +533,7 @@ class QemuVM(BaseNode):
|
||||
|
||||
if not mac_address:
|
||||
# use the node UUID to generate a random MAC address
|
||||
self._mac_address = "00:%s:%s:%s:%s:00" % (self.project.id[-4:-2], self.project.id[-2:], self.id[-4:-2], self.id[-2:])
|
||||
self._mac_address = "52:%s:%s:%s:%s:00" % (self.project.id[-4:-2], self.project.id[-2:], self.id[-4:-2], self.id[-2:])
|
||||
else:
|
||||
self._mac_address = mac_address
|
||||
|
||||
|
||||
@@ -397,12 +397,12 @@ class VMware(BaseManager):
|
||||
try:
|
||||
stdout_data, _ = yield from asyncio.wait_for(process.communicate(), timeout=timeout)
|
||||
except asyncio.TimeoutError:
|
||||
raise VMwareError("vmrun has timed out after {} seconds!\nTry to run {} in a terminal to see more informations.\n\nMake sure GNS3 and VMware run under the same user and whitelist vmrun.exe in your antivirus.".format(timeout, command_string))
|
||||
raise VMwareError("vmrun has timed out after {} seconds!\nTry to run {} in a terminal to see more details.\n\nMake sure GNS3 and VMware run under the same user and whitelist vmrun.exe in your antivirus.".format(timeout, command_string))
|
||||
|
||||
if process.returncode:
|
||||
# vmrun print errors on stdout
|
||||
vmrun_error = stdout_data.decode("utf-8", errors="ignore")
|
||||
raise VMwareError("vmrun has returned an error: {}\nTry to run {} in a terminal to see more informations.\nAnd make sure GNS3 and VMware run under the same user.".format(vmrun_error, command_string))
|
||||
raise VMwareError("vmrun has returned an error: {}\nTry to run {} in a terminal to see more details.\nAnd make sure GNS3 and VMware run under the same user.".format(vmrun_error, command_string))
|
||||
|
||||
return stdout_data.decode("utf-8", errors="ignore").splitlines()
|
||||
|
||||
|
||||
@@ -745,7 +745,7 @@ class VMwareVM(BaseNode):
|
||||
"Please remove it or allow VMware VM '{name}' to use any adapter.".format(attachment=self._vmx_pairs[connection_type],
|
||||
adapter_number=adapter_number,
|
||||
name=self.name))
|
||||
elif self.is_running():
|
||||
elif (yield from self.is_running()):
|
||||
raise VMwareError("Attachment '{attachment}' is configured on network adapter {adapter_number}. "
|
||||
"Please stop VMware VM '{name}' to link to this adapter and allow GNS3 to change the attachment type.".format(attachment=self._vmx_pairs[connection_type],
|
||||
adapter_number=adapter_number,
|
||||
|
||||
Reference in new issue
Block a user