Merge 2.1 into 2.2 branch

This commit is contained in:
grossmj committed 2018-03-12 13:38:50 +07:00
1 parent 2a5f3221b9
commit 658aa4bae9
69 files changed
+846 -448

No files matched your search

+1 -1
View File
@@ -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:
+15 -5
View File
@@ -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):
"""
+1 -1
View File
@@ -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
"""
+6 -2
View File
@@ -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":
+1 -1
View File
@@ -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}"
+4 -1
View File
@@ -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,
+8 -34
View File
@@ -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):
"""
+5 -7
View File
@@ -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)")
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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()
+1 -1
View File
@@ -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,