1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-26 16:58:28 +00:00

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 # 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 ## 2.2.15 07/10/2020
* Fix symbol retrieval issue. Ref #1824 * Fix symbol retrieval issue. Ref #1824

View File

@ -27,10 +27,17 @@
}, },
"images": [ "images": [
{ {
"filename": "vEOS-lab-4.25.0FX-LDP-RSVP.vmdk", "filename": "vEOS-lab-4.25.0F.vmdk",
"version": "4.25.0FX", "version": "4.25.0F",
"md5sum": "b7c2efdbe48301a78f124db989710346", "md5sum": "d420763fdf3bc50e7e5b88418bd9d1fd",
"filesize": 468647936, "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" "download_url": "https://www.arista.com/en/support/software-download"
}, },
{ {
@ -204,10 +211,17 @@
], ],
"versions": [ "versions": [
{ {
"name": "4.25.0FX", "name": "4.25.0F",
"images": { "images": {
"hda_disk_image": "Aboot-veos-serial-8.0.0.iso", "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", "hda_disk_interface": "virtio",
"arch": "x86_64", "arch": "x86_64",
"console_type": "telnet", "console_type": "telnet",
"kvm": "require" "kvm": "require",
"options": "-machine pc-q35-4.2"
}, },
"images": [ "images": [
{ {

View File

@ -26,6 +26,13 @@
"options": "-nographic" "options": "-nographic"
}, },
"images": [ "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", "filename": "VOSSGNS3.8.1.5.0.qcow2",
"version": "8.1.5.0", "version": "8.1.5.0",
@ -56,6 +63,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "v8.2.0.0",
"images": {
"hda_disk_image": "VOSSGNS3.8.2.0.0.qcow2"
}
},
{ {
"name": "8.1.5.0", "name": "8.1.5.0",
"images": { "images": {

View File

@ -27,6 +27,20 @@
"options": "-smp 2 -cpu host" "options": "-smp 2 -cpu host"
}, },
"images": [ "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", "filename": "BIGIP-15.1.0.2-0.0.9.qcow2",
"version": "15.1.0.2", "version": "15.1.0.2",
@ -163,6 +177,20 @@
} }
], ],
"versions": [ "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", "name": "15.1.0.2",
"images": { "images": {

View File

@ -25,31 +25,31 @@
}, },
"images": [ "images": [
{ {
"filename": "OPNsense-18.1.6-OpenSSL-nano-amd64.img", "filename": "OPNsense-20.7-OpenSSL-nano-amd64.img",
"version": "18.1.6", "version": "20.7",
"md5sum": "042f328380ad0c8008759c43435e8843", "md5sum": "453e505e9526d4a0a3d5208efdd13b1a",
"filesize": 272003136, "filesize": 3221225472,
"download_url": "https://opnsense.c0urier.net/releases/18.1/" "download_url": "https://opnsense.c0urier.net/releases/20.7/"
}, },
{ {
"filename": "OPNsense-17.7.5-OpenSSL-nano-amd64.img", "filename": "OPNsense-19.7-OpenSSL-nano-amd64.img",
"version": "17.7.5", "version": "19.7",
"md5sum": "6ec5b7f99cc727f904bbf2aaadcab0b8", "md5sum": "a15a00cfa2de45791d6bc230d8469dc7",
"filesize": 237038601, "filesize": 3221225472,
"download_url": "https://opnsense.c0urier.net/releases/17.7/" "download_url": "https://opnsense.c0urier.net/releases/19.7/"
} }
], ],
"versions": [ "versions": [
{ {
"name": "18.1.6", "name": "20.7",
"images": { "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": { "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", "status": "stable",
"maintainer": "GNS3 Team", "maintainer": "GNS3 Team",
"maintainer_email": "developers@gns3.net", "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}", "port_name_format": "Port{port1}",
"qemu": { "qemu": {
"adapter_type": "e1000", "adapter_type": "e1000",
"adapters": 1, "adapters": 1,
"ram": 1024, "ram": 4096,
"hda_disk_interface": "ide", "hda_disk_interface": "ide",
"arch": "x86_64", "arch": "x86_64",
"console_type": "telnet", "console_type": "telnet",
@ -24,6 +24,13 @@
"kvm": "allow" "kvm": "allow"
}, },
"images": [ "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", "filename": "tac_plus.qcow2",
"version": "201710201114", "version": "201710201114",
@ -33,6 +40,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "0.9.82",
"images": {
"hda_disk_image": "tacgui-0.9.82-20201008.qcow2"
}
},
{ {
"name": "201710201114", "name": "201710201114",
"images": { "images": {

View File

@ -1,7 +1,7 @@
{ {
"name": "Tiny Core Linux", "name": "Tiny Core Linux",
"category": "guest", "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_name": "Team Tiny Core",
"vendor_url": "http://distro.ibiblio.org/tinycorelinux", "vendor_url": "http://distro.ibiblio.org/tinycorelinux",
"documentation_url": "http://wiki.tinycorelinux.net/", "documentation_url": "http://wiki.tinycorelinux.net/",

View File

@ -25,6 +25,13 @@
"options": "-vga virtio" "options": "-vga virtio"
}, },
"images": [ "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", "filename": "Ubuntu 19.04 (64bit).vmdk",
"version": "19.04", "version": "19.04",
@ -55,6 +62,12 @@
} }
], ],
"versions": [ "versions": [
{
"name": "20.04",
"images": {
"hda_disk_image": "Ubuntu 20.04 (64bit).vmdk"
}
},
{ {
"name": "19.04", "name": "19.04",
"images": { "images": {

View File

@ -1184,13 +1184,17 @@ class Router(BaseNode):
if not adapter.wic_slot_available(wic_slot_number): 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)) 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 # Dynamips WICs slot IDs start on a multiple of 16
# WIC1 = 16, WIC2 = 32 and WIC3 = 48 # WIC1 = 16, WIC2 = 32 and WIC3 = 48
internal_wic_slot_number = 16 * (wic_slot_number + 1) 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, await self._hypervisor.send('vm slot_add_binding "{name}" {slot_number} {wic_slot_number} {wic}'.format(name=self._name,
slot_number=slot_number, slot_number=slot_number,
wic_slot_number=internal_wic_slot_number, wic_slot_number=internal_wic_slot_number,
wic=wic)) wic=wic))
log.info('Router "{name}" [{id}]: {wic} inserted into WIC slot {wic_slot_number}'.format(name=self._name, log.info('Router "{name}" [{id}]: {wic} inserted into WIC slot {wic_slot_number}'.format(name=self._name,
id=self._id, id=self._id,
@ -1219,12 +1223,16 @@ class Router(BaseNode):
if adapter.wic_slot_available(wic_slot_number): 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)) 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 # Dynamips WICs slot IDs start on a multiple of 16
# WIC1 = 16, WIC2 = 32 and WIC3 = 48 # WIC1 = 16, WIC2 = 32 and WIC3 = 48
internal_wic_slot_number = 16 * (wic_slot_number + 1) 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, await self._hypervisor.send('vm slot_remove_binding "{name}" {slot_number} {wic_slot_number}'.format(name=self._name,
slot_number=slot_number, slot_number=slot_number,
wic_slot_number=internal_wic_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, log.info('Router "{name}" [{id}]: {wic} removed from WIC slot {wic_slot_number}'.format(name=self._name,
id=self._id, id=self._id,

View File

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

View File

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

View File

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

View File

@ -30,6 +30,7 @@ class BaseGNS3VM:
self._ip_address = None self._ip_address = None
self._port = 80 # value not used, will be overwritten self._port = 80 # value not used, will be overwritten
self._headless = False self._headless = False
self._allocate_vcpus_ram = True
self._vcpus = 1 self._vcpus = 1
self._ram = 1024 self._ram = 1024
self._user = "" self._user = ""
@ -203,6 +204,26 @@ class BaseGNS3VM:
self._headless = value 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 @property
def vcpus(self): def vcpus(self):
""" """

View File

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

View File

@ -15,8 +15,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
from .base_gns3_vm import BaseGNS3VM from .base_gns3_vm import BaseGNS3VM
from .gns3_vm_error import GNS3VMError from .gns3_vm_error import GNS3VMError

View File

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

View File

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

View File

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

View File

@ -58,7 +58,7 @@ class CrashReport:
Report crash to a third party service 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 _instance = None
def __init__(self): 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") 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") 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") 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") 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") ram: Optional[int] = Field(None, description="Amount of memory to allocate for the GNS3 VM")
port: Optional[int] = Field(None, gt=0, le=65535) 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. 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 angular2-indexeddb
MIT MIT
The MIT License (MIT) The MIT License (MIT)

View File

@ -48,5 +48,5 @@
gtag('config', 'G-5D6FZL9923'); gtag('config', 'G-5D6FZL9923');
</script> </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> </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__) 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 Calculates free application_id from given nodes
:param projects: all projects managed by controller :param projects: all projects managed by controller
:param compute: Compute instance :param computes: all computes used by the project
:raises HTTPConflict when exceeds number :raises HTTPConflict when exceeds number
:return: integer first free id :return: integer first free id
""" """
nodes = [] 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(): for project in projects.values():
if project.status == "opened" and compute in project.computes: if project.status == "opened":
nodes.extend(list(project.nodes.values())) 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)) pool = set(range(1, 512))
try: try:
return (pool - used).pop() application_id = (pool - used).pop()
return application_id
except KeyError: 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 User=gns3
Group=gns3 Group=gns3
PermissionsStartOnly=true 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/mkdir -p /var/log/gns3 /var/run/gns3
ExecStartPre=/bin/chown -R gns3:gns3 /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 ExecStart=/usr/bin/gns3server --log /var/log/gns3/gns3.log