Merge branch '2.2' into 3.0

# Conflicts:
#	gns3server/controller/__init__.py
#	gns3server/controller/compute.py
#	gns3server/schemas/gns3vm.py
#	gns3server/utils/application_id.py
#	gns3server/version.py
#	gns3server/web/web_server.py
This commit is contained in:
grossmj 2020-11-05 17:15:25 +10:30
commit a5d89b428a
29 changed files with 206 additions and 85 deletions

View File

@ -1,5 +1,16 @@
# Change Log
## 2.2.16 05/11/2020
* Option to allocate or not the vCPUs and RAM settings for the GNS3 VM. Fixes https://github.com/GNS3/gns3-gui/issues/3069
* Release Web UI version 2.2.16
* Fix wrong defaults for images_path, configs_path, appliances_path. Fixes #1829
* Use EnvironmentFile for Systemd service. Ref https://github.com/GNS3/gns3-gui/issues/3048
* Fix SSL support for controller and local compute. Fixes #1826
* Prevent WIC to be added/removed while Dynamips router is running. Fixes https://github.com/GNS3/gns3-gui/issues/3082
* Fix bug with application id allocation for IOU nodes. Fixes #3079
* Allow commas in image paths and VM name for Qemu VMs. Fixes https://github.com/GNS3/gns3-gui/issues/3065
## 2.2.15 07/10/2020
* Fix symbol retrieval issue. Ref #1824

View File

