diff --git a/MANIFEST.in b/MANIFEST.in index 61bdd940..38cadc48 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,5 @@ include README.rst include AUTHORS -include INSTALL include LICENSE include MANIFEST.in include tox.ini diff --git a/gns3server/appliances/cisco-7200.gns3a b/gns3server/appliances/cisco-7200.gns3a index 538656bd..d3db50e1 100644 --- a/gns3server/appliances/cisco-7200.gns3a +++ b/gns3server/appliances/cisco-7200.gns3a @@ -22,14 +22,14 @@ "images": [ { "filename": "c7200-adventerprisek9-mz.124-24.T5.image", - "version": "124-25.T5", + "version": "124-24.T5", "md5sum": "6b89d0d804e1f2bb5b8bda66b5692047", "filesize": 102345240 } ], "versions": [ { - "name": "124-25.T5", + "name": "124-24.T5", "idlepc": "0x606df838", "images": { "image": "c7200-adventerprisek9-mz.124-24.T5.image" diff --git a/gns3server/appliances/cisco-nxosv9k.gns3a b/gns3server/appliances/cisco-nxosv9k.gns3a index 122f6534..653f4920 100644 --- a/gns3server/appliances/cisco-nxosv9k.gns3a +++ b/gns3server/appliances/cisco-nxosv9k.gns3a @@ -25,6 +25,13 @@ "kvm": "require" }, "images": [ + { + "filename": "nxosv-final.7.0.3.I7.3.qcow2", + "version": "7.0.3.I7.3", + "md5sum": "9d7a20367bf681a239f14097bbce470a", + "filesize": 983629824, + "download_url": "https://software.cisco.com/download/" + }, { "filename": "nxosv-final.7.0.3.I7.2.qcow2", "version": "7.0.3.I7.2", @@ -71,6 +78,13 @@ } ], "versions": [ + { + "name": "7.0.3.I7.3", + "images": { + "bios_image": "OVMF-20160813.fd", + "hda_disk_image": "nxosv-final.7.0.3.I7.3.qcow2" + } + }, { "name": "7.0.3.I7.2", "images": { diff --git a/gns3server/appliances/citrix-netscaler-vpx.gns3a b/gns3server/appliances/citrix-netscaler-vpx.gns3a index 8c62df07..0fcd184a 100644 --- a/gns3server/appliances/citrix-netscaler-vpx.gns3a +++ b/gns3server/appliances/citrix-netscaler-vpx.gns3a @@ -26,6 +26,13 @@ "options": "-smp 2 -cpu host" }, "images": [ + { + "filename": "NSVPX-KVM-12.0-56.20_nc_32.qcow2", + "version": "12.0-56.20", + "md5sum": "0ea1c23e3b8eb8451037d46ee472cfa6", + "filesize": 739704832, + "download_url": "https://www.citrix.com/lp/try/netscaler-vpx-express.html" + }, { "filename": "NSVPX-KVM-11.1-47.14_nc.raw", "version": "11.1-47.14 F", @@ -42,6 +49,12 @@ } ], "versions": [ + { + "name": "12.0-56.20", + "images": { + "hda_disk_image": "NSVPX-KVM-12.0-56.20_nc_32.qcow2" + } + }, { "name": "11.1-47.14 F", "images": { diff --git a/gns3server/appliances/juniper-vsrx.gns3a b/gns3server/appliances/juniper-vsrx.gns3a index 002b9bd9..dfe2cd06 100644 --- a/gns3server/appliances/juniper-vsrx.gns3a +++ b/gns3server/appliances/juniper-vsrx.gns3a @@ -12,6 +12,7 @@ "maintainer": "GNS3 Team", "maintainer_email": "developers@gns3.net", "usage": "Initial username is root, no password.", + "first_port_name": "fxp0", "port_name_format": "ge-0/0/{0}", "qemu": { "adapter_type": "e1000", diff --git a/gns3server/appliances/mikrotik-chr.gns3a b/gns3server/appliances/mikrotik-chr.gns3a index 559b6bd0..fefae16a 100644 --- a/gns3server/appliances/mikrotik-chr.gns3a +++ b/gns3server/appliances/mikrotik-chr.gns3a @@ -26,6 +26,33 @@ "options": "-nographic" }, "images": [ + { + "filename": "chr-6.42.img", + "version": "6.42", + "md5sum": "279bb518497b40f41c8585128916a2fb", + "filesize": 134217728, + "download_url": "http://www.mikrotik.com/download", + "direct_download_url": "https://download2.mikrotik.com/routeros/6.42/chr-6.42.img.zip", + "compression": "zip" + }, + { + "filename": "chr-6.41.4.img", + "version": "6.41.4", + "md5sum": "63b555b2b7f0d78b79edb92f7e7d2ed7", + "filesize": 134217728, + "download_url": "http://www.mikrotik.com/download", + "direct_download_url": "https://download2.mikrotik.com/routeros/6.41.4/chr-6.41.4.img.zip", + "compression": "zip" + }, + { + "filename": "chr-6.40.7.img", + "version": "6.40.7", + "md5sum": "424b897d631c4cac4324ca310e81b494", + "filesize": 134217728, + "download_url": "http://www.mikrotik.com/download", + "direct_download_url": "https://download2.mikrotik.com/routeros/6.40.7/chr-6.40.7.img.zip", + "compression": "zip" + }, { "filename": "chr-6.40.5.img", "version": "6.40.5", @@ -235,6 +262,18 @@ } ], "versions": [ + { + "name": "6.41.4", + "images": { + "hda_disk_image": "chr-6.41.4.img" + } + }, + { + "name": "6.40.7", + "images": { + "hda_disk_image": "chr-6.40.7.img" + } + }, { "name": "6.40.5", "images": { diff --git a/gns3server/appliances/pan-vm-fw.gns3a b/gns3server/appliances/pan-vm-fw.gns3a index 9a196132..dc6fd6df 100644 --- a/gns3server/appliances/pan-vm-fw.gns3a +++ b/gns3server/appliances/pan-vm-fw.gns3a @@ -27,17 +27,31 @@ }, "images": [ { - "filename": "PA-VM-ESX-6.1.0-disk1.vmdk", - "version": "6.1.0 (ESX)", - "md5sum": "64b1e81cd54008318235832ea6d71424", - "filesize": 2959736832, + "filename": "PA-VM-ESX-8.1.0-disk1.vmdk", + "version": "8.1.0", + "md5sum": "49af8e8225c2e90414bde0be15eaf421", + "filesize": 2281454080, "download_url": "https://support.paloaltonetworks.com/Updates/SoftwareUpdates/" }, { - "filename": "PA-VM-KVM-7.1.0.qcow2", - "version": "7.1.0", - "md5sum": "da300253709740068927408239c2e321", - "filesize": 1858797568, + "filename": "PA-VM-KVM-8.1.0.qcow2", + "version": "8.1.0", + "md5sum": "459558515b965b2e43fde2842abbae66", + "filesize": 2260467712, + "download_url": "https://support.paloaltonetworks.com/Updates/SoftwareUpdates/" + }, + { + "filename": "PA-VM-ESX-8.0.0-disk1.vmdk", + "version": "8.0.0", + "md5sum": "a505fb1dbcc855ecf98630fd5d329f9a", + "filesize": 2002713088, + "download_url": "https://support.paloaltonetworks.com/Updates/SoftwareUpdates/" + }, + { + "filename": "PA-VM-KVM-8.0.0.qcow2", + "version": "8.0.0", + "md5sum": "b6a1ddc8552aff87f05f9c0d4cb54dc3", + "filesize": 1987444736, "download_url": "https://support.paloaltonetworks.com/Updates/SoftwareUpdates/" }, { @@ -48,31 +62,37 @@ "download_url": "https://support.paloaltonetworks.com/Updates/SoftwareUpdates/" }, { - "filename": "PA-VM-KVM-8.0.0.qcow2", - "version": "8.0.0", - "md5sum": "b6a1ddc8552aff87f05f9c0d4cb54dc3", - "filesize": 1987444736, + "filename": "PA-VM-KVM-7.1.0.qcow2", + "version": "7.1.0", + "md5sum": "da300253709740068927408239c2e321", + "filesize": 1858797568, "download_url": "https://support.paloaltonetworks.com/Updates/SoftwareUpdates/" - } - + }, + { + "filename": "PA-VM-ESX-6.1.0-disk1.vmdk", + "version": "6.1.0 (ESX)", + "md5sum": "64b1e81cd54008318235832ea6d71424", + "filesize": 2959736832, + "download_url": "https://support.paloaltonetworks.com/Updates/SoftwareUpdates/" + } ], "versions": [ { - "name": "6.1.0 (ESX)", + "name": "8.1.0 (ESX)", "images": { - "hda_disk_image": "PA-VM-ESX-6.1.0-disk1.vmdk" + "hda_disk_image": "PA-VM-ESX-8.1.0-disk1.vmdk" } - }, + }, { - "name": "7.1.0", + "name": "8.1.0", "images": { - "hda_disk_image": "PA-VM-KVM-7.1.0.qcow2" + "hda_disk_image": "PA-VM-KVM-8.1.0.qcow2" } - }, + }, { - "name": "7.1.0 (ESX)", + "name": "8.0.0 (ESX)", "images": { - "hda_disk_image": "PA-VM-ESX-7.1.0-disk1.vmdk" + "hda_disk_image": "PA-VM-ESX-8.0.0-disk1.vmdk2" } }, { @@ -80,6 +100,24 @@ "images": { "hda_disk_image": "PA-VM-KVM-8.0.0.qcow2" } - } + }, + { + "name": "7.1.0 (ESX)", + "images": { + "hda_disk_image": "PA-VM-ESX-7.1.0-disk1.vmdk" + } + }, + { + "name": "7.1.0", + "images": { + "hda_disk_image": "PA-VM-KVM-7.1.0.qcow2" + } + }, + { + "name": "6.1.0 (ESX)", + "images": { + "hda_disk_image": "PA-VM-ESX-6.1.0-disk1.vmdk" + } + } ] } diff --git a/gns3server/appliances/ubuntu-cloud.gns3a b/gns3server/appliances/ubuntu-cloud.gns3a index e8bb8040..d2766b07 100644 --- a/gns3server/appliances/ubuntu-cloud.gns3a +++ b/gns3server/appliances/ubuntu-cloud.gns3a @@ -28,37 +28,30 @@ { "filename": "ubuntu-17.10-server-cloudimg-amd64.img", "version": "17.10", - "md5sum": "5d221878d8b2e49c5de7ebb58a2b35e3", - "filesize": 318373888, - "download_url": "https://cloud-images.ubuntu.com/releases/17.10/release/" - }, - { - "filename": "ubuntu-17.04-server-cloudimg-amd64.img", - "version": "17.04", - "md5sum": "d4da8157dbf2e64f2fa1fb5d121398e5", - "filesize": 351993856, - "download_url": "https://cloud-images.ubuntu.com/releases/17.04/release/" + "md5sum": "331b44f2b05858c251b3ea92c8b65152", + "filesize": 320405504, + "download_url": "https://cloud-images.ubuntu.com/releases/17.10/release-20180404/ubuntu-17.10-server-cloudimg-amd64.img" }, { "filename": "ubuntu-16.04-server-cloudimg-amd64-disk1.img", - "version": "16.04.3", - "md5sum": "bd0c168a83b1f483bd240b3d874edd6c", - "filesize": 288686080, - "download_url": "https://cloud-images.ubuntu.com/releases/16.04/release/" + "version": "16.04", + "md5sum": "22c124ba65ea096cdef8b0a197dd613a", + "filesize": 290193408, + "download_url": "https://cloud-images.ubuntu.com/releases/16.04/release-20180405/ubuntu-16.04-server-cloudimg-amd64-disk1.img" }, { "filename": "ubuntu-14.04-server-cloudimg-amd64-disk1.img", - "version": "14.04.5", - "md5sum": "d7b4112c7d797e5e77ef9995d06a76f1", - "filesize": 262406656, - "download_url": "https://cloud-images.ubuntu.com/releases/14.04/release/" + "version": "14.04", + "md5sum": "d11b89321d41d0eeddcacf73bf0d2262", + "filesize": 262668800, + "download_url": "https://cloud-images.ubuntu.com/releases/14.04/release-20180404/ubuntu-14.04-server-cloudimg-amd64-disk1.img" }, { "filename": "ubuntu-cloud-init-data.iso", "version": "1.0", "md5sum": "328469100156ae8dbf262daa319c27ff", "filesize": 131072, - "download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/ubuntu-cloud-init-data.iso/download" + "download_url": "https://github.com/asenci/gns3-ubuntu-cloud-init-data/raw/master/ubuntu-cloud-init-data.iso" } ], "versions": [ @@ -69,13 +62,6 @@ "cdrom_image": "ubuntu-cloud-init-data.iso" } }, - { - "name": "17.04", - "images": { - "hda_disk_image": "ubuntu-17.04-server-cloudimg-amd64.img", - "cdrom_image": "ubuntu-cloud-init-data.iso" - } - }, { "name": "16.04 (LTS)", "images": { diff --git a/gns3server/appliances/vyos.gns3a b/gns3server/appliances/vyos.gns3a index fc68dedd..7e3cd601 100644 --- a/gns3server/appliances/vyos.gns3a +++ b/gns3server/appliances/vyos.gns3a @@ -80,6 +80,13 @@ "cdrom_image": "vyos-1.2.0-beta1-amd64.iso" } }, + { + "name": "1.1.8", + "images": { + "hda_disk_image": "empty8G.qcow2", + "cdrom_image": "vyos-1.1.8-amd64.iso" + } + }, { "name": "1.1.7", "images": { diff --git a/gns3server/compute/base_node.py b/gns3server/compute/base_node.py index 2fd7802a..ff995a4e 100644 --- a/gns3server/compute/base_node.py +++ b/gns3server/compute/base_node.py @@ -360,6 +360,7 @@ class BaseNode: remaining_trial -= 1 yield from AsyncioTelnetServer.write_client_intro(writer, echo=True) server = AsyncioTelnetServer(reader=reader, writer=writer, binary=True, echo=True) + # warning: this will raise OSError exception if there is a problem... self._wrapper_telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console) @asyncio.coroutine @@ -544,7 +545,7 @@ class BaseNode: try: yield from self._ubridge_hypervisor.send(command) except UbridgeError as e: - raise UbridgeError("{}: {}".format(e, self._ubridge_hypervisor.read_stdout())) + raise UbridgeError("Error while sending command '{}': {}: {}".format(command, e, self._ubridge_hypervisor.read_stdout())) @locked_coroutine def _start_ubridge(self): diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 28a50f36..e8bc3033 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -421,7 +421,10 @@ class DockerVM(BaseNode): stderr=asyncio.subprocess.STDOUT, stdin=asyncio.subprocess.PIPE) server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=True, echo=True) - self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux))) + try: + self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux))) + except OSError as e: + raise DockerError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.aux, e)) log.debug("Docker container '%s' started listen for auxilary telnet on %d", self.name, self.aux) @asyncio.coroutine @@ -518,7 +521,10 @@ class DockerVM(BaseNode): input_stream = InputStream() telnet = AsyncioTelnetServer(reader=output_stream, writer=input_stream, echo=True) - self._telnet_servers.append((yield from asyncio.start_server(telnet.run, self._manager.port_manager.console_host, self.console))) + try: + self._telnet_servers.append((yield from asyncio.start_server(telnet.run, self._manager.port_manager.console_host, self.console))) + except OSError as e: + raise DockerError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)) self._console_websocket = yield from self.manager.websocket_query("containers/{}/attach/ws?stream=1&stdin=1&stdout=1&stderr=1".format(self._cid)) input_stream.ws = self._console_websocket diff --git a/gns3server/compute/dynamips/nodes/ethernet_switch.py b/gns3server/compute/dynamips/nodes/ethernet_switch.py index db40d443..ee761ba2 100644 --- a/gns3server/compute/dynamips/nodes/ethernet_switch.py +++ b/gns3server/compute/dynamips/nodes/ethernet_switch.py @@ -182,8 +182,10 @@ class EthernetSwitch(Device): self._telnet_shell = EthernetSwitchConsole(self) self._telnet_shell.prompt = self._name + '> ' self._telnet = create_telnet_shell(self._telnet_shell) - self._telnet_server = (yield from asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console)) - + try: + self._telnet_server = (yield from asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console)) + 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._hypervisor.devices.append(self) @asyncio.coroutine diff --git a/gns3server/compute/dynamips/nodes/router.py b/gns3server/compute/dynamips/nodes/router.py index 2fb25566..16572173 100644 --- a/gns3server/compute/dynamips/nodes/router.py +++ b/gns3server/compute/dynamips/nodes/router.py @@ -129,14 +129,16 @@ class Router(BaseNode): try: shutil.move(path, dst) except OSError as e: - raise DynamipsError("Can't move {}: {}".format(path, str(e))) + log.error("Can't move {}: {}".format(path, str(e))) + continue for path in glob.glob(os.path.join(glob.escape(dynamips_dir), "*_i{}_*".format(dynamips_id))): dst = os.path.join(self._working_directory, os.path.basename(path)) if not os.path.exists(dst): try: shutil.move(path, dst) except OSError as e: - raise DynamipsError("Can't move {}: {}".format(path, str(e))) + log.error("Can't move {}: {}".format(path, str(e))) + continue def __json__(self): diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index 78b34fbb..f396be87 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -543,7 +543,11 @@ class IOUVM(BaseNode): if self.console and self.console_type == "telnet": server = AsyncioTelnetServer(reader=self._iou_process.stdout, writer=self._iou_process.stdin, binary=True, echo=True) - self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console) + try: + self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console) + except OSError as e: + yield from self.stop() + raise IOUError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)) # configure networking support yield from self._networking() diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index ac5b2958..303141af 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -539,7 +539,7 @@ class QemuVM(BaseNode): if not mac_address: # use the node UUID to generate a random MAC address - 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:]) + self._mac_address = "0c:%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 @@ -918,6 +918,7 @@ class QemuVM(BaseNode): af, socktype, proto, _, sa = res # let the OS find an unused port for the Qemu monitor with socket.socket(af, socktype, proto) as sock: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(sa) self._monitor = sock.getsockname()[1] except OSError as e: diff --git a/gns3server/compute/traceng/traceng_vm.py b/gns3server/compute/traceng/traceng_vm.py index cdd97741..6539b804 100644 --- a/gns3server/compute/traceng/traceng_vm.py +++ b/gns3server/compute/traceng/traceng_vm.py @@ -59,6 +59,7 @@ class TraceNGVM(BaseNode): self._process = None self._started = False self._ip_address = None + self._default_destination = None self._destination = None self._local_udp_tunnel = None self._ethernet_adapter = EthernetAdapter() # one adapter with 1 Ethernet interface @@ -115,6 +116,7 @@ class TraceNGVM(BaseNode): return {"name": self.name, "ip_address": self.ip_address, + "default_destination": self._default_destination, "node_id": self.id, "node_directory": self.working_path, "status": self.status, @@ -167,6 +169,30 @@ class TraceNGVM(BaseNode): id=self.id, ip_address=ip_address)) + @property + def default_destination(self): + """ + Returns the default destination IP/host for this node. + + :returns: destination IP/host + """ + + return self._default_destination + + @default_destination.setter + def default_destination(self, destination): + """ + Sets the destination IP/host for this node. + + :param destination: destination IP/host + """ + + self._default_destination = destination + log.info("{module}: {name} [{id}] set default destination to {destination}".format(module=self.manager.module_name, + name=self.name, + id=self.id, + destination=destination)) + @asyncio.coroutine def start(self, destination=None): """ @@ -400,10 +426,15 @@ class TraceNGVM(BaseNode): (to be passed to subprocess.Popen()) """ + if not destination: + # use the default destination if no specific destination provided + destination = self.default_destination if not destination: raise TraceNGError("Please provide a host or IP address to trace") - if not self._ip_address: + if not self.ip_address: raise TraceNGError("Please configure an IP address for this TraceNG node") + if self.ip_address == destination: + raise TraceNGError("Destination cannot be the same as the IP address") self._destination = destination command = [self._traceng_path()] diff --git a/gns3server/compute/virtualbox/virtualbox_vm.py b/gns3server/compute/virtualbox/virtualbox_vm.py index 5d1a42f8..32645339 100644 --- a/gns3server/compute/virtualbox/virtualbox_vm.py +++ b/gns3server/compute/virtualbox/virtualbox_vm.py @@ -953,7 +953,10 @@ class VirtualBoxVM(BaseNode): writer=self._remote_pipe, binary=True, echo=True) - self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console) + try: + self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console) + 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)}) @asyncio.coroutine def _stop_remote_console(self): diff --git a/gns3server/compute/vmware/__init__.py b/gns3server/compute/vmware/__init__.py index 0ea5451d..d41a48be 100644 --- a/gns3server/compute/vmware/__init__.py +++ b/gns3server/compute/vmware/__init__.py @@ -254,7 +254,7 @@ class VMware(BaseManager): if winreg.QueryInfoKey(hkeyvmnet)[1]: # the vmnet has not been configure if the key has no values vmnet = vmnet.replace("vm", "VM") - if vmnet not in ("VMnet1", "VMnet8"): + if vmnet not in ("VMnet0", "VMnet1", "VMnet8"): vmnet_interfaces.append(vmnet) winreg.CloseKey(hkeyvmnet) winreg.CloseKey(hkey) @@ -279,7 +279,7 @@ class VMware(BaseManager): match = re.search("VNET_([0-9]+)_VIRTUAL_ADAPTER", line) if match: vmnet = "vmnet{}".format(match.group(1)) - if vmnet not in ("vmnet1", "vmnet8"): + if vmnet not in ("vmnet0", "vmnet1", "vmnet8"): vmnet_interfaces.append(vmnet) except OSError as e: raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e)) @@ -298,11 +298,11 @@ class VMware(BaseManager): match = re.search("(VMnet[0-9]+)", windows_name) if match: vmnet = match.group(1) - if vmnet not in ("VMnet1", "VMnet8"): + if vmnet not in ("VMnet0", "VMnet1", "VMnet8"): vmnet_interfaces.append(vmnet) elif interface["name"].startswith("vmnet"): vmnet = interface["name"] - if vmnet not in ("vmnet1", "vmnet8"): + if vmnet not in ("vmnet0", "vmnet1", "vmnet8"): vmnet_interfaces.append(interface["name"]) return vmnet_interfaces diff --git a/gns3server/compute/vmware/vmware_vm.py b/gns3server/compute/vmware/vmware_vm.py index a8d4a5a0..69b85816 100644 --- a/gns3server/compute/vmware/vmware_vm.py +++ b/gns3server/compute/vmware/vmware_vm.py @@ -279,6 +279,7 @@ class VMwareVM(BaseNode): continue self._vmx_pairs["ethernet{}.connectiontype".format(adapter_number)] = "custom" + # make sure we have a vmnet per adapter if we use uBridge allocate_vmnet = False @@ -287,7 +288,7 @@ class VMwareVM(BaseNode): if vnet in self._vmx_pairs: vmnet = os.path.basename(self._vmx_pairs[vnet]) if self.manager.is_managed_vmnet(vmnet) or vmnet in ("vmnet0", "vmnet1", "vmnet8"): - # vmnet already managed, try to allocate a new one + # vmnet already managed or a special vmnet, try to allocate a new one allocate_vmnet = True else: # otherwise allocate a new one @@ -301,7 +302,7 @@ class VMwareVM(BaseNode): self._vmnets.clear() raise - # mark the vmnet managed by us + # mark the vmnet as managed by us if vmnet not in self._vmnets: self._vmnets.append(vmnet) self._vmx_pairs["ethernet{}.vnet".format(adapter_number)] = vmnet @@ -740,17 +741,18 @@ class VMwareVM(BaseNode): if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"): # check for the connection type connection_type = "ethernet{}.connectiontype".format(adapter_number) - if connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"): - if not self._use_any_adapter: - raise VMwareError("Attachment '{attachment}' is already configured on network adapter {adapter_number}. " - "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 (yield from self.is_running()): + if not self._use_any_adapter and connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"): + if (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, name=self.name)) + else: + raise VMwareError("Attachment '{attachment}' is already configured on network adapter {adapter_number}. " + "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)) + adapter.add_nio(0, nio) if self._started and self._ubridge_hypervisor: @@ -847,8 +849,14 @@ class VMwareVM(BaseNode): if self.console and self.console_type == "telnet": self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name()) - server = AsyncioTelnetServer(reader=self._remote_pipe, writer=self._remote_pipe, binary=True, echo=True) - self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console) + server = AsyncioTelnetServer(reader=self._remote_pipe, + writer=self._remote_pipe, + binary=True, + echo=True) + try: + self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console) + 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)}) @asyncio.coroutine def _stop_remote_console(self): diff --git a/gns3server/controller/gns3vm/virtualbox_gns3_vm.py b/gns3server/controller/gns3vm/virtualbox_gns3_vm.py index cad08b71..f491e32f 100644 --- a/gns3server/controller/gns3vm/virtualbox_gns3_vm.py +++ b/gns3server/controller/gns3vm/virtualbox_gns3_vm.py @@ -191,6 +191,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM): try: # get a random port on localhost with socket.socket() as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((ip_address, 0)) api_port = s.getsockname()[1] except OSError as e: diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index ab370320..0834a615 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -68,7 +68,7 @@ class Project: def __init__(self, name=None, project_id=None, path=None, controller=None, status="opened", filename=None, auto_start=False, auto_open=False, auto_close=True, scene_height=1000, scene_width=2000, zoom=100, show_layers=False, snap_to_grid=False, show_grid=False, - show_interface_labels=False): + grid_size=0, show_interface_labels=False): self._controller = controller assert name is not None @@ -83,6 +83,7 @@ class Project: self._show_layers = show_layers self._snap_to_grid = snap_to_grid self._show_grid = show_grid + self._grid_size = grid_size self._show_interface_labels = show_interface_labels self._loading = False @@ -236,6 +237,21 @@ class Project: """ self._show_grid = show_grid + @property + def grid_size(self): + """ + Grid size + :return: integer + """ + return self._grid_size + + @grid_size.setter + def grid_size(self, grid_size): + """ + Setter for grid size + """ + self._grid_size = grid_size + @property def show_interface_labels(self): """ @@ -794,6 +810,7 @@ class Project: "show_layers", "snap_to_grid", "show_grid", + "grid_size", "show_interface_labels" ] @@ -939,12 +956,7 @@ class Project: Start all nodes """ pool = Pool(concurrency=3) - emit_warning = True for node in self.nodes.values(): - if node.node_type == "traceng" and emit_warning: - self.controller.notification.emit("log.warning", {"message": "TraceNG nodes must be started one by one"}) - emit_warning = False - continue pool.append(node.start) yield from pool.join() @@ -1043,6 +1055,7 @@ class Project: "show_layers": self._show_layers, "snap_to_grid": self._snap_to_grid, "show_grid": self._show_grid, + "grid_size": self._grid_size, "show_interface_labels": self._show_interface_labels } diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py index 85a9fcbc..0ba50859 100644 --- a/gns3server/controller/topology.py +++ b/gns3server/controller/topology.py @@ -83,6 +83,7 @@ def project_to_topology(project): "show_layers": project.show_layers, "snap_to_grid": project.snap_to_grid, "show_grid": project.show_grid, + "grid_size": project.grid_size, "show_interface_labels": project.show_interface_labels, "topology": { "nodes": [], diff --git a/gns3server/handlers/api/compute/traceng_handler.py b/gns3server/handlers/api/compute/traceng_handler.py index 188cde5f..fc5d21c8 100644 --- a/gns3server/handlers/api/compute/traceng_handler.py +++ b/gns3server/handlers/api/compute/traceng_handler.py @@ -55,7 +55,8 @@ class TraceNGHandler: request.match_info["project_id"], request.json.get("node_id"), console=request.json.get("console")) - vm.ip_address = request.json.get("ip_address", "") # FIXME, required IP address to create node? + vm.ip_address = request.json.get("ip_address", "") + vm.default_destination = request.json.get("default_destination", "") response.set_status(201) response.json(vm) @@ -99,6 +100,7 @@ class TraceNGHandler: vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) vm.name = request.json.get("name", vm.name) vm.ip_address = request.json.get("ip_address", vm.ip_address) + vm.default_destination = request.json.get("default_destination", vm.default_destination) vm.updated() response.json(vm) @@ -157,7 +159,7 @@ class TraceNGHandler: traceng_manager = TraceNG.instance() vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - yield from vm.start(request.json["destination"]) + yield from vm.start(request.get("destination")) response.json(vm) @Route.post( diff --git a/gns3server/schemas/project.py b/gns3server/schemas/project.py index eb008389..242f5e5b 100644 --- a/gns3server/schemas/project.py +++ b/gns3server/schemas/project.py @@ -66,6 +66,10 @@ PROJECT_CREATE_SCHEMA = { "type": "boolean", "description": "Show the grid on the drawing area" }, + "grid_size": { + "type": "integer", + "description": "Grid size for the drawing area" + }, "show_interface_labels": { "type": "boolean", "description": "Show interface labels on the drawing area" @@ -125,6 +129,10 @@ PROJECT_UPDATE_SCHEMA = { "type": "boolean", "description": "Show the grid on the drawing area" }, + "grid_size": { + "type": "integer", + "description": "Grid size for the drawing area" + }, "show_interface_labels": { "type": "boolean", "description": "Show interface labels on the drawing area" @@ -200,6 +208,10 @@ PROJECT_OBJECT_SCHEMA = { "type": "boolean", "description": "Show the grid on the drawing area" }, + "grid_size": { + "type": "integer", + "description": "Grid size for the drawing area" + }, "show_interface_labels": { "type": "boolean", "description": "Show interface labels on the drawing area" diff --git a/gns3server/schemas/topology.py b/gns3server/schemas/topology.py index bf9fc1cf..faadf81b 100644 --- a/gns3server/schemas/topology.py +++ b/gns3server/schemas/topology.py @@ -89,6 +89,10 @@ TOPOLOGY_SCHEMA = { "type": "boolean", "description": "Show the grid on the drawing area" }, + "grid_size": { + "type": "integer", + "description": "Grid size for the drawing area" + }, "show_interface_labels": { "type": "boolean", "description": "Show interface labels on the drawing area" diff --git a/gns3server/schemas/traceng.py b/gns3server/schemas/traceng.py index 6c0f63cc..d95fd042 100644 --- a/gns3server/schemas/traceng.py +++ b/gns3server/schemas/traceng.py @@ -48,6 +48,10 @@ TRACENG_CREATE_SCHEMA = { "ip_address": { "description": "Source IP address for tracing", "type": ["string"] + }, + "default_destination": { + "description": "Default destination IP address or hostname for tracing", + "type": ["string"] } }, "additionalProperties": False, @@ -77,6 +81,10 @@ TRACENG_UPDATE_SCHEMA = { "ip_address": { "description": "Source IP address for tracing", "type": ["string"] + }, + "default_destination": { + "description": "Default destination IP address or hostname for tracing", + "type": ["string"] } }, "additionalProperties": False, @@ -92,7 +100,6 @@ TRACENG_START_SCHEMA = { "type": ["string"] } }, - "required": ["destination"], } TRACENG_OBJECT_SCHEMA = { @@ -144,8 +151,12 @@ TRACENG_OBJECT_SCHEMA = { "ip_address": { "description": "Source IP address for tracing", "type": ["string"] + }, + "default_destination": { + "description": "Default destination IP address or hostname for tracing", + "type": ["string"] } }, "additionalProperties": False, - "required": ["name", "node_id", "status", "console", "console_type", "project_id", "command_line", "ip_address"] + "required": ["name", "node_id", "status", "console", "console_type", "project_id", "command_line", "ip_address", "default_destination"] } diff --git a/gns3server/ubridge/hypervisor.py b/gns3server/ubridge/hypervisor.py index 6baee153..0ffbbcd0 100644 --- a/gns3server/ubridge/hypervisor.py +++ b/gns3server/ubridge/hypervisor.py @@ -62,6 +62,7 @@ class Hypervisor(UBridgeHypervisor): af, socktype, proto, _, sa = res # let the OS find an unused port for the uBridge hypervisor with socket.socket(af, socktype, proto) as sock: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(sa) port = sock.getsockname()[1] break diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index e0a167a6..927b420b 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -75,7 +75,8 @@ def test_json(tmpdir): "show_grid": False, "show_interface_labels": False, "show_layers": False, - "snap_to_grid": False + "snap_to_grid": False, + "grid_size": 0, } diff --git a/tests/controller/test_topology.py b/tests/controller/test_topology.py index 01dbf245..b5d4b12f 100644 --- a/tests/controller/test_topology.py +++ b/tests/controller/test_topology.py @@ -45,6 +45,7 @@ def test_project_to_topology_empty(tmpdir): "show_interface_labels": False, "show_layers": False, "snap_to_grid": False, + "grid_size": 0, "topology": { "nodes": [], "links": [],