mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 03:08:14 +00:00
Merge branch '2.2' of github.com:GNS3/gns3-server into 2.2
This commit is contained in:
commit
cbb6eccad8
22
CHANGELOG
22
CHANGELOG
@ -1,5 +1,27 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 2.2.0a2 14/03/2019
|
||||||
|
|
||||||
|
* Web-UI v2019.1.0-alpha.1
|
||||||
|
* Update docs for update-bundled-web-ui.sh
|
||||||
|
* Fix issue when loading and quickly closing a project and opening it again. Fixes #1501.
|
||||||
|
* Disable unreliable nested virtualization check.
|
||||||
|
* Fix issue not checking build number on Windows.
|
||||||
|
* Change Hyper-V requirement checks.
|
||||||
|
* Early support for symbol themes.
|
||||||
|
* Re-order handlers in order to prevent CORS
|
||||||
|
* Download custom appliance symbols from GitHub Fix symbol cache issue. Ref https://github.com/GNS3/gns3-gui/issues/2671 Fix temporary directory for symbols was not deleted Fix temporary appliance file was not deleted
|
||||||
|
* Option to export snapshots.
|
||||||
|
* Support tags versioned WebUI when bundling
|
||||||
|
* Support selecting a compression type when exporting a project.
|
||||||
|
* Change how VPCS executable is searched.
|
||||||
|
* Use aiofiles where relevant.
|
||||||
|
* Update paths for binaries moved to the MacOS directory in GNS3.app
|
||||||
|
* Locked state should not be used when duplicating a node.
|
||||||
|
* Handle locking/unlocking items independently from the layer position.
|
||||||
|
* Use aiozipstream for snapshots. Fix tests.
|
||||||
|
* Project duplication support.
|
||||||
|
|
||||||
## 2.2.0a1 29/01/2019
|
## 2.2.0a1 29/01/2019
|
||||||
|
|
||||||
* Restore reload support for nodes.
|
* Restore reload support for nodes.
|
||||||
|
@ -22,7 +22,7 @@ http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L558
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from gns3server.utils import parse_version
|
from gns3server.utils import parse_version
|
||||||
from gns3server.utils.asyncio.embed_shell import EmbedShell, create_telnet_shell
|
#from gns3server.utils.asyncio.embed_shell import EmbedShell, create_telnet_shell
|
||||||
|
|
||||||
|
|
||||||
from .device import Device
|
from .device import Device
|
||||||
@ -34,36 +34,36 @@ import logging
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class EthernetSwitchConsole(EmbedShell):
|
# class EthernetSwitchConsole(EmbedShell):
|
||||||
"""
|
# """
|
||||||
Console for the ethernet switch
|
# Console for the ethernet switch
|
||||||
"""
|
# """
|
||||||
|
#
|
||||||
def __init__(self, node):
|
# def __init__(self, node):
|
||||||
super().__init__(welcome_message="Welcome to GNS3 builtin Ethernet switch.\n\nType help for available commands\n")
|
# super().__init__(welcome_message="Welcome to GNS3 builtin Ethernet switch.\n\nType help for available commands\n")
|
||||||
self._node = node
|
# self._node = node
|
||||||
|
#
|
||||||
async def mac(self):
|
# async def mac(self):
|
||||||
"""
|
# """
|
||||||
Show MAC address table
|
# Show MAC address table
|
||||||
"""
|
# """
|
||||||
res = 'Port Mac VLAN\n'
|
# res = 'Port Mac VLAN\n'
|
||||||
result = (await self._node._hypervisor.send('ethsw show_mac_addr_table {}'.format(self._node.name)))
|
# result = (await self._node._hypervisor.send('ethsw show_mac_addr_table {}'.format(self._node.name)))
|
||||||
for line in result:
|
# for line in result:
|
||||||
mac, vlan, nio = line.replace(' ', ' ').split(' ')
|
# mac, vlan, nio = line.replace(' ', ' ').split(' ')
|
||||||
mac = mac.replace('.', '')
|
# mac = mac.replace('.', '')
|
||||||
mac = "{}:{}:{}:{}:{}:{}".format(
|
# mac = "{}:{}:{}:{}:{}:{}".format(
|
||||||
mac[0:2],
|
# mac[0:2],
|
||||||
mac[2:4],
|
# mac[2:4],
|
||||||
mac[4:6],
|
# mac[4:6],
|
||||||
mac[6:8],
|
# mac[6:8],
|
||||||
mac[8:10],
|
# mac[8:10],
|
||||||
mac[10:12])
|
# mac[10:12])
|
||||||
for port_number, switch_nio in self._node.nios.items():
|
# for port_number, switch_nio in self._node.nios.items():
|
||||||
if switch_nio.name == nio:
|
# if switch_nio.name == nio:
|
||||||
res += 'Ethernet' + str(port_number) + ' ' + mac + ' ' + vlan + '\n'
|
# res += 'Ethernet' + str(port_number) + ' ' + mac + ' ' + vlan + '\n'
|
||||||
break
|
# break
|
||||||
return res
|
# return res
|
||||||
|
|
||||||
|
|
||||||
class EthernetSwitch(Device):
|
class EthernetSwitch(Device):
|
||||||
@ -85,8 +85,8 @@ class EthernetSwitch(Device):
|
|||||||
self._nios = {}
|
self._nios = {}
|
||||||
self._mappings = {}
|
self._mappings = {}
|
||||||
self._telnet_console = None
|
self._telnet_console = None
|
||||||
self._telnet_shell = None
|
#self._telnet_shell = None
|
||||||
self._telnet_server = None
|
#self._telnet_server = None
|
||||||
self._console = console
|
self._console = console
|
||||||
self._console_type = console_type
|
self._console_type = console_type
|
||||||
|
|
||||||
@ -177,13 +177,13 @@ class EthernetSwitch(Device):
|
|||||||
await self._hypervisor.send('ethsw create "{}"'.format(self._name))
|
await self._hypervisor.send('ethsw create "{}"'.format(self._name))
|
||||||
log.info('Ethernet switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
log.info('Ethernet switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||||
|
|
||||||
self._telnet_shell = EthernetSwitchConsole(self)
|
#self._telnet_shell = EthernetSwitchConsole(self)
|
||||||
self._telnet_shell.prompt = self._name + '> '
|
#self._telnet_shell.prompt = self._name + '> '
|
||||||
self._telnet = create_telnet_shell(self._telnet_shell)
|
#self._telnet = create_telnet_shell(self._telnet_shell)
|
||||||
try:
|
#try:
|
||||||
self._telnet_server = (await asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console))
|
# self._telnet_server = (await asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console))
|
||||||
except OSError as e:
|
#except OSError as e:
|
||||||
self.project.emit("log.warning", {"message": "Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)})
|
# self.project.emit("log.warning", {"message": "Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)})
|
||||||
self._hypervisor.devices.append(self)
|
self._hypervisor.devices.append(self)
|
||||||
|
|
||||||
async def set_name(self, new_name):
|
async def set_name(self, new_name):
|
||||||
@ -227,9 +227,9 @@ class EthernetSwitch(Device):
|
|||||||
Deletes this Ethernet switch.
|
Deletes this Ethernet switch.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await self._telnet.close()
|
#await self._telnet.close()
|
||||||
if self._telnet_server:
|
#if self._telnet_server:
|
||||||
self._telnet_server.close()
|
# self._telnet_server.close()
|
||||||
|
|
||||||
for nio in self._nios.values():
|
for nio in self._nios.values():
|
||||||
if nio:
|
if nio:
|
||||||
|
@ -674,6 +674,13 @@ class IOUVM(BaseNode):
|
|||||||
pass
|
pass
|
||||||
self._iou_process = None
|
self._iou_process = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
symlink = os.path.join(self.working_dir, os.path.basename(self.path))
|
||||||
|
if os.path.islink(symlink):
|
||||||
|
os.unlink(symlink)
|
||||||
|
except OSError as e:
|
||||||
|
log.warning("Could not delete symbolic link: {}".format(e))
|
||||||
|
|
||||||
self._started = False
|
self._started = False
|
||||||
self.save_configs()
|
self.save_configs()
|
||||||
|
|
||||||
|
@ -407,7 +407,7 @@ class Project:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
for dirpath, dirnames, filenames in os.walk(self.path):
|
for dirpath, dirnames, filenames in os.walk(self.path, followlinks=False):
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if not filename.endswith(".ghost"):
|
if not filename.endswith(".ghost"):
|
||||||
path = os.path.relpath(dirpath, self.path)
|
path = os.path.relpath(dirpath, self.path)
|
||||||
|
@ -20,6 +20,7 @@ VirtualBox server module.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -177,14 +178,17 @@ class VirtualBox(BaseManager):
|
|||||||
for line in result:
|
for line in result:
|
||||||
if len(line) == 0 or line[0] != '"' or line[-1:] != "}":
|
if len(line) == 0 or line[0] != '"' or line[-1:] != "}":
|
||||||
continue # Broken output (perhaps a carriage return in VM name)
|
continue # Broken output (perhaps a carriage return in VM name)
|
||||||
vmname, _ = line.rsplit(' ', 1)
|
match = re.search(r"\"(.*)\"\ {(.*)}", line)
|
||||||
vmname = vmname.strip('"')
|
if not match:
|
||||||
|
continue
|
||||||
|
vmname = match.group(1)
|
||||||
|
uuid = match.group(2)
|
||||||
if vmname == "<inaccessible>":
|
if vmname == "<inaccessible>":
|
||||||
continue # ignore inaccessible VMs
|
continue # ignore inaccessible VMs
|
||||||
extra_data = await self.execute("getextradata", [vmname, "GNS3/Clone"])
|
extra_data = await self.execute("getextradata", [uuid, "GNS3/Clone"])
|
||||||
if allow_clone or len(extra_data) == 0 or not extra_data[0].strip() == "Value: yes":
|
if allow_clone or len(extra_data) == 0 or not extra_data[0].strip() == "Value: yes":
|
||||||
# get the amount of RAM
|
# get the amount of RAM
|
||||||
info_results = await self.execute("showvminfo", [vmname, "--machinereadable"])
|
info_results = await self.execute("showvminfo", [uuid, "--machinereadable"])
|
||||||
ram = 0
|
ram = 0
|
||||||
for info in info_results:
|
for info in info_results:
|
||||||
try:
|
try:
|
||||||
|
@ -57,6 +57,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
|
|
||||||
super().__init__(name, node_id, project, manager, console=console, linked_clone=linked_clone, console_type=console_type)
|
super().__init__(name, node_id, project, manager, console=console, linked_clone=linked_clone, console_type=console_type)
|
||||||
|
|
||||||
|
self._uuid = None # UUID in VirtualBox
|
||||||
self._maximum_adapters = 8
|
self._maximum_adapters = 8
|
||||||
self._system_properties = {}
|
self._system_properties = {}
|
||||||
self._telnet_server = None
|
self._telnet_server = None
|
||||||
@ -116,7 +117,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
:returns: state (string)
|
:returns: state (string)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
results = await self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
results = await self.manager.execute("showvminfo", [self._uuid, "--machinereadable"])
|
||||||
for info in results:
|
for info in results:
|
||||||
if '=' in info:
|
if '=' in info:
|
||||||
name, value = info.split('=', 1)
|
name, value = info.split('=', 1)
|
||||||
@ -134,7 +135,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
args = shlex.split(params)
|
args = shlex.split(params)
|
||||||
result = await self.manager.execute("controlvm", [self._vmname] + args)
|
result = await self.manager.execute("controlvm", [self._uuid] + args)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def _modify_vm(self, params):
|
async def _modify_vm(self, params):
|
||||||
@ -145,7 +146,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
args = shlex.split(params)
|
args = shlex.split(params)
|
||||||
await self.manager.execute("modifyvm", [self._vmname] + args)
|
await self.manager.execute("modifyvm", [self._uuid] + args)
|
||||||
|
|
||||||
async def _check_duplicate_linked_clone(self):
|
async def _check_duplicate_linked_clone(self):
|
||||||
"""
|
"""
|
||||||
@ -174,6 +175,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
async def create(self):
|
async def create(self):
|
||||||
|
|
||||||
if not self.linked_clone:
|
if not self.linked_clone:
|
||||||
await self._check_duplicate_linked_clone()
|
await self._check_duplicate_linked_clone()
|
||||||
|
|
||||||
@ -184,21 +186,29 @@ class VirtualBoxVM(BaseNode):
|
|||||||
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))
|
||||||
|
|
||||||
|
vm_info = await self._get_vm_info()
|
||||||
|
if "memory" in vm_info:
|
||||||
|
self._ram = int(vm_info["memory"])
|
||||||
|
if "UUID" in vm_info:
|
||||||
|
self._uuid = vm_info["UUID"]
|
||||||
|
if not self._uuid:
|
||||||
|
raise VirtualBoxError("Could not find any UUID for VM '{}'".format(self._vmname))
|
||||||
|
|
||||||
if self.linked_clone:
|
if self.linked_clone:
|
||||||
if self.id and os.path.isdir(os.path.join(self.working_dir, self._vmname)):
|
if self.id and os.path.isdir(os.path.join(self.working_dir, self._vmname)):
|
||||||
self._patch_vm_uuid()
|
self._patch_vm_uuid()
|
||||||
await self.manager.execute("registervm", [self._linked_vbox_file()])
|
await self.manager.execute("registervm", [self._linked_vbox_file()])
|
||||||
await self._reattach_linked_hdds()
|
await self._reattach_linked_hdds()
|
||||||
|
vm_info = await self._get_vm_info()
|
||||||
|
self._uuid = vm_info.get("UUID")
|
||||||
|
if not self._uuid:
|
||||||
|
raise VirtualBoxError("Could not find any UUID for VM '{}'".format(self._vmname))
|
||||||
else:
|
else:
|
||||||
await self._create_linked_clone()
|
await self._create_linked_clone()
|
||||||
|
|
||||||
if self._adapters:
|
if self._adapters:
|
||||||
await self.set_adapters(self._adapters)
|
await self.set_adapters(self._adapters)
|
||||||
|
|
||||||
vm_info = await self._get_vm_info()
|
|
||||||
if "memory" in vm_info:
|
|
||||||
self._ram = int(vm_info["memory"])
|
|
||||||
|
|
||||||
def _linked_vbox_file(self):
|
def _linked_vbox_file(self):
|
||||||
return os.path.join(self.working_dir, self._vmname, self._vmname + ".vbox")
|
return os.path.join(self.working_dir, self._vmname, self._vmname + ".vbox")
|
||||||
|
|
||||||
@ -266,7 +276,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
# check if there is enough RAM to run
|
# check if there is enough RAM to run
|
||||||
self.check_available_ram(self.ram)
|
self.check_available_ram(self.ram)
|
||||||
|
|
||||||
args = [self._vmname]
|
args = [self._uuid]
|
||||||
if self._headless:
|
if self._headless:
|
||||||
args.extend(["--type", "headless"])
|
args.extend(["--type", "headless"])
|
||||||
result = await self.manager.execute("startvm", args)
|
result = await self.manager.execute("startvm", args)
|
||||||
@ -275,9 +285,9 @@ class VirtualBoxVM(BaseNode):
|
|||||||
log.debug("Start result: {}".format(result))
|
log.debug("Start result: {}".format(result))
|
||||||
|
|
||||||
# add a guest property to let the VM know about the GNS3 name
|
# add a guest property to let the VM know about the GNS3 name
|
||||||
await self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name])
|
await self.manager.execute("guestproperty", ["set", self._uuid, "NameInGNS3", self.name])
|
||||||
# add a guest property to let the VM know about the GNS3 project directory
|
# add a guest property to let the VM know about the GNS3 project directory
|
||||||
await self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir])
|
await self.manager.execute("guestproperty", ["set", self._uuid, "ProjectDirInGNS3", self.working_dir])
|
||||||
|
|
||||||
await self._start_ubridge()
|
await self._start_ubridge()
|
||||||
for adapter_number in range(0, self._adapters):
|
for adapter_number in range(0, self._adapters):
|
||||||
@ -739,7 +749,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
vm_info = {}
|
vm_info = {}
|
||||||
results = await self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
results = await self.manager.execute("showvminfo", ["--machinereadable", "--", self._vmname]) # "--" is to protect against vm names containing the "-" character
|
||||||
for info in results:
|
for info in results:
|
||||||
try:
|
try:
|
||||||
name, value = info.split('=', 1)
|
name, value = info.split('=', 1)
|
||||||
@ -775,7 +785,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
|
|
||||||
# set server mode with a pipe on the first serial port
|
# set server mode with a pipe on the first serial port
|
||||||
pipe_name = self._get_pipe_name()
|
pipe_name = self._get_pipe_name()
|
||||||
args = [self._vmname, "--uartmode1", "server", pipe_name]
|
args = [self._uuid, "--uartmode1", "server", pipe_name]
|
||||||
await self.manager.execute("modifyvm", args)
|
await self.manager.execute("modifyvm", args)
|
||||||
|
|
||||||
async def _storage_attach(self, params):
|
async def _storage_attach(self, params):
|
||||||
@ -786,7 +796,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
args = shlex.split(params)
|
args = shlex.split(params)
|
||||||
await self.manager.execute("storageattach", [self._vmname] + args)
|
await self.manager.execute("storageattach", [self._uuid] + args)
|
||||||
|
|
||||||
async def _get_nic_attachements(self, maximum_adapters):
|
async def _get_nic_attachements(self, maximum_adapters):
|
||||||
"""
|
"""
|
||||||
@ -850,7 +860,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
vbox_adapter_type = "82545EM"
|
vbox_adapter_type = "82545EM"
|
||||||
if adapter_type == "Paravirtualized Network (virtio-net)":
|
if adapter_type == "Paravirtualized Network (virtio-net)":
|
||||||
vbox_adapter_type = "virtio"
|
vbox_adapter_type = "virtio"
|
||||||
args = [self._vmname, "--nictype{}".format(adapter_number + 1), vbox_adapter_type]
|
args = [self._uuid, "--nictype{}".format(adapter_number + 1), vbox_adapter_type]
|
||||||
await self.manager.execute("modifyvm", args)
|
await self.manager.execute("modifyvm", args)
|
||||||
|
|
||||||
if isinstance(nio, NIOUDP):
|
if isinstance(nio, NIOUDP):
|
||||||
@ -888,10 +898,10 @@ class VirtualBoxVM(BaseNode):
|
|||||||
gns3_snapshot_exists = True
|
gns3_snapshot_exists = True
|
||||||
|
|
||||||
if not gns3_snapshot_exists:
|
if not gns3_snapshot_exists:
|
||||||
result = await self.manager.execute("snapshot", [self._vmname, "take", "GNS3 Linked Base for clones"])
|
result = await self.manager.execute("snapshot", [self._uuid, "take", "GNS3 Linked Base for clones"])
|
||||||
log.debug("GNS3 snapshot created: {}".format(result))
|
log.debug("GNS3 snapshot created: {}".format(result))
|
||||||
|
|
||||||
args = [self._vmname,
|
args = [self._uuid,
|
||||||
"--snapshot",
|
"--snapshot",
|
||||||
"GNS3 Linked Base for clones",
|
"GNS3 Linked Base for clones",
|
||||||
"--options",
|
"--options",
|
||||||
@ -906,12 +916,12 @@ class VirtualBoxVM(BaseNode):
|
|||||||
log.debug("VirtualBox VM: {} cloned".format(result))
|
log.debug("VirtualBox VM: {} cloned".format(result))
|
||||||
|
|
||||||
self._vmname = self._name
|
self._vmname = self._name
|
||||||
await self.manager.execute("setextradata", [self._vmname, "GNS3/Clone", "yes"])
|
await self.manager.execute("setextradata", [self._uuid, "GNS3/Clone", "yes"])
|
||||||
|
|
||||||
# We create a reset snapshot in order to simplify life of user who want to rollback their VM
|
# We create a reset snapshot in order to simplify life of user who want to rollback their VM
|
||||||
# Warning: Do not document this it's seem buggy we keep it because Raizo students use it.
|
# Warning: Do not document this it's seem buggy we keep it because Raizo students use it.
|
||||||
try:
|
try:
|
||||||
args = [self._vmname, "take", "reset"]
|
args = [self._uuid, "take", "reset"]
|
||||||
result = await self.manager.execute("snapshot", args)
|
result = await self.manager.execute("snapshot", args)
|
||||||
log.debug("Snapshot 'reset' created: {}".format(result))
|
log.debug("Snapshot 'reset' created: {}".format(result))
|
||||||
# It seem sometimes this failed due to internal race condition of Vbox
|
# It seem sometimes this failed due to internal race condition of Vbox
|
||||||
|
@ -27,7 +27,6 @@ import io
|
|||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
from ..utils import parse_version
|
from ..utils import parse_version
|
||||||
from ..utils.images import list_images
|
|
||||||
from ..utils.asyncio import locking
|
from ..utils.asyncio import locking
|
||||||
from ..controller.controller_error import ControllerError
|
from ..controller.controller_error import ControllerError
|
||||||
from ..version import __version__, __version_info__
|
from ..version import __version__, __version_info__
|
||||||
@ -405,7 +404,7 @@ class Compute:
|
|||||||
raise aiohttp.web.HTTPConflict(text=msg)
|
raise aiohttp.web.HTTPConflict(text=msg)
|
||||||
else:
|
else:
|
||||||
msg = "{}\nUsing different versions may result in unexpected problems. Please use at your own risk.".format(msg)
|
msg = "{}\nUsing different versions may result in unexpected problems. Please use at your own risk.".format(msg)
|
||||||
self._controller.notification.emit("log.warning", {"message": msg})
|
self._controller.notification.controller_emit("log.warning", {"message": msg})
|
||||||
|
|
||||||
self._notifications = asyncio.gather(self._connect_notification())
|
self._notifications = asyncio.gather(self._connect_notification())
|
||||||
self._connected = True
|
self._connected = True
|
||||||
@ -571,8 +570,7 @@ class Compute:
|
|||||||
|
|
||||||
async def images(self, type):
|
async def images(self, type):
|
||||||
"""
|
"""
|
||||||
Return the list of images available for this type on controller
|
Return the list of images available for this type on the compute node.
|
||||||
and on the compute node.
|
|
||||||
"""
|
"""
|
||||||
images = []
|
images = []
|
||||||
|
|
||||||
@ -581,9 +579,9 @@ class Compute:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if type in ["qemu", "dynamips", "iou"]:
|
if type in ["qemu", "dynamips", "iou"]:
|
||||||
for local_image in list_images(type):
|
#for local_image in list_images(type):
|
||||||
if local_image['filename'] not in [i['filename'] for i in images]:
|
# if local_image['filename'] not in [i['filename'] for i in images]:
|
||||||
images.append(local_image)
|
# images.append(local_image)
|
||||||
images = sorted(images, key=itemgetter('filename'))
|
images = sorted(images, key=itemgetter('filename'))
|
||||||
else:
|
else:
|
||||||
images = sorted(images, key=itemgetter('image'))
|
images = sorted(images, key=itemgetter('image'))
|
||||||
|
@ -212,7 +212,9 @@ class GNS3VM:
|
|||||||
new_settings = copy.copy(self._settings)
|
new_settings = copy.copy(self._settings)
|
||||||
new_settings.update(settings)
|
new_settings.update(settings)
|
||||||
if self.settings != new_settings:
|
if self.settings != new_settings:
|
||||||
|
try:
|
||||||
await self._stop()
|
await self._stop()
|
||||||
|
finally:
|
||||||
self._settings = settings
|
self._settings = settings
|
||||||
self._controller.save()
|
self._controller.save()
|
||||||
if self.enable:
|
if self.enable:
|
||||||
|
@ -239,6 +239,8 @@ class HyperVGNS3VM(BaseGNS3VM):
|
|||||||
log.info("GNS3 VM has been started")
|
log.info("GNS3 VM has been started")
|
||||||
|
|
||||||
# Get the guest IP address
|
# Get the guest IP address
|
||||||
|
# LIS (Linux Integration Services) must be installed on the guest
|
||||||
|
# See https://oitibs.com/hyper-v-lis-on-ubuntu-18-04/ for details.
|
||||||
trial = 120
|
trial = 120
|
||||||
guest_ip_address = ""
|
guest_ip_address = ""
|
||||||
log.info("Waiting for GNS3 VM IP")
|
log.info("Waiting for GNS3 VM IP")
|
||||||
|
@ -69,15 +69,16 @@ class VMwareGNS3VM(BaseGNS3VM):
|
|||||||
if ram % 4 != 0:
|
if ram % 4 != 0:
|
||||||
raise GNS3VMError("Allocated memory {} for the GNS3 VM must be a multiple of 4".format(ram))
|
raise GNS3VMError("Allocated memory {} for the GNS3 VM must be a multiple of 4".format(ram))
|
||||||
|
|
||||||
available_vcpus = psutil.cpu_count()
|
available_vcpus = psutil.cpu_count(logical=True)
|
||||||
if vcpus > available_vcpus:
|
if vcpus > available_vcpus:
|
||||||
raise GNS3VMError("You have allocated too many vCPUs for the GNS3 VM! (max available is {} vCPUs)".format(available_vcpus))
|
raise GNS3VMError("You have allocated too many vCPUs for the GNS3 VM! (max available is {} vCPUs)".format(available_vcpus))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pairs = VMware.parse_vmware_file(self._vmx_path)
|
pairs = VMware.parse_vmware_file(self._vmx_path)
|
||||||
|
if vcpus > 1:
|
||||||
pairs["numvcpus"] = str(vcpus)
|
pairs["numvcpus"] = str(vcpus)
|
||||||
cores_per_sockets = int(available_vcpus / psutil.cpu_count(logical=False))
|
cores_per_sockets = int(vcpus / psutil.cpu_count(logical=False))
|
||||||
if cores_per_sockets >= 1:
|
if cores_per_sockets > 1:
|
||||||
pairs["cpuid.corespersocket"] = str(cores_per_sockets)
|
pairs["cpuid.corespersocket"] = str(cores_per_sockets)
|
||||||
pairs["memsize"] = str(ram)
|
pairs["memsize"] = str(ram)
|
||||||
VMware.write_vmx_file(self._vmx_path, pairs)
|
VMware.write_vmx_file(self._vmx_path, pairs)
|
||||||
|
@ -58,7 +58,7 @@ class CrashReport:
|
|||||||
Report crash to a third party service
|
Report crash to a third party service
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DSN = "https://f5e2d00b16764b7d8bb996a9c8980185:4c01020e90ee4657956bc680a25f9959@sentry.io/38482"
|
DSN = "https://b53e4cbcb01c453f9728dbf891424d18:061bfafc9c184f9bb2012d394f860fc4@sentry.io/38482"
|
||||||
if hasattr(sys, "frozen"):
|
if hasattr(sys, "frozen"):
|
||||||
cacert = get_resource("cacert.pem")
|
cacert = get_resource("cacert.pem")
|
||||||
if cacert is not None and os.path.isfile(cacert):
|
if cacert is not None and os.path.isfile(cacert):
|
||||||
|
@ -82,13 +82,14 @@ class ComputeHandler:
|
|||||||
@Route.get(
|
@Route.get(
|
||||||
r"/computes/{compute_id}/{emulator}/images",
|
r"/computes/{compute_id}/{emulator}/images",
|
||||||
parameters={
|
parameters={
|
||||||
"compute_id": "Compute UUID"
|
"compute_id": "Compute UUID",
|
||||||
|
"emulator": "Emulator type"
|
||||||
},
|
},
|
||||||
status_codes={
|
status_codes={
|
||||||
200: "OK",
|
200: "OK",
|
||||||
404: "Instance doesn't exist"
|
404: "Instance doesn't exist"
|
||||||
},
|
},
|
||||||
description="Return the list of images available on compute and controller for this emulator type")
|
description="Return the list of images available on compute for this emulator type")
|
||||||
async def images(request, response):
|
async def images(request, response):
|
||||||
controller = Controller.instance()
|
controller = Controller.instance()
|
||||||
compute = controller.get_compute(request.match_info["compute_id"])
|
compute = controller.get_compute(request.match_info["compute_id"])
|
||||||
|
@ -32,8 +32,8 @@ from prompt_toolkit.shortcuts import create_prompt_application, create_asyncio_e
|
|||||||
from prompt_toolkit.terminal.vt100_output import Vt100_Output
|
from prompt_toolkit.terminal.vt100_output import Vt100_Output
|
||||||
from prompt_toolkit.input import StdinInput
|
from prompt_toolkit.input import StdinInput
|
||||||
|
|
||||||
from .telnet_server import AsyncioTelnetServer, TelnetConnection
|
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer, TelnetConnection
|
||||||
from .input_stream import InputStream
|
from gns3server.utils.asyncio.input_stream import InputStream
|
||||||
|
|
||||||
|
|
||||||
class EmbedShell:
|
class EmbedShell:
|
||||||
@ -344,7 +344,7 @@ if __name__ == '__main__':
|
|||||||
else:
|
else:
|
||||||
return 'world\n'
|
return 'world\n'
|
||||||
|
|
||||||
return (await world())
|
return await world()
|
||||||
|
|
||||||
# Demo using telnet
|
# Demo using telnet
|
||||||
shell = Demo(welcome_message="Welcome!\n")
|
shell = Demo(welcome_message="Welcome!\n")
|
||||||
|
@ -139,7 +139,7 @@ def images_directories(type):
|
|||||||
paths.append(directory)
|
paths.append(directory)
|
||||||
# Compatibility with old topologies we look in parent directory
|
# Compatibility with old topologies we look in parent directory
|
||||||
paths.append(img_dir)
|
paths.append(img_dir)
|
||||||
# Return only the existings paths
|
# Return only the existing paths
|
||||||
return [force_unix_path(p) for p in paths if os.path.exists(p)]
|
return [force_unix_path(p) for p in paths if os.path.exists(p)]
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
# or negative for a release candidate or beta (after the base version
|
# or negative for a release candidate or beta (after the base version
|
||||||
# number has been incremented)
|
# number has been incremented)
|
||||||
|
|
||||||
__version__ = "2.2.0dev6"
|
__version__ = "2.2.0dev7"
|
||||||
__version_info__ = (2, 2, 0, 99)
|
__version_info__ = (2, 2, 0, 99)
|
||||||
|
|
||||||
# If it's a git checkout try to add the commit
|
# If it's a git checkout try to add the commit
|
||||||
|
@ -179,6 +179,9 @@ class WebServer:
|
|||||||
return ssl_context
|
return ssl_context
|
||||||
|
|
||||||
async def start_shell(self):
|
async def start_shell(self):
|
||||||
|
|
||||||
|
log.error("The embedded shell has been deactivated in this version of GNS3")
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
from ptpython.repl import embed
|
from ptpython.repl import embed
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -6,6 +6,5 @@ async_generator>=1.10
|
|||||||
Jinja2>=2.7.3
|
Jinja2>=2.7.3
|
||||||
raven>=5.23.0
|
raven>=5.23.0
|
||||||
psutil>=3.0.0
|
psutil>=3.0.0
|
||||||
prompt-toolkit==1.0.15
|
|
||||||
async-timeout==3.0.1
|
async-timeout==3.0.1
|
||||||
distro>=1.3.0
|
distro>=1.3.0
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from tests.utils import AsyncioMagicMock
|
from tests.utils import AsyncioMagicMock
|
||||||
from gns3server.compute.dynamips.nodes.ethernet_switch import EthernetSwitchConsole
|
#from gns3server.compute.dynamips.nodes.ethernet_switch import EthernetSwitchConsole
|
||||||
from gns3server.compute.nios.nio_udp import NIOUDP
|
from gns3server.compute.nios.nio_udp import NIOUDP
|
||||||
|
|
||||||
|
|
||||||
@ -28,10 +28,10 @@ def test_mac_command(async_run):
|
|||||||
node.nios[0].name = "Ethernet0"
|
node.nios[0].name = "Ethernet0"
|
||||||
node.nios[1] = NIOUDP(55, "127.0.0.1", 56)
|
node.nios[1] = NIOUDP(55, "127.0.0.1", 56)
|
||||||
node.nios[1].name = "Ethernet1"
|
node.nios[1].name = "Ethernet1"
|
||||||
node._hypervisor.send = AsyncioMagicMock(return_value=["0050.7966.6801 1 Ethernet0", "0050.7966.6802 1 Ethernet1"])
|
#node._hypervisor.send = AsyncioMagicMock(return_value=["0050.7966.6801 1 Ethernet0", "0050.7966.6802 1 Ethernet1"])
|
||||||
console = EthernetSwitchConsole(node)
|
#console = EthernetSwitchConsole(node)
|
||||||
assert async_run(console.mac()) == \
|
#assert async_run(console.mac()) == \
|
||||||
"Port Mac VLAN\n" \
|
# "Port Mac VLAN\n" \
|
||||||
"Ethernet0 00:50:79:66:68:01 1\n" \
|
# "Ethernet0 00:50:79:66:68:01 1\n" \
|
||||||
"Ethernet1 00:50:79:66:68:02 1\n"
|
# "Ethernet1 00:50:79:66:68:02 1\n"
|
||||||
node._hypervisor.send.assert_called_with("ethsw show_mac_addr_table Test")
|
#node._hypervisor.send.assert_called_with("ethsw show_mac_addr_table Test")
|
||||||
|
@ -74,7 +74,7 @@ def test_vboxmanage_path(manager, tmpdir):
|
|||||||
def test_list_vms(manager, loop):
|
def test_list_vms(manager, loop):
|
||||||
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||||
'"Carriage',
|
'"Carriage',
|
||||||
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c3f3}',
|
||||||
'',
|
'',
|
||||||
'"<inaccessible>" {42b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
'"<inaccessible>" {42b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||||
'"Linux Microcore 4.7.1" {ccd8c50b-c172-457d-99fa-dd69371ede0e}']
|
'"Linux Microcore 4.7.1" {ccd8c50b-c172-457d-99fa-dd69371ede0e}']
|
||||||
@ -83,9 +83,10 @@ def test_list_vms(manager, loop):
|
|||||||
if cmd == "list":
|
if cmd == "list":
|
||||||
return vm_list
|
return vm_list
|
||||||
else:
|
else:
|
||||||
if args[0] == "Windows 8.1":
|
print(args)
|
||||||
|
if args[0] == "27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1":
|
||||||
return ["memory=512"]
|
return ["memory=512"]
|
||||||
elif args[0] == "Linux Microcore 4.7.1":
|
elif args[0] == "ccd8c50b-c172-457d-99fa-dd69371ede0e":
|
||||||
return ["memory=256"]
|
return ["memory=256"]
|
||||||
assert False, "Unknow {} {}".format(cmd, args)
|
assert False, "Unknow {} {}".format(cmd, args)
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ def test_rename_vmname(project, manager, async_run):
|
|||||||
def test_vm_valid_virtualbox_api_version(loop, project, manager):
|
def test_vm_valid_virtualbox_api_version(loop, project, manager):
|
||||||
with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute", return_value=["API version: 4_3"]):
|
with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute", return_value=["API version: 4_3"]):
|
||||||
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
|
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
|
||||||
|
vm._uuid = "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||||
loop.run_until_complete(asyncio.ensure_future(vm.create()))
|
loop.run_until_complete(asyncio.ensure_future(vm.create()))
|
||||||
|
|
||||||
|
|
||||||
|
@ -348,7 +348,7 @@ def test_forward_post(compute, async_run):
|
|||||||
|
|
||||||
def test_images(compute, async_run, images_dir):
|
def test_images(compute, async_run, images_dir):
|
||||||
"""
|
"""
|
||||||
Will return image on compute and on controller
|
Will return image on compute
|
||||||
"""
|
"""
|
||||||
response = MagicMock()
|
response = MagicMock()
|
||||||
response.status = 200
|
response.status = 200
|
||||||
@ -357,14 +357,12 @@ def test_images(compute, async_run, images_dir):
|
|||||||
"path": "linux.qcow2",
|
"path": "linux.qcow2",
|
||||||
"md5sum": "d41d8cd98f00b204e9800998ecf8427e",
|
"md5sum": "d41d8cd98f00b204e9800998ecf8427e",
|
||||||
"filesize": 0}]).encode())
|
"filesize": 0}]).encode())
|
||||||
open(os.path.join(images_dir, "QEMU", "asa.qcow2"), "w+").close()
|
|
||||||
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
|
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
|
||||||
images = async_run(compute.images("qemu"))
|
images = async_run(compute.images("qemu"))
|
||||||
mock.assert_called_with("GET", "https://example.com:84/v2/compute/qemu/images", auth=None, data=None, headers={'content-type': 'application/json'}, chunked=None, timeout=None)
|
mock.assert_called_with("GET", "https://example.com:84/v2/compute/qemu/images", auth=None, data=None, headers={'content-type': 'application/json'}, chunked=None, timeout=None)
|
||||||
async_run(compute.close())
|
async_run(compute.close())
|
||||||
|
|
||||||
assert images == [
|
assert images == [
|
||||||
{"filename": "asa.qcow2", "path": "asa.qcow2", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "filesize": 0},
|
|
||||||
{"filename": "linux.qcow2", "path": "linux.qcow2", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "filesize": 0}
|
{"filename": "linux.qcow2", "path": "linux.qcow2", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "filesize": 0}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from gns3server.utils.asyncio.embed_shell import EmbedShell
|
#from gns3server.utils.asyncio.embed_shell import EmbedShell
|
||||||
|
|
||||||
#FIXME: this is broken with recent Python >= 3.6
|
#FIXME: this is broken with recent Python >= 3.6
|
||||||
# def test_embed_shell_help(async_run):
|
# def test_embed_shell_help(async_run):
|
||||||
@ -39,44 +39,44 @@ from gns3server.utils.asyncio.embed_shell import EmbedShell
|
|||||||
# assert async_run(app._parse_command('? hello')) == 'hello: The hello world function\n\nThe hello usage\n'
|
# assert async_run(app._parse_command('? hello')) == 'hello: The hello world function\n\nThe hello usage\n'
|
||||||
|
|
||||||
|
|
||||||
def test_embed_shell_execute(async_run):
|
# def test_embed_shell_execute(async_run):
|
||||||
class Application(EmbedShell):
|
# class Application(EmbedShell):
|
||||||
|
#
|
||||||
async def hello(self):
|
# async def hello(self):
|
||||||
"""
|
# """
|
||||||
The hello world function
|
# The hello world function
|
||||||
|
#
|
||||||
The hello usage
|
# The hello usage
|
||||||
"""
|
# """
|
||||||
return 'world'
|
# return 'world'
|
||||||
reader = asyncio.StreamReader()
|
# reader = asyncio.StreamReader()
|
||||||
writer = asyncio.StreamReader()
|
# writer = asyncio.StreamReader()
|
||||||
app = Application(reader, writer)
|
# app = Application(reader, writer)
|
||||||
assert async_run(app._parse_command('hello')) == 'world'
|
# assert async_run(app._parse_command('hello')) == 'world'
|
||||||
|
#
|
||||||
|
#
|
||||||
def test_embed_shell_welcome(async_run, loop):
|
# def test_embed_shell_welcome(async_run, loop):
|
||||||
reader = asyncio.StreamReader()
|
# reader = asyncio.StreamReader()
|
||||||
writer = asyncio.StreamReader()
|
# writer = asyncio.StreamReader()
|
||||||
app = EmbedShell(reader, writer, welcome_message="Hello")
|
# app = EmbedShell(reader, writer, welcome_message="Hello")
|
||||||
task = loop.create_task(app.run())
|
# task = loop.create_task(app.run())
|
||||||
assert async_run(writer.read(5)) == b"Hello"
|
# assert async_run(writer.read(5)) == b"Hello"
|
||||||
task.cancel()
|
# task.cancel()
|
||||||
try:
|
# try:
|
||||||
loop.run_until_complete(task)
|
# loop.run_until_complete(task)
|
||||||
except asyncio.CancelledError:
|
# except asyncio.CancelledError:
|
||||||
pass
|
# pass
|
||||||
|
#
|
||||||
|
#
|
||||||
def test_embed_shell_prompt(async_run, loop):
|
# def test_embed_shell_prompt(async_run, loop):
|
||||||
reader = asyncio.StreamReader()
|
# reader = asyncio.StreamReader()
|
||||||
writer = asyncio.StreamReader()
|
# writer = asyncio.StreamReader()
|
||||||
app = EmbedShell(reader, writer)
|
# app = EmbedShell(reader, writer)
|
||||||
app.prompt = "gbash# "
|
# app.prompt = "gbash# "
|
||||||
task = loop.create_task(app.run())
|
# task = loop.create_task(app.run())
|
||||||
assert async_run(writer.read(7)) == b"gbash# "
|
# assert async_run(writer.read(7)) == b"gbash# "
|
||||||
task.cancel()
|
# task.cancel()
|
||||||
try:
|
# try:
|
||||||
loop.run_until_complete(task)
|
# loop.run_until_complete(task)
|
||||||
except asyncio.CancelledError:
|
# except asyncio.CancelledError:
|
||||||
pass
|
# pass
|
||||||
|
Loading…
Reference in New Issue
Block a user