@ -27,10 +27,17 @@
},
"images": [
{
"filename": "vEOS-lab-4.25.0FX-LDP-RSVP.vmdk",
"version": "4.25.0FX",
"md5sum": "b7c2efdbe48301a78f124db989710346",
"filesize": 468647936,
"filename": "vEOS-lab-4.25.0F.vmdk",
"version": "4.25.0F",
"md5sum": "d420763fdf3bc50e7e5b88418bd9d1fd",
"filesize": 468779008,
"download_url": "https://www.arista.com/en/support/software-download"
},
{
"filename": "vEOS-lab-4.24.3M.vmdk",
"version": "4.24.3M",
"md5sum": "0a28e44c7ce4a8965f24a4a463a89b7d",
"filesize": 455213056,
"download_url": "https://www.arista.com/en/support/software-download"
},
{
@ -204,10 +211,17 @@
],
"versions": [
{
"name": "4.25.0FX",
"name": "4.25.0F",
"images": {
"hda_disk_image": "Aboot-veos-serial-8.0.0.iso",
"hdb_disk_image": "vEOS-lab-4.25.0FX-LDP-RSVP.vmdk"
"hdb_disk_image": "vEOS-lab-4.25.0F.vmdk"
}
},
{
"name": "4.24.3M",
"images": {
"hda_disk_image": "Aboot-veos-serial-8.0.0.iso",
"hdb_disk_image": "vEOS-lab-4.24.3M.vmdk"
}
},
{

View File

@ -20,7 +20,8 @@
"hda_disk_interface": "virtio",
"arch": "x86_64",
"console_type": "telnet",
"kvm": "require"
"kvm": "require",
"options": "-machine pc-q35-4.2"
},
"images": [
{

View File

@ -26,6 +26,13 @@
"options": "-nographic"
},
"images": [
{
"filename": "VOSSGNS3.8.2.0.0.qcow2",
"version": "v8.2.0.0",
"md5sum": "9a0cd77c08644abbf3a69771c125c011",
"filesize": 331808768,
"direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_VOSS/VOSSGNS3.8.2.0.0.qcow2"
},
{
"filename": "VOSSGNS3.8.1.5.0.qcow2",
"version": "8.1.5.0",
@ -56,6 +63,12 @@
}
],
"versions": [
{
"name": "v8.2.0.0",
"images": {
"hda_disk_image": "VOSSGNS3.8.2.0.0.qcow2"
}
},
{
"name": "8.1.5.0",
"images": {

View File

@ -27,6 +27,20 @@
"options": "-smp 2 -cpu host"
},
"images": [
{
"filename": "BIGIP-16.0.0.1-0.0.3.qcow2",
"version": "16.0.0.1",
"md5sum": "95ff618b7d0b53c4252299cd49b0e564",
"filesize": 5393088512,
"download_url": "https://downloads.f5.com/esd/serveDownload.jsp?path=/big-ip/big-ip_v16.x/16.0.0/english/16.0.0.1_virtual-edition/&sw=BIG-IP&pro=big-ip_v16.x&ver=16.0.0&container=16.0.0.1_Virtual-Edition&file=BIGIP-16.0.0.1-0.0.3.ALL.qcow2.zip"
},
{
"filename": "BIGIP-16.0.0-0.0.12.qcow2",
"version": "16.0.0",
"md5sum": "c49cd2513e386f3259eb0ee6fe3bb502",
"filesize": 5344722944,
"download_url": "https://downloads.f5.com/esd/serveDownload.jsp?path=/big-ip/big-ip_v16.x/16.0.0/english/16.0.0_virtual-edition/&sw=BIG-IP&pro=big-ip_v16.x&ver=16.0.0&container=16.0.0_Virtual-Edition&file=BIGIP-16.0.0-0.0.12.ALL.qcow2.zip"
},
{
"filename": "BIGIP-15.1.0.2-0.0.9.qcow2",
"version": "15.1.0.2",
@ -163,6 +177,20 @@
}
],
"versions": [
{
"name": "16.0.0.1",
"images": {
"hda_disk_image": "BIGIP-16.0.0.1-0.0.3.qcow2",
"hdb_disk_image": "empty100G.qcow2"
}
},
{
"name": "16.0.0",
"images": {
"hda_disk_image": "BIGIP-16.0.0-0.0.12.qcow2",
"hdb_disk_image": "empty100G.qcow2"
}
},
{
"name": "15.1.0.2",
"images": {

View File

@ -25,31 +25,31 @@
},
"images": [
{
"filename": "OPNsense-18.1.6-OpenSSL-nano-amd64.img",
"version": "18.1.6",
"md5sum": "042f328380ad0c8008759c43435e8843",
"filesize": 272003136,
"download_url": "https://opnsense.c0urier.net/releases/18.1/"
"filename": "OPNsense-20.7-OpenSSL-nano-amd64.img",
"version": "20.7",
"md5sum": "453e505e9526d4a0a3d5208efdd13b1a",
"filesize": 3221225472,
"download_url": "https://opnsense.c0urier.net/releases/20.7/"
},
{
"filename": "OPNsense-17.7.5-OpenSSL-nano-amd64.img",
"version": "17.7.5",
"md5sum": "6ec5b7f99cc727f904bbf2aaadcab0b8",
"filesize": 237038601,
"download_url": "https://opnsense.c0urier.net/releases/17.7/"
"filename": "OPNsense-19.7-OpenSSL-nano-amd64.img",
"version": "19.7",
"md5sum": "a15a00cfa2de45791d6bc230d8469dc7",
"filesize": 3221225472,
"download_url": "https://opnsense.c0urier.net/releases/19.7/"
}
],
"versions": [
{
"name": "18.1.6",
"name": "20.7",
"images": {
"hda_disk_image": "OPNsense-18.1.6-OpenSSL-nano-amd64.img"
"hda_disk_image": "OPNsense-20.7-OpenSSL-nano-amd64.img"
}
},
{
"name": "17.7.5",
"name": "19.7",
"images": {
"hda_disk_image": "OPNsense-17.7.5-OpenSSL-nano-amd64.img"
"hda_disk_image": "OPNsense-19.7-OpenSSL-nano-amd64.img"
}
}
]

View File

@ -11,12 +11,12 @@
"status": "stable",
"maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net",
"usage": "Credentials: SSH ---> username: root ---> password: 1234 MySQL DB: ---> username: root --> password: tacacs Web interface: ---> username: tacgui ---> password: abc123",
"usage": "Credentials:\nSSH ---> username: root ---> password: 1234\nMySQL DB: ---> username: root --> password: tacacs\nWeb interface: ---> username: tacgui ---> password: abc123\n\nDefault for 0.9.82 or above:\nIP Address: 10.0.0.254\nNetmask: 255.0.0.0\nGateway: 10.0.0.1",
"port_name_format": "Port{port1}",
"qemu": {
"adapter_type": "e1000",
"adapters": 1,
"ram": 1024,
"ram": 4096,
"hda_disk_interface": "ide",
"arch": "x86_64",
"console_type": "telnet",
@ -24,6 +24,13 @@
"kvm": "allow"
},
"images": [
{
"filename": "tacgui-0.9.82-20201008.qcow2",
"version": "0.9.82",
"md5sum": "dc0c84aa61d8960a23bf3b309a826f3f",
"filesize": 2914844672,
"download_url": "https://drive.google.com/open?id=1tlDSyoD5dAWgJu6I76CgYV7BkwhScWSS"
},
{
"filename": "tac_plus.qcow2",
"version": "201710201114",
@ -33,6 +40,12 @@
}
],
"versions": [
{
"name": "0.9.82",
"images": {
"hda_disk_image": "tacgui-0.9.82-20201008.qcow2"
}
},
{
"name": "201710201114",
"images": {

View File

@ -1,7 +1,7 @@
{
"name": "Tiny Core Linux",
"category": "guest",
"description": "Core Linux is a smaller variant of Tiny Core without a graphical desktop.\n\nIt's provide a complete Linux system in few MB.",
"description": "Core Linux is a smaller variant of Tiny Core without a graphical desktop.\n\nIt provides a complete Linux system using only a few MiB." ,
"vendor_name": "Team Tiny Core",
"vendor_url": "http://distro.ibiblio.org/tinycorelinux",
"documentation_url": "http://wiki.tinycorelinux.net/",

View File

@ -25,6 +25,13 @@
"options": "-vga virtio"
},
"images": [
{
"filename": "Ubuntu 20.04 (64bit).vmdk",
"version": "20.04",
"md5sum": "cf619dfe9bb8d89e2b18b067f02e57a0",
"filesize": 6629883904,
"download_url": "http://www.osboxes.org/ubuntu/"
},
{
"filename": "Ubuntu 19.04 (64bit).vmdk",
"version": "19.04",
@ -55,6 +62,12 @@
}
],
"versions": [
{
"name": "20.04",
"images": {
"hda_disk_image": "Ubuntu 20.04 (64bit).vmdk"
}
},
{
"name": "19.04",
"images": {

View File

@ -1184,13 +1184,17 @@ class Router(BaseNode):
if not adapter.wic_slot_available(wic_slot_number):
raise DynamipsError("WIC slot {wic_slot_number} is already occupied by another WIC".format(wic_slot_number=wic_slot_number))
if await self.is_running():
raise DynamipsError('WIC "{wic}" cannot be added while router "{name}" is running'.format(wic=wic,
name=self._name))
# Dynamips WICs slot IDs start on a multiple of 16
# WIC1 = 16, WIC2 = 32 and WIC3 = 48
internal_wic_slot_number = 16 * (wic_slot_number + 1)
await self._hypervisor.send('vm slot_add_binding "{name}" {slot_number} {wic_slot_number} {wic}'.format(name=self._name,
slot_number=slot_number,
wic_slot_number=internal_wic_slot_number,
wic=wic))
slot_number=slot_number,
wic_slot_number=internal_wic_slot_number,
wic=wic))
log.info('Router "{name}" [{id}]: {wic} inserted into WIC slot {wic_slot_number}'.format(name=self._name,
id=self._id,
@ -1219,12 +1223,16 @@ class Router(BaseNode):
if adapter.wic_slot_available(wic_slot_number):
raise DynamipsError("No WIC is installed in WIC slot {wic_slot_number}".format(wic_slot_number=wic_slot_number))
if await self.is_running():
raise DynamipsError('WIC cannot be removed from slot {wic_slot_number} while router "{name}" is running'.format(wic_slot_number=wic_slot_number,
name=self._name))
# Dynamips WICs slot IDs start on a multiple of 16
# WIC1 = 16, WIC2 = 32 and WIC3 = 48
internal_wic_slot_number = 16 * (wic_slot_number + 1)
await self._hypervisor.send('vm slot_remove_binding "{name}" {slot_number} {wic_slot_number}'.format(name=self._name,
slot_number=slot_number,
wic_slot_number=internal_wic_slot_number))
slot_number=slot_number,
wic_slot_number=internal_wic_slot_number))
log.info('Router "{name}" [{id}]: {wic} removed from WIC slot {wic_slot_number}'.format(name=self._name,
id=self._id,

View File

@ -268,7 +268,7 @@ class Controller:
"""
server_config = Config.instance().get_section_config("Server")
images_path = os.path.expanduser(server_config.get("images_path", "~/GNS3/projects"))
images_path = os.path.expanduser(server_config.get("images_path", "~/GNS3/images"))
os.makedirs(images_path, exist_ok=True)
return images_path
@ -278,9 +278,9 @@ class Controller:
"""
server_config = Config.instance().get_section_config("Server")
images_path = os.path.expanduser(server_config.get("configs_path", "~/GNS3/projects"))
os.makedirs(images_path, exist_ok=True)
return images_path
configs_path = os.path.expanduser(server_config.get("configs_path", "~/GNS3/configs"))
os.makedirs(configs_path, exist_ok=True)
return configs_path
async def add_compute(self, compute_id=None, name=None, force=False, connect=True, **kwargs):
"""

View File

@ -71,7 +71,7 @@ class ApplianceManager:
"""
server_config = Config.instance().get_section_config("Server")
appliances_path = os.path.expanduser(server_config.get("appliances_path", "~/GNS3/projects"))
appliances_path = os.path.expanduser(server_config.get("appliances_path", "~/GNS3/appliances"))
os.makedirs(appliances_path, exist_ok=True)
return appliances_path

View File

@ -64,8 +64,8 @@ class Compute:
A GNS3 compute.
"""
def __init__(self, compute_id, controller=None, protocol="http", host="localhost", port=3080, user=None,
password=None, name=None, console_host=None):
def __init__(self, compute_id, controller=None, protocol="http", host="localhost",
port=3080, user=None, password=None, name=None, console_host=None, ssl_context=None):
self._http_session = None
assert controller is not None
log.info("Create compute %s", compute_id)
@ -90,6 +90,7 @@ class Compute:
self._memory_usage_percent = 0
self._disk_usage_percent = 0
self._last_error = None
self._ssl_context = ssl_context
self._capabilities = {
"version": "",
"platform": "",
@ -105,7 +106,7 @@ class Compute:
def _session(self):
if self._http_session is None or self._http_session.closed is True:
connector = aiohttp.TCPConnector(force_close=True)
connector = aiohttp.TCPConnector(force_close=True, ssl_context=self._ssl_context)
self._http_session = aiohttp.ClientSession(connector=connector)
return self._http_session

View File

@ -49,6 +49,7 @@ class GNS3VM:
"headless": False,
"enable": False,
"engine": "vmware",
"allocate_vcpus_ram": True,
"ram": 2048,
"vcpus": 1,
"port": 80,
@ -311,6 +312,7 @@ class GNS3VM:
if self._settings["vmname"] is None:
return
log.info("Start the GNS3 VM")
engine.allocate_vcpus_ram = self._settings["allocate_vcpus_ram"]
engine.vmname = self._settings["vmname"]
engine.ram = self._settings["ram"]
engine.vcpus = self._settings["vcpus"]

View File

@ -30,6 +30,7 @@ class BaseGNS3VM:
self._ip_address = None
self._port = 80 # value not used, will be overwritten
self._headless = False
self._allocate_vcpus_ram = True
self._vcpus = 1
self._ram = 1024
self._user = ""
@ -203,6 +204,26 @@ class BaseGNS3VM:
self._headless = value
@property
def allocate_vcpus_ram(self):
"""
Returns whether VCPUs and RAM settings should be configured for the GNS3 VM.
:returns: boolean
"""
return self._allocate_vcpus_ram
@allocate_vcpus_ram.setter
def allocate_vcpus_ram(self, value):
"""
Sets whether VCPUs and RAM settings should be configured for the GNS3 VM.
:param value: boolean
"""
self._allocate_vcpus_ram = value
@property
def vcpus(self):
"""

View File

@ -251,9 +251,10 @@ class HyperVGNS3VM(BaseGNS3VM):
raise GNS3VMError("Could not find Hyper-V VM {}".format(self.vmname))
if not self._is_running():
log.info("Update GNS3 VM settings (CPU and RAM)")
# set the number of vCPUs and amount of RAM
self._set_vcpus_ram(self.vcpus, self.ram)
if self.allocate_vcpus_ram:
log.info("Update GNS3 VM settings (CPU and RAM)")
# set the number of vCPUs and amount of RAM
self._set_vcpus_ram(self.vcpus, self.ram)
# start the VM
try:

View File

@ -15,8 +15,6 @@
# 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 asyncio
from .base_gns3_vm import BaseGNS3VM
from .gns3_vm_error import GNS3VMError

View File

@ -260,9 +260,12 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
log.info('"{}" state is {}'.format(self._vmname, vm_state))
if vm_state == "poweroff":
log.info("Update GNS3 VM settings (CPU, RAM and Hardware Virtualization)")
await self.set_vcpus(self.vcpus)
await self.set_ram(self.ram)
if self.allocate_vcpus_ram:
log.info("Update GNS3 VM vCPUs and RAM settings")
await self.set_vcpus(self.vcpus)
await self.set_ram(self.ram)
log.info("Update GNS3 VM Hardware Virtualization setting")
await self.enable_nested_hw_virt()
if vm_state in ("poweroff", "saved"):

View File

@ -148,9 +148,12 @@ class VMwareGNS3VM(BaseGNS3VM):
except VMwareError as e:
raise GNS3VMError("Could not list VMware VMs: {}".format(str(e)))
if not running:
log.info("Update GNS3 VM settings (CPU, RAM and Hardware Virtualization)")
# set the number of vCPUs and amount of RAM
await self._set_vcpus_ram(self.vcpus, self.ram)
if self.allocate_vcpus_ram:
log.info("Update GNS3 VM vCPUs and RAM settings")
await self._set_vcpus_ram(self.vcpus, self.ram)
log.info("Update GNS3 VM Hardware Virtualization setting")
await self._set_extra_options()
# start the VM

View File

@ -174,6 +174,7 @@ class Project:
self._links = {}
self._drawings = {}
self._snapshots = {}
self._computes = []
# List the available snapshots
snapshot_dir = os.path.join(self.path, "snapshots")
@ -564,6 +565,9 @@ class Project:
if node_id in self._nodes:
return self._nodes[node_id]
if compute.id not in self._computes:
self._computes.append(compute.id)
if node_type == "iou":
async with self._iou_id_lock:
# wait for a IOU node to be completely created before adding a new one
@ -571,10 +575,10 @@ class Project:
# to generate MAC addresses) when creating multiple IOU node at the same time
if "properties" in kwargs.keys():
# allocate a new application id for nodes loaded from the project
kwargs.get("properties")["application_id"] = get_next_application_id(self._controller.projects, compute)
kwargs.get("properties")["application_id"] = get_next_application_id(self._controller.projects, self._computes)
elif "application_id" not in kwargs.keys() and not kwargs.get("properties"):
# allocate a new application id for nodes added to the project
kwargs["application_id"] = get_next_application_id(self._controller.projects, compute)
kwargs["application_id"] = get_next_application_id(self._controller.projects, self._computes)
node = await self._create_node(compute, name, node_id, node_type, **kwargs)
else:
node = await self._create_node(compute, name, node_id, node_type, **kwargs)
@ -604,6 +608,8 @@ class Project:
self.remove_allocated_node_name(node.name)
del self._nodes[node.id]
await node.destroy()
# refresh the compute IDs list
self._computes = [n.compute.id for n in self.nodes.values()]
self.dump()
self.emit_notification("node.deleted", node.__json__())
@ -931,6 +937,14 @@ class Project:
topology = project_data["topology"]
for compute in topology.get("computes", []):
await self.controller.add_compute(**compute)
# Get all compute used in the project
# used to allocate application IDs for IOU nodes.
for node in topology.get("nodes", []):
compute_id = node.get("compute_id")
if compute_id not in self._computes:
self._computes.append(compute_id)
for node in topology.get("nodes", []):
compute = self.controller.get_compute(node.pop("compute_id"))
name = node.pop("name")

View File

@ -58,7 +58,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "https://c0b6ce011d024391831923745a47c33f:459ea5884d3944f092b02e4183cb6d52@o19455.ingest.sentry.io/38482"
DSN = "https://026410fd151843438d078e604f2e4455:1792bf69988342c7b44f8a69ae0cad6f@o19455.ingest.sentry.io/38482"
_instance = None
def __init__(self):

View File

@ -51,6 +51,7 @@ class GNS3VM(BaseModel):
when_exit: Optional[WhenExit] = Field(None, description="Action when the GNS3 VM exits")
headless: Optional[bool] = Field(None, description="Start the GNS3 VM GUI or not")
engine: Optional[Engine] = Field(None, description="The engine to use for the GNS3 VM")
allocate_vcpus_ram: Optional[bool] = Field(None, description="Allocate vCPUS and RAM settings")
vcpus: Optional[int] = Field(None, description="Number of CPUs to allocate for the GNS3 VM")
ram: Optional[int] = Field(None, description="Amount of memory to allocate for the GNS3 VM")
port: Optional[int] = Field(None, gt=0, le=65535)

View File

@ -418,31 +418,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
angular2-hotkeys
MIT
The MIT License (MIT)
Copyright (c) 2016 Nick Richardson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
angular2-indexeddb
MIT
The MIT License (MIT)

View File

@ -48,5 +48,5 @@
gtag('config', 'G-5D6FZL9923');
</script>
<script src="runtime.acf0dec4155e77772545.js" defer></script><script src="polyfills-es5.81e70f503de320d59b44.js" nomodule defer></script><script src="polyfills.7ae454d57e44d75e5d10.js" defer></script><script src="main.b8ab802a67c1c69cf879.js" defer></script></body>
<script src="runtime.acf0dec4155e77772545.js" defer></script><script src="polyfills-es5.81e70f503de320d59b44.js" nomodule defer></script><script src="polyfills.7ae454d57e44d75e5d10.js" defer></script><script src="main.8367ffc0bf45ea7cf3c7.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -21,26 +21,27 @@ import logging
log = logging.getLogger(__name__)
def get_next_application_id(projects, compute):
def get_next_application_id(projects, computes):
"""
Calculates free application_id from given nodes
:param projects: all projects managed by controller
:param compute: Compute instance
:param computes: all computes used by the project
:raises HTTPConflict when exceeds number
:return: integer first free id
"""
nodes = []
# look for application id for in all nodes across all opened projects that share the same compute
# look for application id for in all nodes across all opened projects that share the same computes
for project in projects.values():
if project.status == "opened" and compute in project.computes:
if project.status == "opened":
nodes.extend(list(project.nodes.values()))
used = set([n.properties["application_id"] for n in nodes if n.node_type == "iou"])
used = set([n.properties["application_id"] for n in nodes if n.node_type == "iou" and n.compute.id in computes])
pool = set(range(1, 512))
try:
return (pool - used).pop()
application_id = (pool - used).pop()
return application_id
except KeyError:
raise ComputeError("Cannot create a new IOU node (limit of 512 nodes across all opened projects using compute {} reached".format(compute.name))
raise ComputeError(text="Cannot create a new IOU node (limit of 512 nodes across all opened projects using the same computes)")

View File

View File

@ -260,7 +260,7 @@ Conflicts=shutdown.target
User=gns3
Group=gns3
PermissionsStartOnly=true
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
EnvironmentFile=/etc/environment
ExecStartPre=/bin/mkdir -p /var/log/gns3 /var/run/gns3
ExecStartPre=/bin/chown -R gns3:gns3 /var/log/gns3 /var/run/gns3
ExecStart=/usr/bin/gns3server --log /var/log/gns3/gns3.log